diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 099714811..f9384a09a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -73,6 +73,7 @@ BITCOIN_TESTS =\
   test/test_bitcoin.h \
   test/timedata_tests.cpp \
   test/transaction_tests.cpp \
+  test/txvalidationcache_tests.cpp \
   test/uint256_tests.cpp \
   test/univalue_tests.cpp \
   test/util_tests.cpp
diff --git a/src/test/README.md b/src/test/README.md
index 7efce6f05..e36112bd4 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -18,4 +18,16 @@ uint256_tests.cpp.
 
 For further reading, I found the following website to be helpful in
 explaining how the boost unit test framework works:
-[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/).
\ No newline at end of file
+[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/).
+
+test_bitcoin has some built-in command-line arguments; for
+example, to run just the getarg_tests verbosely:
+
+    test_bitcoin --log_level=all --run_test=getarg_tests
+
+... or to run just the doubledash test:
+
+    test_bitcoin --run_test=getarg_tests/doubledash
+
+Run  test_bitcoin --help   for the full list.
+
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index ba616365f..8d81275a6 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -7,8 +7,12 @@
 #include "test_bitcoin.h"
 
 #include "chainparams.h"
+#include "consensus/consensus.h"
+#include "consensus/validation.h"
 #include "key.h"
 #include "main.h"
+#include "miner.h"
+#include "pubkey.h"
 #include "random.h"
 #include "txdb.h"
 #include "ui_interface.h"
@@ -28,20 +32,22 @@ CWallet* pwalletMain;
 extern bool fPrintToConsole;
 extern void noui_connect();
 
-BasicTestingSetup::BasicTestingSetup()
+BasicTestingSetup::BasicTestingSetup(CBaseChainParams::Network network)
 {
         ECC_Start();
         SetupEnvironment();
         fPrintToDebugLog = false; // don't want to write to debug.log file
         fCheckBlockIndex = true;
-        SelectParams(CBaseChainParams::MAIN);
+        SelectParams(network);
+        noui_connect();
 }
+
 BasicTestingSetup::~BasicTestingSetup()
 {
         ECC_Stop();
 }
 
-TestingSetup::TestingSetup()
+TestingSetup::TestingSetup(CBaseChainParams::Network network) : BasicTestingSetup(network)
 {
 #ifdef ENABLE_WALLET
         bitdb.MakeMock();
@@ -87,6 +93,51 @@ TestingSetup::~TestingSetup()
         boost::filesystem::remove_all(pathTemp);
 }
 
+TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
+{
+    // Generate a 100-block chain:
+    coinbaseKey.MakeNewKey(true);
+    CScript scriptPubKey = CScript() <<  ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+    for (int i = 0; i < COINBASE_MATURITY; i++)
+    {
+        std::vector<CMutableTransaction> noTxns;
+        CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
+        coinbaseTxns.push_back(b.vtx[0]);
+    }
+}
+
+//
+// Create a new block with just given transactions, coinbase paying to
+// scriptPubKey, and try to add it to the current chain.
+//
+CBlock
+TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
+{
+    CBlockTemplate *pblocktemplate = CreateNewBlock(scriptPubKey);
+    CBlock& block = pblocktemplate->block;
+
+    // Replace mempool-selected txns with just coinbase plus passed-in txns:
+    block.vtx.resize(1);
+    BOOST_FOREACH(const CMutableTransaction& tx, txns)
+        block.vtx.push_back(tx);
+    // IncrementExtraNonce creates a valid coinbase and merkleRoot
+    unsigned int extraNonce = 0;
+    IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
+
+    while (!CheckProofOfWork(block.GetHash(), block.nBits, Params(CBaseChainParams::REGTEST).GetConsensus())) ++block.nNonce;
+
+    CValidationState state;
+    ProcessNewBlock(state, NULL, &block, true, NULL);
+
+    CBlock result = block;
+    delete pblocktemplate;
+    return result;
+}
+
+TestChain100Setup::~TestChain100Setup()
+{
+}
+
 void Shutdown(void* parg)
 {
   exit(0);
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 2f75332d4..b9314d061 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -1,6 +1,8 @@
 #ifndef BITCOIN_TEST_TEST_BITCOIN_H
 #define BITCOIN_TEST_TEST_BITCOIN_H
 
+#include "chainparamsbase.h"
+#include "key.h"
 #include "txdb.h"
 
 #include <boost/filesystem.hpp>
@@ -10,7 +12,7 @@
  * This just configures logging and chain parameters.
  */
 struct BasicTestingSetup {
-    BasicTestingSetup();
+    BasicTestingSetup(CBaseChainParams::Network network = CBaseChainParams::MAIN);
     ~BasicTestingSetup();
 };
 
@@ -23,8 +25,30 @@ struct TestingSetup: public BasicTestingSetup {
     boost::filesystem::path pathTemp;
     boost::thread_group threadGroup;
 
-    TestingSetup();
+    TestingSetup(CBaseChainParams::Network network = CBaseChainParams::MAIN);
     ~TestingSetup();
 };
 
+class CBlock;
+struct CMutableTransaction;
+class CScript;
+
+//
+// Testing fixture that pre-creates a
+// 100-block REGTEST-mode block chain
+//
+struct TestChain100Setup : public TestingSetup {
+    TestChain100Setup();
+
+    // Create a new block with just given transactions, coinbase paying to
+    // scriptPubKey, and try to add it to the current chain.
+    CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
+                                 const CScript& scriptPubKey);
+
+    ~TestChain100Setup();
+
+    std::vector<CTransaction> coinbaseTxns; // For convenience, coinbase transactions
+    CKey coinbaseKey; // private/public key needed to spend coinbase transactions
+};
+
 #endif
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
new file mode 100644
index 000000000..edad18644
--- /dev/null
+++ b/src/test/txvalidationcache_tests.cpp
@@ -0,0 +1,86 @@
+// Copyright (c) 2011-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.
+
+#include "consensus/validation.h"
+#include "key.h"
+#include "main.h"
+#include "miner.h"
+#include "pubkey.h"
+#include "txmempool.h"
+#include "random.h"
+#include "script/standard.h"
+#include "test/test_bitcoin.h"
+#include "utiltime.h"
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
+
+static bool
+ToMemPool(CMutableTransaction& tx)
+{
+    LOCK(cs_main);
+
+    CValidationState state;
+    return AcceptToMemoryPool(mempool, state, tx, false, NULL, false);
+}
+
+BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
+{
+    // Make sure skipping validation of transctions that were
+    // validated going into the memory pool does not allow
+    // double-spends in blocks to pass validation when they should not.
+
+    CScript scriptPubKey = CScript() <<  ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+
+    // Create a double-spend of mature coinbase txn:
+    std::vector<CMutableTransaction> spends;
+    spends.resize(2);
+    for (int i = 0; i < 2; i++)
+    {
+        spends[i].vin.resize(1);
+        spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash();
+        spends[i].vin[0].prevout.n = 0;
+        spends[i].vout.resize(1);
+        spends[i].vout[0].nValue = 11*CENT;
+        spends[i].vout[0].scriptPubKey = scriptPubKey;
+
+        // Sign:
+        std::vector<unsigned char> vchSig;
+        uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL);
+        BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+        vchSig.push_back((unsigned char)SIGHASH_ALL);
+        spends[i].vin[0].scriptSig << vchSig;
+    }
+
+    CBlock block;
+
+    // Test 1: block with both of those transactions should be rejected.
+    block = CreateAndProcessBlock(spends, scriptPubKey);
+    BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+
+    // Test 2: ... and should be rejected if spend1 is in the memory pool
+    BOOST_CHECK(ToMemPool(spends[0]));
+    block = CreateAndProcessBlock(spends, scriptPubKey);
+    BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+    mempool.clear();
+
+    // Test 3: ... and should be rejected if spend2 is in the memory pool
+    BOOST_CHECK(ToMemPool(spends[1]));
+    block = CreateAndProcessBlock(spends, scriptPubKey);
+    BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
+    mempool.clear();
+
+    // Final sanity test: first spend in mempool, second in block, that's OK:
+    std::vector<CMutableTransaction> oneSpend;
+    oneSpend.push_back(spends[0]);
+    BOOST_CHECK(ToMemPool(spends[1]));
+    block = CreateAndProcessBlock(oneSpend, scriptPubKey);
+    BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
+    // spends[1] should have been removed from the mempool when the
+    // block with spends[0] is accepted:
+    BOOST_CHECK_EQUAL(mempool.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()