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