1 // Copyright (c) 2012-2019 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <wallet/wallet.h>
6 
7 #include <memory>
8 #include <set>
9 #include <stdint.h>
10 #include <utility>
11 #include <vector>
12 
13 #include <consensus/validation.h>
14 #include <interfaces/chain.h>
15 #include <rpc/server.h>
16 #include <test/test_bitcoin.h>
17 #include <validation.h>
18 #include <wallet/coincontrol.h>
19 #include <wallet/test/wallet_test_fixture.h>
20 #include <policy/policy.h>
21 
22 #include <boost/test/unit_test.hpp>
23 #include <univalue.h>
24 
25 extern UniValue importmulti(const JSONRPCRequest& request);
26 extern UniValue dumpwallet(const JSONRPCRequest& request);
27 extern UniValue importwallet(const JSONRPCRequest& request);
28 
BOOST_FIXTURE_TEST_SUITE(wallet_tests,WalletTestingSetup)29 BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
30 
31 static void AddKey(CWallet& wallet, const CKey& key)
32 {
33     LOCK(wallet.cs_wallet);
34     wallet.AddKeyPubKey(key, key.GetPubKey());
35 }
36 
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions,TestChain100Setup)37 BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
38 {
39     auto chain = interfaces::MakeChain();
40 
41     // Cap last block file size, and mine new block in a new block file.
42     CBlockIndex* oldTip = chainActive.Tip();
43     GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
44     CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
45     CBlockIndex* newTip = chainActive.Tip();
46 
47     LockAnnotation lock(::cs_main);
48     auto locked_chain = chain->lock();
49 
50     // Verify ScanForWalletTransactions accommodates a null start block.
51     {
52         CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
53         AddKey(wallet, coinbaseKey);
54         WalletRescanReserver reserver(&wallet);
55         reserver.reserve();
56         CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */);
57         BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
58         BOOST_CHECK(result.last_failed_block.IsNull());
59         BOOST_CHECK(result.last_scanned_block.IsNull());
60         BOOST_CHECK(!result.last_scanned_height);
61         BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
62     }
63 
64     // Verify ScanForWalletTransactions picks up transactions in both the old
65     // and new block files.
66     {
67         CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
68         AddKey(wallet, coinbaseKey);
69         WalletRescanReserver reserver(&wallet);
70         reserver.reserve();
71         CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
72         BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
73         BOOST_CHECK(result.last_failed_block.IsNull());
74         BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
75         BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
76         BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
77     }
78 
79     // Prune the older block file.
80     PruneOneBlockFile(oldTip->GetBlockPos().nFile);
81     UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
82 
83     // Verify ScanForWalletTransactions only picks transactions in the new block
84     // file.
85     {
86         CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
87         AddKey(wallet, coinbaseKey);
88         WalletRescanReserver reserver(&wallet);
89         reserver.reserve();
90         CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
91         BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
92         BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
93         BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
94         BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
95         BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
96     }
97 
98     // Prune the remaining block file.
99     PruneOneBlockFile(newTip->GetBlockPos().nFile);
100     UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
101 
102     // Verify ScanForWalletTransactions scans no blocks.
103     {
104         CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
105         AddKey(wallet, coinbaseKey);
106         WalletRescanReserver reserver(&wallet);
107         reserver.reserve();
108         CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
109         BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
110         BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
111         BOOST_CHECK(result.last_scanned_block.IsNull());
112         BOOST_CHECK(!result.last_scanned_height);
113         BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
114     }
115 }
116 
BOOST_FIXTURE_TEST_CASE(importmulti_rescan,TestChain100Setup)117 BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
118 {
119     auto chain = interfaces::MakeChain();
120 
121     // Cap last block file size, and mine new block in a new block file.
122     CBlockIndex* oldTip = chainActive.Tip();
123     GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
124     CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
125     CBlockIndex* newTip = chainActive.Tip();
126 
127     LockAnnotation lock(::cs_main);
128     auto locked_chain = chain->lock();
129 
130     // Prune the older block file.
131     PruneOneBlockFile(oldTip->GetBlockPos().nFile);
132     UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
133 
134     // Verify importmulti RPC returns failure for a key whose creation time is
135     // before the missing block, and success for a key whose creation time is
136     // after.
137     {
138         std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
139         AddWallet(wallet);
140         UniValue keys;
141         keys.setArray();
142         UniValue key;
143         key.setObject();
144         key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
145         key.pushKV("timestamp", 0);
146         key.pushKV("internal", UniValue(true));
147         keys.push_back(key);
148         key.clear();
149         key.setObject();
150         CKey futureKey;
151         futureKey.MakeNewKey(true);
152         key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
153         key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
154         key.pushKV("internal", UniValue(true));
155         keys.push_back(key);
156         JSONRPCRequest request;
157         request.params.setArray();
158         request.params.push_back(keys);
159 
160         UniValue response = importmulti(request);
161         BOOST_CHECK_EQUAL(response.write(),
162             strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
163                       "timestamp %d. There was an error reading a block from time %d, which is after or within %d "
164                       "seconds of key creation, and could contain transactions pertaining to the key. As a result, "
165                       "transactions and coins using this key may not appear in the wallet. This error could be caused "
166                       "by pruning or data corruption (see litecoind log for details) and could be dealt with by "
167                       "downloading and rescanning the relevant blocks (see -reindex and -rescan "
168                       "options).\"}},{\"success\":true}]",
169                               0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
170         RemoveWallet(wallet);
171     }
172 }
173 
174 // Verify importwallet RPC starts rescan at earliest block with timestamp
175 // greater or equal than key birthday. Previously there was a bug where
176 // importwallet RPC would start the scan at the latest block with timestamp less
177 // than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan,TestChain100Setup)178 BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
179 {
180     auto chain = interfaces::MakeChain();
181 
182     // Create two blocks with same timestamp to verify that importwallet rescan
183     // will pick up both blocks, not just the first.
184     const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
185     SetMockTime(BLOCK_TIME);
186     m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
187     m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
188 
189     // Set key birthday to block time increased by the timestamp window, so
190     // rescan will start at the block time.
191     const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
192     SetMockTime(KEY_TIME);
193     m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
194 
195     auto locked_chain = chain->lock();
196 
197     std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
198 
199     // Import key into wallet and call dumpwallet to create backup file.
200     {
201         std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
202         LOCK(wallet->cs_wallet);
203         wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
204         wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
205 
206         JSONRPCRequest request;
207         request.params.setArray();
208         request.params.push_back(backup_file);
209         AddWallet(wallet);
210         ::dumpwallet(request);
211         RemoveWallet(wallet);
212     }
213 
214     // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
215     // were scanned, and no prior blocks were scanned.
216     {
217         std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
218 
219         JSONRPCRequest request;
220         request.params.setArray();
221         request.params.push_back(backup_file);
222         AddWallet(wallet);
223         ::importwallet(request);
224         RemoveWallet(wallet);
225 
226         LOCK(wallet->cs_wallet);
227         BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
228         BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
229         for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
230             bool found = wallet->GetWalletTx(m_coinbase_txns[i]->GetHash());
231             bool expected = i >= 100;
232             BOOST_CHECK_EQUAL(found, expected);
233         }
234     }
235 
236     SetMockTime(0);
237 }
238 
239 // Check that GetImmatureCredit() returns a newly calculated value instead of
240 // the cached value after a MarkDirty() call.
241 //
242 // This is a regression test written to verify a bugfix for the immature credit
243 // function. Similar tests probably should be written for the other credit and
244 // debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit,TestChain100Setup)245 BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
246 {
247     auto chain = interfaces::MakeChain();
248     CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
249     CWalletTx wtx(&wallet, m_coinbase_txns.back());
250     auto locked_chain = chain->lock();
251     LOCK(wallet.cs_wallet);
252     wtx.hashBlock = chainActive.Tip()->GetBlockHash();
253     wtx.nIndex = 0;
254 
255     // Call GetImmatureCredit() once before adding the key to the wallet to
256     // cache the current immature credit amount, which is 0.
257     BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0);
258 
259     // Invalidate the cached value, add the key, and make sure a new immature
260     // credit amount is calculated.
261     wtx.MarkDirty();
262     wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
263     BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 50*COIN);
264 }
265 
AddTx(CWallet & wallet,uint32_t lockTime,int64_t mockTime,int64_t blockTime)266 static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
267 {
268     CMutableTransaction tx;
269     tx.nLockTime = lockTime;
270     SetMockTime(mockTime);
271     CBlockIndex* block = nullptr;
272     if (blockTime > 0) {
273         LockAnnotation lock(::cs_main);
274         auto locked_chain = wallet.chain().lock();
275         auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
276         assert(inserted.second);
277         const uint256& hash = inserted.first->first;
278         block = inserted.first->second;
279         block->nTime = blockTime;
280         block->phashBlock = &hash;
281     }
282 
283     CWalletTx wtx(&wallet, MakeTransactionRef(tx));
284     if (block) {
285         wtx.SetMerkleBranch(block->GetBlockHash(), 0);
286     }
287     {
288         LOCK(cs_main);
289         wallet.AddToWallet(wtx);
290     }
291     LOCK(wallet.cs_wallet);
292     return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
293 }
294 
295 // Simple test to verify assignment of CWalletTx::nSmartTime value. Could be
296 // expanded to cover more corner cases of smart time logic.
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)297 BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
298 {
299     // New transaction should use clock time if lower than block time.
300     BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
301 
302     // Test that updating existing transaction does not change smart time.
303     BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
304 
305     // New transaction should use clock time if there's no block time.
306     BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
307 
308     // New transaction should use block time if lower than clock time.
309     BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
310 
311     // New transaction should use latest entry time if higher than
312     // min(block time, clock time).
313     BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
314 
315     // If there are future entries, new transaction should use time of the
316     // newest entry that is no more than 300 seconds ahead of the clock time.
317     BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
318 
319     // Reset mock time for other tests.
320     SetMockTime(0);
321 }
322 
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)323 BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
324 {
325     CTxDestination dest = CKeyID();
326     LOCK(m_wallet.cs_wallet);
327     m_wallet.AddDestData(dest, "misc", "val_misc");
328     m_wallet.AddDestData(dest, "rr0", "val_rr0");
329     m_wallet.AddDestData(dest, "rr1", "val_rr1");
330 
331     auto values = m_wallet.GetDestValues("rr");
332     BOOST_CHECK_EQUAL(values.size(), 2U);
333     BOOST_CHECK_EQUAL(values[0], "val_rr0");
334     BOOST_CHECK_EQUAL(values[1], "val_rr1");
335 }
336 
337 class ListCoinsTestingSetup : public TestChain100Setup
338 {
339 public:
ListCoinsTestingSetup()340     ListCoinsTestingSetup()
341     {
342         CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
343         wallet = MakeUnique<CWallet>(*m_chain, WalletLocation(), WalletDatabase::CreateMock());
344         bool firstRun;
345         wallet->LoadWallet(firstRun);
346         AddKey(*wallet, coinbaseKey);
347         WalletRescanReserver reserver(wallet.get());
348         reserver.reserve();
349         CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
350         BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
351         BOOST_CHECK_EQUAL(result.last_scanned_block, chainActive.Tip()->GetBlockHash());
352         BOOST_CHECK_EQUAL(*result.last_scanned_height, chainActive.Height());
353         BOOST_CHECK(result.last_failed_block.IsNull());
354     }
355 
~ListCoinsTestingSetup()356     ~ListCoinsTestingSetup()
357     {
358         wallet.reset();
359     }
360 
AddTx(CRecipient recipient)361     CWalletTx& AddTx(CRecipient recipient)
362     {
363         CTransactionRef tx;
364         CReserveKey reservekey(wallet.get());
365         CAmount fee;
366         int changePos = -1;
367         std::string error;
368         CCoinControl dummy;
369         BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
370         CValidationState state;
371         BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state));
372         CMutableTransaction blocktx;
373         {
374             LOCK(wallet->cs_wallet);
375             blocktx = CMutableTransaction(*wallet->mapWallet.at(tx->GetHash()).tx);
376         }
377         CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
378         LOCK(wallet->cs_wallet);
379         auto it = wallet->mapWallet.find(tx->GetHash());
380         BOOST_CHECK(it != wallet->mapWallet.end());
381         it->second.SetMerkleBranch(chainActive.Tip()->GetBlockHash(), 1);
382         return it->second;
383     }
384 
385     std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
386     std::unique_ptr<interfaces::Chain::Lock> m_locked_chain = m_chain->assumeLocked();  // Temporary. Removed in upcoming lock cleanup
387     std::unique_ptr<CWallet> wallet;
388 };
389 
BOOST_FIXTURE_TEST_CASE(ListCoins,ListCoinsTestingSetup)390 BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
391 {
392     std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString();
393 
394     // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey
395     // address.
396     std::map<CTxDestination, std::vector<COutput>> list;
397     {
398         LOCK2(cs_main, wallet->cs_wallet);
399         list = wallet->ListCoins(*m_locked_chain);
400     }
401     BOOST_CHECK_EQUAL(list.size(), 1U);
402     BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
403     BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
404 
405     // Check initial balance from one mature coinbase transaction.
406     BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance());
407 
408     // Add a transaction creating a change address, and confirm ListCoins still
409     // returns the coin associated with the change address underneath the
410     // coinbaseKey pubkey, even though the change address has a different
411     // pubkey.
412     AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
413     {
414         LOCK2(cs_main, wallet->cs_wallet);
415         list = wallet->ListCoins(*m_locked_chain);
416     }
417     BOOST_CHECK_EQUAL(list.size(), 1U);
418     BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
419     BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
420 
421     // Lock both coins. Confirm number of available coins drops to 0.
422     {
423         LOCK2(cs_main, wallet->cs_wallet);
424         std::vector<COutput> available;
425         wallet->AvailableCoins(*m_locked_chain, available);
426         BOOST_CHECK_EQUAL(available.size(), 2U);
427     }
428     for (const auto& group : list) {
429         for (const auto& coin : group.second) {
430             LOCK(wallet->cs_wallet);
431             wallet->LockCoin(COutPoint(coin.tx->GetHash(), coin.i));
432         }
433     }
434     {
435         LOCK2(cs_main, wallet->cs_wallet);
436         std::vector<COutput> available;
437         wallet->AvailableCoins(*m_locked_chain, available);
438         BOOST_CHECK_EQUAL(available.size(), 0U);
439     }
440     // Confirm ListCoins still returns same result as before, despite coins
441     // being locked.
442     {
443         LOCK2(cs_main, wallet->cs_wallet);
444         list = wallet->ListCoins(*m_locked_chain);
445     }
446     BOOST_CHECK_EQUAL(list.size(), 1U);
447     BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
448     BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
449 }
450 
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys,TestChain100Setup)451 BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
452 {
453     auto chain = interfaces::MakeChain();
454     std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
455     wallet->SetMinVersion(FEATURE_LATEST);
456     wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
457     BOOST_CHECK(!wallet->TopUpKeyPool(1000));
458     CPubKey pubkey;
459     BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
460 }
461 
462 // Explicit calculation which is used to test the wallet constant
463 // We get the same virtual size due to rounding(weight/4) for both use_max_sig values
CalculateNestedKeyhashInputSize(bool use_max_sig)464 static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
465 {
466     // Generate ephemeral valid pubkey
467     CKey key;
468     key.MakeNewKey(true);
469     CPubKey pubkey = key.GetPubKey();
470 
471     // Generate pubkey hash
472     uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));
473 
474     // Create inner-script to enter into keystore. Key hash can't be 0...
475     CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
476 
477     // Create outer P2SH script for the output
478     uint160 script_id(Hash160(inner_script.begin(), inner_script.end()));
479     CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
480 
481     // Add inner-script to key store and key to watchonly
482     CBasicKeyStore keystore;
483     keystore.AddCScript(inner_script);
484     keystore.AddKeyPubKey(key, pubkey);
485 
486     // Fill in dummy signatures for fee calculation.
487     SignatureData sig_data;
488 
489     if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
490         // We're hand-feeding it correct arguments; shouldn't happen
491         assert(false);
492     }
493 
494     CTxIn tx_in;
495     UpdateInput(tx_in, sig_data);
496     return (size_t)GetVirtualTransactionInputSize(tx_in);
497 }
498 
BOOST_FIXTURE_TEST_CASE(dummy_input_size_test,TestChain100Setup)499 BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
500 {
501     BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
502     BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
503 }
504 
505 BOOST_AUTO_TEST_SUITE_END()
506