// Copyright (c) 2015-2019 The LBRY Foundation
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://opensource.org/licenses/mit-license.php

#include <test/claimtriefixture.h>
#include <validation.h>

using namespace std;

BOOST_FIXTURE_TEST_SUITE(claimtriebranching_tests, RegTestingSetup)

BOOST_AUTO_TEST_CASE(claim_replace_test) {
    // no competing bids
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "bass", "one", 1);
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "basso", "two", 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("bass", tx1));
    fixture.Spend(tx1);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "bassfisher", "one", 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.checkConsistency());
    BOOST_CHECK(!fixture.is_best_claim("bass", tx1));
    BOOST_CHECK(fixture.is_best_claim("bassfisher", tx2));
}

BOOST_AUTO_TEST_CASE(takeover_stability_test) {
    // no competing bids
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "one", 1);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "two", 2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("@bass", tx2));
    uint160 id; int takeover;
    BOOST_REQUIRE(fixture.getLastTakeoverForName("@bass", id, takeover));
    auto height = chainActive.Tip()->nHeight;
    BOOST_CHECK_EQUAL(takeover, height);
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "three", 3);
    fixture.Spend(tx3);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("@bass", tx2));
    BOOST_REQUIRE(fixture.getLastTakeoverForName("@bass", id, takeover));
    BOOST_CHECK_EQUAL(takeover, height);
}

BOOST_AUTO_TEST_CASE(unaffected_children_get_new_parents_test) {
    // this happens first on block 193976
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "longest123", "one", 1);
    fixture.IncrementBlocks(1);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "longest", "two", 2);
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "longest1", "three", 3);
    CMutableTransaction tx4 = fixture.MakeClaim(tx3, "longest1", "three1234", 2);
    fixture.IncrementBlocks(1);
    auto n1 = fixture.getNodeChildren("longest");
    auto n2 = fixture.getNodeChildren("longest1");
    BOOST_CHECK_EQUAL(n1[0], "longest1");
    BOOST_CHECK_EQUAL(n2[0], "longest123");
    // TODO: this test at present fails to cover the split node case of the same thing (which occurs on block 202577)
}

/*
    claims
        no competing bids
        there is a competing bid inserted same height
            check the greater one wins
                - quantity is same, check outpoint greater wins
        there is an existing competing bid
            check that rules for delays are observed
            check that a greater amount wins
            check that a smaller amount does not win

*/
BOOST_AUTO_TEST_CASE(claim_test)
{
    // no competing bids
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx1));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test",tx1));

    // there is a competing bid inserted same height
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",1);
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx3));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());

    fixture.DecrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test",tx2));
    BOOST_CHECK(!fixture.is_best_claim("test",tx3));
    BOOST_CHECK_EQUAL(0U, fixture.getClaimsForName("test").claimsNsupports.size());

    // make two claims , one older
    CMutableTransaction tx4 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx4));
    CMutableTransaction tx5 = fixture.MakeClaim(fixture.GetCoinbase(),"test","two",1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_claim_in_queue("test",tx5));
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx4));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    BOOST_CHECK(fixture.is_claim_in_queue("test",tx5));
    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    fixture.DecrementBlocks(1);

    // check claim takeover, note that CClaimTrie.nProportionalDelayFactor is set to 1
    // instead of 32 in test_bitcoin.cpp
    CMutableTransaction tx6 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",1);
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test",tx6));
    CMutableTransaction tx7 = fixture.MakeClaim(fixture.GetCoinbase(),"test","two",2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_claim_in_queue("test",tx7));
    BOOST_CHECK(fixture.is_best_claim("test", tx6));
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test",tx7));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());

    fixture.DecrementBlocks(10);
    BOOST_CHECK(fixture.is_claim_in_queue("test",tx7));
    BOOST_CHECK(fixture.is_best_claim("test", tx6));
    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx6));
    fixture.DecrementBlocks(10);
}

/*
  Testing deferred claim activation via a tx with a locktime.
*/
BOOST_AUTO_TEST_CASE(claim_locktime_test)
{
    ClaimTrieChainFixture fixture;
    // Effectively disable expiration for this test.
    fixture.setExpirationForkHeight(1, 1, 10000);
    fixture.IncrementBlocks(1);

    // Create tx1 with a relative locktime for validity 10 blocks in
    // the future, staged for automatic takeover if accepted.
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2, 10);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test", tx1));
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx2));

    // Forward to locktime expiration and takeover delay time.
    fixture.IncrementBlocks(25);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));

    // Abandon/Spend tx1.
    fixture.Spend(tx1);
    fixture.IncrementBlocks(1);

    // Ensure tx2 is now best.
    BOOST_CHECK(fixture.is_best_claim("test", tx2));

    // Rewind and check tx1 is best again.
    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));

    // Rewind to before locktime and activation.
    fixture.DecrementBlocks(25);
    BOOST_CHECK(fixture.is_best_claim("test", tx2));
}

/*
    spent claims
        spending winning claim will make losing active claim winner
        spending winning claim will make inactive claim winner
        spending winning claim will empty out claim trie
*/
BOOST_AUTO_TEST_CASE(spend_claim_test)
{
    ClaimTrieChainFixture fixture;

    // spending winning claim will make losing active claim winner
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));
    fixture.Spend(tx1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx2));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));
    fixture.DecrementBlocks(1);

    // spending winning claim will make inactive claim winner
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test",tx3));
    CMutableTransaction tx4 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx3));
    fixture.Spend(tx3);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx4));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx3));
    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx3));
    fixture.DecrementBlocks(10);


    //spending winning claim will empty out claim trie
    CMutableTransaction tx5 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx5));
    fixture.Spend(tx5);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test",tx5));
    BOOST_CHECK(pclaimTrie->empty());

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx5));
    fixture.DecrementBlocks(1);
}

/*
    supports
        check support with wrong name does not work
        check claim with more support wins
        check support delay
*/
BOOST_AUTO_TEST_CASE(support_test)
{
    ClaimTrieChainFixture fixture;
    // check claim with more support wins
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, "test", 1);
    CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(), tx2, "test", 10);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx2));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 11));
    fixture.DecrementBlocks(1);

    // check support delay
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    CMutableTransaction tx4 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "two", 2);
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 2));
    CMutableTransaction s4 = fixture.MakeSupport(fixture.GetCoinbase(), tx3, "test", 10); //10 delay
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 2));
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx3));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 11));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 2));
    fixture.DecrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test", 2));
    fixture.DecrementBlocks(10);
}
/*
    support on abandon
        supporting a claim the same block it gets abandoned,
        or abandoning a support the same block claim gets abandoned,
        when there were no other claims would crash lbrycrd,
        make sure this doesn't happen in below three tests
        (https://github.com/lbryio/lbrycrd/issues/77)
*/
BOOST_AUTO_TEST_CASE(support_on_abandon_test)
{
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",2);
    fixture.IncrementBlocks(1);

    //supporting and abandoning on the same block will cause it to crash
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",1);
    fixture.Spend(tx1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test",tx1));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx1));
}

BOOST_AUTO_TEST_CASE(support_on_abandon_2_test)
{
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",2);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",1);
    fixture.IncrementBlocks(1);

    //abandoning a support and abandoning claim on the same block will cause it to crash
    fixture.Spend(tx1);
    fixture.Spend(s1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test",tx1));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx1));
}

BOOST_AUTO_TEST_CASE(support_on_abandon_3_test)
{
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",2);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",1);
    CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",1);
    fixture.IncrementBlocks(1);

    auto supports = fixture.getSupportsForName("test");
    BOOST_CHECK_EQUAL(supports.size(), 2);

    fixture.Spend(tx1);
    fixture.Spend(s1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.is_best_claim("test", tx1));
    supports = fixture.getSupportsForName("test");
    BOOST_CHECK_EQUAL(supports.size(), 1);

    fixture.Spend(s2);
    fixture.IncrementBlocks(1);
    supports = fixture.getSupportsForName("test");
    BOOST_CHECK(supports.empty());

    fixture.DecrementBlocks(2);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));
    supports = fixture.getSupportsForName("test");
    BOOST_CHECK_EQUAL(supports.size(), 2);

    fixture.DecrementBlocks(1);

    supports = fixture.getSupportsForName("test");
    BOOST_CHECK(supports.empty());
}

BOOST_AUTO_TEST_CASE(update_on_support_test)
{
    // make sure that support on abandon bug does not affect
    // updates happening on the same block as a support
    // (it should not)
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",3);
    fixture.IncrementBlocks(1);

    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",1);
    CMutableTransaction u1 = fixture.MakeUpdate(tx1,"test","two",ClaimIdHash(tx1.GetHash(),0),1);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(fixture.is_best_claim("test",u1));
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test",2));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));
}

/*
    support spend
        spending suport on winning claim will cause it to lose

        spending a support on txin[i] where i is not 0
*/
BOOST_AUTO_TEST_CASE(support_spend_test)
{
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",1);
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(),"test","one",2);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(),tx1,"test",2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx1));
    CMutableTransaction sp1 = fixture.Spend(s1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx2));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx1));
    fixture.DecrementBlocks(1);

    // spend a support on txin[i] where i is not 0
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(),"x","one",3);
    CMutableTransaction tx4 = fixture.MakeClaim(fixture.GetCoinbase(),"test","two",2);
    CMutableTransaction tx5 = fixture.MakeClaim(fixture.GetCoinbase(),"test","three",1);
    CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(),tx5,"test",2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx5));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());

    // build the spend where s2 is sppent on txin[1] and tx3 is  spent on txin[0]
    uint32_t prevout = 0;
    CMutableTransaction tx;
    tx.nVersion = CTransaction::CURRENT_VERSION;
    tx.nLockTime = 1U << 31; // Disable BIP68
    tx.vin.resize(2);
    tx.vout.resize(1);
    tx.vin[0].prevout.hash = tx3.GetHash();
    tx.vin[0].prevout.n = prevout;
    tx.vin[0].scriptSig = CScript();
    tx.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
    tx.vin[1].prevout.hash = s2.GetHash();
    tx.vin[1].prevout.n = prevout;
    tx.vin[1].scriptSig = CScript();
    tx.vin[1].nSequence = std::numeric_limits<unsigned int>::max();
    tx.vout[0].scriptPubKey = CScript() << OP_TRUE;
    tx.vout[0].nValue = 1;

    fixture.CommitTx(tx);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(fixture.is_best_claim("test", tx4));
    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx5));
}

BOOST_AUTO_TEST_CASE(claimtrie_update_takeover_test)
{
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 5);
    auto cid = ClaimIdHash(tx1.GetHash(), 0);
    fixture.IncrementBlocks(1);
    uint160 cid2;
    int takeover;
    int height = chainActive.Tip()->nHeight;
    fixture.getLastTakeoverForName("test", cid2, takeover);
    BOOST_CHECK_EQUAL(height, takeover);
    CMutableTransaction u1 = fixture.MakeUpdate(tx1, "test", "a", cid, 4);
    fixture.IncrementBlocks(1);
    fixture.getLastTakeoverForName("test", cid2, takeover);
    BOOST_CHECK_EQUAL(height, takeover);
    CMutableTransaction u2 = fixture.MakeUpdate(u1, "test", "b", cid, 3);
    fixture.IncrementBlocks(1);
    fixture.getLastTakeoverForName("test", cid2, takeover);
    CClaimValue value;
    BOOST_REQUIRE(fixture.getInfoForName("test", value) && value.nAmount == 3);
    BOOST_CHECK_EQUAL(cid, uint160(cid2));
    BOOST_CHECK_EQUAL(height, takeover);
    fixture.DecrementBlocks(1);
    fixture.getLastTakeoverForName("test", cid2, takeover);
    BOOST_CHECK_EQUAL(cid, uint160(cid2));
    BOOST_CHECK_EQUAL(height, takeover);
    fixture.DecrementBlocks(1);
    fixture.getLastTakeoverForName("test", cid2, takeover);
    BOOST_CHECK_EQUAL(cid, uint160(cid2));
    BOOST_CHECK_EQUAL(height, takeover);
}

/*
    update
        update preserves claim id
        update preserves supports
        winning update on winning claim happens without delay
        losing update on winning claim happens without delay
        update on losing claim happens with delay , and wins


*/
BOOST_AUTO_TEST_CASE(claimtrie_update_test)
{
    //update preserves claim id
    ClaimTrieChainFixture fixture;
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    CMutableTransaction u1 = fixture.MakeUpdate(tx1, "test", "one", ClaimIdHash(tx1.GetHash(), 0), 2);
    fixture.IncrementBlocks(1);
    CClaimValue val;
    fixture.getInfoForName("test",val);
    BOOST_CHECK_EQUAL(val.claimId, ClaimIdHash(tx1.GetHash(),0));
    BOOST_CHECK(fixture.is_best_claim("test",u1));
    fixture.DecrementBlocks(1);

    // update preserves supports
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(), tx2, "test", 1);
    CMutableTransaction u2 = fixture.MakeUpdate(tx2, "test", "one", ClaimIdHash(tx2.GetHash(), 0), 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.best_claim_effective_amount_equals("test",2));
    fixture.DecrementBlocks(1);

    // winning update on winning claim happens without delay
    CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    CMutableTransaction tx4 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 1);
    fixture.IncrementBlocks(10);
    CMutableTransaction u3 = fixture.MakeUpdate(tx3, "test", "one", ClaimIdHash(tx3.GetHash(), 0), 2);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",u3));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());
    fixture.DecrementBlocks(11);

    // losing update on winning claim happens without delay
    CMutableTransaction tx5 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 3);
    CMutableTransaction tx6 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test", tx5));
    BOOST_CHECK_EQUAL(2U, fixture.getClaimsForName("test").claimsNsupports.size());
    CMutableTransaction u4 = fixture.MakeUpdate(tx5, "test", "one", ClaimIdHash(tx5.GetHash(), 0), 1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx6));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test", tx5));
    fixture.DecrementBlocks(10);

    // update on losing claim happens with delay , and wins
    CMutableTransaction tx7 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 3);
    CMutableTransaction tx8 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test", tx7));

    CMutableTransaction tx;
    tx.nVersion = CTransaction::CURRENT_VERSION;
    tx.nLockTime = 1U << 31U; // Disable BIP68
    tx.vin.resize(2);
    tx.vout.resize(1);
    tx.vin[0].prevout.hash = tx8.GetHash();
    tx.vin[0].prevout.n = 0;
    tx.vin[0].scriptSig = CScript();
    tx.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
    tx.vin[1].prevout.hash = fixture.GetCoinbase().GetHash();
    tx.vin[1].prevout.n = 0;
    tx.vin[1].scriptSig = CScript();
    tx.vin[1].nSequence = std::numeric_limits<unsigned int>::max();
    tx.vout[0].scriptPubKey = UpdateClaimScript("test",ClaimIdHash(tx8.GetHash(),0),"one");
    tx.vout[0].nValue = 4;
    fixture.CommitTx(tx);

    fixture.IncrementBlocks(1);
    BOOST_CHECK(fixture.is_best_claim("test",tx7));
    fixture.IncrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test",tx));

    fixture.DecrementBlocks(10);
    BOOST_CHECK(fixture.is_best_claim("test",tx7));
    fixture.DecrementBlocks(11);
}

/*
 * tests for effectiveAmount
 */
BOOST_AUTO_TEST_CASE(get_effective_amount_for_claim)
{
    // simplest scenario. One claim, no supports
    ClaimTrieChainFixture fixture;
    CMutableTransaction claimtx = fixture.MakeClaim(fixture.GetCoinbase(), "test", "one", 2);
    uint160 claimId = ClaimIdHash(claimtx.GetHash(), 0);
    fixture.IncrementBlocks(1);

    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId).effectiveAmount, 2);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("inexistent").find(claimId).effectiveAmount, 0); //not found returns 0

    // one claim, one support
    fixture.MakeSupport(fixture.GetCoinbase(), claimtx, "test", 40);
    fixture.IncrementBlocks(1);

    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId).effectiveAmount, 42);

    // Two claims, first one with supports
    CMutableTransaction claimtx2 = fixture.MakeClaim(fixture.GetCoinbase(), "test", "two", 1);
    uint160 claimId2 = ClaimIdHash(claimtx2.GetHash(), 0);
    fixture.IncrementBlocks(10);

    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId).effectiveAmount, 42);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId2).effectiveAmount, 1);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("inexistent").find(claimId).effectiveAmount, 0);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("inexistent").find(claimId2).effectiveAmount, 0);

    // Two claims, both with supports, second claim effective amount being less than first claim
    fixture.MakeSupport(fixture.GetCoinbase(), claimtx2, "test", 6);
    fixture.IncrementBlocks(13); //delay

    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId).effectiveAmount, 42);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId2).effectiveAmount, 7);

    // Two claims, both with supports, second one taking over
    fixture.MakeSupport(fixture.GetCoinbase(), claimtx2, "test", 1330);
    fixture.IncrementBlocks(26); //delay

    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId).effectiveAmount, 42);
    BOOST_CHECK_EQUAL(fixture.getClaimsForName("test").find(claimId2).effectiveAmount, 1337);
}

/*
 * tests for getClaimById basic consistency checks
 */
BOOST_AUTO_TEST_CASE(get_claim_by_id_test)
{
    ClaimTrieChainFixture fixture;
    const std::string name = "test";
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), name, "one", 2);
    uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
    fixture.IncrementBlocks(1);

    CClaimValue claimValue;
    std::string claimName;
    BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId);

    fixture.MakeSupport(fixture.GetCoinbase(), tx1, name, 4);
    fixture.IncrementBlocks(1);

    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), name, "two", 2);
    uint160 claimId2 = ClaimIdHash(tx2.GetHash(), 0);
    fixture.IncrementBlocks(5);

    BOOST_CHECK(fixture.getClaimById(claimId2, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId2);


    CMutableTransaction u1 = fixture.MakeUpdate(tx1, name, "updated one", claimId, 1);
    fixture.IncrementBlocks(2);
    BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId);
    BOOST_CHECK_EQUAL(claimValue.nAmount, 1);
    BOOST_CHECK_EQUAL(claimValue.outPoint.hash, u1.GetHash());

    fixture.Spend(u1);
    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue));

    fixture.DecrementBlocks(8);

    CClaimValue claimValue2;
    claimName = "";
    BOOST_CHECK(!fixture.getClaimById(claimId2, claimName, claimValue2));
    BOOST_CHECK(claimName != name);
    BOOST_CHECK(claimValue2.claimId != claimId2);

    BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId);

    fixture.DecrementBlocks(2);

    claimName = "";
    BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue2));
    BOOST_CHECK(claimName != name);
    BOOST_CHECK(claimValue2.claimId != claimId);
}

BOOST_AUTO_TEST_CASE(insert_update_claim_test)
{
    ClaimTrieChainFixture fixture;

    std::string sName1("atest");
    std::string sName2("btest");
    std::string sName3("atest123");
    std::string sValue1("testa");
    std::string sValue2("testb");
    std::string sValue3("123testa");

    uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
    BOOST_CHECK_EQUAL(fixture.getMerkleHash(), hash0);

    CMutableTransaction tx1 = BuildTransaction(fixture.GetCoinbase());
    tx1.vout[0].scriptPubKey = CScript() << OP_CLAIM_NAME
                                         << std::vector<unsigned char>(sName1.begin(), sName1.end())
                                         << std::vector<unsigned char>(sValue1.begin(), sValue1.end()) << OP_2DROP << OP_DROP << OP_TRUE;
    uint160 tx1ClaimId = ClaimIdHash(tx1.GetHash(), 0);
    COutPoint tx1OutPoint(tx1.GetHash(), 0);

    CMutableTransaction tx7 = fixture.MakeClaim(fixture.GetCoinbase(), sName1, sValue2, tx1.vout[0].nValue - 10001);
    uint160 tx7ClaimId = ClaimIdHash(tx7.GetHash(), 0);
    COutPoint tx7OutPoint(tx7.GetHash(), 0);

    CClaimValue val;
    int nThrowaway;

    // Verify claims (tx7) for uncontrolled names go in immediately
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx7OutPoint);

    // Verify claims for controlled names are delayed, and that the bigger claim wins when inserted
    fixture.IncrementBlocks(5);
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(5); // 12

    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(1);

    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx1OutPoint);

    // Verify updates to the best claim get inserted immediately, and others don't.
    CMutableTransaction tx3 = fixture.MakeUpdate(tx1, sName1, sValue1, tx1ClaimId, tx1.vout[0].nValue - 10000);
    COutPoint tx3OutPoint(tx3.GetHash(), 0);
    CMutableTransaction tx9 = fixture.MakeUpdate(tx7, sName1, sValue2, tx7ClaimId, tx7.vout[0].nValue - 10000);
    COutPoint tx9OutPoint(tx9.GetHash(), 0);
    fixture.IncrementBlocks(1, true); // 14

    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaim(sName1, tx1OutPoint));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx7OutPoint));
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx3OutPoint);
    BOOST_CHECK(fixture.haveClaimInQueue(sName1, tx9OutPoint, nThrowaway));

    // Roll back the last block, make sure tx1 and tx7 are put back in the trie
    fixture.DecrementBlocks(); // 13

    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx1OutPoint);
    BOOST_CHECK(fixture.haveClaim(sName1, tx7OutPoint));
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!pclaimTrie->empty());

    // Roll all the way back, make sure all txs are out of the trie
    fixture.DecrementBlocks();

    BOOST_CHECK(!fixture.getInfoForName(sName1, val));
    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK_EQUAL(fixture.getMerkleHash(), hash0);
    BOOST_CHECK(fixture.queueEmpty());

    // Test undoing a claim before the claim gets into the trie

    // Put tx1 in the chain, then advance a few blocks.
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    fixture.IncrementBlocks(10); // 11

    // Put tx7 in the chain, verify it goes into the queue
    fixture.CommitTx(tx7);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // Undo that block and make sure it's not in the queue
    fixture.DecrementBlocks();

    // Make sure it's not in the queue
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Go back to the beginning
    fixture.DecrementBlocks();

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Test spend a claim which was just inserted into the trie

    // Immediately spend tx2 with tx4, verify nothing gets put in the trie
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), sName2, sValue2, tx1.vout[0].nValue - 1);
    COutPoint tx2OutPoint(tx2.GetHash(), 0);
    CMutableTransaction tx4 = fixture.Spend(tx2);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    fixture.DecrementBlocks(1);
    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Verify that if a claim in the queue is spent, it does not get into the trie

    // Put tx5 into the chain, advance until it's in the trie for a few blocks

    CMutableTransaction tx5 = fixture.MakeClaim(fixture.GetCoinbase(), sName2, sValue2);
    COutPoint tx5OutPoint(tx5.GetHash(), 0);
    fixture.IncrementBlocks(6, true);

    // Put tx2 into the chain, and then advance a few blocks but not far enough for it to get into the trie
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(fixture.haveClaimInQueue(sName2, tx2OutPoint, nThrowaway));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(3);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // Spend tx2 with tx4, and then advance to where tx2 would be inserted into the trie and verify it hasn't happened

    fixture.CommitTx(tx4);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    fixture.IncrementBlocks(5);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaim(sName2, tx2OutPoint));

    // Undo spending tx2 with tx4, and then advance and verify tx2 is inserted into the trie when it should be

    fixture.DecrementBlocks();
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.haveClaimInQueue(sName2, tx2OutPoint, nThrowaway));

    fixture.IncrementBlocks(2);

    BOOST_CHECK(fixture.haveClaim(sName2, tx2OutPoint));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName2, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx5OutPoint);
    BOOST_CHECK(fixture.haveClaim(sName2, tx2OutPoint));

    // Test undoing a spend which involves a claim in the trie

    // spend tx2, which is in the trie, with tx4
    fixture.CommitTx(tx4);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaim(sName2, tx2OutPoint));

    // undo spending tx2 with tx4, and verify tx2 is back in the trie
    fixture.DecrementBlocks();

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName2, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx5OutPoint);
    BOOST_CHECK(fixture.haveClaim(sName2, tx2OutPoint));

    // roll back to the beginning
    fixture.DecrementBlocks();

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Test undoing a spent update which updated a claim still in the queue

    // Create the claim that will cause the others to be in the queue

    fixture.CommitTx(tx7);
    fixture.IncrementBlocks(6, true);

    // Create the original claim (tx1)

    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.haveClaimInQueue(sName1, tx1OutPoint, nThrowaway));

    // move forward some, but not far enough for the claim to get into the trie
    fixture.IncrementBlocks(2);

    // update the original claim (tx3 spends tx1)
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx1OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx1OutPoint));
    BOOST_CHECK(fixture.haveClaimInQueue(sName1, tx3OutPoint, nThrowaway));

    // spend the update (tx6 spends tx3)

    CMutableTransaction tx6 = fixture.Spend(tx3);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaim(sName1, tx3OutPoint));

    // undo spending the update (undo tx6 spending tx3)
    fixture.DecrementBlocks();

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // make sure the update (tx3) still goes into effect when it's supposed to
    fixture.IncrementBlocks(9);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx3OutPoint);

    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.haveClaim(sName1, tx3OutPoint));

    // roll all the way back
    fixture.DecrementBlocks();

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Test undoing an spent update which updated the best claim to a name

    // move forward until the original claim is inserted into the trie

    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    fixture.IncrementBlocks(5);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx1OutPoint);

    // update the original claim (tx3 spends tx1)
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx3OutPoint);

    // spend the update (tx6 spends tx3)
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // undo spending the update (undo tx6 spending tx3)
    fixture.DecrementBlocks();

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx3OutPoint);

    // Test having two updates to a claim in the same transaction

    // Add both txouts (tx8 spends tx3)
    CMutableTransaction tx8 = BuildTransaction(tx3, 0, 2);
    tx8.vout[0].scriptPubKey = CScript() << OP_UPDATE_CLAIM
                                         << std::vector<unsigned char>(sName1.begin(), sName1.end())
                                         << std::vector<unsigned char>(tx1ClaimId.begin(), tx1ClaimId.end())
                                         << std::vector<unsigned char>(sValue1.begin(), sValue1.end()) << OP_2DROP << OP_2DROP << OP_TRUE;
    tx8.vout[0].nValue -= 1;
    tx8.vout[1].scriptPubKey = CScript() << OP_CLAIM_NAME
                                         << std::vector<unsigned char>(sName1.begin(), sName1.end())
                                         << std::vector<unsigned char>(sValue2.begin(), sValue2.end()) << OP_2DROP << OP_DROP << OP_TRUE;
    COutPoint tx8OutPoint0(tx8.GetHash(), 0);
    COutPoint tx8OutPoint1(tx8.GetHash(), 1);

    fixture.CommitTx(tx8);
    fixture.IncrementBlocks(1, true);

    // ensure txout 0 made it into the trie and txout 1 did not

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx8OutPoint0);

    // roll forward until tx8 output 1 gets into the trie
    fixture.IncrementBlocks(6);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(1);

    // ensure txout 1 made it into the trie and is now in control

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx8OutPoint1);

    // roll back to before tx8
    fixture.DecrementBlocks();

    // roll all the way back
    fixture.DecrementBlocks();

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // make sure invalid updates don't wreak any havoc

    // put tx1 into the trie
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(fixture.getInfoForName(sName1, val));
    BOOST_CHECK_EQUAL(val.outPoint, tx1OutPoint);
    BOOST_CHECK(fixture.queueEmpty());

    // advance a few blocks
    fixture.IncrementBlocks(5);

    // put in bad tx10
    CTransaction root = fixture.GetCoinbase();
    CMutableTransaction tx10 = fixture.MakeUpdate(root, sName1, sValue1, tx1ClaimId, root.vout[0].nValue);
    COutPoint tx10OutPoint(tx10.GetHash(), 0);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx10OutPoint, nThrowaway));
    BOOST_CHECK(fixture.queueEmpty());

    // roll back, make sure nothing bad happens
    fixture.DecrementBlocks();

    // put it back in
    fixture.CommitTx(tx10);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx10OutPoint, nThrowaway));
    BOOST_CHECK(fixture.queueEmpty());

    // update it
    CMutableTransaction tx11 = fixture.MakeUpdate(tx10, sName1, sValue1, tx1ClaimId, tx10.vout[0].nValue);
    COutPoint tx11OutPoint(tx11.GetHash(), 0);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx11OutPoint, nThrowaway));
    BOOST_CHECK(fixture.queueEmpty());

    fixture.IncrementBlocks(10);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx11OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx11OutPoint));
    BOOST_CHECK(fixture.queueEmpty());

    // roll back to before the update
    fixture.DecrementBlocks();

    BOOST_CHECK(!fixture.haveClaim(sName1, tx11OutPoint));
    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx11OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx10OutPoint));
    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx10OutPoint, nThrowaway));
    BOOST_CHECK(fixture.queueEmpty());

    // make sure tx10 would have gotten into the trie, then run tests again

    fixture.IncrementBlocks(10);

    BOOST_CHECK(!fixture.haveClaim(sName1, tx10OutPoint));
    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx10OutPoint, nThrowaway));
    BOOST_CHECK(fixture.queueEmpty());

    // update it
    fixture.CommitTx(tx11);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx11OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx11OutPoint));
    BOOST_CHECK(fixture.queueEmpty());

    // make sure tx11 would have gotten into the trie

    fixture.IncrementBlocks(20);

    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx11OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx11OutPoint));
    BOOST_CHECK(!fixture.haveClaimInQueue(sName1, tx10OutPoint, nThrowaway));
    BOOST_CHECK(!fixture.haveClaim(sName1, tx10OutPoint));
    BOOST_CHECK(fixture.queueEmpty());

    // roll all the way back
    fixture.DecrementBlocks();

    // Put tx10 and tx11 in without tx1 in
    fixture.CommitTx(tx10);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // update with tx11
    fixture.CommitTx(tx11);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // roll back to before tx11
    fixture.DecrementBlocks();

    // spent tx10 with tx12 instead which is not a claim operation of any kind
    CMutableTransaction tx12 = BuildTransaction(tx10);

    fixture.IncrementBlocks(1);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // roll all the way back
    fixture.DecrementBlocks();

    // make sure all claim for names which exist in the trie but have no
    // values get inserted immediately

    CMutableTransaction tx13 = fixture.MakeClaim(fixture.GetCoinbase(), sName3, sValue3, 1);
    fixture.IncrementBlocks(1, true);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // roll back
    fixture.DecrementBlocks();
}

BOOST_AUTO_TEST_CASE(basic_merkle_test)
{
    ClaimTrieChainFixture fixture;

    std::string sName("atest");
    std::string sValue("testa");

    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue, 10);
    fixture.IncrementBlocks(20);
    auto tx1MerkleHash = fixture.getMerkleHash();
    fixture.DecrementBlocks(20);
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(20);
    BOOST_CHECK_EQUAL(tx1MerkleHash, fixture.getMerkleHash());
}

BOOST_AUTO_TEST_CASE(supporting_claims_test)
{
    ClaimTrieChainFixture fixture;

    std::string sName("atest");
    std::string sValue1("testa");
    std::string sValue2("testb");

    int initialHeight = chainActive.Height();

    CClaimValue val;

    // Test 1: create 1 LBC claim (tx1), create 5 LBC support (tx3), create 5 LBC claim (tx2)
    // Verify that tx1 retains control throughout
    // spend tx3, verify that tx2 gains control
    // roll back to before tx3 is spent, verify tx1 regains control
    // update tx1 with tx7, verify tx7 has control
    // roll back to before tx7 is inserted, verify tx1 regains control
    // roll back to before tx2 is valid, spend tx3
    // advance to tx2 valid, verify tx2 gains control
    // roll back to before tx3 is valid, spend tx3
    // advance to tx2 valid, verify tx2 gains control
    // roll back to insertion of tx3, and don't insert it
    // insert tx2, advance until it is valid, verify tx2 gains control

    // Put tx1 in the blockchain

    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue1, 1);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx1.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // advance a few blocks
    fixture.IncrementBlocks(5); // 6

    // Put tx3 into the blockchain
    CMutableTransaction tx3 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, sName, 5);
    fixture.IncrementBlocks(1); // 7

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx3.GetHash(), 0)));
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // advance a few blocks
    fixture.IncrementBlocks(3); // 10

    // Put tx2 into the blockchain
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue2, 5);
    fixture.IncrementBlocks(1); // 11

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx2.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // advance until tx2 is valid
    fixture.IncrementBlocks(9); // 20

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(1); // 21

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(val.outPoint.n, 0);
    auto tx1MerkleHash = fixture.getMerkleHash();

    CMutableTransaction tx4 = BuildTransaction(tx1);
    CMutableTransaction tx5 = BuildTransaction(tx2);
    CMutableTransaction tx6 = BuildTransaction(tx3);

    // spend tx3
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1); // 22

    // verify tx2 gains control
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    // unspend tx3, verify tx1 regains control
    fixture.DecrementBlocks(1); // 21

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(tx1MerkleHash, fixture.getMerkleHash());

    // update tx1 with tx7, verify tx7 has control
    uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
    CMutableTransaction tx7 = fixture.MakeUpdate(tx1, sName, sValue1, claimId, tx1.vout[0].nValue);
    fixture.IncrementBlocks(1); // 22

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx7.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx7.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    // roll back to before tx7 is inserted, verify tx1 has control
    fixture.DecrementBlocks(1); // 21

    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(tx1MerkleHash, fixture.getMerkleHash());

    // roll back to before tx2 is valid
    fixture.DecrementBlocks(1); // 20

    // spend tx3
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1); // 21

    // Verify tx2 gains control
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    // roll back to before tx3 is inserted
    fixture.DecrementBlocks(15); // 6

    // advance a few blocks
    fixture.IncrementBlocks(4); // 10

    // Put tx2 into the blockchain
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1); // 11

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx2.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // advance until tx2 is valid
    fixture.IncrementBlocks(9); // 20

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    fixture.IncrementBlocks(2); // 22

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    // roll all the way back
    fixture.DecrementBlocks(22); // 0
    BOOST_CHECK_EQUAL(initialHeight, chainActive.Height());

    // Make sure that when a support in the queue gets spent and then the spend is
    // undone, it goes back into the queue in the right spot

    // put tx2 and tx1 into the trie

    fixture.CommitTx(tx1);
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    // advance a few blocks
    fixture.IncrementBlocks(5); // 6

    // put tx3 into the support queue
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1); // 7

    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());

    // advance a couple of blocks
    fixture.IncrementBlocks(2); // 9

    // spend tx3
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1); // 10

    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // undo spend of tx3, verify it gets back in the right place in the queue
    fixture.DecrementBlocks(1); // 9

    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());

    fixture.IncrementBlocks(4); // 13

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(val.outPoint.n, 0);
    // tx1MerkleHash doesn't match right here because it is at a different activation height (part of the node hash)
    tx1MerkleHash = fixture.getMerkleHash();

    // spend tx3 again, then undo the spend and roll back until it's back in the queue
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1); // 14

    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(tx1MerkleHash != fixture.getMerkleHash());

    fixture.DecrementBlocks(1); // 13

    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(tx1MerkleHash, fixture.getMerkleHash());

    // roll all the way back
    fixture.DecrementBlocks(13);
    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
}

BOOST_AUTO_TEST_CASE(supporting_claims2_test)
{
    ClaimTrieChainFixture fixture;

    std::string sName("atest");
    std::string sValue1("testa");
    std::string sValue2("testb");

    CClaimValue val;
    int nThrowaway;

    // Test 2: create 1 LBC claim (tx1), create 5 LBC claim (tx2), create 5 LBC support (tx3)
    // Verify that tx1 loses control when tx2 becomes valid, and then tx1 gains control when tx3 becomes valid
    // Then, verify that tx2 regains control when A) tx3 is spent and B) tx3 is undone

    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue1, 1);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx1.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // advance a few blocks
    fixture.IncrementBlocks(4); // 5

    // put tx2 into the blockchain
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue2, 5);
    fixture.IncrementBlocks(1); // 6

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx2.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // advance until tx2 is in the trie
    fixture.IncrementBlocks(4); // 10

    BOOST_CHECK(!fixture.queueEmpty());
    COutPoint tx2cp(tx2.GetHash(), 0);
    BOOST_CHECK(fixture.haveClaimInQueue(sName, tx2cp, nThrowaway));

    fixture.IncrementBlocks(1); // 11
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());

    // advance a few blocks
    fixture.IncrementBlocks(4); // 15

    // put tx3 into the blockchain

    CMutableTransaction tx3 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, sName, 5);
    fixture.IncrementBlocks(1); // 16

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx3.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());

    // advance until tx3 is valid
    fixture.IncrementBlocks(4); // 20

    BOOST_CHECK(!fixture.supportQueueEmpty());

    fixture.IncrementBlocks(1); // 21

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    CMutableTransaction tx4 = BuildTransaction(tx1);
    CMutableTransaction tx5 = BuildTransaction(tx2);
    CMutableTransaction tx6 = BuildTransaction(tx3);

    // spend tx3
    fixture.CommitTx(tx6);
    fixture.IncrementBlocks(1); // 22

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());

    // undo spend
    fixture.DecrementBlocks(1); // 21

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // roll back to before tx3 is valid
    fixture.DecrementBlocks(1); // 20

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());

    // roll all the way back
    fixture.DecrementBlocks(20); // 0

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // Test 4: create 1 LBC claim (tx1), wait till valid, create 5 LBC claim (tx2), create 5 LBC support (tx3)
    // Verify that tx1 retains control throughout

    // put tx1 into the blockchain

    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx1.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // advance a few blocks
    fixture.IncrementBlocks(5); // 6

    // put tx2 into the blockchain
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1); // 7

    auto rootMerkleHash = fixture.getMerkleHash();

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(rootMerkleHash, fixture.getMerkleHash());

    // advance some, insert tx3, should be immediately valid
    fixture.IncrementBlocks(2); // 9
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1); // 10

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(rootMerkleHash, fixture.getMerkleHash());

    // advance until tx2 is valid, verify tx1 retains control
    fixture.IncrementBlocks(3); // 13

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());
    BOOST_CHECK_EQUAL(rootMerkleHash, fixture.getMerkleHash());
    BOOST_CHECK(fixture.haveClaim(sName, tx2cp));

    // roll all the way back
    fixture.DecrementBlocks(13); // 0

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // Test 5: create 5 LBC claim (tx2), wait till valid, create 1 LBC claim (tx1), create 5 LBC support (tx3)
    // Verify that tx2 retains control until support becomes valid

    // insert tx2 into the blockchain
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(rootMerkleHash != fixture.getMerkleHash());

    // advance a few blocks
    fixture.IncrementBlocks(9); // 10

    // insert tx1 into the block chain
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1); // 11

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(rootMerkleHash != fixture.getMerkleHash());

    // advance some
    fixture.IncrementBlocks(5); // 16

    // insert tx3 into the block chain
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1); // 17

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    BOOST_CHECK(rootMerkleHash != fixture.getMerkleHash());

    // advance until tx1 is valid
    fixture.IncrementBlocks(5); // 22

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(!fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());
    COutPoint tx1cp(tx1.GetHash(), 0);
    BOOST_CHECK(fixture.haveClaim(sName, tx1cp));

    // advance until tx3 is valid
    fixture.IncrementBlocks(11); // 33

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // roll all the way back
    fixture.DecrementBlocks(33); // 0

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());


    // Test 6: create 1 LBC claim (tx1), wait till valid, create 5 LBC claim (tx2), create 5 LBC support (tx3), spend tx1
    // Verify that tx1 retains control until it is spent
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // advance a few blocks
    fixture.IncrementBlocks(5); // 6

    // insert tx2 into the blockchain
    fixture.CommitTx(tx2);
    fixture.IncrementBlocks(1); // 7

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // advance some, insert tx3
    fixture.IncrementBlocks(2); // 9
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1); // 10

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // advance until tx2 is valid
    fixture.IncrementBlocks(3); // 13

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // spend tx1
    fixture.CommitTx(tx4);
    fixture.IncrementBlocks(1); // 14

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx2.GetHash());

    // undo spend of tx1
    fixture.DecrementBlocks(1); // 13

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // roll all the way back
    fixture.DecrementBlocks(13); // 0

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // Test 7: create 1 LBC claim (tx1), wait till valid, create 5 LBC support (tx3), spend tx1
    // Verify name trie is empty

    // insert tx1 into blockchain
    fixture.CommitTx(tx1);
    fixture.IncrementBlocks(1); // 1

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // insert tx3 into blockchain
    fixture.CommitTx(tx3);
    fixture.IncrementBlocks(1); // 2

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // spent tx1
    fixture.CommitTx(tx4);
    fixture.IncrementBlocks(1); // 3

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // roll all the way back
    fixture.DecrementBlocks(3);

    BOOST_CHECK(pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());
}

BOOST_AUTO_TEST_CASE(invalid_claimid_test)
{
    ClaimTrieChainFixture fixture;

    std::string sName("atest");
    std::string sValue1("testa");
    std::string sValue2("testb");

    CClaimValue val;
    std::vector<uint256> blocks_to_invalidate;

    // Verify that supports expire

    // Create a 1 LBC claim (tx1)
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue1, 1);
    fixture.IncrementBlocks(1, true); // 1

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx1.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(fixture.supportEmpty());
    BOOST_CHECK(fixture.supportQueueEmpty());

    // Make sure it gets way in there
    fixture.IncrementBlocks(100); // 101

    // Create a 5 LBC claim (tx2)
    CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), sName, sValue2, 5);
    fixture.IncrementBlocks(1); // 102

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx2.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(!fixture.queueEmpty());

    // Create a tx with a bogus claimId
    uint160 tx1ClaimId = ClaimIdHash(tx1.GetHash(), 0);
    CMutableTransaction tx3 = fixture.MakeUpdate(tx2, sName, sValue2, tx1ClaimId, 4);
    CMutableTransaction tx4 = BuildTransaction(tx3);
    COutPoint tx4OutPoint(tx4.GetHash(), 0);

    fixture.IncrementBlocks(1); // 103

    BOOST_CHECK(pcoinsTip->HaveCoin(COutPoint(tx3.GetHash(), 0)));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Verify it's not in the claim trie
    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    BOOST_CHECK(!fixture.haveClaim(sName, tx4OutPoint));

    // Update the tx with the bogus claimId
    fixture.CommitTx(tx4);
    fixture.IncrementBlocks(1); // 104

    BOOST_CHECK(pcoinsTip->HaveCoin(tx4OutPoint));
    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());

    // Verify it's not in the claim trie

    BOOST_CHECK(!fixture.haveClaim(sName, tx4OutPoint));

    BOOST_CHECK(fixture.getInfoForName(sName, val));
    BOOST_CHECK_EQUAL(val.outPoint.hash, tx1.GetHash());

    // Go forward a few hundred blocks and verify it's not in there

    fixture.IncrementBlocks(101); // 205

    BOOST_CHECK(!pclaimTrie->empty());
    BOOST_CHECK(fixture.queueEmpty());
    BOOST_CHECK(!fixture.haveClaim(sName, tx4OutPoint));

    // go all the way back
    fixture.DecrementBlocks();
    BOOST_CHECK(pclaimTrie->empty());
}

/*
 * tests for getClaimById basic consistency checks
 */
BOOST_AUTO_TEST_CASE(get_claim_by_id_test_2)
{
    ClaimTrieChainFixture fixture;
    const std::string name = "test";
    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), name, "one", 1);
    uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
    CMutableTransaction txx = fixture.MakeClaim(fixture.GetCoinbase(), name, "one", 1);
    uint160 claimIdx = ClaimIdHash(txx.GetHash(), 0);

    fixture.IncrementBlocks(1);

    CClaimValue claimValue;
    std::string claimName;
    BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId);

    CMutableTransaction txa = fixture.Spend(tx1);
    CMutableTransaction txb = fixture.Spend(txx);

    fixture.IncrementBlocks(1);
    BOOST_CHECK(!fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK(!fixture.getClaimById(claimIdx, claimName, claimValue));

    fixture.DecrementBlocks(1);
    BOOST_CHECK(fixture.getClaimById(claimId, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimId);

    // this test fails
    BOOST_CHECK(fixture.getClaimById(claimIdx, claimName, claimValue));
    BOOST_CHECK_EQUAL(claimName, name);
    BOOST_CHECK_EQUAL(claimValue.claimId, claimIdx);
}

BOOST_AUTO_TEST_CASE(update_on_support2_test)
{
    ClaimTrieChainFixture fixture;

    std::string name = "test";
    std::string value = "one";

    auto height = chainActive.Height();

    CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), name, value, 3);
    CMutableTransaction s1 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, name, 1);
    CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, name, 1);
    fixture.IncrementBlocks(1);

    uint160 claimId;
    int lastTakeover;
    BOOST_CHECK(fixture.getLastTakeoverForName(name, claimId, lastTakeover));
    BOOST_CHECK_EQUAL(lastTakeover, height + 1);
    BOOST_CHECK_EQUAL(ClaimIdHash(tx1.GetHash(), 0), uint160(claimId));

    fixture.Spend(s1);
    fixture.Spend(s2);
    CMutableTransaction u1 = fixture.MakeUpdate(tx1, name, value, ClaimIdHash(tx1.GetHash(), 0), 3);
    fixture.IncrementBlocks(1);

    BOOST_CHECK(fixture.getLastTakeoverForName(name, claimId, lastTakeover));
    BOOST_CHECK_EQUAL(lastTakeover, height + 1);
}

BOOST_AUTO_TEST_SUITE_END()