diff --git a/.gitignore b/.gitignore index 323aac218..d456eb159 100644 --- a/.gitignore +++ b/.gitignore @@ -37,8 +37,10 @@ lib/generated_plugin_registrant.dart test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart test/services/coins/firo/firo_wallet_test_parameters.dart test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart -test/services/coins/namecoin/namecoin_wallet_test_parameters.dart +test/services/coins/namecoin/namecoin_wallet_test_parameters.dart # Legacy +test/services/coins/namecoin/namecoin_wallet_test_parameters.txt # Legacy test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart +test/services/coins/particl/particl_wallet_test_parameters.dart /integration_test/private.dart # Exceptions to above rules. @@ -48,5 +50,3 @@ test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart coverage scripts/**/build /lib/external_api_keys.dart -/test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart -/test/services/coins/namecoin/namecoin_wallet_test_parameters.dart.txt diff --git a/test/services/coins/particl/particl_history_sample_data.dart b/test/services/coins/particl/particl_history_sample_data.dart new file mode 100644 index 000000000..53a082778 --- /dev/null +++ b/test/services/coins/particl/particl_history_sample_data.dart @@ -0,0 +1,188 @@ +// TODO these test vectors are valid for Namecoin: update for Particl + +final Map> historyBatchArgs0 = { + "k_0_0": ["d17132f41b2d55c730db5b27db721020abbd4a5087c15edcccbaa106eef8cbf3"], + "k_0_1": ["cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9"], + "k_0_2": ["82a12031d679c9dd3124047742dc22c2c7c03afa9644bddf55d4c95da41bca1c"], + "k_0_3": ["bbe10c5d3c102fd805770ed2d6c5438dce42c04d3f87e3260056d04245b17ddd"], + "k_0_4": ["d9ca5255516f963d8f348911451e2c69489a70dec7f34a4810ee8b0e32fcb04d"], + "k_0_5": ["2284461fd01b17e7443775e39b19f4378a063ff148938d2e4191cea3fd80368d"], + "k_0_6": ["cd3c32fddbf265410c34a58fefcc849b02fc16978d75e501f88f9effcbecd8fe"], + "k_0_7": ["a3bcc0c3c4a140fbcc4c4f4dff18790d8a2d5f868821f47460f68f0426291b57"], + "k_0_8": ["e400f9431798c87ea35ea19b265d9e56a73fd44c239957d9947ae79e16718fb4"], + "k_0_9": ["1fe8bb16b49725bf3703274e205a4695c398e664284cc68d92d15087a54da299"], + "k_0_10": [ + "2fabf8d61308c8b2d914489a9f02f669ed9fa68047666815cf1f3cd1bb5d8819" + ], + "k_0_11": ["42a567d344189430afe7d45d6854ef6e9d256d9ef4186afd31a1a5ff90a6a0dd"] +}; +final Map> historyBatchArgs1 = { + "k_0_0": ["bcf7aec7c10dfba33ce80149300a7c4fe66460c1dd05503b5df5780884498186"], + "k_0_1": ["587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e"], + "k_0_2": ["fe6ad514f7427782f964b25995a90a3233589904b88f66a2d0e73e2560c9af7c"], + "k_0_3": ["6b962c5f9b4cfc004c74c5ab849304c405b02fc0e2f34ee17c185984f13c9da4"], + "k_0_4": ["720b79fab9a163ce6534828e8a673c5bf600161eba92c2b81555e79add59994c"], + "k_0_5": ["a10f4cf239abd4bcdb03dbe40b5c1d57ae3a7982adf8f177d481feb0ad3a52cd"], + "k_0_6": ["061f28e17ba1a56404b08a5899163011c7d6317e534ccd8e4d38911574f574b0"], + "k_0_7": ["ffc6297d487a13cb80689c448a3aef16cbd367a503d236d0aebd7218cc568e88"], + "k_0_8": ["f4a6c41fc432300509f97ca68db3b9d802d29f90c35a429e3886c480cdce44a2"], + "k_0_9": ["52f3bf96d02cd7e8c631b8ef36262994a3ec658427b930197ed004c8599cd7fd"], + "k_0_10": [ + "7993aef51bebe79bae2d29b775c499f390e99fdb6b9accb8034f657b6e72927a" + ], + "k_0_11": ["430214c9805d90c6a8c4489187db08157a93e60509e96b518dc8b5ba3d567206"] +}; +final Map> historyBatchArgs2 = { + "k_0_0": ["afe5085dd514032810d5b266007557ba8a0f4bee84535cb10754c6d46ab8689b"], + "k_0_1": ["dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c"], + "k_0_2": ["e65d4274e8edc5cc1e7b676652e2e13b0b00648d15cf13caa982ecd6a67731ba"], + "k_0_3": ["6c69ca274f7d7f2fae882a87bcee93d9429328995c5bc6b326b548b4cefcaa9f"], + "k_0_4": ["86f1a5e17dc42c27cdb0dff8a41c2434575ab05ed2f3689fd7b674677e5ea446"], + "k_0_5": ["a5d9b8df5b80c56e6053497a8c89a37267010926e80e0d225a019b78673a7aa7"], + "k_0_6": ["a0030024518874720b82b38d965fb5b3083d9f42fab40e6be4797c789eeb06f2"], + "k_0_7": ["f20077f7c6a6b92a1f75bbbad8dbece9ae4609cfdfc85e40ccac7d463bdfd6e0"], + "k_0_8": ["07b7bb4020c377e0741587efe9c0b3931e2e45f667bc6f1fa81a8f15fbe86ce4"], + "k_0_9": ["ca0322fc293f6e4d8c8adac178ed4aaedbd9acd2ec84acaaf1529f9ab7bda6d2"], + "k_0_10": [ + "06df1d13aa43375775d7d2838595a0c4c642f8af15b06a99d5115d9236e9a79e" + ], + "k_0_11": ["1a146c5a8dd5bf49faca3c6f65c9d955d361c6c00893c48d69cf7ff42c7b387b"] +}; + +final Map> historyBatchArgs3 = { + "k_0_0": ["5c2c77a3671417c5831c336805770344b81e6c7ef0d873c986ba65a7bacd5f68"], + "k_0_1": ["c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b"], + "k_0_2": ["f430c440e90c48b9e4c7e5356083e7c1495b7cad53f39ebba64cca9fb3d05c82"], + "k_0_3": ["30a7ac6789383f7f6def9a927f3b6fb661cf9406fec71a1d118c7d86052382fb"], + "k_0_4": ["a797225a9155417ab18e16b9d7ce9bf4962ae5c05df572a33c60b36a0523f257"], + "k_0_5": ["24d1e3ac9e53727d943688e67eb5c000d993e9c3cf9585d630624197fb73bed3"], + "k_0_6": ["d667a44404519649cb65632d6a3be948a1f0971025c96cb4211943d301fe0d3e"], + "k_0_7": ["be8da400f004546b528fb989c14a88324b8b0c2d5680cf080ae1e1dac4401f68"], + "k_0_8": ["addfa7682c0a2461ab0e82b3c9302b38986b442a1a76c3c839b6c2f0eaa805fe"], + "k_0_9": ["98bb3aab55f4f305fd9994334b8dd3321eda50b25fad2ef3e636714b650d0bb0"], + "k_0_10": [ + "bee1eee20d7169d03ce68d340a17f4598f589920513ec19c458db45399639a9f" + ], + "k_0_11": ["928a988dd65d100d1677a0478abfcd4d2a70aabb0812c58a2b1b4b51c395ed54"] +}; + +final Map> historyBatchArgs4 = { + "k_0_0": ["6bbfd9c1c28d6984646db4736196f67f2d1075894bb1d8990294ca7d663bece6"], + "k_0_1": ["42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3"], + "k_0_2": ["191c977174dc50a57628aea6684c428d3a5e90bbe16c4e412be51b0cfc589d38"], + "k_0_3": ["0daaf61564fd07a25ef106d958216992896f931f5bed4fbf56cc3f94443dc164"], + "k_0_4": ["ac5aca40fed2903def31c9ef1d60874247cdcc5b85238c7a1d83c67d2924d6b9"], + "k_0_5": ["c4102ff0556d863b4bab9d8232fe1f0c0fde4b6e4fe23064b4ecd0958f9726cc"], + "k_0_6": ["1c4bd1554e4992e5914dcd8f3e13927ffd46302dfdcbd2dca0cfd47c040c4256"], + "k_0_7": ["eaf5562ebef7cafa58e2c1fc4ae023e5ae8dd71ee637b08c4bc7e274e401a9a4"], + "k_0_8": ["06f7f55c221fee1b36284b5360155b8380cb9d7172b7e28eb37c61b7ebb6f227"], + "k_0_9": ["7e7ca801131ec1c5797f2c4aa46908ee50e9958cf1cbf53c2481d110800c3d6d"], + "k_0_10": [ + "3895e073aa034add7d2589bfdd1e54f6b9a8d7688d63fff0c3aac7950c6f9697" + ], + "k_0_11": ["ec17dd7c4fe8fbcfce94e9237d3c7ed7f5c91a45b1a060406e206df7e814b006"] +}; + +final Map> historyBatchArgs5 = { + "k_0_0": ["83b744ccb88827d544081c1a03ea782a7d00d6224ff9fddb7d0fbad399e1cae7"], + "k_0_1": ["86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874"], + "k_0_2": ["5baba32b1899d5e740838559ef39b7d8e9ba302bd24b732eeedd4c0e6ec65b51"], + "k_0_3": ["9892eb48394b0e155f63879fb89c3b068fcc071fed2e5cb11fe0729b85b53d67"], + "k_0_4": ["64192782cdaecb5e2a871a2d0fb3f541873e4750cd4e7d28e4d858ab40664a36"], + "k_0_5": ["4047ff48e96d25628acfeaec6ca75c1a668c54fd70a14414827cb59976a3b666"], + "k_0_6": ["299e8bc634ef6438c5bf99c12c2340c77c56ab974ffd767e77c17994e5cfaef8"], + "k_0_7": ["ab649fa14452563b385eb025e0b4cf2dd869c02fcdf2ec0f72725bbe2adaa3bd"], + "k_0_8": ["6be1ca4f8ee923e32137b6cdae324b841a0a60afbee4f4ae457fe31f29e001a6"], + "k_0_9": ["2a99ceea87df667135cc1801682d2c5dc7b95b7efadc48e156345ba46f4c0dc6"], + "k_0_10": [ + "9304094916a19040d3c8f10df90dae1144d1f09ac9e676e66bb76341c70388ac" + ], + "k_0_11": ["01b12fb2ea2533226471dfa863133ce390e3e13a804734e8af995a45aa7c7582"] +}; + +final Map>> historyBatchResponse = { + "k_0_0": [], + "s_0_0": [{}, {}], + "w_0_0": [], + "k_0_1": [{}], + "s_0_1": [], + "w_0_1": [{}, {}, {}], + "k_0_2": [], + "s_0_2": [], + "w_0_2": [], + "k_0_3": [], + "s_0_3": [], + "w_0_3": [], + "k_0_4": [], + "s_0_4": [], + "w_0_4": [], + "k_0_5": [], + "s_0_5": [], + "w_0_5": [], + "k_0_6": [], + "s_0_6": [], + "w_0_6": [], + "k_0_7": [], + "s_0_7": [], + "w_0_7": [], + "k_0_8": [], + "s_0_8": [], + "w_0_8": [], + "k_0_9": [], + "s_0_9": [], + "w_0_9": [], + "k_0_10": [], + "s_0_10": [], + "w_0_10": [], + "k_0_11": [], + "s_0_11": [], + "w_0_11": [] +}; + +final Map>> emptyHistoryBatchResponse = { + "k_0_0": [], + "s_0_0": [], + "w_0_0": [], + "k_0_1": [], + "s_0_1": [], + "w_0_1": [], + "k_0_2": [], + "s_0_2": [], + "w_0_2": [], + "k_0_3": [], + "s_0_3": [], + "w_0_3": [], + "k_0_4": [], + "s_0_4": [], + "w_0_4": [], + "k_0_5": [], + "s_0_5": [], + "w_0_5": [], + "k_0_6": [], + "s_0_6": [], + "w_0_6": [], + "k_0_7": [], + "s_0_7": [], + "w_0_7": [], + "k_0_8": [], + "s_0_8": [], + "w_0_8": [], + "k_0_9": [], + "s_0_9": [], + "w_0_9": [], + "k_0_10": [], + "s_0_10": [], + "w_0_10": [], + "k_0_11": [], + "s_0_11": [], + "w_0_11": [] +}; + +final List activeScriptHashes = [ + "dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c", + "587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e", + "cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9", + "86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874", + "c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b", + "42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3" +]; diff --git a/test/services/coins/particl/particl_transaction_data_samples.dart b/test/services/coins/particl/particl_transaction_data_samples.dart new file mode 100644 index 000000000..fb2a2a932 --- /dev/null +++ b/test/services/coins/particl/particl_transaction_data_samples.dart @@ -0,0 +1,357 @@ +// TODO these test vectors are valid for Namecoin: update for Particl + +import 'package:stackwallet/models/paymint/transactions_model.dart'; + +final transactionData = TransactionData.fromMap({ + "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6": tx1, + "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7": tx2, + "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d": tx3, + "c7e700f7e23a85bbdd9de86d502322a933607ee7ea7e16adaf02e477cdd849b9": tx4, +}); + +final tx1 = Transaction( + txid: "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6", + confirmedStatus: true, + confirmations: 212, + txType: "Received", + amount: 1000000, + fees: 23896, + height: 629633, + address: "nc1qwfda4s9qmdqpnykgpjf85n09ath983srtuxcqx", + timestamp: 1663093275, + worthNow: "0.00", + worthAtBlockTimestamp: "0.00", + inputSize: 2, + outputSize: 2, + inputs: [ + Input( + txid: "290904699ccbebd0921c4acc4f7a10f41141ee6a07bc64ebca5674c1e5ee8dfa", + vout: 1, + ), + Input( + txid: "bd84ae7e09414b0ccf5dcbf70a1f89f2fd42119a98af35dd4ecc80210fed0487", + vout: 0, + ), + ], + outputs: [ + Output( + scriptpubkeyAddress: "nc1qwfda4s9qmdqpnykgpjf85n09ath983srtuxcqx", + value: 1000000, + ), + Output( + scriptpubkeyAddress: "nc1qp7h7fxcnkqcpul202z6nh8yjy8jpt39jcpeapj", + value: 29853562, + ) + ], +); + +final tx2 = Transaction( + txid: "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + confirmedStatus: true, + confirmations: 150, + txType: "Sent", + amount: 988567, + fees: 11433, + height: 629695, + address: "nc1qraffwaq3cxngwp609e03ynwsx8ykgjnjve9f3y", + timestamp: 1663142110, + worthNow: "0.00", + worthAtBlockTimestamp: "0.00", + inputSize: 1, + outputSize: 1, + inputs: [ + Input( + txid: "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6", + vout: 0, + ), + ], + outputs: [ + Output( + scriptpubkeyAddress: "nc1qraffwaq3cxngwp609e03ynwsx8ykgjnjve9f3y", + value: 988567, + ), + ], +); + +final tx3 = Transaction( + txid: "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d", + confirmedStatus: true, + confirmations: 147, + txType: "Received", + amount: 988567, + fees: 11433, + height: 629699, + address: "nc1qw4srwqq2semrxje4x6zcrg53g07q0pr3yqv5kr", + timestamp: 1663145287, + worthNow: "0.00", + worthAtBlockTimestamp: "0.00", + inputSize: 2, + outputSize: 1, + inputs: [ + Input( + txid: "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + vout: 0, + ), + Input( + txid: "80f8c6de5be2243013348219bbb7043a6d8d00ddc716baf6a69eab517f9a6fc1", + vout: 1, + ), + ], + outputs: [ + Output( + scriptpubkeyAddress: "nc1qw4srwqq2semrxje4x6zcrg53g07q0pr3yqv5kr", + value: 1000000, + ), + Output( + scriptpubkeyAddress: "nc1qsgr7u4hd22rc64r9vlef69en9wzlvmjt8dzyrm", + value: 28805770, + ), + ], +); + +final tx4 = Transaction( + txid: "c7e700f7e23a85bbdd9de86d502322a933607ee7ea7e16adaf02e477cdd849b9", + confirmedStatus: true, + confirmations: 130, + txType: "Sent", + amount: 988567, + fees: 11433, + height: 629717, + address: "nc1qmdt0fxhpwx7x5ymmm9gvh229adu0kmtukfcsjk", + timestamp: 1663155739, + worthNow: "0.00", + worthAtBlockTimestamp: "0.00", + inputSize: 1, + outputSize: 1, + inputs: [ + Input( + txid: "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d", + vout: 0, + ), + ], + outputs: [ + Output( + scriptpubkeyAddress: "nc1qmdt0fxhpwx7x5ymmm9gvh229adu0kmtukfcsjk", + value: 988567, + ), + ], +); + +final tx1Raw = { + "txid": "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6", + "hash": "40c8dd876cf111dc00d3aa2fedc93a77c18b391931939d4f99a760226cbff675", + "version": 2, + "size": 394, + "vsize": 232, + "weight": 925, + "locktime": 0, + "vin": [ + { + "txid": + "290904699ccbebd0921c4acc4f7a10f41141ee6a07bc64ebca5674c1e5ee8dfa", + "vout": 1, + "scriptSig": { + "asm": "001466d2173325f3d379c6beb0a4949e937308edb152", + "hex": "16001466d2173325f3d379c6beb0a4949e937308edb152" + }, + "txinwitness": [ + "3044022062d0f32dc051ed1e91889a96070121c77d895f69d2ed5a307d8b320e0352186702206a0c2613e708e5ef8a935aba61b8fa14ddd6ca4e9a80a8b4ded126a879217dd101", + "0303cd92ed121ef22398826af055f3006769210e019f8fb43bd2f5556282d84997" + ], + "sequence": 4294967295 + }, + { + "txid": + "bd84ae7e09414b0ccf5dcbf70a1f89f2fd42119a98af35dd4ecc80210fed0487", + "vout": 0, + "scriptSig": {"asm": "", "hex": ""}, + "txinwitness": [ + "3045022100e8814706766a2d7588908c51209c3b7095241bbc681febdd6b317b7e9b6ea97502205c33c63e4d8a675c19122bfe0057afce2159e6bd86f2c9aced214de77099dc8b01", + "03c35212e3a4c0734735eccae9219987dc78d9cf6245ab247942d430d0a01d61be" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.01, + "n": 0, + "scriptPubKey": { + "asm": "0 725bdac0a0db401992c80c927a4de5eaee53c603", + "hex": "0014725bdac0a0db401992c80c927a4de5eaee53c603", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qwfda4s9qmdqpnykgpjf85n09ath983srtuxcqx"] + } + }, + { + "value": 0.29853562, + "n": 1, + "scriptPubKey": { + "asm": "0 0fafe49b13b0301e7d4f50b53b9c9221e415c4b2", + "hex": "00140fafe49b13b0301e7d4f50b53b9c9221e415c4b2", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qp7h7fxcnkqcpul202z6nh8yjy8jpt39jcpeapj"] + } + } + ], + "hex": + "02000000000102fa8deee5c17456caeb64bc076aee4111f4107a4fcc4a1c92d0ebcb9c69040929010000001716001466d2173325f3d379c6beb0a4949e937308edb152ffffffff8704ed0f2180cc4edd35af989a1142fdf2891f0af7cb5dcf0c4b41097eae84bd0000000000ffffffff0240420f0000000000160014725bdac0a0db401992c80c927a4de5eaee53c6037a87c701000000001600140fafe49b13b0301e7d4f50b53b9c9221e415c4b202473044022062d0f32dc051ed1e91889a96070121c77d895f69d2ed5a307d8b320e0352186702206a0c2613e708e5ef8a935aba61b8fa14ddd6ca4e9a80a8b4ded126a879217dd101210303cd92ed121ef22398826af055f3006769210e019f8fb43bd2f5556282d8499702483045022100e8814706766a2d7588908c51209c3b7095241bbc681febdd6b317b7e9b6ea97502205c33c63e4d8a675c19122bfe0057afce2159e6bd86f2c9aced214de77099dc8b012103c35212e3a4c0734735eccae9219987dc78d9cf6245ab247942d430d0a01d61be00000000", + "blockhash": + "c9f53cc7cbf654cbcc400e17b33e03a32706d6e6647ad7085c688540f980a378", + "confirmations": 212, + "time": 1663093275, + "blocktime": 1663093275 +}; + +final tx2Raw = { + "txid": "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + "hash": "32dbc0d21327e0cb94ec6069a8d235affd99689ffc5f68959bfb720bafc04bcf", + "version": 2, + "size": 192, + "vsize": 110, + "weight": 438, + "locktime": 0, + "vin": [ + { + "txid": + "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6", + "vout": 0, + "scriptSig": {"asm": "", "hex": ""}, + "txinwitness": [ + "30450221009d58ebfaab8eae297910bca93a7fd48f94ce52a1731cf27fb4c043368fa10e8d02207e88f5d868113d9567999793be0a5b752ad704d04224046839763cefe46463a501", + "02f6ca5274b59dfb014f6a0d690671964290dac7f97fe825f723204e6cb8daf086" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00988567, + "n": 0, + "scriptPubKey": { + "asm": "0 1f52977411c1a687074f2e5f124dd031c9644a72", + "hex": "00141f52977411c1a687074f2e5f124dd031c9644a72", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qraffwaq3cxngwp609e03ynwsx8ykgjnjve9f3y"] + } + } + ], + "hex": + "02000000000101c6ccf4ddc2a21434ed634636378923d01014b2d3b2f124999f3e7c88d043f53e0000000000ffffffff0197150f00000000001600141f52977411c1a687074f2e5f124dd031c9644a72024830450221009d58ebfaab8eae297910bca93a7fd48f94ce52a1731cf27fb4c043368fa10e8d02207e88f5d868113d9567999793be0a5b752ad704d04224046839763cefe46463a5012102f6ca5274b59dfb014f6a0d690671964290dac7f97fe825f723204e6cb8daf08600000000", + "blockhash": + "ae1129ee834853c45b9edbb7228497c7fa423d7d1bdec8fd155f9e3c429c84d3", + "confirmations": 150, + "time": 1663142110, + "blocktime": 1663142110 +}; + +final tx3Raw = { + "txid": "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d", + "hash": "bb25567e1ffb2fd6ec9aa3925a7a8dd3055a29521f7811b2b2bc01ce7d8a216e", + "version": 2, + "size": 370, + "vsize": 208, + "weight": 832, + "locktime": 0, + "vin": [ + { + "txid": + "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + "vout": 0, + "scriptSig": {"asm": "", "hex": ""}, + "txinwitness": [ + "304402203535cf570aca7c1acfa6e8d2f43e0b188b76d0b7a75ffca448e6af953ffe8b6302202ea52b312aaaf6d615d722bd92535d1e8b25fa9584a8dbe34dfa1ea9c18105ca01", + "038b68078a95f73f8710e8464dec52c61f9e21675ddf69d4f61b93cc417cf73d74" + ], + "sequence": 4294967295 + }, + { + "txid": + "80f8c6de5be2243013348219bbb7043a6d8d00ddc716baf6a69eab517f9a6fc1", + "vout": 1, + "scriptSig": {"asm": "", "hex": ""}, + "txinwitness": [ + "3044022045268613674326251c46caeaf435081ca753e4ee2018d79480c4930ad7d5e19f022050090a9add82e7272b8206b9d369675e7e9a5f1396fc93490143f0053666102901", + "028e2ede901e69887cb80603c8e207839f61a477d59beff17705162a2045dd974e" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.01, + "n": 0, + "scriptPubKey": { + "asm": "0 756037000a8676334b35368581a29143fc078471", + "hex": "0014756037000a8676334b35368581a29143fc078471", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qw4srwqq2semrxje4x6zcrg53g07q0pr3yqv5kr"] + } + }, + { + "value": 0.2880577, + "n": 1, + "scriptPubKey": { + "asm": "0 8207ee56ed52878d546567f29d17332b85f66e4b", + "hex": "00148207ee56ed52878d546567f29d17332b85f66e4b", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qsgr7u4hd22rc64r9vlef69en9wzlvmjt8dzyrm"] + } + } + ], + "hex": + "02000000000102d7609f2ebf00afdc6b8cda9a5e92b4b9a0b8aaafadf890fbf99721854395fadf0000000000ffffffffc16f9a7f51ab9ea6f6ba16c7dd008d6d3a04b7bb198234133024e25bdec6f8800100000000ffffffff0240420f0000000000160014756037000a8676334b35368581a29143fc0784718a8ab701000000001600148207ee56ed52878d546567f29d17332b85f66e4b0247304402203535cf570aca7c1acfa6e8d2f43e0b188b76d0b7a75ffca448e6af953ffe8b6302202ea52b312aaaf6d615d722bd92535d1e8b25fa9584a8dbe34dfa1ea9c18105ca0121038b68078a95f73f8710e8464dec52c61f9e21675ddf69d4f61b93cc417cf73d7402473044022045268613674326251c46caeaf435081ca753e4ee2018d79480c4930ad7d5e19f022050090a9add82e7272b8206b9d369675e7e9a5f1396fc93490143f005366610290121028e2ede901e69887cb80603c8e207839f61a477d59beff17705162a2045dd974e00000000", + "blockhash": + "98f388ba99e3b6fc421c23edf3c699ada082b01e5a5d130af7550b7fa6184f2f", + "confirmations": 147, + "time": 1663145287, + "blocktime": 1663145287 +}; + +final tx4Raw = { + "txid": "c7e700f7e23a85bbdd9de86d502322a933607ee7ea7e16adaf02e477cdd849b9", + "hash": "c6b544ddd7d901fcc7218208a6cfc8e1819c403a22cc8a1f1a7029aafa427925", + "version": 2, + "size": 192, + "vsize": 110, + "weight": 438, + "locktime": 0, + "vin": [ + { + "txid": + "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d", + "vout": 0, + "scriptSig": {"asm": "", "hex": ""}, + "txinwitness": [ + "3045022100c664c6ad206999e019954c5206a26c2eca1ae2572288c0f78074c279a4a210ce022017456fdf85f744d694fa2e4638acee782d809268ea4808c04d91da3ac4fe7fd401", + "035456b63e86c0a6235cb3debfb9654966a4c2362ec678ae3b9beec53d31a25eba" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00988567, + "n": 0, + "scriptPubKey": { + "asm": "0 db56f49ae171bc6a137bd950cba945eb78fb6d7c", + "hex": "0014db56f49ae171bc6a137bd950cba945eb78fb6d7c", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": ["nc1qmdt0fxhpwx7x5ymmm9gvh229adu0kmtukfcsjk"] + } + } + ], + "hex": + "020000000001014da0dde1ee465c062356dd3e2f9d04430753148b0f0dc3d81b32e7e93265b5710000000000ffffffff0197150f0000000000160014db56f49ae171bc6a137bd950cba945eb78fb6d7c02483045022100c664c6ad206999e019954c5206a26c2eca1ae2572288c0f78074c279a4a210ce022017456fdf85f744d694fa2e4638acee782d809268ea4808c04d91da3ac4fe7fd40121035456b63e86c0a6235cb3debfb9654966a4c2362ec678ae3b9beec53d31a25eba00000000", + "blockhash": + "6f60029ff3a32ca2d7e7e23c02b9cb35f61e7f9481992f9c3ded2c60c7b1de9b", + "confirmations": 130, + "time": 1663155739, + "blocktime": 1663155739 +}; diff --git a/test/services/coins/particl/particl_utxo_sample_data.dart b/test/services/coins/particl/particl_utxo_sample_data.dart new file mode 100644 index 000000000..5a0dff492 --- /dev/null +++ b/test/services/coins/particl/particl_utxo_sample_data.dart @@ -0,0 +1,60 @@ +// TODO these test vectors are valid for Namecoin: update for Particl + +import 'package:stackwallet/models/paymint/utxo_model.dart'; + +final Map>> batchGetUTXOResponse0 = { + "some id 0": [ + { + "tx_pos": 0, + "value": 988567, + "tx_hash": + "32dbc0d21327e0cb94ec6069a8d235affd99689ffc5f68959bfb720bafc04bcf", + "height": 629695 + }, + { + "tx_pos": 0, + "value": 1000000, + "tx_hash": + "40c8dd876cf111dc00d3aa2fedc93a77c18b391931939d4f99a760226cbff675", + "height": 629633 + }, + ], + "some id 1": [], +}; + +final utxoList = [ + UtxoObject( + txid: "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + vout: 0, + status: Status( + confirmed: true, + confirmations: 150, + blockHeight: 629695, + blockTime: 1663142110, + blockHash: + "32dbc0d21327e0cb94ec6069a8d235affd99689ffc5f68959bfb720bafc04bcf", + ), + value: 988567, + fiatWorth: "\$0", + txName: "nc1qraffwaq3cxngwp609e03ynwsx8ykgjnjve9f3y", + blocked: false, + isCoinbase: false, + ), + UtxoObject( + txid: "3ef543d0887c3e9f9924f1b2d3b21410d0238937364663ed3414a2c2ddf4ccc6", + vout: 0, + status: Status( + confirmed: true, + confirmations: 212, + blockHeight: 629633, + blockTime: 1663093275, + blockHash: + "40c8dd876cf111dc00d3aa2fedc93a77c18b391931939d4f99a760226cbff675", + ), + value: 1000000, + fiatWorth: "\$0", + txName: "nc1qwfda4s9qmdqpnykgpjf85n09ath983srtuxcqx", + blocked: false, + isCoinbase: false, + ), +]; diff --git a/test/services/coins/particl/particl_wallet_test.dart b/test/services/coins/particl/particl_wallet_test.dart new file mode 100644 index 000000000..d867def8e --- /dev/null +++ b/test/services/coins/particl/particl_wallet_test.dart @@ -0,0 +1,1743 @@ +import 'package:decimal/decimal.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_test/hive_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; +import 'package:stackwallet/electrumx_rpc/electrumx.dart'; +import 'package:stackwallet/hive/db.dart'; +import 'package:stackwallet/models/paymint/fee_object_model.dart'; +import 'package:stackwallet/models/paymint/transactions_model.dart'; +import 'package:stackwallet/models/paymint/utxo_model.dart'; +import 'package:stackwallet/services/coins/particl/particl_wallet.dart'; +import 'package:stackwallet/services/price.dart'; +import 'package:stackwallet/services/transaction_notification_tracker.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; +import 'package:tuple/tuple.dart'; + +import 'particl_history_sample_data.dart'; +import 'particl_transaction_data_samples.dart'; +import 'particl_utxo_sample_data.dart'; +import 'particl_wallet_test.mocks.dart'; +import 'particl_wallet_test_parameters.dart'; + +@GenerateMocks( + [ElectrumX, CachedElectrumX, PriceAPI, TransactionNotificationTracker]) +void main() { + group("particl constants", () { + test("particl minimum confirmations", () async { + expect(MINIMUM_CONFIRMATIONS, 2); + }); + test("particl dust limit", () async { + expect(DUST_LIMIT, 546); + }); + test("particl mainnet genesis block hash", () async { + expect(GENESIS_HASH_MAINNET, + "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770"); + }); + test("particl testnet genesis block hash", () async { + expect(GENESIS_HASH_TESTNET, + "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); + }); + }); + + test("particl DerivePathType enum", () { + expect(DerivePathType.values.length, 3); + expect(DerivePathType.values.toString(), + "[DerivePathType.bip44, DerivePathType.bip49, DerivePathType.bip84]"); + }); + + group("bip32 node/root", () { + test("getBip32Root", () { + final root = getBip32Root(TEST_MNEMONIC, namecoin); + expect(root.toWIF(), ROOT_WIF); + }); + + // test("getBip32NodeFromRoot", () { + // final root = getBip32Root(TEST_MNEMONIC, namecoin); + // // two mainnet + // final node44 = getBip32NodeFromRoot(0, 0, root, DerivePathType.bip44); + // expect(node44.toWIF(), NODE_WIF_44); + // final node49 = getBip32NodeFromRoot(0, 0, root, DerivePathType.bip49); + // expect(node49.toWIF(), NODE_WIF_49); + // // and one on testnet + // final node84 = getBip32NodeFromRoot( + // 0, 0, getBip32Root(TEST_MNEMONIC, testnet), DerivePathType.bip84); + // expect(node84.toWIF(), NODE_WIF_84); + // // a bad derive path + // bool didThrow = false; + // try { + // getBip32NodeFromRoot(0, 0, root, null); + // } catch (_) { + // didThrow = true; + // } + // expect(didThrow, true); + // // finally an invalid network + // didThrow = false; + // final invalidNetwork = NetworkType( + // messagePrefix: '\x18hello world\n', + // bech32: 'gg', + // bip32: Bip32Type(public: 0x055521e, private: 0x055555), + // pubKeyHash: 0x55, + // scriptHash: 0x55, + // wif: 0x00); + // try { + // getBip32NodeFromRoot(0, 0, getBip32Root(TEST_MNEMONIC, invalidNetwork), + // DerivePathType.bip44); + // } catch (_) { + // didThrow = true; + // } + // expect(didThrow, true); + // }); + + // test("basic getBip32Node", () { + // final node = + // getBip32Node(0, 0, TEST_MNEMONIC, testnet, DerivePathType.bip84); + // expect(node.toWIF(), NODE_WIF_84); + // }); + }); + + group("validate mainnet namecoin addresses", () { + MockElectrumX? client; + MockCachedElectrumX? cachedClient; + MockPriceAPI? priceAPI; + late FakeSecureStorage secureStore; + MockTransactionNotificationTracker? tracker; + + NamecoinWallet? mainnetWallet; + + setUp(() { + client = MockElectrumX(); + cachedClient = MockCachedElectrumX(); + priceAPI = MockPriceAPI(); + secureStore = FakeSecureStorage(); + tracker = MockTransactionNotificationTracker(); + + mainnetWallet = NamecoinWallet( + walletId: "validateAddressMainNet", + walletName: "validateAddressMainNet", + coin: Coin.particl, + client: client!, + cachedClient: cachedClient!, + tracker: tracker!, + priceAPI: priceAPI, + secureStore: secureStore, + ); + }); + + test("valid mainnet legacy/p2pkh address type", () { + expect( + mainnetWallet?.addressType( + address: "N673DDbjPcrNgJmrhJ1xQXF9LLizQzvjEs"), + DerivePathType.bip44); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + + test("valid mainnet bech32 p2wpkh address type", () { + expect( + mainnetWallet?.addressType( + address: "nc1q6k4x8ye6865z3rc8zkt8gyu52na7njqt6hsk4v"), + DerivePathType.bip84); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + + test("invalid bech32 address type", () { + expect( + () => mainnetWallet?.addressType( + address: "tb1qzzlm6mnc8k54mx6akehl8p9ray8r439va5ndyq"), + throwsArgumentError); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + + test("address has no matching script", () { + expect( + () => mainnetWallet?.addressType( + address: "mpMk94ETazqonHutyC1v6ajshgtP8oiFKU"), + throwsArgumentError); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + }); + + group("testNetworkConnection", () { + MockElectrumX? client; + MockCachedElectrumX? cachedClient; + MockPriceAPI? priceAPI; + late FakeSecureStorage secureStore; + MockTransactionNotificationTracker? tracker; + + NamecoinWallet? nmc; + + setUp(() { + client = MockElectrumX(); + cachedClient = MockCachedElectrumX(); + priceAPI = MockPriceAPI(); + secureStore = FakeSecureStorage(); + tracker = MockTransactionNotificationTracker(); + + nmc = NamecoinWallet( + walletId: "testNetworkConnection", + walletName: "testNetworkConnection", + coin: Coin.particl, + client: client!, + cachedClient: cachedClient!, + tracker: tracker!, + priceAPI: priceAPI, + secureStore: secureStore, + ); + }); + + test("attempted connection fails due to server error", () async { + when(client?.ping()).thenAnswer((_) async => false); + final bool? result = await nmc?.testNetworkConnection(); + expect(result, false); + expect(secureStore.interactions, 0); + verify(client?.ping()).called(1); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("attempted connection fails due to exception", () async { + when(client?.ping()).thenThrow(Exception); + final bool? result = await nmc?.testNetworkConnection(); + expect(result, false); + expect(secureStore.interactions, 0); + verify(client?.ping()).called(1); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("attempted connection test success", () async { + when(client?.ping()).thenAnswer((_) async => true); + final bool? result = await nmc?.testNetworkConnection(); + expect(result, true); + expect(secureStore.interactions, 0); + verify(client?.ping()).called(1); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + }); + + group("basic getters, setters, and functions", () { + final testWalletId = "NMCtestWalletID"; + final testWalletName = "NMCWallet"; + + MockElectrumX? client; + MockCachedElectrumX? cachedClient; + MockPriceAPI? priceAPI; + late FakeSecureStorage secureStore; + MockTransactionNotificationTracker? tracker; + + NamecoinWallet? nmc; + + setUp(() async { + client = MockElectrumX(); + cachedClient = MockCachedElectrumX(); + priceAPI = MockPriceAPI(); + secureStore = FakeSecureStorage(); + tracker = MockTransactionNotificationTracker(); + + nmc = NamecoinWallet( + walletId: testWalletId, + walletName: testWalletName, + coin: Coin.particl, + client: client!, + cachedClient: cachedClient!, + tracker: tracker!, + priceAPI: priceAPI, + secureStore: secureStore, + ); + }); + + test("get networkType main", () async { + expect(Coin.particl, Coin.particl); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get networkType test", () async { + nmc = NamecoinWallet( + walletId: testWalletId, + walletName: testWalletName, + coin: Coin.particl, + client: client!, + cachedClient: cachedClient!, + tracker: tracker!, + priceAPI: priceAPI, + secureStore: secureStore, + ); + expect(Coin.particl, Coin.particl); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get cryptoCurrency", () async { + expect(Coin.particl, Coin.particl); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get coinName", () async { + expect(Coin.particl, Coin.particl); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get coinTicker", () async { + expect(Coin.particl, Coin.particl); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get and set walletName", () async { + expect(Coin.particl, Coin.particl); + nmc?.walletName = "new name"; + expect(nmc?.walletName, "new name"); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("estimateTxFee", () async { + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 1), 356); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 900), 356); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 999), 356); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 1000), 356); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 1001), 712); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 1699), 712); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 2000), 712); + expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 12345), 4628); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get fees succeeds", () async { + when(client?.ping()).thenAnswer((_) async => true); + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.estimateFee(blocks: 1)) + .thenAnswer((realInvocation) async => Decimal.zero); + when(client?.estimateFee(blocks: 5)) + .thenAnswer((realInvocation) async => Decimal.one); + when(client?.estimateFee(blocks: 20)) + .thenAnswer((realInvocation) async => Decimal.ten); + + final fees = await nmc?.fees; + expect(fees, isA()); + expect(fees?.slow, 1000000000); + expect(fees?.medium, 100000000); + expect(fees?.fast, 0); + + verify(client?.estimateFee(blocks: 1)).called(1); + verify(client?.estimateFee(blocks: 5)).called(1); + verify(client?.estimateFee(blocks: 20)).called(1); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get fees fails", () async { + when(client?.ping()).thenAnswer((_) async => true); + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.estimateFee(blocks: 1)) + .thenAnswer((realInvocation) async => Decimal.zero); + when(client?.estimateFee(blocks: 5)) + .thenAnswer((realInvocation) async => Decimal.one); + when(client?.estimateFee(blocks: 20)) + .thenThrow(Exception("some exception")); + + bool didThrow = false; + try { + await nmc?.fees; + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + verify(client?.estimateFee(blocks: 1)).called(1); + verify(client?.estimateFee(blocks: 5)).called(1); + verify(client?.estimateFee(blocks: 20)).called(1); + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + // test("get maxFee", () async { + // when(client?.ping()).thenAnswer((_) async => true); + // when(client?.getServerFeatures()).thenAnswer((_) async => { + // "hosts": {}, + // "pruning": null, + // "server_version": "Unit tests", + // "protocol_min": "1.4", + // "protocol_max": "1.4.2", + // "genesis_hash": GENESIS_HASH_TESTNET, + // "hash_function": "sha256", + // "services": [] + // }); + // when(client?.estimateFee(blocks: 20)) + // .thenAnswer((realInvocation) async => Decimal.zero); + // when(client?.estimateFee(blocks: 5)) + // .thenAnswer((realInvocation) async => Decimal.one); + // when(client?.estimateFee(blocks: 1)) + // .thenAnswer((realInvocation) async => Decimal.ten); + // + // final maxFee = await nmc?.maxFee; + // expect(maxFee, 1000000000); + // + // verify(client?.estimateFee(blocks: 1)).called(1); + // verify(client?.estimateFee(blocks: 5)).called(1); + // verify(client?.estimateFee(blocks: 20)).called(1); + // expect(secureStore.interactions, 0); + // verifyNoMoreInteractions(client); + // verifyNoMoreInteractions(cachedClient); + // verifyNoMoreInteractions(tracker); + // verifyNoMoreInteractions(priceAPI); + // }); + }); + + group("Particl service class functions that depend on shared storage", () { + final testWalletId = "NMCtestWalletID"; + final testWalletName = "NMCWallet"; + + bool hiveAdaptersRegistered = false; + + MockElectrumX? client; + MockCachedElectrumX? cachedClient; + MockPriceAPI? priceAPI; + late FakeSecureStorage secureStore; + MockTransactionNotificationTracker? tracker; + + NamecoinWallet? nmc; + + setUp(() async { + await setUpTestHive(); + if (!hiveAdaptersRegistered) { + hiveAdaptersRegistered = true; + + // Registering Transaction Model Adapters + Hive.registerAdapter(TransactionDataAdapter()); + Hive.registerAdapter(TransactionChunkAdapter()); + Hive.registerAdapter(TransactionAdapter()); + Hive.registerAdapter(InputAdapter()); + Hive.registerAdapter(OutputAdapter()); + + // Registering Utxo Model Adapters + Hive.registerAdapter(UtxoDataAdapter()); + Hive.registerAdapter(UtxoObjectAdapter()); + Hive.registerAdapter(StatusAdapter()); + + final wallets = await Hive.openBox('wallets'); + await wallets.put('currentWalletName', testWalletName); + } + + client = MockElectrumX(); + cachedClient = MockCachedElectrumX(); + priceAPI = MockPriceAPI(); + secureStore = FakeSecureStorage(); + tracker = MockTransactionNotificationTracker(); + + nmc = NamecoinWallet( + walletId: testWalletId, + walletName: testWalletName, + coin: Coin.particl, + client: client!, + cachedClient: cachedClient!, + tracker: tracker!, + priceAPI: priceAPI, + secureStore: secureStore, + ); + }); + + // test("initializeWallet no network", () async { + // when(client?.ping()).thenAnswer((_) async => false); + // expect(await nmc?.initializeWallet(), false); + // expect(secureStore.interactions, 0); + // verify(client?.ping()).called(1); + // verifyNoMoreInteractions(client); + // verifyNoMoreInteractions(cachedClient); + // verifyNoMoreInteractions(priceAPI); + // }); + + // test("initializeWallet no network exception", () async { + // when(client?.ping()).thenThrow(Exception("Network connection failed")); + // final wallets = await Hive.openBox(testWalletId); + // expect(await nmc?.initializeExisting(), false); + // expect(secureStore.interactions, 0); + // verify(client?.ping()).called(1); + // verifyNoMoreInteractions(client); + // verifyNoMoreInteractions(cachedClient); + // verifyNoMoreInteractions(priceAPI); + // }); + + test("initializeWallet mainnet throws bad network", () async { + when(client?.ping()).thenAnswer((_) async => true); + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + // await nmc?.initializeNew(); + final wallets = await Hive.openBox(testWalletId); + + expectLater(() => nmc?.initializeExisting(), throwsA(isA())) + .then((_) { + expect(secureStore.interactions, 0); + // verify(client?.ping()).called(1); + // verify(client?.getServerFeatures()).called(1); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + }); + + test("initializeWallet throws mnemonic overwrite exception", () async { + when(client?.ping()).thenAnswer((_) async => true); + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + await secureStore.write( + key: "${testWalletId}_mnemonic", value: "some mnemonic"); + + final wallets = await Hive.openBox(testWalletId); + expectLater(() => nmc?.initializeExisting(), throwsA(isA())) + .then((_) { + expect(secureStore.interactions, 1); + // verify(client?.ping()).called(1); + // verify(client?.getServerFeatures()).called(1); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + }); + + test( + "recoverFromMnemonic using empty seed on mainnet fails due to bad genesis hash match", + () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_TESTNET, + "hash_function": "sha256", + "services": [] + }); + + bool hasThrown = false; + try { + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, true); + + verify(client?.getServerFeatures()).called(1); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test( + "recoverFromMnemonic using empty seed on mainnet fails due to attempted overwrite of mnemonic", + () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + + await secureStore.write( + key: "${testWalletId}_mnemonic", value: "some mnemonic words"); + + bool hasThrown = false; + try { + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, true); + + verify(client?.getServerFeatures()).called(1); + + expect(secureStore.interactions, 2); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("recoverFromMnemonic using empty seed on mainnet succeeds", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + await DB.instance.init(); + final wallet = await Hive.openBox(testWalletId); + bool hasThrown = false; + try { + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + expect(secureStore.interactions, 20); + expect(secureStore.writes, 7); + expect(secureStore.reads, 13); + expect(secureStore.deletes, 0); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("get mnemonic list", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => emptyHistoryBatchResponse); + + final wallet = await Hive.openBox(testWalletId); + + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + expect(await nmc?.mnemonic, TEST_MNEMONIC.split(" ")); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("recoverFromMnemonic using non empty seed on mainnet succeeds", + () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); + + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); + + bool hasThrown = false; + try { + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } + + expect(secureStore.interactions, 14); + expect(secureStore.writes, 7); + expect(secureStore.reads, 7); + expect(secureStore.deletes, 0); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("fullRescan succeeds", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); + when(cachedClient?.clearSharedTransactionCache(coin: Coin.particl)) + .thenAnswer((realInvocation) async {}); + + when(client?.getBatchHistory(args: { + "0": [ + "dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + when(client?.getBatchHistory(args: { + "0": [ + "587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + when(client?.getBatchHistory(args: { + "0": [ + "42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + final wallet = await Hive.openBox(testWalletId); + + // restore so we have something to rescan + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + // fetch valid wallet data + final preReceivingAddressesP2PKH = + await wallet.get('receivingAddressesP2PKH'); + final preReceivingAddressesP2SH = + await wallet.get('receivingAddressesP2SH'); + final preReceivingAddressesP2WPKH = + await wallet.get('receivingAddressesP2WPKH'); + final preChangeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); + final preChangeAddressesP2SH = await wallet.get('changeAddressesP2SH'); + final preChangeAddressesP2WPKH = + await wallet.get('changeAddressesP2WPKH'); + final preReceivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); + final preReceivingIndexP2SH = await wallet.get('receivingIndexP2SH'); + final preReceivingIndexP2WPKH = await wallet.get('receivingIndexP2WPKH'); + final preChangeIndexP2PKH = await wallet.get('changeIndexP2PKH'); + final preChangeIndexP2SH = await wallet.get('changeIndexP2SH'); + final preChangeIndexP2WPKH = await wallet.get('changeIndexP2WPKH'); + final preUtxoData = await wallet.get('latest_utxo_model'); + final preReceiveDerivationsStringP2PKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2PKH"); + final preChangeDerivationsStringP2PKH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH"); + final preReceiveDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH"); + final preChangeDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH"); + final preReceiveDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2WPKH"); + final preChangeDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_changeDerivationsP2WPKH"); + + // destroy the data that the rescan will fix + await wallet.put( + 'receivingAddressesP2PKH', ["some address", "some other address"]); + await wallet.put( + 'receivingAddressesP2SH', ["some address", "some other address"]); + await wallet.put( + 'receivingAddressesP2WPKH', ["some address", "some other address"]); + await wallet + .put('changeAddressesP2PKH', ["some address", "some other address"]); + await wallet + .put('changeAddressesP2SH', ["some address", "some other address"]); + await wallet + .put('changeAddressesP2WPKH', ["some address", "some other address"]); + await wallet.put('receivingIndexP2PKH', 123); + await wallet.put('receivingIndexP2SH', 123); + await wallet.put('receivingIndexP2WPKH', 123); + await wallet.put('changeIndexP2PKH', 123); + await wallet.put('changeIndexP2SH', 123); + await wallet.put('changeIndexP2WPKH', 123); + await secureStore.write( + key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}"); + await secureStore.write( + key: "${testWalletId}_changeDerivationsP2PKH", value: "{}"); + await secureStore.write( + key: "${testWalletId}_receiveDerivationsP2SH", value: "{}"); + await secureStore.write( + key: "${testWalletId}_changeDerivationsP2SH", value: "{}"); + await secureStore.write( + key: "${testWalletId}_receiveDerivationsP2WPKH", value: "{}"); + await secureStore.write( + key: "${testWalletId}_changeDerivationsP2WPKH", value: "{}"); + + bool hasThrown = false; + try { + await nmc?.fullRescan(2, 1000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, false); + + // fetch wallet data again + final receivingAddressesP2PKH = + await wallet.get('receivingAddressesP2PKH'); + final receivingAddressesP2SH = await wallet.get('receivingAddressesP2SH'); + final receivingAddressesP2WPKH = + await wallet.get('receivingAddressesP2WPKH'); + final changeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); + final changeAddressesP2SH = await wallet.get('changeAddressesP2SH'); + final changeAddressesP2WPKH = await wallet.get('changeAddressesP2WPKH'); + final receivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); + final receivingIndexP2SH = await wallet.get('receivingIndexP2SH'); + final receivingIndexP2WPKH = await wallet.get('receivingIndexP2WPKH'); + final changeIndexP2PKH = await wallet.get('changeIndexP2PKH'); + final changeIndexP2SH = await wallet.get('changeIndexP2SH'); + final changeIndexP2WPKH = await wallet.get('changeIndexP2WPKH'); + final utxoData = await wallet.get('latest_utxo_model'); + final receiveDerivationsStringP2PKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2PKH"); + final changeDerivationsStringP2PKH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH"); + final receiveDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH"); + final changeDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH"); + final receiveDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2WPKH"); + final changeDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_changeDerivationsP2WPKH"); + + expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH); + expect(preReceivingAddressesP2SH, receivingAddressesP2SH); + expect(preReceivingAddressesP2WPKH, receivingAddressesP2WPKH); + expect(preChangeAddressesP2PKH, changeAddressesP2PKH); + expect(preChangeAddressesP2SH, changeAddressesP2SH); + expect(preChangeAddressesP2WPKH, changeAddressesP2WPKH); + expect(preReceivingIndexP2PKH, receivingIndexP2PKH); + expect(preReceivingIndexP2SH, receivingIndexP2SH); + expect(preReceivingIndexP2WPKH, receivingIndexP2WPKH); + expect(preChangeIndexP2PKH, changeIndexP2PKH); + expect(preChangeIndexP2SH, changeIndexP2SH); + expect(preChangeIndexP2WPKH, changeIndexP2WPKH); + expect(preUtxoData, utxoData); + expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH); + expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH); + expect(preReceiveDerivationsStringP2SH, receiveDerivationsStringP2SH); + expect(preChangeDerivationsStringP2SH, changeDerivationsStringP2SH); + expect(preReceiveDerivationsStringP2WPKH, receiveDerivationsStringP2WPKH); + expect(preChangeDerivationsStringP2WPKH, changeDerivationsStringP2WPKH); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874" + ] + })).called(2); + + verify(client?.getBatchHistory(args: { + "0": [ + "c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b" + ] + })).called(2); + + verify(client?.getBatchHistory(args: { + "0": [ + "cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9" + ] + })).called(2); + + verify(client?.getBatchHistory(args: { + "0": [ + "587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e" + ] + })).called(2); + + verify(client?.getBatchHistory(args: { + "0": [ + "42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3" + ] + })).called(2); + verify(cachedClient?.clearSharedTransactionCache(coin: Coin.particl)) + .called(1); + + // for (final arg in dynamicArgValues) { + // final map = Map>.from(arg as Map); + // Map argCount = {}; + // + // // verify(client?.getBatchHistory(args: map)).called(1); + // // expect(activeScriptHashes.contains(map.values.first.first as String), + // // true); + // } + + // Map argCount = {}; + // + // for (final arg in dynamicArgValues) { + // final map = Map>.from(arg as Map); + // + // final str = jsonEncode(map); + // + // if (argCount[str] == null) { + // argCount[str] = 1; + // } else { + // argCount[str] = argCount[str]! + 1; + // } + // } + // + // argCount.forEach((key, value) => print("arg: $key\ncount: $value")); + + expect(secureStore.writes, 25); + expect(secureStore.reads, 32); + expect(secureStore.deletes, 6); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("fullRescan fails", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); + + when(client?.getBatchHistory(args: { + "0": [ + "dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(client?.getBatchHistory(args: { + "0": [ + "c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b" + ] + })).thenAnswer((realInvocation) async => {"0": []}); + + when(cachedClient?.clearSharedTransactionCache(coin: Coin.particl)) + .thenAnswer((realInvocation) async {}); + + final wallet = await Hive.openBox(testWalletId); + + // restore so we have something to rescan + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + // fetch wallet data + final preReceivingAddressesP2PKH = + await wallet.get('receivingAddressesP2PKH'); + final preReceivingAddressesP2SH = + await wallet.get('receivingAddressesP2SH'); + final preReceivingAddressesP2WPKH = + await wallet.get('receivingAddressesP2WPKH'); + final preChangeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); + final preChangeAddressesP2SH = await wallet.get('changeAddressesP2SH'); + final preChangeAddressesP2WPKH = + await wallet.get('changeAddressesP2WPKH'); + final preReceivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); + final preReceivingIndexP2SH = await wallet.get('receivingIndexP2SH'); + final preReceivingIndexP2WPKH = await wallet.get('receivingIndexP2WPKH'); + final preChangeIndexP2PKH = await wallet.get('changeIndexP2PKH'); + final preChangeIndexP2SH = await wallet.get('changeIndexP2SH'); + final preChangeIndexP2WPKH = await wallet.get('changeIndexP2WPKH'); + final preUtxoData = await wallet.get('latest_utxo_model'); + final preReceiveDerivationsStringP2PKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2PKH"); + final preChangeDerivationsStringP2PKH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH"); + final preReceiveDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH"); + final preChangeDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH"); + final preReceiveDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2WPKH"); + final preChangeDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_changeDerivationsP2WPKH"); + + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenThrow(Exception("fake exception")); + + bool hasThrown = false; + try { + await nmc?.fullRescan(2, 1000); + } catch (_) { + hasThrown = true; + } + expect(hasThrown, true); + + // fetch wallet data again + final receivingAddressesP2PKH = + await wallet.get('receivingAddressesP2PKH'); + final receivingAddressesP2SH = await wallet.get('receivingAddressesP2SH'); + final receivingAddressesP2WPKH = + await wallet.get('receivingAddressesP2WPKH'); + final changeAddressesP2PKH = await wallet.get('changeAddressesP2PKH'); + final changeAddressesP2SH = await wallet.get('changeAddressesP2SH'); + final changeAddressesP2WPKH = await wallet.get('changeAddressesP2WPKH'); + final receivingIndexP2PKH = await wallet.get('receivingIndexP2PKH'); + final receivingIndexP2SH = await wallet.get('receivingIndexP2SH'); + final receivingIndexP2WPKH = await wallet.get('receivingIndexP2WPKH'); + final changeIndexP2PKH = await wallet.get('changeIndexP2PKH'); + final changeIndexP2SH = await wallet.get('changeIndexP2SH'); + final changeIndexP2WPKH = await wallet.get('changeIndexP2WPKH'); + final utxoData = await wallet.get('latest_utxo_model'); + final receiveDerivationsStringP2PKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2PKH"); + final changeDerivationsStringP2PKH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH"); + final receiveDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH"); + final changeDerivationsStringP2SH = + await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH"); + final receiveDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_receiveDerivationsP2WPKH"); + final changeDerivationsStringP2WPKH = await secureStore.read( + key: "${testWalletId}_changeDerivationsP2WPKH"); + + expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH); + expect(preReceivingAddressesP2SH, receivingAddressesP2SH); + expect(preReceivingAddressesP2WPKH, receivingAddressesP2WPKH); + expect(preChangeAddressesP2PKH, changeAddressesP2PKH); + expect(preChangeAddressesP2SH, changeAddressesP2SH); + expect(preChangeAddressesP2WPKH, changeAddressesP2WPKH); + expect(preReceivingIndexP2PKH, receivingIndexP2PKH); + expect(preReceivingIndexP2SH, receivingIndexP2SH); + expect(preReceivingIndexP2WPKH, receivingIndexP2WPKH); + expect(preChangeIndexP2PKH, changeIndexP2PKH); + expect(preChangeIndexP2SH, changeIndexP2SH); + expect(preChangeIndexP2WPKH, changeIndexP2WPKH); + expect(preUtxoData, utxoData); + expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH); + expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH); + expect(preReceiveDerivationsStringP2SH, receiveDerivationsStringP2SH); + expect(preChangeDerivationsStringP2SH, changeDerivationsStringP2SH); + expect(preReceiveDerivationsStringP2WPKH, receiveDerivationsStringP2WPKH); + expect(preChangeDerivationsStringP2WPKH, changeDerivationsStringP2WPKH); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2); + + verify(client?.getBatchHistory(args: { + "0": [ + "dd63fc12f5e6c1ada2cf3c941d1648e6d561ce4024747bb2117d72112d83287c" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "cd3dd4abe4f9efc7149ba334d2d6790020331805b0bd5c7ed89a3ac6a22f10b9" + ] + })).called(1); + verify(client?.getBatchHistory(args: { + "0": [ + "42d6e40636f4740f9c7f95ef0bbc2a4c17f54da2bc98a32a622e2bf73eb675c3" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "587943864cefed4f1643a5ee2ce2b3c13a0c6ad7c435373f0ac328e144a15c1e" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "86906979fc9107d06d560275d7de8305b69d7189c3206ac9070ad76e6abff874" + ] + })).called(2); + verify(client?.getBatchHistory(args: { + "0": [ + "c068e7fa4aa0b8a63114f6d11c047ca4be6a8fa333eb0dac48506e8f150af73b" + ] + })).called(2); + verify(cachedClient?.clearSharedTransactionCache(coin: Coin.particl)) + .called(1); + + expect(secureStore.writes, 19); + expect(secureStore.reads, 32); + expect(secureStore.deletes, 12); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("prepareSend fails", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); + + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); + + when(cachedClient?.getTransaction( + txHash: + "dffa9543852197f9fb90f8adafaab8a0b9b4925e9ada8c6bdcaf00bf2e9f60d7", + coin: Coin.particl)) + .thenAnswer((_) async => tx2Raw); + when(cachedClient?.getTransaction( + txHash: + "71b56532e9e7321bd8c30d0f8b14530743049d2f3edd5623065c46eee1dda04d", + coin: Coin.particl)) + .thenAnswer((_) async => tx3Raw); + when(cachedClient?.getTransaction( + txHash: + "c7e700f7e23a85bbdd9de86d502322a933607ee7ea7e16adaf02e477cdd849b9", + coin: Coin.particl, + )).thenAnswer((_) async => tx4Raw); + + // recover to fill data + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + // modify addresses to properly mock data to build a tx + final rcv44 = await secureStore.read( + key: testWalletId + "_receiveDerivationsP2PKH"); + await secureStore.write( + key: testWalletId + "_receiveDerivationsP2PKH", + value: rcv44?.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw", + "16FuTPaeRSPVxxCnwQmdyx2PQWxX6HWzhQ")); + final rcv49 = + await secureStore.read(key: testWalletId + "_receiveDerivationsP2SH"); + await secureStore.write( + key: testWalletId + "_receiveDerivationsP2SH", + value: rcv49?.replaceFirst("3AV74rKfibWmvX34F99yEvUcG4LLQ9jZZk", + "36NvZTcMsMowbt78wPzJaHHWaNiyR73Y4g")); + final rcv84 = await secureStore.read( + key: testWalletId + "_receiveDerivationsP2WPKH"); + await secureStore.write( + key: testWalletId + "_receiveDerivationsP2WPKH", + value: rcv84?.replaceFirst( + "bc1qggtj4ka8jsaj44hhd5mpamx7mp34m2d3w7k0m0", + "bc1q42lja79elem0anu8q8s3h2n687re9jax556pcc")); + + nmc?.outputsList = utxoList; + + bool didThrow = false; + try { + await nmc?.prepareSend( + address: "nc1q6k4x8ye6865z3rc8zkt8gyu52na7njqt6hsk4v", + satoshiAmount: 15000); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + verify(client?.getServerFeatures()).called(1); + + /// verify transaction no matching calls + + // verify(cachedClient?.getTransaction( + // txHash: + // "2087ce09bc316877c9f10971526a2bffa3078d52ea31752639305cdcd8230703", + // coin: Coin.particl, + // callOutSideMainIsolate: false)) + // .called(1); + // verify(cachedClient?.getTransaction( + // txHash: + // "ed32c967a0e86d51669ac21c2bb9bc9c50f0f55fbacdd8db21d0a986fba93bd7", + // coin: Coin.particl, + // callOutSideMainIsolate: false)) + // .called(1); + // verify(cachedClient?.getTransaction( + // txHash: + // "3f0032f89ac44b281b50314cff3874c969c922839dddab77ced54e86a21c3fd4", + // coin: Coin.particl, + // callOutSideMainIsolate: false)) + // .called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } + + expect(secureStore.interactions, 20); + expect(secureStore.writes, 10); + expect(secureStore.reads, 10); + expect(secureStore.deletes, 0); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("confirmSend no hex", () async { + bool didThrow = false; + try { + await nmc?.confirmSend(txData: {"some": "strange map"}); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("confirmSend hex is not string", () async { + bool didThrow = false; + try { + await nmc?.confirmSend(txData: {"hex": true}); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("confirmSend hex is string but missing other data", () async { + bool didThrow = false; + try { + await nmc?.confirmSend(txData: {"hex": "a string"}); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + verify(client?.broadcastTransaction( + rawTx: "a string", requestID: anyNamed("requestID"))) + .called(1); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("confirmSend fails due to vSize being greater than fee", () async { + bool didThrow = false; + try { + await nmc + ?.confirmSend(txData: {"hex": "a string", "fee": 1, "vSize": 10}); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + verify(client?.broadcastTransaction( + rawTx: "a string", requestID: anyNamed("requestID"))) + .called(1); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + test("confirmSend fails when broadcast transactions throws", () async { + when(client?.broadcastTransaction( + rawTx: "a string", requestID: anyNamed("requestID"))) + .thenThrow(Exception("some exception")); + + bool didThrow = false; + try { + await nmc + ?.confirmSend(txData: {"hex": "a string", "fee": 10, "vSize": 10}); + } catch (_) { + didThrow = true; + } + + expect(didThrow, true); + + verify(client?.broadcastTransaction( + rawTx: "a string", requestID: anyNamed("requestID"))) + .called(1); + + expect(secureStore.interactions, 0); + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + // + // // this test will create a non mocked electrumx client that will try to connect + // // to the provided ipAddress below. This will throw a bunch of errors + // // which what we want here as actually calling electrumx calls here is unwanted. + // // test("listen to NodesChangedEvent", () async { + // // nmc = NamecoinWallet( + // // walletId: testWalletId, + // // walletName: testWalletName, + // // networkType: BasicNetworkType.test, + // // client: client, + // // cachedClient: cachedClient, + // // priceAPI: priceAPI, + // // secureStore: secureStore, + // // ); + // // + // // // set node + // // final wallet = await Hive.openBox(testWalletId); + // // await wallet.put("nodes", { + // // "default": { + // // "id": "some nodeID", + // // "ipAddress": "some address", + // // "port": "9000", + // // "useSSL": true, + // // } + // // }); + // // await wallet.put("activeNodeID_Bitcoin", "default"); + // // + // // final a = nmc.cachedElectrumXClient; + // // + // // // return when refresh is called on node changed trigger + // // nmc.longMutex = true; + // // + // // GlobalEventBus.instance + // // .fire(NodesChangedEvent(NodesChangedEventType.updatedCurrentNode)); + // // + // // // make sure event has processed before continuing + // // await Future.delayed(Duration(seconds: 5)); + // // + // // final b = nmc.cachedElectrumXClient; + // // + // // expect(identical(a, b), false); + // // + // // await nmc.exit(); + // // + // // expect(secureStore.interactions, 0); + // // verifyNoMoreInteractions(client); + // // verifyNoMoreInteractions(cachedClient); + // // verifyNoMoreInteractions(priceAPI); + // // }); + + test("refresh wallet mutex locked", () async { + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getBatchHistory(args: historyBatchArgs0)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs1)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs2)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs3)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs4)) + .thenAnswer((_) async => historyBatchResponse); + when(client?.getBatchHistory(args: historyBatchArgs5)) + .thenAnswer((_) async => historyBatchResponse); + + List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + if (realInvocation.namedArguments.values.first.length == 1) { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + } + + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); + + // recover to fill data + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + nmc?.refreshMutex = true; + + await nmc?.refresh(); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1); + verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + expect(activeScriptHashes.contains(map.values.first.first as String), + true); + } + + expect(secureStore.interactions, 14); + expect(secureStore.writes, 7); + expect(secureStore.reads, 7); + expect(secureStore.deletes, 0); + + verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(tracker); + verifyNoMoreInteractions(priceAPI); + }); + + test("refresh wallet normally", () async { + when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async => + {"height": 520481, "hex": "some block hex"}); + when(client?.getServerFeatures()).thenAnswer((_) async => { + "hosts": {}, + "pruning": null, + "server_version": "Unit tests", + "protocol_min": "1.4", + "protocol_max": "1.4.2", + "genesis_hash": GENESIS_HASH_MAINNET, + "hash_function": "sha256", + "services": [] + }); + when(client?.getHistory(scripthash: anyNamed("scripthash"))) + .thenAnswer((_) async => []); + when(client?.estimateFee(blocks: anyNamed("blocks"))) + .thenAnswer((_) async => Decimal.one); + + when(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")) + .thenAnswer((_) async => {Coin.particl: Tuple2(Decimal.one, 0.3)}); + + final List dynamicArgValues = []; + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((realInvocation) async { + dynamicArgValues.add(realInvocation.namedArguments.values.first); + return historyBatchResponse; + }); + + await Hive.openBox(testWalletId); + + // recover to fill data + await nmc?.recoverFromMnemonic( + mnemonic: TEST_MNEMONIC, + maxUnusedAddressGap: 2, + maxNumberOfIndexesToCheck: 1000, + height: 4000); + + when(client?.getBatchHistory(args: anyNamed("args"))) + .thenAnswer((_) async => {}); + when(client?.getBatchUTXOs(args: anyNamed("args"))) + .thenAnswer((_) async => emptyHistoryBatchResponse); + + await nmc?.refresh(); + + verify(client?.getServerFeatures()).called(1); + verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(4); + verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3); + verify(client?.getBlockHeadTip()).called(1); + verify(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")).called(2); + + for (final arg in dynamicArgValues) { + final map = Map>.from(arg as Map); + + verify(client?.getBatchHistory(args: map)).called(1); + } + + expect(secureStore.interactions, 14); + expect(secureStore.writes, 7); + expect(secureStore.reads, 7); + expect(secureStore.deletes, 0); + + // verifyNoMoreInteractions(client); + verifyNoMoreInteractions(cachedClient); + verifyNoMoreInteractions(priceAPI); + }); + + tearDown(() async { + await tearDownTestHive(); + }); + }); +} diff --git a/test/services/coins/particl/particl_wallet_test.mocks.dart b/test/services/coins/particl/particl_wallet_test.mocks.dart new file mode 100644 index 000000000..91c3e5bfa --- /dev/null +++ b/test/services/coins/particl/particl_wallet_test.mocks.dart @@ -0,0 +1,629 @@ +// Mocks generated by Mockito 5.3.2 from annotations +// in stackwallet/test/services/coins/namecoin/namecoin_wallet_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; + +import 'package:decimal/decimal.dart' as _i2; +import 'package:http/http.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; +import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i7; +import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i5; +import 'package:stackwallet/services/price.dart' as _i9; +import 'package:stackwallet/services/transaction_notification_tracker.dart' + as _i11; +import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i8; +import 'package:stackwallet/utilities/prefs.dart' as _i3; +import 'package:tuple/tuple.dart' as _i10; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeDecimal_0 extends _i1.SmartFake implements _i2.Decimal { + _FakeDecimal_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePrefs_1 extends _i1.SmartFake implements _i3.Prefs { + _FakePrefs_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeClient_2 extends _i1.SmartFake implements _i4.Client { + _FakeClient_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [ElectrumX]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockElectrumX extends _i1.Mock implements _i5.ElectrumX { + MockElectrumX() { + _i1.throwOnMissingStub(this); + } + + @override + set failovers(List<_i5.ElectrumXNode>? _failovers) => super.noSuchMethod( + Invocation.setter( + #failovers, + _failovers, + ), + returnValueForMissingStub: null, + ); + @override + int get currentFailoverIndex => (super.noSuchMethod( + Invocation.getter(#currentFailoverIndex), + returnValue: 0, + ) as int); + @override + set currentFailoverIndex(int? _currentFailoverIndex) => super.noSuchMethod( + Invocation.setter( + #currentFailoverIndex, + _currentFailoverIndex, + ), + returnValueForMissingStub: null, + ); + @override + String get host => (super.noSuchMethod( + Invocation.getter(#host), + returnValue: '', + ) as String); + @override + int get port => (super.noSuchMethod( + Invocation.getter(#port), + returnValue: 0, + ) as int); + @override + bool get useSSL => (super.noSuchMethod( + Invocation.getter(#useSSL), + returnValue: false, + ) as bool); + @override + _i6.Future request({ + required String? command, + List? args = const [], + Duration? connectionTimeout = const Duration(seconds: 60), + String? requestID, + int? retries = 2, + }) => + (super.noSuchMethod( + Invocation.method( + #request, + [], + { + #command: command, + #args: args, + #connectionTimeout: connectionTimeout, + #requestID: requestID, + #retries: retries, + }, + ), + returnValue: _i6.Future.value(), + ) as _i6.Future); + @override + _i6.Future>> batchRequest({ + required String? command, + required Map>? args, + Duration? connectionTimeout = const Duration(seconds: 60), + int? retries = 2, + }) => + (super.noSuchMethod( + Invocation.method( + #batchRequest, + [], + { + #command: command, + #args: args, + #connectionTimeout: connectionTimeout, + #retries: retries, + }, + ), + returnValue: _i6.Future>>.value( + >[]), + ) as _i6.Future>>); + @override + _i6.Future ping({ + String? requestID, + int? retryCount = 1, + }) => + (super.noSuchMethod( + Invocation.method( + #ping, + [], + { + #requestID: requestID, + #retryCount: retryCount, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.Future); + @override + _i6.Future> getBlockHeadTip({String? requestID}) => + (super.noSuchMethod( + Invocation.method( + #getBlockHeadTip, + [], + {#requestID: requestID}, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future> getServerFeatures({String? requestID}) => + (super.noSuchMethod( + Invocation.method( + #getServerFeatures, + [], + {#requestID: requestID}, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future broadcastTransaction({ + required String? rawTx, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #broadcastTransaction, + [], + { + #rawTx: rawTx, + #requestID: requestID, + }, + ), + returnValue: _i6.Future.value(''), + ) as _i6.Future); + @override + _i6.Future> getBalance({ + required String? scripthash, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getBalance, + [], + { + #scripthash: scripthash, + #requestID: requestID, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future>> getHistory({ + required String? scripthash, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getHistory, + [], + { + #scripthash: scripthash, + #requestID: requestID, + }, + ), + returnValue: _i6.Future>>.value( + >[]), + ) as _i6.Future>>); + @override + _i6.Future>>> getBatchHistory( + {required Map>? args}) => + (super.noSuchMethod( + Invocation.method( + #getBatchHistory, + [], + {#args: args}, + ), + returnValue: _i6.Future>>>.value( + >>{}), + ) as _i6.Future>>>); + @override + _i6.Future>> getUTXOs({ + required String? scripthash, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getUTXOs, + [], + { + #scripthash: scripthash, + #requestID: requestID, + }, + ), + returnValue: _i6.Future>>.value( + >[]), + ) as _i6.Future>>); + @override + _i6.Future>>> getBatchUTXOs( + {required Map>? args}) => + (super.noSuchMethod( + Invocation.method( + #getBatchUTXOs, + [], + {#args: args}, + ), + returnValue: _i6.Future>>>.value( + >>{}), + ) as _i6.Future>>>); + @override + _i6.Future> getTransaction({ + required String? txHash, + bool? verbose = true, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getTransaction, + [], + { + #txHash: txHash, + #verbose: verbose, + #requestID: requestID, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future> getAnonymitySet({ + String? groupId = r'1', + String? blockhash = r'', + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getAnonymitySet, + [], + { + #groupId: groupId, + #blockhash: blockhash, + #requestID: requestID, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future getMintData({ + dynamic mints, + String? requestID, + }) => + (super.noSuchMethod( + Invocation.method( + #getMintData, + [], + { + #mints: mints, + #requestID: requestID, + }, + ), + returnValue: _i6.Future.value(), + ) as _i6.Future); + @override + _i6.Future> getUsedCoinSerials({ + String? requestID, + required int? startNumber, + }) => + (super.noSuchMethod( + Invocation.method( + #getUsedCoinSerials, + [], + { + #requestID: requestID, + #startNumber: startNumber, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future getLatestCoinId({String? requestID}) => (super.noSuchMethod( + Invocation.method( + #getLatestCoinId, + [], + {#requestID: requestID}, + ), + returnValue: _i6.Future.value(0), + ) as _i6.Future); + @override + _i6.Future> getFeeRate({String? requestID}) => + (super.noSuchMethod( + Invocation.method( + #getFeeRate, + [], + {#requestID: requestID}, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future<_i2.Decimal> estimateFee({ + String? requestID, + required int? blocks, + }) => + (super.noSuchMethod( + Invocation.method( + #estimateFee, + [], + { + #requestID: requestID, + #blocks: blocks, + }, + ), + returnValue: _i6.Future<_i2.Decimal>.value(_FakeDecimal_0( + this, + Invocation.method( + #estimateFee, + [], + { + #requestID: requestID, + #blocks: blocks, + }, + ), + )), + ) as _i6.Future<_i2.Decimal>); + @override + _i6.Future<_i2.Decimal> relayFee({String? requestID}) => (super.noSuchMethod( + Invocation.method( + #relayFee, + [], + {#requestID: requestID}, + ), + returnValue: _i6.Future<_i2.Decimal>.value(_FakeDecimal_0( + this, + Invocation.method( + #relayFee, + [], + {#requestID: requestID}, + ), + )), + ) as _i6.Future<_i2.Decimal>); +} + +/// A class which mocks [CachedElectrumX]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX { + MockCachedElectrumX() { + _i1.throwOnMissingStub(this); + } + + @override + String get server => (super.noSuchMethod( + Invocation.getter(#server), + returnValue: '', + ) as String); + @override + int get port => (super.noSuchMethod( + Invocation.getter(#port), + returnValue: 0, + ) as int); + @override + bool get useSSL => (super.noSuchMethod( + Invocation.getter(#useSSL), + returnValue: false, + ) as bool); + @override + _i3.Prefs get prefs => (super.noSuchMethod( + Invocation.getter(#prefs), + returnValue: _FakePrefs_1( + this, + Invocation.getter(#prefs), + ), + ) as _i3.Prefs); + @override + List<_i5.ElectrumXNode> get failovers => (super.noSuchMethod( + Invocation.getter(#failovers), + returnValue: <_i5.ElectrumXNode>[], + ) as List<_i5.ElectrumXNode>); + @override + _i6.Future> getAnonymitySet({ + required String? groupId, + String? blockhash = r'', + required _i8.Coin? coin, + }) => + (super.noSuchMethod( + Invocation.method( + #getAnonymitySet, + [], + { + #groupId: groupId, + #blockhash: blockhash, + #coin: coin, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + String base64ToHex(String? source) => (super.noSuchMethod( + Invocation.method( + #base64ToHex, + [source], + ), + returnValue: '', + ) as String); + @override + String base64ToReverseHex(String? source) => (super.noSuchMethod( + Invocation.method( + #base64ToReverseHex, + [source], + ), + returnValue: '', + ) as String); + @override + _i6.Future> getTransaction({ + required String? txHash, + required _i8.Coin? coin, + bool? verbose = true, + }) => + (super.noSuchMethod( + Invocation.method( + #getTransaction, + [], + { + #txHash: txHash, + #coin: coin, + #verbose: verbose, + }, + ), + returnValue: + _i6.Future>.value({}), + ) as _i6.Future>); + @override + _i6.Future> getUsedCoinSerials({ + required _i8.Coin? coin, + int? startNumber = 0, + }) => + (super.noSuchMethod( + Invocation.method( + #getUsedCoinSerials, + [], + { + #coin: coin, + #startNumber: startNumber, + }, + ), + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); + @override + _i6.Future clearSharedTransactionCache({required _i8.Coin? coin}) => + (super.noSuchMethod( + Invocation.method( + #clearSharedTransactionCache, + [], + {#coin: coin}, + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); +} + +/// A class which mocks [PriceAPI]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPriceAPI extends _i1.Mock implements _i9.PriceAPI { + MockPriceAPI() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Client get client => (super.noSuchMethod( + Invocation.getter(#client), + returnValue: _FakeClient_2( + this, + Invocation.getter(#client), + ), + ) as _i4.Client); + @override + void resetLastCalledToForceNextCallToUpdateCache() => super.noSuchMethod( + Invocation.method( + #resetLastCalledToForceNextCallToUpdateCache, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i6.Future< + Map<_i8.Coin, _i10.Tuple2<_i2.Decimal, double>>> getPricesAnd24hChange( + {required String? baseCurrency}) => + (super.noSuchMethod( + Invocation.method( + #getPricesAnd24hChange, + [], + {#baseCurrency: baseCurrency}, + ), + returnValue: + _i6.Future>>.value( + <_i8.Coin, _i10.Tuple2<_i2.Decimal, double>>{}), + ) as _i6.Future>>); +} + +/// A class which mocks [TransactionNotificationTracker]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTransactionNotificationTracker extends _i1.Mock + implements _i11.TransactionNotificationTracker { + MockTransactionNotificationTracker() { + _i1.throwOnMissingStub(this); + } + + @override + String get walletId => (super.noSuchMethod( + Invocation.getter(#walletId), + returnValue: '', + ) as String); + @override + List get pendings => (super.noSuchMethod( + Invocation.getter(#pendings), + returnValue: [], + ) as List); + @override + List get confirmeds => (super.noSuchMethod( + Invocation.getter(#confirmeds), + returnValue: [], + ) as List); + @override + bool wasNotifiedPending(String? txid) => (super.noSuchMethod( + Invocation.method( + #wasNotifiedPending, + [txid], + ), + returnValue: false, + ) as bool); + @override + _i6.Future addNotifiedPending(String? txid) => (super.noSuchMethod( + Invocation.method( + #addNotifiedPending, + [txid], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override + bool wasNotifiedConfirmed(String? txid) => (super.noSuchMethod( + Invocation.method( + #wasNotifiedConfirmed, + [txid], + ), + returnValue: false, + ) as bool); + @override + _i6.Future addNotifiedConfirmed(String? txid) => (super.noSuchMethod( + Invocation.method( + #addNotifiedConfirmed, + [txid], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); +}