diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp index 745e1ff2c..adad0af9b 100644 --- a/src/test/fuzz/locale.cpp +++ b/src/test/fuzz/locale.cpp @@ -1,856 +1,853 @@ // Copyright (c) 2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include namespace { const std::string locale_identifiers[] = {"C", "C.UTF-8", "aa_DJ", "aa_DJ.ISO-8859-1", "aa_DJ.UTF-8", "aa_ER", "aa_ER.UTF-8", "aa_ET", "aa_ET.UTF-8", "af_ZA", "af_ZA.ISO-8859-1", "af_ZA.UTF-8", "agr_PE", "agr_PE.UTF-8", "ak_GH", "ak_GH.UTF-8", "am_ET", "am_ET.UTF-8", "an_ES", "an_ES.ISO-8859-15", "an_ES.UTF-8", "anp_IN", "anp_IN.UTF-8", "ar_AE", "ar_AE.ISO-8859-6", "ar_AE.UTF-8", "ar_BH", "ar_BH.ISO-8859-6", "ar_BH.UTF-8", "ar_DZ", "ar_DZ.ISO-8859-6", "ar_DZ.UTF-8", "ar_EG", "ar_EG.ISO-8859-6", "ar_EG.UTF-8", "ar_IN", "ar_IN.UTF-8", "ar_IQ", "ar_IQ.ISO-8859-6", "ar_IQ.UTF-8", "ar_JO", "ar_JO.ISO-8859-6", "ar_JO.UTF-8", "ar_KW", "ar_KW.ISO-8859-6", "ar_KW.UTF-8", "ar_LB", "ar_LB.ISO-8859-6", "ar_LB.UTF-8", "ar_LY", "ar_LY.ISO-8859-6", "ar_LY.UTF-8", "ar_MA", "ar_MA.ISO-8859-6", "ar_MA.UTF-8", "ar_OM", "ar_OM.ISO-8859-6", "ar_OM.UTF-8", "ar_QA", "ar_QA.ISO-8859-6", "ar_QA.UTF-8", "ar_SA", "ar_SA.ISO-8859-6", "ar_SA.UTF-8", "ar_SD", "ar_SD.ISO-8859-6", "ar_SD.UTF-8", "ar_SS", "ar_SS.UTF-8", "ar_SY", "ar_SY.ISO-8859-6", "ar_SY.UTF-8", "ar_TN", "ar_TN.ISO-8859-6", "ar_TN.UTF-8", "ar_YE", "ar_YE.ISO-8859-6", "ar_YE.UTF-8", "as_IN", "as_IN.UTF-8", "ast_ES", "ast_ES.ISO-8859-15", "ast_ES.UTF-8", "ayc_PE", "ayc_PE.UTF-8", "az_AZ", "az_AZ.UTF-8", "az_IR", "az_IR.UTF-8", "be_BY", "be_BY.CP1251", "be_BY.UTF-8", "bem_ZM", "bem_ZM.UTF-8", "ber_DZ", "ber_DZ.UTF-8", "ber_MA", "ber_MA.UTF-8", "bg_BG", "bg_BG.CP1251", "bg_BG.UTF-8", "bho_IN", "bho_IN.UTF-8", "bho_NP", "bho_NP.UTF-8", "bi_VU", "bi_VU.UTF-8", "bn_BD", "bn_BD.UTF-8", "bn_IN", "bn_IN.UTF-8", "bo_CN", "bo_CN.UTF-8", "bo_IN", "bo_IN.UTF-8", "br_FR", "br_FR.ISO-8859-1", "br_FR.UTF-8", "brx_IN", "brx_IN.UTF-8", "bs_BA", "bs_BA.ISO-8859-2", "bs_BA.UTF-8", "byn_ER", "byn_ER.UTF-8", "ca_AD", "ca_AD.ISO-8859-15", "ca_AD.UTF-8", "ca_ES", "ca_ES.ISO-8859-1", "ca_ES.UTF-8", "ca_FR", "ca_FR.ISO-8859-15", "ca_FR.UTF-8", "ca_IT", "ca_IT.ISO-8859-15", "ca_IT.UTF-8", "ce_RU", "ce_RU.UTF-8", "chr_US", "chr_US.UTF-8", "ckb_IQ", "ckb_IQ.UTF-8", "cmn_TW", "cmn_TW.UTF-8", "crh_UA", "crh_UA.UTF-8", "csb_PL", "csb_PL.UTF-8", "cs_CZ", "cs_CZ.ISO-8859-2", "cs_CZ.UTF-8", "cv_RU", "cv_RU.UTF-8", "cy_GB", "cy_GB.ISO-8859-14", "cy_GB.UTF-8", "da_DK", "da_DK.ISO-8859-1", "da_DK.UTF-8", "de_AT", "de_AT.ISO-8859-1", "de_AT.UTF-8", "de_BE", "de_BE.ISO-8859-1", "de_BE.UTF-8", "de_CH", "de_CH.ISO-8859-1", "de_CH.UTF-8", "de_DE", "de_DE.ISO-8859-1", "de_DE.UTF-8", "de_IT", "de_IT.ISO-8859-1", "de_IT.UTF-8", "de_LU", "de_LU.ISO-8859-1", "de_LU.UTF-8", "doi_IN", "doi_IN.UTF-8", "dv_MV", "dv_MV.UTF-8", "dz_BT", "dz_BT.UTF-8", "el_CY", "el_CY.ISO-8859-7", "el_CY.UTF-8", "el_GR", "el_GR.ISO-8859-7", "el_GR.UTF-8", "en_AG", "en_AG.UTF-8", "en_AU", "en_AU.ISO-8859-1", "en_AU.UTF-8", "en_BW", "en_BW.ISO-8859-1", "en_BW.UTF-8", "en_CA", "en_CA.ISO-8859-1", "en_CA.UTF-8", "en_DK", "en_DK.ISO-8859-1", "en_DK.ISO-8859-15", "en_DK.UTF-8", "en_GB", "en_GB.ISO-8859-1", "en_GB.ISO-8859-15", "en_GB.UTF-8", "en_HK", "en_HK.ISO-8859-1", "en_HK.UTF-8", "en_IE", "en_IE.ISO-8859-1", "en_IE.UTF-8", "en_IL", "en_IL.UTF-8", "en_IN", "en_IN.UTF-8", "en_NG", "en_NG.UTF-8", "en_NZ", "en_NZ.ISO-8859-1", "en_NZ.UTF-8", "en_PH", "en_PH.ISO-8859-1", "en_PH.UTF-8", "en_SG", "en_SG.ISO-8859-1", "en_SG.UTF-8", "en_US", "en_US.ISO-8859-1", "en_US.ISO-8859-15", "en_US.UTF-8", "en_ZA", "en_ZA.ISO-8859-1", "en_ZA.UTF-8", "en_ZM", "en_ZM.UTF-8", "en_ZW", "en_ZW.ISO-8859-1", "en_ZW.UTF-8", "es_AR", "es_AR.ISO-8859-1", "es_AR.UTF-8", "es_BO", "es_BO.ISO-8859-1", "es_BO.UTF-8", "es_CL", "es_CL.ISO-8859-1", "es_CL.UTF-8", "es_CO", "es_CO.ISO-8859-1", "es_CO.UTF-8", "es_CR", "es_CR.ISO-8859-1", "es_CR.UTF-8", "es_CU", "es_CU.UTF-8", "es_DO", "es_DO.ISO-8859-1", "es_DO.UTF-8", "es_EC", "es_EC.ISO-8859-1", "es_EC.UTF-8", "es_ES", "es_ES.ISO-8859-1", "es_ES.UTF-8", "es_GT", "es_GT.ISO-8859-1", "es_GT.UTF-8", "es_HN", "es_HN.ISO-8859-1", "es_HN.UTF-8", "es_MX", "es_MX.ISO-8859-1", "es_MX.UTF-8", "es_NI", "es_NI.ISO-8859-1", "es_NI.UTF-8", "es_PA", "es_PA.ISO-8859-1", "es_PA.UTF-8", "es_PE", "es_PE.ISO-8859-1", "es_PE.UTF-8", "es_PR", "es_PR.ISO-8859-1", "es_PR.UTF-8", "es_PY", "es_PY.ISO-8859-1", "es_PY.UTF-8", "es_SV", "es_SV.ISO-8859-1", "es_SV.UTF-8", "es_US", "es_US.ISO-8859-1", "es_US.UTF-8", "es_UY", "es_UY.ISO-8859-1", "es_UY.UTF-8", "es_VE", "es_VE.ISO-8859-1", "es_VE.UTF-8", "et_EE", "et_EE.ISO-8859-1", "et_EE.ISO-8859-15", "et_EE.UTF-8", "eu_ES", "eu_ES.ISO-8859-1", "eu_ES.UTF-8", "eu_FR", "eu_FR.ISO-8859-1", "eu_FR.UTF-8", "fa_IR", "fa_IR.UTF-8", "ff_SN", "ff_SN.UTF-8", "fi_FI", "fi_FI.ISO-8859-1", "fi_FI.UTF-8", "fil_PH", "fil_PH.UTF-8", "fo_FO", "fo_FO.ISO-8859-1", "fo_FO.UTF-8", "fr_BE", "fr_BE.ISO-8859-1", "fr_BE.UTF-8", "fr_CA", "fr_CA.ISO-8859-1", "fr_CA.UTF-8", "fr_CH", "fr_CH.ISO-8859-1", "fr_CH.UTF-8", "fr_FR", "fr_FR.ISO-8859-1", "fr_FR.UTF-8", "fr_LU", "fr_LU.ISO-8859-1", "fr_LU.UTF-8", "fur_IT", "fur_IT.UTF-8", "fy_DE", "fy_DE.UTF-8", "fy_NL", "fy_NL.UTF-8", "ga_IE", "ga_IE.ISO-8859-1", "ga_IE.UTF-8", "gd_GB", "gd_GB.ISO-8859-15", "gd_GB.UTF-8", "gez_ER", "gez_ER.UTF-8", "gez_ET", "gez_ET.UTF-8", "gl_ES", "gl_ES.ISO-8859-1", "gl_ES.UTF-8", "gu_IN", "gu_IN.UTF-8", "gv_GB", "gv_GB.ISO-8859-1", "gv_GB.UTF-8", "hak_TW", "hak_TW.UTF-8", "ha_NG", "ha_NG.UTF-8", "he_IL", "he_IL.ISO-8859-8", "he_IL.UTF-8", "hif_FJ", "hif_FJ.UTF-8", "hi_IN", "hi_IN.UTF-8", "hne_IN", "hne_IN.UTF-8", "hr_HR", "hr_HR.ISO-8859-2", "hr_HR.UTF-8", "hsb_DE", "hsb_DE.ISO-8859-2", "hsb_DE.UTF-8", "ht_HT", "ht_HT.UTF-8", "hu_HU", "hu_HU.ISO-8859-2", "hu_HU.UTF-8", "hy_AM", "hy_AM.ARMSCII-8", "hy_AM.UTF-8", "ia_FR", "ia_FR.UTF-8", "id_ID", "id_ID.ISO-8859-1", "id_ID.UTF-8", "ig_NG", "ig_NG.UTF-8", "ik_CA", "ik_CA.UTF-8", "is_IS", "is_IS.ISO-8859-1", "is_IS.UTF-8", "it_CH", "it_CH.ISO-8859-1", "it_CH.UTF-8", "it_IT", "it_IT.ISO-8859-1", "it_IT.UTF-8", "iu_CA", "iu_CA.UTF-8", "kab_DZ", "kab_DZ.UTF-8", "ka_GE", "ka_GE.GEORGIAN-PS", "ka_GE.UTF-8", "kk_KZ", "kk_KZ.PT154", "kk_KZ.RK1048", "kk_KZ.UTF-8", "kl_GL", "kl_GL.ISO-8859-1", "kl_GL.UTF-8", "km_KH", "km_KH.UTF-8", "kn_IN", "kn_IN.UTF-8", "kok_IN", "kok_IN.UTF-8", "ks_IN", "ks_IN.UTF-8", "ku_TR", "ku_TR.ISO-8859-9", "ku_TR.UTF-8", "kw_GB", "kw_GB.ISO-8859-1", "kw_GB.UTF-8", "ky_KG", "ky_KG.UTF-8", "lb_LU", "lb_LU.UTF-8", "lg_UG", "lg_UG.ISO-8859-10", "lg_UG.UTF-8", "li_BE", "li_BE.UTF-8", "lij_IT", "lij_IT.UTF-8", "li_NL", "li_NL.UTF-8", "ln_CD", "ln_CD.UTF-8", "lo_LA", "lo_LA.UTF-8", "lt_LT", "lt_LT.ISO-8859-13", "lt_LT.UTF-8", "lv_LV", "lv_LV.ISO-8859-13", "lv_LV.UTF-8", "lzh_TW", "lzh_TW.UTF-8", "mag_IN", "mag_IN.UTF-8", "mai_IN", "mai_IN.UTF-8", "mai_NP", "mai_NP.UTF-8", "mfe_MU", "mfe_MU.UTF-8", "mg_MG", "mg_MG.ISO-8859-15", "mg_MG.UTF-8", "mhr_RU", "mhr_RU.UTF-8", "mi_NZ", "mi_NZ.ISO-8859-13", "mi_NZ.UTF-8", "miq_NI", "miq_NI.UTF-8", "mjw_IN", "mjw_IN.UTF-8", "mk_MK", "mk_MK.ISO-8859-5", "mk_MK.UTF-8", "ml_IN", "ml_IN.UTF-8", "mni_IN", "mni_IN.UTF-8", "mn_MN", "mn_MN.UTF-8", "mr_IN", "mr_IN.UTF-8", "ms_MY", "ms_MY.ISO-8859-1", "ms_MY.UTF-8", "mt_MT", "mt_MT.ISO-8859-3", "mt_MT.UTF-8", "my_MM", "my_MM.UTF-8", "nan_TW", "nan_TW.UTF-8", "nb_NO", "nb_NO.ISO-8859-1", "nb_NO.UTF-8", "nds_DE", "nds_DE.UTF-8", "nds_NL", "nds_NL.UTF-8", "ne_NP", "ne_NP.UTF-8", "nhn_MX", "nhn_MX.UTF-8", "niu_NU", "niu_NU.UTF-8", "niu_NZ", "niu_NZ.UTF-8", "nl_AW", "nl_AW.UTF-8", "nl_BE", "nl_BE.ISO-8859-1", "nl_BE.UTF-8", "nl_NL", "nl_NL.ISO-8859-1", "nl_NL.UTF-8", "nn_NO", "nn_NO.ISO-8859-1", "nn_NO.UTF-8", "nr_ZA", "nr_ZA.UTF-8", "nso_ZA", "nso_ZA.UTF-8", "oc_FR", "oc_FR.ISO-8859-1", "oc_FR.UTF-8", "om_ET", "om_ET.UTF-8", "om_KE", "om_KE.ISO-8859-1", "om_KE.UTF-8", "or_IN", "or_IN.UTF-8", "os_RU", "os_RU.UTF-8", "pa_IN", "pa_IN.UTF-8", "pap_AW", "pap_AW.UTF-8", "pap_CW", "pap_CW.UTF-8", "pa_PK", "pa_PK.UTF-8", "pl_PL", "pl_PL.ISO-8859-2", "pl_PL.UTF-8", "ps_AF", "ps_AF.UTF-8", "pt_BR", "pt_BR.ISO-8859-1", "pt_BR.UTF-8", "pt_PT", "pt_PT.ISO-8859-1", "pt_PT.UTF-8", "quz_PE", "quz_PE.UTF-8", "raj_IN", "raj_IN.UTF-8", "ro_RO", "ro_RO.ISO-8859-2", "ro_RO.UTF-8", "ru_RU", "ru_RU.CP1251", "ru_RU.ISO-8859-5", "ru_RU.KOI8-R", "ru_RU.UTF-8", "ru_UA", "ru_UA.KOI8-U", "ru_UA.UTF-8", "rw_RW", "rw_RW.UTF-8", "sa_IN", "sa_IN.UTF-8", "sat_IN", "sat_IN.UTF-8", "sc_IT", "sc_IT.UTF-8", "sd_IN", "sd_IN.UTF-8", "sd_PK", "sd_PK.UTF-8", "se_NO", "se_NO.UTF-8", "sgs_LT", "sgs_LT.UTF-8", "shn_MM", "shn_MM.UTF-8", "shs_CA", "shs_CA.UTF-8", "sid_ET", "sid_ET.UTF-8", "si_LK", "si_LK.UTF-8", "sk_SK", "sk_SK.ISO-8859-2", "sk_SK.UTF-8", "sl_SI", "sl_SI.ISO-8859-2", "sl_SI.UTF-8", "sm_WS", "sm_WS.UTF-8", "so_DJ", "so_DJ.ISO-8859-1", "so_DJ.UTF-8", "so_ET", "so_ET.UTF-8", "so_KE", "so_KE.ISO-8859-1", "so_KE.UTF-8", "so_SO", "so_SO.ISO-8859-1", "so_SO.UTF-8", "sq_AL", "sq_AL.ISO-8859-1", "sq_AL.UTF-8", "sq_MK", "sq_MK.UTF-8", "sr_ME", "sr_ME.UTF-8", "sr_RS", "sr_RS.UTF-8", "ss_ZA", "ss_ZA.UTF-8", "st_ZA", "st_ZA.ISO-8859-1", "st_ZA.UTF-8", "sv_FI", "sv_FI.ISO-8859-1", "sv_FI.UTF-8", "sv_SE", "sv_SE.ISO-8859-1", "sv_SE.ISO-8859-15", "sv_SE.UTF-8", "sw_KE", "sw_KE.UTF-8", "sw_TZ", "sw_TZ.UTF-8", "szl_PL", "szl_PL.UTF-8", "ta_IN", "ta_IN.UTF-8", "ta_LK", "ta_LK.UTF-8", "te_IN", "te_IN.UTF-8", "tg_TJ", "tg_TJ.KOI8-T", "tg_TJ.UTF-8", "the_NP", "the_NP.UTF-8", "th_TH", "th_TH.TIS-620", "th_TH.UTF-8", "ti_ER", "ti_ER.UTF-8", "ti_ET", "ti_ET.UTF-8", "tig_ER", "tig_ER.UTF-8", "tk_TM", "tk_TM.UTF-8", "tl_PH", "tl_PH.ISO-8859-1", "tl_PH.UTF-8", "tn_ZA", "tn_ZA.UTF-8", "to_TO", "to_TO.UTF-8", "tpi_PG", "tpi_PG.UTF-8", "tr_CY", "tr_CY.ISO-8859-9", "tr_CY.UTF-8", "tr_TR", "tr_TR.ISO-8859-9", "tr_TR.UTF-8", "ts_ZA", "ts_ZA.UTF-8", "tt_RU", "tt_RU.UTF-8", "ug_CN", "ug_CN.UTF-8", "uk_UA", "uk_UA.KOI8-U", "uk_UA.UTF-8", "unm_US", "unm_US.UTF-8", "ur_IN", "ur_IN.UTF-8", "ur_PK", "ur_PK.UTF-8", "uz_UZ", "uz_UZ.ISO-8859-1", "uz_UZ.UTF-8", "ve_ZA", "ve_ZA.UTF-8", "vi_VN", "vi_VN.UTF-8", "wa_BE", "wa_BE.ISO-8859-1", "wa_BE.UTF-8", "wae_CH", "wae_CH.UTF-8", "wal_ET", "wal_ET.UTF-8", "wo_SN", "wo_SN.UTF-8", "xh_ZA", "xh_ZA.ISO-8859-1", "xh_ZA.UTF-8", "yi_US", "yi_US.CP1255", "yi_US.UTF-8", "yo_NG", "yo_NG.UTF-8", "yue_HK", "yue_HK.UTF-8", "yuw_PG", "yuw_PG.UTF-8", "zh_CN", "zh_CN.GB18030", "zh_CN.GB2312", "zh_CN.GBK", "zh_CN.UTF-8", "zh_HK", "zh_HK.BIG5-HKSCS", "zh_HK.UTF-8", "zh_SG", "zh_SG.GB2312", "zh_SG.GBK", "zh_SG.UTF-8", "zh_TW", "zh_TW.BIG5", "zh_TW.EUC-TW", "zh_TW.UTF-8", "zu_ZA", "zu_ZA.ISO-8859-1", "zu_ZA.UTF-8"}; std::string ConsumeLocaleIdentifier(FuzzedDataProvider &fuzzed_data_provider) { return fuzzed_data_provider.PickValueInArray( locale_identifiers); } bool IsAvailableLocale(const std::string &locale_identifier) { try { (void)std::locale(locale_identifier); } catch (const std::runtime_error &) { return false; } return true; } } // namespace void test_one_input(const std::vector &buffer) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string locale_identifier = ConsumeLocaleIdentifier(fuzzed_data_provider); if (!IsAvailableLocale(locale_identifier)) { return; } const char *c_locale = std::setlocale(LC_ALL, "C"); assert(c_locale != nullptr); const std::string random_string = fuzzed_data_provider.ConsumeRandomLengthString(5); int32_t parseint32_out_without_locale; const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale); int64_t parseint64_out_without_locale; const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale); const int64_t atoi64_without_locale = atoi64(random_string); const int atoi_without_locale = atoi(random_string); - const int64_t atoi64c_without_locale = atoi64(random_string.c_str()); const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral(); const std::string tostring_without_locale = ToString(random_int64); // The variable `random_int32` is no longer used, but the harness still // needs to consume the same data that it did previously to not invalidate // existing seeds. const int32_t random_int32 = fuzzed_data_provider.ConsumeIntegral(); (void)random_int32; const std::string strprintf_int_without_locale = strprintf("%d", random_int64); const double random_double = fuzzed_data_provider.ConsumeFloatingPoint(); const std::string strprintf_double_without_locale = strprintf("%f", random_double); const char *new_locale = std::setlocale(LC_ALL, locale_identifier.c_str()); assert(new_locale != nullptr); int32_t parseint32_out_with_locale; const bool parseint32_with_locale = ParseInt32(random_string, &parseint32_out_with_locale); assert(parseint32_without_locale == parseint32_with_locale); if (parseint32_without_locale) { assert(parseint32_out_without_locale == parseint32_out_with_locale); } int64_t parseint64_out_with_locale; const bool parseint64_with_locale = ParseInt64(random_string, &parseint64_out_with_locale); assert(parseint64_without_locale == parseint64_with_locale); if (parseint64_without_locale) { assert(parseint64_out_without_locale == parseint64_out_with_locale); } const int64_t atoi64_with_locale = atoi64(random_string); assert(atoi64_without_locale == atoi64_with_locale); - const int64_t atoi64c_with_locale = atoi64(random_string.c_str()); - assert(atoi64c_without_locale == atoi64c_with_locale); const int atoi_with_locale = atoi(random_string); assert(atoi_without_locale == atoi_with_locale); const std::string tostring_with_locale = ToString(random_int64); assert(tostring_without_locale == tostring_with_locale); const std::string strprintf_int_with_locale = strprintf("%d", random_int64); assert(strprintf_int_without_locale == strprintf_int_with_locale); const std::string strprintf_double_with_locale = strprintf("%f", random_double); assert(strprintf_double_without_locale == strprintf_double_with_locale); const std::locale current_cpp_locale; assert(current_cpp_locale == std::locale::classic()); } diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index e94af2a2b..56fbd55c6 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -1,647 +1,639 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const std::string SAFE_CHARS[] = { // SAFE_CHARS_DEFAULT CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_UA_COMMENT CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_FILENAME CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_URI CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", }; std::string SanitizeString(const std::string &str, int rule) { std::string strResult; for (std::string::size_type i = 0; i < str.size(); i++) { if (SAFE_CHARS[rule].find(str[i]) != std::string::npos) { strResult.push_back(str[i]); } } return strResult; } const signed char p_util_hexdigit[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; signed char HexDigit(char c) { return p_util_hexdigit[(uint8_t)c]; } bool IsHex(const std::string &str) { for (std::string::const_iterator it(str.begin()); it != str.end(); ++it) { if (HexDigit(*it) < 0) { return false; } } return (str.size() > 0) && (str.size() % 2 == 0); } bool IsHexNumber(const std::string &str) { size_t starting_location = 0; if (str.size() > 2 && *str.begin() == '0' && *(str.begin() + 1) == 'x') { starting_location = 2; } for (auto c : str.substr(starting_location)) { if (HexDigit(c) < 0) { return false; } } // Return false for empty string or "0x". return (str.size() > starting_location); } std::vector ParseHex(const char *psz) { // convert hex dump to vector std::vector vch; while (true) { while (IsSpace(*psz)) { psz++; } signed char c = HexDigit(*psz++); if (c == (signed char)-1) { break; } uint8_t n = (c << 4); c = HexDigit(*psz++); if (c == (signed char)-1) { break; } n |= c; vch.push_back(n); } return vch; } std::vector ParseHex(const std::string &str) { return ParseHex(str.c_str()); } void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { size_t colon = in.find_last_of(':'); // if a : is found, and it either follows a [...], or no other : is in the // string, treat it as port separator bool fHaveColon = colon != in.npos; // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is // safe bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { int32_t n; if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { in = in.substr(0, colon); portOut = n; } } if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') { hostOut = in.substr(1, in.size() - 2); } else { hostOut = in; } } std::string EncodeBase64(const uint8_t *pch, size_t len) { static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string str; str.reserve(((len + 2) / 3) * 4); ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len); while (str.size() % 4) { str += '='; } return str; } std::string EncodeBase64(const std::string &str) { return EncodeBase64((const uint8_t *)str.data(), str.size()); } std::vector DecodeBase64(const char *p, bool *pf_invalid) { static const int decode64_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; const char *e = p; std::vector val; val.reserve(strlen(p)); while (*p != 0) { int x = decode64_table[(uint8_t)*p]; if (x == -1) { break; } val.push_back(x); ++p; } std::vector ret; ret.reserve((val.size() * 3) / 4); bool valid = ConvertBits<6, 8, false>([&](uint8_t c) { ret.push_back(c); }, val.begin(), val.end()); const char *q = p; while (valid && *p != 0) { if (*p != '=') { valid = false; break; } ++p; } valid = valid && (p - e) % 4 == 0 && p - q < 4; if (pf_invalid) { *pf_invalid = !valid; } return ret; } std::string DecodeBase64(const std::string &str, bool *pf_invalid) { if (!ValidAsCString(str)) { if (pf_invalid) { *pf_invalid = true; } return {}; } std::vector vchRet = DecodeBase64(str.c_str(), pf_invalid); return std::string((const char *)vchRet.data(), vchRet.size()); } std::string EncodeBase32(const uint8_t *pch, size_t len) { static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; std::string str; str.reserve(((len + 4) / 5) * 8); ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len); while (str.size() % 8) { str += '='; } return str; } std::string EncodeBase32(const std::string &str) { return EncodeBase32((const uint8_t *)str.data(), str.size()); } std::vector DecodeBase32(const char *p, bool *pf_invalid) { static const int decode32_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; const char *e = p; std::vector val; val.reserve(strlen(p)); while (*p != 0) { int x = decode32_table[(uint8_t)*p]; if (x == -1) { break; } val.push_back(x); ++p; } std::vector ret; ret.reserve((val.size() * 5) / 8); bool valid = ConvertBits<5, 8, false>([&](uint8_t c) { ret.push_back(c); }, val.begin(), val.end()); const char *q = p; while (valid && *p != 0) { if (*p != '=') { valid = false; break; } ++p; } valid = valid && (p - e) % 8 == 0 && p - q < 8; if (pf_invalid) { *pf_invalid = !valid; } return ret; } std::string DecodeBase32(const std::string &str, bool *pf_invalid) { if (!ValidAsCString(str)) { if (pf_invalid) { *pf_invalid = true; } return {}; } std::vector vchRet = DecodeBase32(str.c_str(), pf_invalid); return std::string((const char *)vchRet.data(), vchRet.size()); } NODISCARD static bool ParsePrechecks(const std::string &str) { // No empty string allowed if (str.empty()) { return false; } // No padding allowed if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size() - 1]))) { return false; } // No embedded NUL characters allowed if (!ValidAsCString(str)) { return false; } return true; } bool ParseInt32(const std::string &str, int32_t *out) { if (!ParsePrechecks(str)) { return false; } char *endp = nullptr; // strtol will not set errno if valid errno = 0; long int n = strtol(str.c_str(), &endp, 10); if (out) { *out = (int32_t)n; } // Note that strtol returns a *long int*, so even if strtol doesn't report // an over/underflow we still have to check that the returned value is // within the range of an *int32_t*. On 64-bit platforms the size of these // types may be different. return endp && *endp == 0 && !errno && n >= std::numeric_limits::min() && n <= std::numeric_limits::max(); } bool ParseInt64(const std::string &str, int64_t *out) { if (!ParsePrechecks(str)) { return false; } char *endp = nullptr; // strtoll will not set errno if valid errno = 0; long long int n = strtoll(str.c_str(), &endp, 10); if (out) { *out = (int64_t)n; } // Note that strtoll returns a *long long int*, so even if strtol doesn't // report an over/underflow we still have to check that the returned value // is within the range of an *int64_t*. return endp && *endp == 0 && !errno && n >= std::numeric_limits::min() && n <= std::numeric_limits::max(); } bool ParseUInt32(const std::string &str, uint32_t *out) { if (!ParsePrechecks(str)) { return false; } // Reject negative values, unfortunately strtoul accepts these by default if // they fit in the range if (str.size() >= 1 && str[0] == '-') { return false; } char *endp = nullptr; // strtoul will not set errno if valid errno = 0; unsigned long int n = strtoul(str.c_str(), &endp, 10); if (out) { *out = (uint32_t)n; } // Note that strtoul returns a *unsigned long int*, so even if it doesn't // report an over/underflow we still have to check that the returned value // is within the range of an *uint32_t*. On 64-bit platforms the size of // these types may be different. return endp && *endp == 0 && !errno && n <= std::numeric_limits::max(); } bool ParseUInt64(const std::string &str, uint64_t *out) { if (!ParsePrechecks(str)) { return false; } // Reject negative values, unfortunately strtoull accepts these by default // if they fit in the range if (str.size() >= 1 && str[0] == '-') { return false; } char *endp = nullptr; // strtoull will not set errno if valid errno = 0; unsigned long long int n = strtoull(str.c_str(), &endp, 10); if (out) { *out = (uint64_t)n; } // Note that strtoull returns a *unsigned long long int*, so even if it // doesn't report an over/underflow we still have to check that the returned // value is within the range of an *uint64_t*. return endp && *endp == 0 && !errno && n <= std::numeric_limits::max(); } bool ParseDouble(const std::string &str, double *out) { if (!ParsePrechecks(str)) { return false; } // No hexadecimal floats allowed if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') { return false; } std::istringstream text(str); text.imbue(std::locale::classic()); double result; text >> result; if (out) { *out = result; } return text.eof() && !text.fail(); } std::string FormatParagraph(const std::string &in, size_t width, size_t indent) { std::stringstream out; size_t ptr = 0; size_t indented = 0; while (ptr < in.size()) { size_t lineend = in.find_first_of('\n', ptr); if (lineend == std::string::npos) { lineend = in.size(); } const size_t linelen = lineend - ptr; const size_t rem_width = width - indented; if (linelen <= rem_width) { out << in.substr(ptr, linelen + 1); ptr = lineend + 1; indented = 0; } else { size_t finalspace = in.find_last_of(" \n", ptr + rem_width); if (finalspace == std::string::npos || finalspace < ptr) { // No place to break; just include the entire word and move on finalspace = in.find_first_of("\n ", ptr); if (finalspace == std::string::npos) { // End of the string, just add it and break out << in.substr(ptr); break; } } out << in.substr(ptr, finalspace - ptr) << "\n"; if (in[finalspace] == '\n') { indented = 0; } else if (indent) { out << std::string(indent, ' '); indented = indent; } ptr = finalspace + 1; } } return out.str(); } -int64_t atoi64(const char *psz) { -#ifdef _MSC_VER - return _atoi64(psz); -#else - return strtoll(psz, nullptr, 10); -#endif -} - int64_t atoi64(const std::string &str) { #ifdef _MSC_VER return _atoi64(str.c_str()); #else return strtoll(str.c_str(), nullptr, 10); #endif } int atoi(const std::string &str) { return atoi(str.c_str()); } /** * Upper bound for mantissa. * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit * integer. Larger integers cannot consist of arbitrary combinations of 0-9: * * 999999999999999999 1^18-1 * 9223372036854775807 (1<<63)-1 (max int64_t) * 9999999999999999999 1^19-1 (would overflow) */ static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL; /** Helper function for ParseFixedPoint */ static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros) { if (ch == '0') { ++mantissa_tzeros; } else { for (int i = 0; i <= mantissa_tzeros; ++i) { // overflow if (mantissa > (UPPER_BOUND / 10LL)) { return false; } mantissa *= 10; } mantissa += ch - '0'; mantissa_tzeros = 0; } return true; } bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) { int64_t mantissa = 0; int64_t exponent = 0; int mantissa_tzeros = 0; bool mantissa_sign = false; bool exponent_sign = false; int ptr = 0; int end = val.size(); int point_ofs = 0; if (ptr < end && val[ptr] == '-') { mantissa_sign = true; ++ptr; } if (ptr < end) { if (val[ptr] == '0') { // pass single 0 ++ptr; } else if (val[ptr] >= '1' && val[ptr] <= '9') { while (ptr < end && IsDigit(val[ptr])) { if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) { // overflow return false; } ++ptr; } } else { // missing expected digit return false; } } else { // empty string or loose '-' return false; } if (ptr < end && val[ptr] == '.') { ++ptr; if (ptr < end && IsDigit(val[ptr])) { while (ptr < end && IsDigit(val[ptr])) { if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) { // overflow return false; } ++ptr; ++point_ofs; } } else { // missing expected digit return false; } } if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) { ++ptr; if (ptr < end && val[ptr] == '+') { ++ptr; } else if (ptr < end && val[ptr] == '-') { exponent_sign = true; ++ptr; } if (ptr < end && IsDigit(val[ptr])) { while (ptr < end && IsDigit(val[ptr])) { if (exponent > (UPPER_BOUND / 10LL)) { // overflow return false; } exponent = exponent * 10 + val[ptr] - '0'; ++ptr; } } else { // missing expected digit return false; } } if (ptr != end) { // trailing garbage return false; } // finalize exponent if (exponent_sign) { exponent = -exponent; } exponent = exponent - point_ofs + mantissa_tzeros; // finalize mantissa if (mantissa_sign) { mantissa = -mantissa; } // convert to one 64-bit fixed-point value exponent += decimals; if (exponent < 0) { // cannot represent values smaller than 10^-decimals return false; } if (exponent >= 18) { // cannot represent values larger than or equal to 10^(18-decimals) return false; } for (int i = 0; i < exponent; ++i) { if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) { // overflow return false; } mantissa *= 10; } if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) { // overflow return false; } if (amount_out) { *amount_out = mantissa; } return true; } std::string ToLower(const std::string &str) { std::string r; for (auto ch : str) { r += ToLower(ch); } return r; } std::string ToUpper(const std::string &str) { std::string r; for (auto ch : str) { r += ToUpper(ch); } return r; } std::string Capitalize(std::string str) { if (str.empty()) { return str; } str[0] = ToUpper(str.front()); return str; } diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 68d9aef03..915fd390a 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -1,272 +1,271 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** * Utilities for converting data from/to strings. */ #ifndef BITCOIN_UTIL_STRENCODINGS_H #define BITCOIN_UTIL_STRENCODINGS_H #include #include #include #include #include #define ARRAYLEN(array) (sizeof(array) / sizeof((array)[0])) /** Used by SanitizeString() */ enum SafeChars { //! The full set of allowed chars SAFE_CHARS_DEFAULT, //! BIP-0014 subset SAFE_CHARS_UA_COMMENT, //! Chars allowed in filenames SAFE_CHARS_FILENAME, //! Chars allowed in URIs (RFC 3986) SAFE_CHARS_URI, }; /** * Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email * addresses, but avoid anything even possibly remotely dangerous like & or > * @param[in] str The string to sanitize * @param[in] rule The set of safe chars to choose (default: least * restrictive) * @return A new string without unsafe chars */ std::string SanitizeString(const std::string &str, int rule = SAFE_CHARS_DEFAULT); std::vector ParseHex(const char *psz); std::vector ParseHex(const std::string &str); signed char HexDigit(char c); /** * Returns true if each character in str is a hex character, and has an even * number of hex digits. */ bool IsHex(const std::string &str); /** * Return true if the string is a hex number, optionally prefixed with "0x" */ bool IsHexNumber(const std::string &str); std::vector DecodeBase64(const char *p, bool *pf_invalid = nullptr); std::string DecodeBase64(const std::string &str, bool *pf_invalid = nullptr); std::string EncodeBase64(const uint8_t *pch, size_t len); std::string EncodeBase64(const std::string &str); std::vector DecodeBase32(const char *p, bool *pf_invalid = nullptr); std::string DecodeBase32(const std::string &str, bool *pf_invalid = nullptr); std::string EncodeBase32(const uint8_t *pch, size_t len); std::string EncodeBase32(const std::string &str); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); -int64_t atoi64(const char *psz); int64_t atoi64(const std::string &str); int atoi(const std::string &str); /** * Tests if the given character is a decimal digit. * @param[in] c character to test * @return true if the argument is a decimal digit; otherwise false. */ constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; } /** * Tests if the given character is a whitespace character. The whitespace * characters are: space, form-feed ('\f'), newline ('\n'), carriage return * ('\r'), horizontal tab ('\t'), and vertical tab ('\v'). * * This function is locale independent. Under the C locale this function gives * the same result as std::isspace. * * @param[in] c character to test * @return true if the argument is a whitespace character; otherwise * false */ constexpr inline bool IsSpace(char c) noexcept { return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; } /** * Convert string to signed 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ NODISCARD bool ParseInt32(const std::string &str, int32_t *out); /** * Convert string to signed 64-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ NODISCARD bool ParseInt64(const std::string &str, int64_t *out); /** * Convert decimal string to unsigned 32-bit integer with strict parse error * feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ NODISCARD bool ParseUInt32(const std::string &str, uint32_t *out); /** * Convert decimal string to unsigned 64-bit integer with strict parse error * feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ NODISCARD bool ParseUInt64(const std::string &str, uint64_t *out); /** * Convert string to double with strict parse error feedback. * @returns true if the entire string could be parsed as valid double, false if * not the entire string could be parsed or when overflow or underflow occurred. */ NODISCARD bool ParseDouble(const std::string &str, double *out); template std::string HexStr(const T itbegin, const T itend) { std::string rv; static const char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; rv.reserve(std::distance(itbegin, itend) * 2); for (T it = itbegin; it < itend; ++it) { uint8_t val = uint8_t(*it); rv.push_back(hexmap[val >> 4]); rv.push_back(hexmap[val & 15]); } return rv; } template inline std::string HexStr(const T &vch) { return HexStr(vch.begin(), vch.end()); } /** * Format a paragraph of text to a fixed width, adding spaces for indentation to * any added line. */ std::string FormatParagraph(const std::string &in, size_t width = 79, size_t indent = 0); /** * Timing-attack-resistant comparison. * Takes time proportional to length of first argument. */ template bool TimingResistantEqual(const T &a, const T &b) { if (b.size() == 0) return a.size() == 0; size_t accumulator = a.size() ^ b.size(); for (size_t i = 0; i < a.size(); i++) accumulator |= a[i] ^ b[i % b.size()]; return accumulator == 0; } /** * Parse number as fixed point according to JSON number syntax. * See http://json.org/number.gif * @returns true on success, false on error. * @note The result must be in the range (-10^18,10^18), otherwise an overflow * error will trigger. */ NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); /** * Convert from one power-of-2 number base to another. * * If padding is enabled, this always return true. If not, then it returns true * of all the bits of the input are encoded in the output. */ template bool ConvertBits(const O &outfn, I it, I end) { size_t acc = 0; size_t bits = 0; constexpr size_t maxv = (1 << tobits) - 1; constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; while (it != end) { acc = ((acc << frombits) | *it) & max_acc; bits += frombits; while (bits >= tobits) { bits -= tobits; outfn((acc >> bits) & maxv); } ++it; } if (pad) { if (bits) { outfn((acc << (tobits - bits)) & maxv); } } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { return false; } return true; } /** * Converts the given character to its lowercase equivalent. * This function is locale independent. It only converts uppercase * characters in the standard 7-bit ASCII range. * This is a feature, not a limitation. * * @param[in] c the character to convert to lowercase. * @return the lowercase equivalent of c; or the argument * if no conversion is possible. */ constexpr char ToLower(char c) { return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c); } /** * Returns the lowercase equivalent of the given string. * This function is locale independent. It only converts uppercase * characters in the standard 7-bit ASCII range. * This is a feature, not a limitation. * * @param[in] str the string to convert to lowercase. * @returns lowercased equivalent of str */ std::string ToLower(const std::string &str); /** * Converts the given character to its uppercase equivalent. * This function is locale independent. It only converts lowercase * characters in the standard 7-bit ASCII range. * This is a feature, not a limitation. * * @param[in] c the character to convert to uppercase. * @return the uppercase equivalent of c; or the argument * if no conversion is possible. */ constexpr char ToUpper(char c) { return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c); } /** * Returns the uppercase equivalent of the given string. * This function is locale independent. It only converts lowercase * characters in the standard 7-bit ASCII range. * This is a feature, not a limitation. * * @param[in] str the string to convert to uppercase. * @returns UPPERCASED EQUIVALENT OF str */ std::string ToUpper(const std::string &str); /** * Capitalizes the first character of the given string. * This function is locale independent. It only converts lowercase * characters in the standard 7-bit ASCII range. * This is a feature, not a limitation. * * @param[in] str the string to capitalize. * @returns string with the first letter capitalized. */ std::string Capitalize(std::string str); #endif // BITCOIN_UTIL_STRENCODINGS_H diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9453478a9..d0b6e8e35 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1,1612 +1,1612 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2018-2020 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_WALLET_H #define BITCOIN_WALLET_WALLET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using LoadWalletFn = std::function wallet)>; struct bilingual_str; //! Explicitly unload and delete the wallet. //! Blocks the current thread after signaling the unload intent so that all //! wallet clients release the wallet. //! Note that, when blocking is not required, the wallet is implicitly unloaded //! by the shared pointer deleter. void UnloadWallet(std::shared_ptr &&wallet); bool AddWallet(const std::shared_ptr &wallet); bool RemoveWallet(const std::shared_ptr &wallet); bool HasWallets(); std::vector> GetWallets(); std::shared_ptr GetWallet(const std::string &name); std::shared_ptr LoadWallet(const CChainParams &chainParams, interfaces::Chain &chain, const WalletLocation &location, bilingual_str &error, std::vector &warnings); std::unique_ptr HandleLoadWallet(LoadWalletFn load_wallet); enum class WalletCreationStatus { SUCCESS, CREATION_FAILED, ENCRYPTION_FAILED }; WalletCreationStatus CreateWallet(const CChainParams ¶ms, interfaces::Chain &chain, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector &warnings, std::shared_ptr &result); //! -paytxfee default constexpr Amount DEFAULT_PAY_TX_FEE = Amount::zero(); //! -fallbackfee default static const Amount DEFAULT_FALLBACK_FEE = Amount::zero(); //! -mintxfee default static const Amount DEFAULT_TRANSACTION_MINFEE_PER_KB = 1000 * SATOSHI; /** * maximum fee increase allowed to do partial spend avoidance, even for nodes * with this feature disabled by default * * A value of -1 disables this feature completely. * A value of 0 (current default) means to attempt to do partial spend * avoidance, and use its results if the fees remain *unchanged* A value > 0 * means to do partial spend avoidance if the fee difference against a regular * coin selection instance is in the range [0..value]. */ static const Amount DEFAULT_MAX_AVOIDPARTIALSPEND_FEE = Amount::zero(); //! discourage APS fee higher than this amount constexpr Amount HIGH_APS_FEE{COIN / 10000}; //! minimum recommended increment for BIP 125 replacement txs static const Amount WALLET_INCREMENTAL_RELAY_FEE(5000 * SATOSHI); //! Default for -spendzeroconfchange static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true; //! Default for -walletrejectlongchains static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; //! -maxtxfee default constexpr Amount DEFAULT_TRANSACTION_MAXFEE{COIN / 10}; //! Discourage users to set fees higher than this amount (in satoshis) per kB constexpr Amount HIGH_TX_FEE_PER_KB{COIN / 100}; //! -maxtxfee will warn if called with a higher fee than this amount (in //! satoshis) constexpr Amount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB}; //! Pre-calculated constants for input size estimation static constexpr size_t DUMMY_P2PKH_INPUT_SIZE = 148; class CChainParams; class CCoinControl; class COutput; class CScript; class CTxMemPool; class CWalletTx; class ReserveDestination; //! Default for -addresstype constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::LEGACY}; //! Default for -changetype constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO}; static constexpr uint64_t KNOWN_WALLET_FLAGS = WALLET_FLAG_AVOID_REUSE | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_KEY_ORIGIN_METADATA | WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_DESCRIPTORS; static constexpr uint64_t MUTABLE_WALLET_FLAGS = WALLET_FLAG_AVOID_REUSE; static const std::map WALLET_FLAG_MAP{ {"avoid_reuse", WALLET_FLAG_AVOID_REUSE}, {"blank", WALLET_FLAG_BLANK_WALLET}, {"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA}, {"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS}, {"descriptor_wallet", WALLET_FLAG_DESCRIPTORS}, }; extern const std::map WALLET_FLAG_CAVEATS; /** * A wrapper to reserve an address from a wallet * * ReserveDestination is used to reserve an address. * It is currently only used inside of CreateTransaction. * * Instantiating a ReserveDestination does not reserve an address. To do so, * GetReservedDestination() needs to be called on the object. Once an address * has been reserved, call KeepDestination() on the ReserveDestination object to * make sure it is not returned. Call ReturnDestination() to return the address * so it can be re-used (for example, if the address was used in a new * transaction and that transaction was not completed and needed to be aborted). * * If an address is reserved and KeepDestination() is not called, then the * address will be returned when the ReserveDestination goes out of scope. */ class ReserveDestination { protected: //! The wallet to reserve from const CWallet *const pwallet; //! The ScriptPubKeyMan to reserve from. Based on type when //! GetReservedDestination is called ScriptPubKeyMan *m_spk_man{nullptr}; OutputType const type; //! The index of the address's key in the keypool int64_t nIndex{-1}; //! The destination CTxDestination address; //! Whether this is from the internal (change output) keypool bool fInternal{false}; public: //! Construct a ReserveDestination object. This does NOT reserve an address //! yet explicit ReserveDestination(CWallet *_pwallet, OutputType _type) : pwallet(_pwallet), type(_type) {} ReserveDestination(const ReserveDestination &) = delete; ReserveDestination &operator=(const ReserveDestination &) = delete; //! Destructor. If a key has been reserved and not KeepKey'ed, it will be //! returned to the keypool ~ReserveDestination() { ReturnDestination(); } //! Reserve an address bool GetReservedDestination(CTxDestination &pubkey, bool internal); //! Return reserved address void ReturnDestination(); //! Keep the address. Do not return it's key to the keypool when this object //! goes out of scope void KeepDestination(); }; /** Address book data */ class CAddressBookData { private: bool m_change{true}; std::string m_label; public: std::string purpose; CAddressBookData() : purpose("unknown") {} typedef std::map StringMap; StringMap destdata; bool IsChange() const { return m_change; } const std::string &GetLabel() const { return m_label; } void SetLabel(const std::string &label) { m_change = false; m_label = label; } }; struct CRecipient { CScript scriptPubKey; Amount nAmount; bool fSubtractFeeFromAmount; }; typedef std::map mapValue_t; static inline void ReadOrderPos(int64_t &nOrderPos, mapValue_t &mapValue) { if (!mapValue.count("n")) { // TODO: calculate elsewhere nOrderPos = -1; return; } - nOrderPos = atoi64(mapValue["n"].c_str()); + nOrderPos = atoi64(mapValue["n"]); } static inline void WriteOrderPos(const int64_t &nOrderPos, mapValue_t &mapValue) { if (nOrderPos == -1) { return; } mapValue["n"] = ToString(nOrderPos); } struct COutputEntry { CTxDestination destination; Amount amount; int vout; }; /** * Legacy class used for deserializing vtxPrev for backwards compatibility. * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3, * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs. * These need to get deserialized for field alignment when deserializing * a CWalletTx, but the deserialized values are discarded. */ class CMerkleTx { public: template void Unserialize(Stream &s) { CTransactionRef tx; BlockHash hashBlock; std::vector vMerkleBranch; int nIndex = 0; s >> tx >> hashBlock >> vMerkleBranch >> nIndex; } }; // Get the marginal bytes of spending the specified output int CalculateMaximumSignedInputSize(const CTxOut &txout, const CWallet *pwallet, bool use_max_sig = false); /** * A transaction with a bunch of additional info that only the owner cares * about. It includes any unrecorded transactions needed to link it back to the * block chain. */ class CWalletTx { private: const CWallet *pwallet; /** * Constant used in hashBlock to indicate tx has been abandoned, only used * at serialization/deserialization to avoid ambiguity with conflicted. */ static const BlockHash ABANDON_HASH; public: /** * Key/value map with information about the transaction. * * The following keys can be read and written through the map and are * serialized in the wallet database: * * "comment", "to" - comment strings provided to sendtoaddress, * and sendmany wallet RPCs * "replaces_txid" - txid (as HexStr) of transaction replaced by * bumpfee on transaction created by bumpfee * "replaced_by_txid" - txid (as HexStr) of transaction created by * bumpfee on transaction replaced by bumpfee * "from", "message" - obsolete fields that could be set in UI prior to * 2011 (removed in commit 4d9b223) * * The following keys are serialized in the wallet database, but shouldn't * be read or written through the map (they will be temporarily added and * removed from the map during serialization): * * "fromaccount" - serialized strFromAccount value * "n" - serialized nOrderPos value * "timesmart" - serialized nTimeSmart value * "spent" - serialized vfSpent value that existed prior to * 2014 (removed in commit 93a18a3) */ mapValue_t mapValue; std::vector> vOrderForm; unsigned int fTimeReceivedIsTxTime; //! time received by this node unsigned int nTimeReceived; /** * Stable timestamp that never changes, and reflects the order a transaction * was added to the wallet. Timestamp is based on the block time for a * transaction added as part of a block, or else the time when the * transaction was received if it wasn't part of a block, with the timestamp * adjusted in both cases so timestamp order matches the order transactions * were added to the wallet. More details can be found in * CWallet::ComputeTimeSmart(). */ unsigned int nTimeSmart; /** * From me flag is set to 1 for transactions that were created by the wallet * on this bitcoin node, and set to 0 for transactions that were created * externally and came in through the network or sendrawtransaction RPC. */ bool fFromMe; //! position in ordered transaction list int64_t nOrderPos; std::multimap::const_iterator m_it_wtxOrdered; // memory only enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS }; Amount GetCachableAmount(AmountType type, const isminefilter &filter, bool recalculate = false) const; mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]; /** * This flag is true if all m_amounts caches are empty. This is particularly * useful in places where MarkDirty is conditionally called and the * condition can be expensive and thus can be skipped if the flag is true. * See MarkDestinationsDirty. */ mutable bool m_is_cache_empty{true}; mutable bool fChangeCached; mutable bool fInMempool; mutable Amount nChangeCached; CWalletTx(const CWallet *wallet, CTransactionRef arg) : pwallet(wallet), tx(std::move(arg)) { Init(); } void Init() { mapValue.clear(); vOrderForm.clear(); fTimeReceivedIsTxTime = false; nTimeReceived = 0; nTimeSmart = 0; fFromMe = false; fChangeCached = false; fInMempool = false; nChangeCached = Amount::zero(); nOrderPos = -1; m_confirm = Confirmation{}; } CTransactionRef tx; /** * New transactions start as UNCONFIRMED. At BlockConnected, * they will transition to CONFIRMED. In case of reorg, at * BlockDisconnected, they roll back to UNCONFIRMED. If we detect a * conflicting transaction at block connection, we update conflicted tx and * its dependencies as CONFLICTED. If tx isn't confirmed and outside of * mempool, the user may switch it to ABANDONED by using the * abandontransaction call. This last status may be override by a CONFLICTED * or CONFIRMED transition. */ enum Status { UNCONFIRMED, CONFIRMED, CONFLICTED, ABANDONED }; /** * Confirmation includes tx status and a triplet of {block height/block * hash/tx index in block} at which tx has been confirmed. All three are set * to 0 if tx is unconfirmed or abandoned. Meaning of these fields changes * with CONFLICTED state where they instead point to block hash and block * height of the deepest conflicting tx. */ struct Confirmation { Status status; int block_height; BlockHash hashBlock; int nIndex; Confirmation(Status s = UNCONFIRMED, int b = 0, BlockHash h = BlockHash(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {} }; Confirmation m_confirm; template void Serialize(Stream &s) const { mapValue_t mapValueCopy = mapValue; mapValueCopy["fromaccount"] = ""; WriteOrderPos(nOrderPos, mapValueCopy); if (nTimeSmart) { mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); } //! Used to be vMerkleBranch std::vector dummy_vector1; //! Used to be vtxPrev std::vector dummy_vector2; //! Used to be fSpent bool dummy_bool = false; uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock; int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex; s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool; } template void Unserialize(Stream &s) { Init(); //! Used to be vMerkleBranch std::vector dummy_vector1; //! Used to be vtxPrev std::vector dummy_vector2; //! Used to be fSpent bool dummy_bool; int serializedIndex; s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool; /* * At serialization/deserialization, an nIndex == -1 means that * hashBlock refers to the earliest block in the chain we know this or * any in-wallet ancestor conflicts with. If nIndex == -1 and hashBlock * is ABANDON_HASH, it means transaction is abandoned. In same context, * an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) * or unconfirmed one. Older clients interpret nIndex == -1 as * unconfirmed for backward compatibility (pre-commit 9ac63d6). */ if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) { setAbandoned(); } else if (serializedIndex == -1) { setConflicted(); } else if (!m_confirm.hashBlock.IsNull()) { m_confirm.nIndex = serializedIndex; setConfirmed(); } ReadOrderPos(nOrderPos, mapValue); nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; mapValue.erase("fromaccount"); mapValue.erase("spent"); mapValue.erase("n"); mapValue.erase("timesmart"); } void SetTx(CTransactionRef arg) { tx = std::move(arg); } //! make sure balances are recalculated void MarkDirty() { m_amounts[DEBIT].Reset(); m_amounts[CREDIT].Reset(); m_amounts[IMMATURE_CREDIT].Reset(); m_amounts[AVAILABLE_CREDIT].Reset(); fChangeCached = false; m_is_cache_empty = true; } //! filter decides which addresses will count towards the debit Amount GetDebit(const isminefilter &filter) const; Amount GetCredit(const isminefilter &filter) const; Amount GetImmatureCredit(bool fUseCache = true) const; // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid // having to resolve the issue of member access into incomplete type // CWallet. Amount GetAvailableCredit(bool fUseCache = true, const isminefilter &filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS; Amount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const; Amount GetChange() const; // Get the marginal bytes if spending the specified output from this // transaction int GetSpendSize(unsigned int out, bool use_max_sig = false) const { return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig); } void GetAmounts(std::list &listReceived, std::list &listSent, Amount &nFee, const isminefilter &filter) const; bool IsFromMe(const isminefilter &filter) const { return GetDebit(filter) > Amount::zero(); } // True if only scriptSigs are different bool IsEquivalentTo(const CWalletTx &tx) const; bool InMempool() const; bool IsTrusted() const; bool IsTrusted(std::set &trusted_parents) const; int64_t GetTxTime() const; // Pass this transaction to node for mempool insertion and relay to peers if // flag set to true bool SubmitMemoryPoolAndRelay(std::string &err_string, bool relay); // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to // resolve the issue of member access into incomplete type CWallet. Note // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" // in place. std::set GetConflicts() const NO_THREAD_SAFETY_ANALYSIS; /** * Return depth of transaction in blockchain: * <0 : conflicts with a transaction this deep in the blockchain * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain */ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to // resolve the issue of member access into incomplete type CWallet. Note // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)" // in place. int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS; bool IsInMainChain() const { return GetDepthInMainChain() > 0; } /** * @return number of blocks to maturity for this transaction: * 0 : is not a coinbase transaction, or is a mature coinbase transaction * >0 : is a coinbase transaction which matures in this many blocks */ int GetBlocksToMaturity() const; bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } void setAbandoned() { m_confirm.status = CWalletTx::ABANDONED; m_confirm.hashBlock = BlockHash(); m_confirm.block_height = 0; m_confirm.nIndex = 0; } bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; } void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; } bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; } void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; } bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; } void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } TxId GetId() const { return tx->GetId(); } bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsImmatureCoinBase() const; // Disable copying of CWalletTx objects to prevent bugs where instances get // copied in and out of the mapWallet map, and fields are updated in the // wrong copy. CWalletTx(CWalletTx const &) = delete; void operator=(CWalletTx const &x) = delete; }; class COutput { public: const CWalletTx *tx; int i; int nDepth; /** * Pre-computed estimated size of this output as a fully-signed input in a * transaction. Can be -1 if it could not be calculated. */ int nInputBytes; /** Whether we have the private keys to spend this output */ bool fSpendable; /** Whether we know how to spend this output, ignoring the lack of keys */ bool fSolvable; /** * Whether to use the maximum sized, 72 byte signature when calculating the * size of the input spend. This should only be set when watch-only outputs * are allowed. */ bool use_max_sig; /** * Whether this output is considered safe to spend. Unconfirmed transactions * from outside keys are considered unsafe and will not be used to fund new * spending transactions. */ bool fSafe; COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false) { tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in; // If known and signable by the given wallet, compute nInputBytes // Failure will keep this value -1 if (fSpendable && tx) { nInputBytes = tx->GetSpendSize(i, use_max_sig); } } std::string ToString() const; inline CInputCoin GetInputCoin() const { return CInputCoin(tx->tx, i, nInputBytes); } }; struct CoinSelectionParams { bool use_bnb = true; size_t change_output_size = 0; size_t change_spend_size = 0; CFeeRate effective_fee = CFeeRate(Amount::zero()); size_t tx_noinputs_size = 0; //! Indicate that we are subtracting the fee from outputs bool m_subtract_fee_outputs = false; CoinSelectionParams(bool use_bnb_, size_t change_output_size_, size_t change_spend_size_, CFeeRate effective_fee_, size_t tx_noinputs_size_) : use_bnb(use_bnb_), change_output_size(change_output_size_), change_spend_size(change_spend_size_), effective_fee(effective_fee_), tx_noinputs_size(tx_noinputs_size_) {} CoinSelectionParams() {} }; // forward declarations for ScanForWalletTransactions/RescanFromTime class WalletRescanReserver; /** * A CWallet maintains a set of transactions and balances, and provides the * ability to create new transactions. */ class CWallet final : public WalletStorage, public interfaces::Chain::Notifications { private: CKeyingMaterial vMasterKey GUARDED_BY(cs_wallet); bool Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys = false); std::atomic fAbortRescan{false}; // controlled by WalletRescanReserver std::atomic fScanningWallet{false}; std::atomic m_scanning_start{0}; std::atomic m_scanning_progress{0}; friend class WalletRescanReserver; //! the current wallet version: clients below this version are not able to //! load the wallet int nWalletVersion GUARDED_BY(cs_wallet) = FEATURE_BASE; //! the maximum wallet format version: memory-only variable that specifies //! to what version this wallet may be upgraded int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE; int64_t nNextResend = 0; bool fBroadcastTransactions = false; // Local time that the tip block was received. Used to schedule wallet // rebroadcasts. std::atomic m_best_block_time{0}; /** * Used to keep track of spent outpoints, and detect and report conflicts * (double-spends or mutated transactions where the mutant gets mined). */ typedef std::multimap TxSpends; TxSpends mapTxSpends GUARDED_BY(cs_wallet); void AddToSpends(const COutPoint &outpoint, const TxId &wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void AddToSpends(const TxId &wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Add a transaction to the wallet, or update it. pIndex and posInBlock * should be set when the transaction was known to be included in a * block. When *pIndex == nullptr, then wallet state is not updated in * AddToWallet, but notifications happen and cached balances are marked * dirty. * * If fUpdate is true, existing transactions will be updated. * TODO: One exception to this is that the abandoned state is cleared under * the assumption that any further notification of a transaction that was * considered abandoned is an indication that it is not safe to be * considered abandoned. Abandoned state should probably be more carefully * tracked via different posInBlock signals or by checking mempool presence * when necessary. */ bool AddToWalletIfInvolvingMe(const CTransactionRef &tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Mark a transaction (and its in-wallet descendants) as conflicting with a * particular block. */ void MarkConflicted(const BlockHash &hashBlock, int conflicting_height, const TxId &txid); /** * Mark a transaction's inputs dirty, thus forcing the outputs to be * recomputed */ void MarkInputsDirty(const CTransactionRef &tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SyncMetaData(std::pair) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Used by * TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions. * Should be called with non-zero block_hash and posInBlock if this is for a * transaction that is included in a block. */ void SyncTransaction(const CTransactionRef &tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::atomic m_wallet_flags{0}; bool SetAddressBookWithDB(WalletBatch &batch, const CTxDestination &address, const std::string &strName, const std::string &strPurpose); //! Unsets a wallet flag and saves it to disk void UnsetWalletFlagWithDB(WalletBatch &batch, uint64_t flag); //! Unset the blank wallet flag and saves it to disk void UnsetBlankWalletFlag(WalletBatch &batch) override; /** Interface for accessing chain state. */ interfaces::Chain *m_chain; /** * Wallet location which includes wallet name (see WalletLocation). */ WalletLocation m_location; /** Internal database handle. */ std::unique_ptr database; /** * The following is used to keep track of how far behind the wallet is * from the chain sync, and to allow clients to block on us being caught up. * * Processed hash is a pointer on node's tip and doesn't imply that the * wallet has scanned sequentially all blocks up to this one. */ BlockHash m_last_block_processed GUARDED_BY(cs_wallet); /* Height of last block processed is used by wallet to know depth of * transactions without relying on Chain interface beyond asynchronous * updates. For safety, we initialize it to -1. Height is a pointer on * node's tip and doesn't imply that the wallet has scanned sequentially all * blocks up to this one. */ int m_last_block_processed_height GUARDED_BY(cs_wallet) = -1; bool CreateTransactionInternal(const std::vector &vecSend, CTransactionRef &tx, Amount &nFeeRet, int &nChangePosInOut, bilingual_str &error, const CCoinControl &coin_control, bool sign); std::map m_external_spk_managers; std::map m_internal_spk_managers; // Indexed by a unique identifier produced by each ScriptPubKeyMan using // ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal // structure std::map> m_spk_managers; public: /* * Main wallet lock. * This lock protects all the fields added by CWallet. */ mutable RecursiveMutex cs_wallet; /** * Get database handle used by this wallet. Ideally this function would not * be necessary. */ WalletDatabase &GetDBHandle() { return *database; } WalletDatabase &GetDatabase() override { return *database; } /** * Select a set of coins such that nValueRet >= nTargetValue and at least * all coins from coinControl are selected; Never select unconfirmed coins * if they are not ours. */ bool SelectCoins(const std::vector &vAvailableCoins, const Amount nTargetValue, std::set &setCoinsRet, Amount &nValueRet, const CCoinControl &coin_control, CoinSelectionParams &coin_selection_params, bool &bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); const WalletLocation &GetLocation() const { return m_location; } /** * Get a name for this wallet for logging/debugging purposes. */ const std::string &GetName() const { return m_location.GetName(); } typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID = 0; /** Construct wallet with specified name and database implementation. */ CWallet(interfaces::Chain *chain, const WalletLocation &location, std::unique_ptr _database) : m_chain(chain), m_location(location), database(std::move(_database)) { } ~CWallet() { // Should not have slots connected at this point. assert(NotifyUnload.empty()); } /* Returns the chain params used by this wallet. */ const CChainParams &GetChainParams() const override; bool IsCrypted() const; bool IsLocked() const override; bool Lock(); /** Interface to assert chain access */ bool HaveChain() const { return m_chain ? true : false; } std::map mapWallet GUARDED_BY(cs_wallet); typedef std::multimap TxItems; TxItems wtxOrdered; int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0; uint64_t nAccountingEntryNumber = 0; std::map m_address_book GUARDED_BY(cs_wallet); const CAddressBookData * FindAddressBookEntry(const CTxDestination &, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::set setLockedCoins GUARDED_BY(cs_wallet); /** Registered interfaces::Chain::Notifications handler. */ std::unique_ptr m_chain_notifications_handler; /** Interface for accessing chain state. */ interfaces::Chain &chain() const { assert(m_chain); return *m_chain; } const CWalletTx *GetWalletTx(const TxId &txid) const; //! check whether we are allowed to upgrade (or already support) to the //! named feature bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } /** * populate vCoins with vector of available COutputs. */ void AvailableCoins(std::vector &vCoins, bool fOnlySafe = true, const CCoinControl *coinControl = nullptr, const Amount nMinimumAmount = SATOSHI, const Amount nMaximumAmount = MAX_MONEY, const Amount nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Return list of available coins and locked coins grouped by non-change * output address. */ std::map> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Find non-change parent output. */ const CTxOut &FindNonChangeParentOutput(const CTransaction &tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Shuffle and select coins until nTargetValue is reached while avoiding * small change; This method is stochastic for some inputs and upon * completion the coin set and corresponding actual target value is * assembled. */ bool SelectCoinsMinConf(const Amount nTargetValue, const CoinEligibilityFilter &eligibility_filter, std::vector groups, std::set &setCoinsRet, Amount &nValueRet, const CoinSelectionParams &coin_selection_params, bool &bnb_used) const; bool IsSpent(const COutPoint &outpoint) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); // Whether this or any UTXO with the same CTxDestination has been spent. bool IsSpentKey(const TxId &txid, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetSpentKeyState(WalletBatch &batch, const TxId &txid, unsigned int n, bool used, std::set &tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::vector GroupOutputs(const std::vector &outputs, bool single_coin, const size_t max_ancestors) const; bool IsLockedCoin(const COutPoint &outpoint) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void LockCoin(const COutPoint &output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void UnlockCoin(const COutPoint &output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ListLockedCoins(std::vector &vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* * Rescan abort properties */ void AbortRescan() { fAbortRescan = true; } bool IsAbortingRescan() const { return fAbortRescan; } bool IsScanning() const { return fScanningWallet; } int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; } double ScanningProgress() const { return fScanningWallet ? double(m_scanning_progress) : 0; } //! Upgrade stored CKeyMetadata objects to store key origin info as //! KeyOriginInfo void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } /** * Adds a destination data tuple to the store, and saves it to disk * When adding new fields, take care to consider how DelAddressBook should * handle it! */ bool AddDestData(WalletBatch &batch, const CTxDestination &dest, const std::string &key, const std::string &value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Erases a destination data tuple in the store and on disk bool EraseDestData(WalletBatch &batch, const CTxDestination &dest, const std::string &key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a destination data tuple to the store, without saving it to disk void LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Look up a destination data tuple in the store, return true if found //! false otherwise bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Get all destination values matching a prefix. std::vector GetDestValues(const std::string &prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Holds a timestamp at which point the wallet is scheduled (externally) to //! be relocked. Caller must arrange for actual relocking to occur via //! Lock(). int64_t nRelockTime GUARDED_BY(cs_wallet){0}; // Used to prevent concurrent calls to walletpassphrase RPC. Mutex m_unlock_mutex; bool Unlock(const SecureString &strWalletPassphrase, bool accept_no_keys = false); bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase, const SecureString &strNewWalletPassphrase); bool EncryptWallet(const SecureString &strWalletPassphrase); void GetKeyBirthTimes(std::map &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); unsigned int ComputeTimeSmart(const CWalletTx &wtx) const; /** * Increment the next transaction order id * @return next transaction order id */ int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); DBErrors ReorderTransactions(); void MarkDirty(); //! Callback for updating transaction metadata in mapWallet. //! //! @param wtx - reference to mapWallet transaction to update //! @param new_tx - true if wtx is newly inserted, false if it previously //! existed //! //! @return true if wtx is changed and needs to be saved to disk, otherwise //! false using UpdateWalletTxFn = std::function; CWalletTx *AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation &confirm, const UpdateWalletTxFn &update_wtx = nullptr, bool fFlushOnClose = true); bool LoadToWallet(const TxId &txid, const UpdateWalletTxFn &fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void transactionAddedToMempool(const CTransactionRef &tx) override; void blockConnected(const CBlock &block, int height) override; void blockDisconnected(const CBlock &block, int height) override; void updatedBlockTip() override; int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver &reserver, bool update); struct ScanResult { enum { SUCCESS, FAILURE, USER_ABORT } status = SUCCESS; //! Hash and height of most recent block that was successfully scanned. //! Unset if no blocks were scanned due to read errors or the chain //! being empty. BlockHash last_scanned_block; std::optional last_scanned_height; //! Hash of the most recent block that could not be scanned due to //! read errors or pruning. Will be set if status is FAILURE, unset if //! status is SUCCESS, and may or may not be set if status is //! USER_ABORT. BlockHash last_failed_block; }; ScanResult ScanForWalletTransactions(const BlockHash &start_block, int start_height, std::optional max_height, const WalletRescanReserver &reserver, bool fUpdate); void transactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ResendWalletTransactions(); struct Balance { //! Trusted, at depth=GetBalance.min_depth or more Amount m_mine_trusted{Amount::zero()}; //! Untrusted, but in mempool (pending) Amount m_mine_untrusted_pending{Amount::zero()}; //! Immature coinbases in the main chain Amount m_mine_immature{Amount::zero()}; Amount m_watchonly_trusted{Amount::zero()}; Amount m_watchonly_untrusted_pending{Amount::zero()}; Amount m_watchonly_immature{Amount::zero()}; }; Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const; Amount GetAvailableBalance(const CCoinControl *coinControl = nullptr) const; OutputType TransactionChangeType(OutputType change_type, const std::vector &vecSend); /** * Insert additional inputs into the transaction by calling * CreateTransaction(); */ bool FundTransaction(CMutableTransaction &tx, Amount &nFeeRet, int &nChangePosInOut, bilingual_str &error, bool lockUnspents, const std::set &setSubtractFeeFromOutputs, CCoinControl coinControl); // Fetch the inputs and sign with SIGHASH_ALL. bool SignTransaction(CMutableTransaction &tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); // Sign the tx given the input coins and sighash. bool SignTransaction(CMutableTransaction &tx, const std::map &coins, SigHashType sighash, std::map &input_errors) const; SigningResult SignMessage(const std::string &message, const PKHash &pkhash, std::string &str_sig) const; /** * Fills out a PSBT with information from the wallet. Fills in UTXOs if we * have them. Tries to sign if sign=true. Sets `complete` if the PSBT is now * complete (i.e. has all required signatures or signature-parts, and is * ready to finalize.) Sets `error` and returns false if something goes * wrong. * * @param[in] psbtx PartiallySignedTransaction to fill in * @param[out] complete indicates whether the PSBT is now complete * @param[in] sighash_type the sighash type to use when signing (if PSBT * does not specify) * @param[in] sign whether to sign or not * @param[in] bip32derivs whether to fill in bip32 derivation information * if available return error */ TransactionError FillPSBT(PartiallySignedTransaction &psbtx, bool &complete, SigHashType sighash_type = SigHashType().withForkId(), bool sign = true, bool bip32derivs = true) const; /** * Create a new transaction paying the recipients with a set of coins * selected by SelectCoins(); Also create the change output, when needed * @note passing nChangePosInOut as -1 will result in setting a random * position */ bool CreateTransaction(const std::vector &vecSend, CTransactionRef &tx, Amount &nFeeRet, int &nChangePosInOut, bilingual_str &error, const CCoinControl &coin_control, bool sign = true); /** * Submit the transaction to the node's mempool and then relay to peers. * Should be called after CreateTransaction unless you want to abort * broadcasting the transaction. * * @param[in] tx The transaction to be broadcast. * @param[in] mapValue key-values to be set on the transaction. * @param[in] orderForm BIP 70 / BIP 21 order form details to be set on the * transaction. */ void CommitTransaction( CTransactionRef tx, mapValue_t mapValue, std::vector> orderForm); bool DummySignTx(CMutableTransaction &txNew, const std::set &txouts, bool use_max_sig = false) const { std::vector v_txouts(txouts.size()); std::copy(txouts.begin(), txouts.end(), v_txouts.begin()); return DummySignTx(txNew, v_txouts, use_max_sig); } bool DummySignTx(CMutableTransaction &txNew, const std::vector &txouts, bool use_max_sig = false) const; bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const; bool ImportScripts(const std::set scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool ImportPrivKeys(const std::map &privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool ImportPubKeys( const std::vector &ordered_pubkeys, const std::map &pubkey_map, const std::map> &key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool ImportScriptPubKeys(const std::string &label, const std::set &script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE}; bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; //! will be false if -fallbackfee=0 bool m_allow_fallback_fee{true}; // Override with -mintxfee CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE_PER_KB}; /** * If fee estimation does not have enough data to provide estimates, use * this fee instead. Has no effect if not using fee estimation Override with * -fallbackfee */ CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE}; //! note: this is absolute fee, not fee rate Amount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE}; OutputType m_default_change_type{DEFAULT_CHANGE_TYPE}; /** * Absolute maximum transaction fee (in satoshis) used by default for the * wallet. */ Amount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE}; size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0); int64_t GetOldestKeyPoolTime() const; std::set> GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::map GetAddressBalances() const; std::set GetLabelAddresses(const std::string &label) const; /** * Marks all outputs in each one of the destinations dirty, so their cache * is reset and does not return outdated information. */ void MarkDestinationsDirty(const std::set &destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool GetNewDestination(const OutputType type, const std::string label, CTxDestination &dest, std::string &error); bool GetNewChangeDestination(const OutputType type, CTxDestination &dest, std::string &error); isminetype IsMine(const CTxDestination &dest) const; isminetype IsMine(const CScript &script) const; isminetype IsMine(const CTxIn &txin) const; /** * Returns amount of debit if the input matches the filter, otherwise * returns 0 */ Amount GetDebit(const CTxIn &txin, const isminefilter &filter) const; isminetype IsMine(const CTxOut &txout) const; Amount GetCredit(const CTxOut &txout, const isminefilter &filter) const; bool IsChange(const CTxOut &txout) const; bool IsChange(const CScript &script) const; Amount GetChange(const CTxOut &txout) const; bool IsMine(const CTransaction &tx) const; /** should probably be renamed to IsRelevantToMe */ bool IsFromMe(const CTransaction &tx) const; Amount GetDebit(const CTransaction &tx, const isminefilter &filter) const; /** Returns whether all of the inputs match the filter */ bool IsAllFromMe(const CTransaction &tx, const isminefilter &filter) const; Amount GetCredit(const CTransaction &tx, const isminefilter &filter) const; Amount GetChange(const CTransaction &tx) const; void chainStateFlushed(const CBlockLocator &loc) override; DBErrors LoadWallet(bool &fFirstRunRet); DBErrors ZapWalletTx(std::list &vWtx); DBErrors ZapSelectTx(std::vector &txIdsIn, std::vector &txIdsOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &purpose); bool DelAddressBook(const CTxDestination &address); unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! signify that a particular wallet feature is now used. this may change //! nWalletVersion and nWalletMaxVersion if those are lower void SetMinVersion(enum WalletFeature, WalletBatch *batch_in = nullptr, bool fExplicit = false) override; //! change which version we're allowed to upgrade to (note that this does //! not immediately imply upgrading to that format) bool SetMaxVersion(int nVersion); //! get the current wallet format (the oldest client version guaranteed to //! understand this wallet) int GetVersion() const { LOCK(cs_wallet); return nWalletVersion; } //! Get wallet transactions that conflict with given transaction (spend same //! outputs) std::set GetConflicts(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Check if a given transaction has any of its outputs spent by another //! transaction in the wallet bool HasWalletSpend(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Flush wallet (bitdb flush) void Flush(bool shutdown = false); /** Wallet is about to be unloaded */ boost::signals2::signal NotifyUnload; /** * Address book entry changed. * @note called with lock cs_wallet held. */ boost::signals2::signal NotifyAddressBookChanged; /** * Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ boost::signals2::signal NotifyTransactionChanged; /** Show progress e.g. for rescan */ boost::signals2::signal ShowProgress; /** Watch-only address added */ boost::signals2::signal NotifyWatchonlyChanged; /** Keypool has new keys */ boost::signals2::signal NotifyCanGetAddressesChanged; /** * Wallet status (encrypted, locked) changed. * Note: Called without locks held. */ boost::signals2::signal NotifyStatusChanged; /** Inquire whether this wallet broadcasts transactions. */ bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; } /** Return whether transaction can be abandoned */ bool TransactionCanBeAbandoned(const TxId &txid) const; /** * Mark a transaction (and it in-wallet descendants) as abandoned so its * inputs may be respent. */ bool AbandonTransaction(const TxId &txid); //! Verify wallet naming and perform salvage on the wallet if required static bool Verify(const CChainParams &chainParams, interfaces::Chain &chain, const WalletLocation &location, bilingual_str &error_string, std::vector &warnings); /** * Initializes the wallet, returns a new CWallet instance or a null pointer * in case of an error. */ static std::shared_ptr CreateWalletFromFile(const CChainParams &chainParams, interfaces::Chain &chain, const WalletLocation &location, bilingual_str &error, std::vector &warnings, uint64_t wallet_creation_flags = 0); /** * Wallet post-init setup * Gives the wallet a chance to register repetitive tasks and complete * post-init tasks */ void postInitProcess(); bool BackupWallet(const std::string &strDest) const; /* Returns true if HD is enabled */ bool IsHDEnabled() const; /** * Returns true if the wallet can give out new addresses. This means it has * keys in the keypool or can generate new keys. */ bool CanGetAddresses(bool internal = false) const; /** * Blocks until the wallet state is up-to-date to /at least/ the current * chain at the time this function is entered. * Obviously holding cs_main/cs_wallet when going into this call may cause * deadlock */ void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet); /** * Set a single wallet flag. */ void SetWalletFlag(uint64_t flags); /** * Unsets a single wallet flag. */ void UnsetWalletFlag(uint64_t flag); /** * Check if a certain wallet flag is set. */ bool IsWalletFlagSet(uint64_t flag) const override; /** * Overwrite all flags by the given uint64_t. * Returns false if unknown, non-tolerable flags are present. */ bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly); /** Determine if we are a legacy wallet */ bool IsLegacy() const; /** * Returns a bracketed wallet name for displaying in logs, will return * [default wallet] if the wallet has no name. */ const std::string GetDisplayName() const override { std::string wallet_name = GetName().length() == 0 ? "default wallet" : GetName(); return strprintf("[%s]", wallet_name); }; /** * Prepends the wallet name in logging output to ease debugging in * multi-wallet use cases. */ template void WalletLogPrintf(std::string fmt, Params... parameters) const { LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...); }; template void WalletLogPrintfToBeContinued(std::string fmt, Params... parameters) const { LogPrintfToBeContinued(("%s " + fmt).c_str(), GetDisplayName(), parameters...); }; /** Upgrade the wallet */ bool UpgradeWallet(int version, bilingual_str &error, std::vector &warnings); //! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and //! m_external_spk_managers std::set GetActiveScriptPubKeyMans() const; //! Returns all unique ScriptPubKeyMans std::set GetAllScriptPubKeyMans() const; //! Get the ScriptPubKeyMan for the given OutputType and internal/external //! chain. ScriptPubKeyMan *GetScriptPubKeyMan(const OutputType &type, bool internal) const; //! Get the ScriptPubKeyMan for a script ScriptPubKeyMan *GetScriptPubKeyMan(const CScript &script) const; //! Get the ScriptPubKeyMan by id ScriptPubKeyMan *GetScriptPubKeyMan(const uint256 &id) const; //! Get all of the ScriptPubKeyMans for a script given additional //! information in sigdata (populated by e.g. a psbt) std::set GetScriptPubKeyMans(const CScript &script, SignatureData &sigdata) const; //! Get the SigningProvider for a script std::unique_ptr GetSolvingProvider(const CScript &script) const; std::unique_ptr GetSolvingProvider(const CScript &script, SignatureData &sigdata) const; //! Get the LegacyScriptPubKeyMan which is used for all types, internal, and //! external. LegacyScriptPubKeyMan *GetLegacyScriptPubKeyMan() const; LegacyScriptPubKeyMan *GetOrCreateLegacyScriptPubKeyMan(); //! Make a LegacyScriptPubKeyMan and set it for all types, internal, and //! external. void SetupLegacyScriptPubKeyMan(); const CKeyingMaterial &GetEncryptionKey() const override; bool HasEncryptionKeys() const override; /** Get last block processed height */ int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); assert(m_last_block_processed_height >= 0); return m_last_block_processed_height; }; BlockHash GetLastBlockHash() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); assert(m_last_block_processed_height >= 0); return m_last_block_processed; } /** Set last block processed height, currently only use in unit test */ void SetLastBlockProcessed(int block_height, BlockHash block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); m_last_block_processed_height = block_height; m_last_block_processed = block_hash; }; //! Connect the signals from ScriptPubKeyMans to the signals in CWallet void ConnectScriptPubKeyManNotifiers(); //! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and //! load it void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor &desc); //! Sets the active ScriptPubKeyMan for the specified type and internal //! @param[in] id The unique id for the ScriptPubKeyMan //! @param[in] type The OutputType this ScriptPubKeyMan provides addresses //! for //! @param[in] internal Whether this ScriptPubKeyMan provides change //! addresses //! @param[in] memonly Whether to record this update to the database. Set to //! true for wallet loading, normally false when actually //! updating the wallet. void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly = false); //! Create new DescriptorScriptPubKeyMans and add them to the wallet void SetupDescriptorScriptPubKeyMans(); //! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is //! already in the wallet DescriptorScriptPubKeyMan * GetDescriptorScriptPubKeyMan(const WalletDescriptor &desc) const; //! Add a descriptor to the wallet, return a ScriptPubKeyMan & associated //! output type ScriptPubKeyMan * AddWalletDescriptor(WalletDescriptor &desc, const FlatSigningProvider &signing_provider, const std::string &label); }; /** * Called periodically by the schedule thread. Prompts individual wallets to * resend their transactions. Actual rebroadcast schedule is managed by the * wallets themselves. */ void MaybeResendWalletTxs(); /** RAII object to check and reserve a wallet rescan */ class WalletRescanReserver { private: CWallet &m_wallet; bool m_could_reserve; public: explicit WalletRescanReserver(CWallet &w) : m_wallet(w), m_could_reserve(false) {} bool reserve() { assert(!m_could_reserve); if (m_wallet.fScanningWallet.exchange(true)) { return false; } m_wallet.m_scanning_start = GetTimeMillis(); m_wallet.m_scanning_progress = 0; m_could_reserve = true; return true; } bool isReserved() const { return (m_could_reserve && m_wallet.fScanningWallet); } ~WalletRescanReserver() { if (m_could_reserve) { m_wallet.fScanningWallet = false; } } }; // Calculate the size of the transaction assuming all signatures are max size // Use DummySignatureCreator, which inserts 71 byte signatures everywhere. // NOTE: this requires that all inputs must be in mapWallet (eg the tx should // be IsAllFromMe). int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector &txouts, bool use_max_sig = false); #endif // BITCOIN_WALLET_WALLET_H