diff options
Diffstat (limited to 'src')
40 files changed, 787 insertions, 868 deletions
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 9ad66bc85b..a1821cafe3 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -365,12 +365,12 @@ translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCO $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) @test -f $(RCC) @cp -f $< $(@D)/temp_$(<F) - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale $(@D)/temp_$(<F) > $@ + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale --format-version 1 $(@D)/temp_$(<F) > $@ @rm $(@D)/temp_$(<F) $(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION) @test -f $(RCC) - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< > $@ + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin --format-version 1 $< > $@ CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_bitcoin_locale.qrc diff --git a/src/chain.h b/src/chain.h index 04a5db5a17..84a3a4e1e7 100644 --- a/src/chain.h +++ b/src/chain.h @@ -182,7 +182,7 @@ public: //! //! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot //! load to avoid the block index being spuriously rewound. - //! @sa RewindBlockIndex + //! @sa NeedsRedownload //! @sa ActivateSnapshot uint32_t nStatus{0}; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 994d420885..08587e2b37 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -659,518 +659,6 @@ static const uint8_t chainparams_seed_main[] = { 0x02,0x10,0x2a,0x0f,0xdf,0x00,0x00,0x00,0x02,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x20,0x8d, 0x02,0x10,0x2c,0x0f,0xf5,0x98,0x00,0x05,0x00,0x01,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d, 0x02,0x10,0x2c,0x0f,0xfc,0xe8,0x00,0x00,0x04,0x00,0x0b,0x7c,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d, - 0x03,0x0a,0xd6,0xbc,0x4a,0x3c,0x6d,0x03,0xa9,0x4e,0x1f,0x55,0x20,0x8d, - 0x03,0x0a,0xd6,0x8f,0xf0,0xf8,0xbb,0x10,0x00,0x18,0x42,0x54,0x20,0x8d, - 0x03,0x0a,0xd6,0xec,0x32,0xc1,0x59,0x9c,0xd8,0x46,0xd5,0x48,0x20,0x8d, - 0x03,0x0a,0xd6,0xf0,0x8d,0x96,0x37,0xc3,0x27,0x61,0x9a,0x24,0x20,0x8d, - 0x03,0x0a,0xd0,0x12,0xe6,0xed,0x8e,0xc1,0x78,0x8d,0x1c,0x21,0x20,0x8d, - 0x03,0x0a,0xd0,0x4a,0xc5,0xbd,0x5d,0xe9,0xca,0x57,0xaf,0xc4,0x20,0x8d, - 0x03,0x0a,0xd0,0x94,0xc0,0x97,0xd2,0x32,0xed,0x81,0x92,0x67,0x20,0x8d, - 0x03,0x0a,0xd1,0xd5,0x49,0x23,0xa6,0x10,0x01,0x49,0xda,0x05,0x20,0x8d, - 0x03,0x0a,0xd2,0x2a,0x76,0x2c,0x37,0x09,0x9a,0xa1,0x61,0x4b,0x20,0x8d, - 0x03,0x0a,0xd3,0x19,0x77,0x50,0xf5,0xf3,0x48,0x17,0x59,0x50,0x20,0x8d, - 0x03,0x0a,0xd4,0x24,0xda,0xf8,0x97,0x6d,0x28,0x80,0x47,0xf9,0x20,0x8d, - 0x03,0x0a,0xd4,0x28,0x30,0x9d,0x6d,0xac,0x1e,0xb4,0x6e,0x59,0x20,0x8d, - 0x03,0x0a,0xd5,0x13,0x71,0x95,0xd5,0x2e,0x12,0xf6,0x0e,0x6e,0x20,0x8d, - 0x03,0x0a,0xd5,0xc6,0x62,0x50,0xb1,0x22,0xb6,0x4a,0x31,0x56,0x20,0x8d, - 0x03,0x0a,0xd5,0xc7,0x99,0x46,0x87,0x91,0x13,0xc9,0xc9,0x16,0x20,0x8d, - 0x03,0x0a,0xdf,0x22,0x06,0xea,0xce,0x87,0x08,0x09,0x32,0x52,0x20,0x8d, - 0x03,0x0a,0xdf,0xa1,0xf5,0x1c,0xe4,0x4e,0x97,0x71,0xee,0xc5,0x20,0x8d, - 0x03,0x0a,0xdf,0xf7,0x64,0x8e,0x4f,0xa3,0xbb,0xaa,0x4f,0x30,0x20,0x8d, - 0x03,0x0a,0xdf,0xfc,0xa5,0x92,0x7d,0xbc,0x03,0x13,0x69,0x35,0x20,0x8d, - 0x03,0x0a,0xdf,0xd5,0x61,0xfc,0xb7,0x73,0xff,0xef,0x2f,0xaa,0x20,0x8d, - 0x03,0x0a,0xd9,0x3b,0x3f,0x9e,0x1c,0x02,0xe7,0xd9,0xba,0xb7,0x20,0x8d, - 0x03,0x0a,0xd9,0x83,0x73,0x90,0x25,0x3b,0xa9,0x4b,0x18,0x5b,0x20,0x8d, - 0x03,0x0a,0xd9,0xcc,0x14,0xe4,0x9a,0x68,0x6d,0x8a,0x12,0xc5,0x20,0x8d, - 0x03,0x0a,0xda,0x29,0x4a,0xc4,0x7a,0xb0,0x0e,0x0d,0x0a,0xee,0x20,0x8d, - 0x03,0x0a,0xda,0x67,0x7a,0x24,0x60,0x45,0x8f,0xe4,0x2e,0x74,0x20,0x8d, - 0x03,0x0a,0xda,0xfa,0x48,0x68,0x74,0xfb,0x2b,0x21,0x27,0x80,0x20,0x8d, - 0x03,0x0a,0xdb,0x5c,0x56,0x99,0xb0,0x5c,0x08,0x43,0xb7,0xee,0x20,0x8d, - 0x03,0x0a,0xdc,0x79,0xc1,0x8f,0x29,0x44,0xf2,0xdc,0x00,0xf6,0x20,0x8d, - 0x03,0x0a,0xdd,0x66,0x1a,0x59,0x93,0x73,0x7f,0x58,0x76,0x19,0x20,0x8d, - 0x03,0x0a,0xe7,0x9c,0x7c,0xce,0x79,0xe3,0xc8,0xa4,0x73,0x66,0x20,0x8d, - 0x03,0x0a,0xe7,0xca,0xbd,0xa2,0xab,0xe5,0x7b,0xe4,0xca,0x71,0x20,0x8d, - 0x03,0x0a,0xe7,0xd1,0xe8,0x45,0x7a,0x42,0x60,0x2b,0x2c,0xde,0x20,0x8d, - 0x03,0x0a,0xe7,0xe9,0x47,0x9b,0x22,0x6c,0x6c,0x03,0xba,0x6e,0x20,0x8d, - 0x03,0x0a,0xe0,0xba,0x25,0x23,0x7f,0x25,0x5c,0x51,0xcb,0xc3,0x20,0x8d, - 0x03,0x0a,0xe1,0x21,0xbf,0x26,0x37,0xfd,0xe9,0x89,0x95,0xe2,0x20,0x8d, - 0x03,0x0a,0xe1,0x2c,0xa1,0xde,0xa2,0x37,0x7e,0x01,0xc5,0xa8,0x20,0x8d, - 0x03,0x0a,0xe1,0x57,0x53,0x20,0x2d,0x66,0x9a,0xb1,0xed,0xa0,0x20,0x8d, - 0x03,0x0a,0xe2,0x00,0xe6,0xcf,0x0c,0xe7,0xd0,0xc0,0x58,0x9c,0x20,0x8d, - 0x03,0x0a,0xe2,0x6f,0x9d,0xfd,0xce,0xa7,0x40,0x6f,0xfb,0x62,0x20,0x8d, - 0x03,0x0a,0xe3,0x1a,0xaa,0xa7,0xc7,0x07,0xf6,0x48,0x34,0x2a,0x20,0x8d, - 0x03,0x0a,0xe3,0x5b,0x4c,0x5d,0x9d,0x57,0x66,0xbc,0x26,0x1b,0x20,0x8d, - 0x03,0x0a,0xe3,0x73,0xac,0x1b,0x82,0x6b,0xa6,0x4d,0x91,0x3f,0x20,0x8d, - 0x03,0x0a,0xe3,0xdd,0x9b,0x1f,0xdd,0xf7,0x30,0x6c,0x8c,0x6a,0x20,0x8d, - 0x03,0x0a,0xe4,0x0c,0x50,0xf7,0xd1,0xab,0xc2,0xc2,0x4a,0xff,0x20,0x8d, - 0x03,0x0a,0xe4,0x64,0x0b,0xeb,0x73,0x04,0x33,0x66,0x21,0x89,0x20,0x8d, - 0x03,0x0a,0xe5,0x3a,0x9e,0x83,0x1e,0x88,0x24,0xeb,0x4f,0x8c,0x20,0x8d, - 0x03,0x0a,0xe5,0x5d,0x1a,0xcd,0xd8,0x21,0x8f,0xcc,0x86,0xb1,0x20,0x8d, - 0x03,0x0a,0xee,0xa5,0xc4,0xf5,0xeb,0x1d,0x96,0xfc,0x9e,0x76,0x20,0x8d, - 0x03,0x0a,0xe8,0x02,0xf4,0x22,0x05,0xa9,0x14,0xe2,0x26,0x2e,0x20,0x8d, - 0x03,0x0a,0xe8,0x30,0x3c,0xde,0xfe,0x4e,0x1d,0x9d,0xb4,0x99,0x20,0x8d, - 0x03,0x0a,0xe8,0xaf,0x91,0xca,0x33,0x72,0xba,0x33,0x3b,0x80,0x20,0x8d, - 0x03,0x0a,0xe9,0x07,0x44,0x29,0xf4,0x1a,0x09,0xb4,0xe2,0x25,0x20,0x8d, - 0x03,0x0a,0xe9,0x1e,0x40,0x15,0x4c,0xc0,0x38,0x5a,0xf4,0x7d,0x20,0x8d, - 0x03,0x0a,0xe9,0x71,0x75,0xe6,0x68,0x16,0xe7,0xe6,0xba,0x79,0x20,0x8d, - 0x03,0x0a,0xe9,0xc7,0xe2,0x60,0x96,0xee,0x02,0xd8,0x78,0xc1,0x20,0x8d, - 0x03,0x0a,0xea,0x70,0x5c,0x9e,0xca,0x90,0x7d,0x48,0xc5,0xfa,0x20,0x8d, - 0x03,0x0a,0xeb,0x5c,0xe8,0x18,0x53,0xef,0xbe,0x83,0x77,0xf5,0x20,0x8d, - 0x03,0x0a,0xeb,0x64,0x56,0x71,0xb0,0x86,0x72,0xf8,0xa6,0x2f,0x20,0x8d, - 0x03,0x0a,0xeb,0xa1,0x29,0xde,0x4f,0xc9,0xd6,0x64,0x90,0xbe,0x20,0x8d, - 0x03,0x0a,0xeb,0xf3,0x96,0x0f,0x93,0x2b,0x9b,0x18,0x64,0x3a,0x20,0x8d, - 0x03,0x0a,0xec,0x84,0xa6,0x5f,0x98,0xa0,0x82,0xd7,0xf1,0x0e,0x20,0x8d, - 0x03,0x0a,0xed,0x09,0xfb,0x3a,0x39,0x0b,0x7c,0x77,0x37,0x64,0x20,0x8d, - 0x03,0x0a,0xed,0xae,0x7b,0xea,0x6e,0xcb,0xdd,0x52,0xfb,0x3b,0x20,0x8d, - 0x03,0x0a,0xed,0xd5,0xbc,0x51,0xbb,0xf1,0x37,0xa2,0x6f,0x88,0x20,0x8d, - 0x03,0x0a,0xee,0x4c,0x79,0xe8,0xdf,0xa8,0xa4,0x07,0xa3,0xdd,0x20,0x8d, - 0x03,0x0a,0xf6,0x86,0x21,0xbe,0xa3,0x72,0xcb,0x95,0x0f,0x2b,0x20,0x8d, - 0x03,0x0a,0xf6,0xc2,0xa7,0x69,0x87,0x45,0xda,0xdd,0x07,0xe3,0x20,0x8d, - 0x03,0x0a,0xf7,0xce,0x9a,0x96,0xbe,0xb2,0x05,0x30,0x2d,0x9d,0x20,0x8d, - 0x03,0x0a,0xf1,0xbc,0xa7,0x71,0x4b,0x51,0x7a,0x09,0xac,0x68,0x20,0x8d, - 0x03,0x0a,0xf2,0xbb,0x98,0x90,0x97,0xb7,0x04,0x01,0xdd,0x1d,0x20,0x8d, - 0x03,0x0a,0xf2,0x8b,0xd0,0x60,0xeb,0x79,0x1b,0x8b,0x18,0x12,0x20,0x8d, - 0x03,0x0a,0xf3,0x00,0x83,0x5d,0x35,0x11,0x27,0xc7,0xa2,0x64,0x20,0x8d, - 0x03,0x0a,0xf4,0x49,0x29,0x57,0x83,0xab,0xd6,0x1e,0xa0,0xe7,0x20,0x8d, - 0x03,0x0a,0xf4,0x52,0x4b,0xf8,0xd8,0xa0,0x28,0x8d,0x8b,0xa4,0x20,0x8d, - 0x03,0x0a,0xf4,0x94,0x66,0x97,0x9b,0x7b,0xce,0x3a,0xa6,0x80,0x20,0x8d, - 0x03,0x0a,0xf4,0xa7,0x70,0x38,0x74,0xb2,0x24,0x6e,0xca,0x07,0x20,0x8d, - 0x03,0x0a,0xf5,0xe1,0x8e,0x4e,0x5e,0x0b,0xbd,0x4e,0x8c,0xcc,0x20,0x8d, - 0x03,0x0a,0xf8,0x2a,0xd5,0xec,0x70,0x79,0xa9,0xad,0xa6,0xa0,0x20,0x8d, - 0x03,0x0a,0xf9,0x4b,0xcb,0x2b,0x5e,0xf3,0x5d,0xad,0xce,0xed,0x20,0x8d, - 0x03,0x0a,0xf9,0xd0,0xf0,0xd3,0x25,0x18,0xb1,0x98,0x29,0x46,0x20,0x8d, - 0x03,0x0a,0xfc,0x92,0xc5,0xe6,0x33,0x3a,0x56,0xf2,0xe0,0x6a,0x20,0x8d, - 0x03,0x0a,0xfc,0xe9,0x3d,0xe6,0x7a,0x02,0xad,0x16,0x5b,0xd7,0x20,0x8d, - 0x03,0x0a,0xfd,0x0f,0x24,0xe5,0x3e,0x6d,0xf6,0x32,0xb6,0xf3,0x20,0x8d, - 0x03,0x0a,0xfd,0x99,0xcb,0x49,0xdb,0xb5,0x41,0x3b,0xb4,0x33,0x20,0x8d, - 0x03,0x0a,0xfe,0x14,0xcc,0xd3,0x01,0xb0,0xf4,0xf9,0xe4,0xdc,0x20,0x8d, - 0x03,0x0a,0x06,0xbe,0x1a,0x9d,0x0d,0x07,0x31,0xad,0xa6,0xee,0x20,0x8d, - 0x03,0x0a,0x07,0x77,0x59,0x8d,0x9f,0xa2,0x09,0x3e,0xd4,0x6b,0x20,0x8d, - 0x03,0x0a,0x07,0x7d,0xdf,0xea,0xe9,0xa3,0x8a,0xd9,0xe8,0x6f,0x20,0x8d, - 0x03,0x0a,0x00,0x5f,0xae,0xa9,0xa8,0x28,0xe4,0xd1,0x6a,0x35,0x20,0x8d, - 0x03,0x0a,0x01,0x0b,0x7f,0xd0,0x39,0x78,0x17,0xf1,0x2c,0x0a,0x20,0x8d, - 0x03,0x0a,0x02,0x3b,0x1d,0x0d,0x0e,0xcb,0x89,0xf8,0xc4,0x79,0x20,0x8d, - 0x03,0x0a,0x02,0x1f,0x47,0xbc,0xe8,0x9e,0xc8,0xd3,0x19,0xe9,0x20,0x8d, - 0x03,0x0a,0x02,0xcc,0xf4,0xa7,0x06,0x1e,0xcd,0x36,0xb1,0xef,0x20,0x8d, - 0x03,0x0a,0x02,0xd0,0x7a,0x03,0xf1,0x3e,0x05,0xce,0xe8,0xf1,0x20,0x8d, - 0x03,0x0a,0x03,0x36,0x6c,0x60,0xb8,0x6d,0xf3,0x6c,0x5c,0xf7,0x20,0x8d, - 0x03,0x0a,0x03,0x54,0xec,0xe4,0xa7,0x5e,0xa3,0xba,0x0b,0xd4,0x20,0x8d, - 0x03,0x0a,0x04,0xf7,0x3b,0x25,0x61,0x98,0xb4,0xb8,0x36,0x1d,0x20,0x8d, - 0x03,0x0a,0x05,0x60,0xe0,0xaf,0xfa,0x7b,0x05,0xee,0x0f,0x08,0x20,0x8d, - 0x03,0x0a,0x05,0x98,0x3c,0xe8,0xb2,0xd8,0x7a,0x7e,0xd2,0x7d,0x20,0x8d, - 0x03,0x0a,0x06,0x31,0x67,0xa3,0x1f,0xf8,0x69,0x31,0xa6,0x29,0x20,0x8d, - 0x03,0x0a,0x0e,0x91,0xb7,0xa7,0xe2,0xd7,0x05,0x57,0xc6,0x5f,0x20,0x8d, - 0x03,0x0a,0x0f,0x10,0xb2,0x07,0x17,0x15,0x3c,0xd9,0xcd,0x0e,0x20,0x8d, - 0x03,0x0a,0x0f,0x2b,0x55,0x06,0x08,0x78,0x98,0xab,0x3f,0x95,0x20,0x8d, - 0x03,0x0a,0x08,0xc6,0x58,0x5d,0xf2,0xea,0x02,0x3d,0x96,0x76,0x20,0x8d, - 0x03,0x0a,0x09,0x3a,0x13,0x09,0xee,0xe3,0x9d,0x4b,0xf6,0x18,0x20,0x8d, - 0x03,0x0a,0x09,0x96,0x0e,0x33,0xd9,0x24,0xeb,0x3a,0xfd,0x72,0x20,0x8d, - 0x03,0x0a,0x09,0xf7,0xa3,0x66,0xdb,0x6e,0x04,0xac,0xc2,0x93,0x20,0x8d, - 0x03,0x0a,0x09,0xdd,0xc5,0x38,0x6f,0x21,0xdb,0xfb,0xc7,0x77,0x20,0x8d, - 0x03,0x0a,0x0a,0x26,0x27,0x21,0xbc,0x8a,0xca,0x0e,0x5a,0x17,0x20,0x8d, - 0x03,0x0a,0x0a,0x2d,0xf9,0x79,0x25,0xf4,0x74,0xc2,0xec,0x54,0x20,0x8d, - 0x03,0x0a,0x0a,0xbf,0x87,0xf8,0x8f,0x6b,0x04,0xb5,0xc3,0xfa,0x20,0x8d, - 0x03,0x0a,0x0a,0xc4,0xa9,0xc4,0xd5,0x27,0x6a,0x49,0xa6,0x4a,0x20,0x8d, - 0x03,0x0a,0x0a,0xec,0x17,0xfc,0xc5,0x19,0x4a,0x39,0x5f,0x86,0x20,0x8d, - 0x03,0x0a,0x0b,0x6e,0xdf,0x42,0x02,0xef,0x4d,0x56,0xf5,0xcf,0x20,0x8d, - 0x03,0x0a,0x0b,0xfe,0xed,0x69,0x75,0x12,0x41,0x62,0x2e,0xb5,0x20,0x8d, - 0x03,0x0a,0x0c,0x21,0x88,0x50,0x46,0x4f,0x26,0x23,0xb7,0xdc,0x20,0x8d, - 0x03,0x0a,0x0d,0x47,0x96,0x52,0x62,0x81,0x7e,0x6c,0xe5,0xbd,0x20,0x8d, - 0x03,0x0a,0x16,0xfd,0x96,0x10,0xc9,0x52,0x1a,0x59,0xb2,0x65,0x20,0x8d, - 0x03,0x0a,0x17,0x0a,0xdf,0x68,0xcd,0x5c,0xd6,0x68,0xbe,0x75,0x20,0x8d, - 0x03,0x0a,0x10,0x00,0x45,0xf7,0x04,0x1d,0x50,0xe7,0x43,0x2a,0x20,0x8d, - 0x03,0x0a,0x10,0x21,0xde,0x00,0x2b,0x28,0x62,0xda,0x30,0x63,0x20,0x8d, - 0x03,0x0a,0x11,0x22,0xd8,0xb2,0x2a,0xee,0x5c,0xcc,0xbb,0x2d,0x20,0x8d, - 0x03,0x0a,0x11,0xe2,0x8f,0x22,0x66,0x48,0x00,0x67,0x17,0x93,0x20,0x8d, - 0x03,0x0a,0x13,0x45,0x64,0x2b,0x73,0x68,0xf4,0x44,0xb3,0xb9,0x20,0x8d, - 0x03,0x0a,0x15,0x30,0x98,0x3b,0x28,0x23,0x04,0xcb,0x02,0xeb,0x20,0x8d, - 0x03,0x0a,0x15,0xff,0x00,0x68,0xcf,0x86,0x1f,0xf7,0xac,0x7d,0x20,0x8d, - 0x03,0x0a,0x16,0x5f,0xfb,0x18,0x14,0x97,0x0d,0x54,0x3b,0xfa,0x20,0x8d, - 0x03,0x0a,0x1e,0x8a,0xde,0xf2,0x25,0xc2,0x46,0x06,0x99,0x1c,0x20,0x8d, - 0x03,0x0a,0x1e,0xa4,0xae,0x76,0x9e,0x10,0x3d,0xcc,0x12,0x07,0x20,0x8d, - 0x03,0x0a,0x1e,0xc0,0xeb,0x31,0xa6,0xaa,0xa7,0x2c,0xa0,0x04,0x20,0x8d, - 0x03,0x0a,0x1f,0x51,0x4e,0x01,0x19,0xde,0x34,0xa3,0x08,0xc9,0x20,0x8d, - 0x03,0x0a,0x1f,0xb2,0x1b,0x6a,0x57,0x6d,0xcc,0x9e,0xca,0xbb,0x20,0x8d, - 0x03,0x0a,0x18,0x7b,0x11,0xf4,0x9c,0xf4,0xfe,0xc3,0x21,0xa8,0x20,0x8d, - 0x03,0x0a,0x18,0x91,0xa3,0x51,0x6e,0x8a,0xf9,0xcc,0x27,0xbd,0x20,0x8d, - 0x03,0x0a,0x18,0xdf,0x33,0xe9,0x96,0x9e,0xe3,0x2a,0xb9,0xc6,0x20,0x8d, - 0x03,0x0a,0x19,0x63,0x6c,0x83,0xe5,0x11,0x04,0xa6,0xb5,0x92,0x20,0x8d, - 0x03,0x0a,0x1a,0x6c,0x74,0x95,0x3c,0x89,0xf6,0xec,0xef,0x09,0x20,0x8d, - 0x03,0x0a,0x1a,0x95,0xd6,0x31,0xe4,0xea,0x66,0x97,0x0d,0x5d,0x20,0x8d, - 0x03,0x0a,0x1b,0x93,0xbc,0x99,0x92,0x0e,0x69,0x16,0x40,0xcf,0x20,0x8d, - 0x03,0x0a,0x1b,0xc4,0x4e,0x17,0x71,0x14,0x06,0x3c,0x86,0xfd,0x20,0x8d, - 0x03,0x0a,0x1c,0x6c,0xed,0xd5,0xb7,0x11,0xfa,0xec,0x94,0x2e,0x20,0x8d, - 0x03,0x0a,0x1d,0x10,0xa5,0x20,0x77,0x43,0xf6,0xbc,0x12,0xed,0x20,0x8d, - 0x03,0x0a,0x1d,0x20,0x35,0xa1,0xf3,0x16,0xb4,0x8f,0x1c,0xbd,0x20,0x8d, - 0x03,0x0a,0x1d,0x30,0xfe,0x09,0xc7,0xe8,0xfe,0xd3,0xee,0x83,0x20,0x8d, - 0x03,0x0a,0x1d,0x33,0xd9,0xd9,0xdb,0xcf,0xc5,0xde,0xae,0xe9,0x20,0x8d, - 0x03,0x0a,0x1d,0x69,0xe1,0xac,0x11,0xf1,0x32,0x2f,0x5c,0x8d,0x20,0x8d, - 0x03,0x0a,0x1e,0x3d,0x98,0x4b,0x9e,0xc0,0x96,0x40,0x63,0x0f,0x20,0x8d, - 0x03,0x0a,0x1e,0x75,0x81,0xb1,0x3b,0xc4,0x22,0x26,0x72,0x3f,0x20,0x8d, - 0x03,0x0a,0x26,0x83,0xa0,0x76,0x54,0xa8,0xc1,0x6c,0xde,0x83,0x20,0x8d, - 0x03,0x0a,0x26,0xf6,0x7e,0xfd,0x3a,0x25,0x94,0xa8,0x49,0xbd,0x20,0x8d, - 0x03,0x0a,0x27,0x54,0x94,0x03,0x1f,0x7e,0x53,0xd8,0x3f,0x35,0x20,0x8d, - 0x03,0x0a,0x27,0xd0,0xa7,0x73,0x43,0xd5,0xb2,0x26,0x57,0x1c,0x20,0x8d, - 0x03,0x0a,0x20,0x3c,0x17,0x1f,0x8a,0x74,0xe1,0xdf,0x5a,0x5d,0x20,0x8d, - 0x03,0x0a,0x21,0x47,0x7f,0x18,0x5c,0x97,0x49,0x9c,0x40,0x86,0x20,0x8d, - 0x03,0x0a,0x21,0x62,0xfa,0x51,0x02,0xf5,0x14,0x4c,0x40,0x52,0x20,0x8d, - 0x03,0x0a,0x21,0xa3,0x41,0x6c,0x28,0xda,0x27,0x1a,0x78,0xd0,0x20,0x8d, - 0x03,0x0a,0x24,0x45,0xe9,0xa6,0x5a,0xa0,0xb0,0x01,0xaf,0x5b,0x20,0x8d, - 0x03,0x0a,0x25,0x09,0xa6,0xf6,0x4a,0xec,0xd5,0x33,0x74,0x35,0x20,0x8d, - 0x03,0x0a,0x26,0x55,0x1f,0xca,0x70,0xe5,0xbe,0xe3,0xa6,0x33,0x20,0x8d, - 0x03,0x0a,0x2e,0xdb,0x8c,0x24,0x20,0xf2,0x9f,0x7c,0xb4,0xea,0x20,0x8d, - 0x03,0x0a,0x28,0x21,0xfd,0xd5,0x3c,0x78,0xa5,0xfd,0xcc,0xf4,0x20,0x8d, - 0x03,0x0a,0x28,0xeb,0x35,0xa7,0x6f,0x90,0x83,0x7a,0x1f,0xfd,0x20,0x8d, - 0x03,0x0a,0x29,0x86,0xfb,0xba,0xbc,0x6e,0x6f,0x53,0x89,0xf5,0x20,0x8d, - 0x03,0x0a,0x2a,0x25,0x08,0x7a,0xb9,0x56,0xd9,0xe9,0xeb,0x5d,0x20,0x8d, - 0x03,0x0a,0x2a,0x8c,0xfd,0xc2,0xc4,0x30,0x05,0x11,0xe8,0x29,0x20,0x8d, - 0x03,0x0a,0x2b,0xb7,0x31,0x96,0xd7,0xd7,0xe6,0x05,0x42,0x1d,0x20,0x8d, - 0x03,0x0a,0x2c,0x15,0x79,0x88,0xf6,0xc3,0xd1,0x27,0xa9,0xf5,0x20,0x8d, - 0x03,0x0a,0x2c,0x28,0xda,0x1d,0x76,0xa8,0xff,0x18,0x78,0x7d,0x20,0x8d, - 0x03,0x0a,0x2c,0x6d,0x3e,0xb2,0x42,0x7e,0x0e,0x8a,0x59,0xe4,0x20,0x8d, - 0x03,0x0a,0x2c,0xc1,0xc3,0x15,0x28,0xa5,0x7c,0x5d,0x2c,0x9a,0x20,0x8d, - 0x03,0x0a,0x2d,0x1d,0x8d,0x21,0xf4,0x84,0x61,0x62,0x74,0x45,0x20,0x8d, - 0x03,0x0a,0x2e,0x7c,0xd9,0x21,0x3e,0x4a,0x31,0x4b,0x2e,0x42,0x20,0x8d, - 0x03,0x0a,0x36,0xea,0xb6,0x80,0x00,0x71,0xbb,0x23,0x51,0x1d,0x20,0x8d, - 0x03,0x0a,0x37,0x38,0x8f,0x26,0xd2,0xa4,0xd5,0x66,0x49,0xf9,0x20,0x8d, - 0x03,0x0a,0x37,0x7b,0x3f,0x74,0x7d,0x12,0x92,0x8b,0x89,0xb6,0x20,0x8d, - 0x03,0x0a,0x30,0x12,0x3f,0x13,0x11,0x5e,0xa1,0x65,0x15,0x86,0x20,0x8d, - 0x03,0x0a,0x30,0x57,0x42,0x6c,0xf1,0xee,0xdf,0xc3,0x46,0xff,0x20,0x8d, - 0x03,0x0a,0x30,0x5f,0x17,0x76,0x79,0x1d,0x11,0x42,0x97,0x95,0x20,0x8d, - 0x03,0x0a,0x30,0xb8,0xbd,0xce,0x0b,0xde,0xa0,0x72,0x99,0x88,0x20,0x8d, - 0x03,0x0a,0x30,0x9a,0xb7,0x46,0xb3,0x7e,0x05,0x40,0x24,0x5e,0x20,0x8d, - 0x03,0x0a,0x30,0xe4,0x80,0xe9,0xaa,0xd1,0x08,0xe4,0x0c,0xc2,0x20,0x8d, - 0x03,0x0a,0x31,0x3a,0x66,0x7c,0x5e,0xb7,0xf0,0x03,0xbf,0x3f,0x20,0x8d, - 0x03,0x0a,0x31,0x0c,0x29,0x90,0x84,0x7f,0x05,0x62,0xcd,0x7d,0x20,0x8d, - 0x03,0x0a,0x31,0x5d,0x88,0x82,0x83,0x35,0x7b,0x04,0x8d,0x54,0x20,0x8d, - 0x03,0x0a,0x31,0x9e,0x1a,0x61,0xec,0xb9,0x91,0xaf,0x2c,0x5e,0x20,0x8d, - 0x03,0x0a,0x31,0xe0,0x8a,0xe0,0x9f,0x11,0x44,0xa4,0x49,0xb3,0x20,0x8d, - 0x03,0x0a,0x32,0x22,0x05,0x5d,0xcc,0x69,0x3a,0x50,0xe3,0xdc,0x20,0x8d, - 0x03,0x0a,0x32,0xf3,0xd3,0x15,0x5b,0xdc,0xe9,0x43,0x75,0xa4,0x20,0x8d, - 0x03,0x0a,0x33,0xd6,0x09,0xdd,0xd8,0x37,0x5b,0x75,0xf6,0x29,0x20,0x8d, - 0x03,0x0a,0x34,0x50,0xf5,0xf6,0xe9,0xb6,0x34,0x31,0x47,0xc2,0x20,0x8d, - 0x03,0x0a,0x34,0xce,0x7c,0xad,0x90,0x12,0x35,0xa6,0xde,0x34,0x20,0x8d, - 0x03,0x0a,0x34,0xdd,0xa1,0xfb,0x92,0xb3,0xa4,0x56,0x2b,0xc2,0x20,0x8d, - 0x03,0x0a,0x35,0x00,0x24,0x34,0x98,0xee,0x98,0x61,0x05,0xfa,0x20,0x8d, - 0x03,0x0a,0x35,0x95,0x33,0x45,0x93,0xb2,0xbc,0xda,0xf6,0x42,0x20,0x8d, - 0x03,0x0a,0x35,0x9d,0x76,0xb9,0x43,0x15,0x85,0xf3,0xe3,0x8f,0x20,0x8d, - 0x03,0x0a,0x3e,0xf7,0xb2,0xf2,0x0d,0xb1,0x3e,0xc8,0xe1,0x8d,0x20,0x8d, - 0x03,0x0a,0x3f,0x81,0xbd,0x37,0x81,0x58,0x6d,0x6c,0x37,0x83,0x20,0x8d, - 0x03,0x0a,0x38,0x0b,0x69,0xc4,0x2e,0x74,0xb2,0xe2,0x30,0x2c,0x20,0x8d, - 0x03,0x0a,0x38,0x6c,0x73,0x48,0x3b,0x21,0x10,0xd6,0xc7,0xd3,0x20,0x8d, - 0x03,0x0a,0x38,0xab,0xe2,0xba,0xe7,0xeb,0x15,0xf2,0x9c,0x3d,0x20,0x8d, - 0x03,0x0a,0x38,0xfc,0x75,0x4c,0x4b,0xf5,0x80,0xcc,0xaf,0x2c,0x20,0x8d, - 0x03,0x0a,0x38,0xc1,0xe6,0x48,0x1c,0xaf,0x23,0x3f,0xfc,0xd7,0x20,0x8d, - 0x03,0x0a,0x38,0xcc,0xdb,0xaa,0x90,0x90,0xfd,0x64,0xda,0xd7,0x20,0x8d, - 0x03,0x0a,0x39,0x8f,0xb0,0x65,0xbb,0x21,0x24,0x31,0xd4,0x46,0x20,0x8d, - 0x03,0x0a,0x39,0xf1,0x7a,0x78,0x36,0x52,0x48,0x52,0x25,0xd9,0x20,0x8d, - 0x03,0x0a,0x3a,0x32,0xdf,0x45,0x8e,0x2c,0x8d,0xba,0x3d,0x8d,0x20,0x8d, - 0x03,0x0a,0x3a,0x61,0x7b,0xcb,0x1a,0x74,0x88,0xc2,0xd4,0x95,0x20,0x8d, - 0x03,0x0a,0x3a,0x82,0xff,0xb0,0x26,0xb7,0x94,0xb5,0xcb,0x92,0x20,0x8d, - 0x03,0x0a,0x3a,0xdc,0x9a,0x59,0x16,0x0a,0x9c,0x9e,0x28,0x79,0x20,0x8d, - 0x03,0x0a,0x3a,0xf3,0x79,0x26,0x3f,0x70,0x77,0x0c,0xe6,0x10,0x20,0x8d, - 0x03,0x0a,0x3b,0x51,0x4c,0xbd,0x64,0xc9,0x03,0x83,0xd7,0xe0,0x20,0x8d, - 0x03,0x0a,0x3b,0x9a,0x32,0x59,0x49,0xe4,0xb9,0x11,0x8a,0xc5,0x20,0x8d, - 0x03,0x0a,0x3c,0x17,0xd6,0xd7,0xd5,0x38,0x88,0x81,0xec,0x2d,0x20,0x8d, - 0x03,0x0a,0x3c,0x9e,0x97,0x7d,0x90,0x8c,0x49,0xd3,0x62,0xf1,0x20,0x8d, - 0x03,0x0a,0x3d,0x3d,0xc9,0x69,0x83,0x8e,0xef,0xfc,0x5d,0x40,0x20,0x8d, - 0x03,0x0a,0x3d,0x6d,0x58,0x6a,0x56,0x54,0x2d,0xb8,0x57,0x0e,0x20,0x8d, - 0x03,0x0a,0x3d,0x9d,0xa0,0xa3,0x0d,0x1c,0x63,0x57,0xaf,0xc5,0x20,0x8d, - 0x03,0x0a,0x3e,0x6e,0x9d,0x8e,0x67,0xde,0x35,0x79,0xf3,0xae,0x20,0x8d, - 0x03,0x0a,0x46,0xe8,0x5b,0xd2,0xdb,0x9f,0xc5,0x72,0x8d,0xf0,0x20,0x8d, - 0x03,0x0a,0x40,0x36,0xdd,0xc3,0xb4,0xe7,0x4d,0x57,0xdf,0xe0,0x20,0x8d, - 0x03,0x0a,0x40,0x8a,0x69,0x6c,0xa2,0x98,0x94,0x3e,0x60,0x8e,0x20,0x8d, - 0x03,0x0a,0x40,0x9f,0x9f,0x4c,0xf0,0xa8,0xd2,0x2b,0x2e,0xa1,0x20,0x8d, - 0x03,0x0a,0x41,0x77,0xac,0xbb,0xb4,0xe3,0x0e,0x3a,0x34,0xa3,0x20,0x8d, - 0x03,0x0a,0x41,0xb8,0xb3,0x52,0x0b,0xf5,0x6e,0xa0,0xb1,0x91,0x20,0x8d, - 0x03,0x0a,0x41,0xce,0x28,0xfc,0xa7,0x16,0x60,0x30,0x0b,0x98,0x20,0x8d, - 0x03,0x0a,0x42,0x59,0xa9,0xe2,0xee,0x0f,0xea,0xaa,0x83,0x39,0x20,0x8d, - 0x03,0x0a,0x43,0xf6,0xfa,0x52,0x06,0x3d,0x18,0x5c,0xf6,0xd6,0x20,0x8d, - 0x03,0x0a,0x44,0x36,0xa2,0x4f,0xfa,0x2e,0xf1,0xa2,0xc5,0xe6,0x20,0x8d, - 0x03,0x0a,0x44,0x00,0x69,0xf4,0x4e,0xe0,0xe7,0xf3,0xf8,0xe5,0x20,0x8d, - 0x03,0x0a,0x45,0x0d,0x6e,0x69,0x07,0xf1,0xdf,0x18,0x47,0x5e,0x20,0x8d, - 0x03,0x0a,0x45,0x4b,0xff,0xf2,0xbc,0x9f,0xd5,0xed,0xa3,0xc3,0x20,0x8d, - 0x03,0x0a,0x45,0x4a,0x01,0x0c,0xbf,0x12,0x0d,0xac,0xeb,0x1a,0x20,0x8d, - 0x03,0x0a,0x45,0x65,0x71,0xd9,0x54,0xeb,0x8d,0xac,0xa7,0x8b,0x20,0x8d, - 0x03,0x0a,0x45,0xec,0x68,0x9c,0x0a,0x5d,0x69,0xc3,0x79,0xdf,0x20,0x8d, - 0x03,0x0a,0x46,0x7b,0xe6,0x39,0xde,0x62,0x9f,0xb3,0x7e,0xee,0x20,0x8d, - 0x03,0x0a,0x4e,0x84,0xfe,0xb2,0x96,0xea,0x76,0xba,0x30,0x57,0x20,0x8d, - 0x03,0x0a,0x4e,0x97,0x15,0x46,0xd4,0x32,0xc7,0x62,0x5a,0xd2,0x20,0x8d, - 0x03,0x0a,0x4e,0xa1,0x36,0x28,0x7a,0x18,0x02,0xb9,0x4b,0x3c,0x20,0x8d, - 0x03,0x0a,0x4f,0x49,0xac,0x50,0x0d,0xef,0xeb,0xa3,0xf4,0x8b,0x20,0x8d, - 0x03,0x0a,0x4f,0x52,0x58,0x8e,0x67,0x84,0xfa,0x6d,0x76,0xf9,0x20,0x8d, - 0x03,0x0a,0x4f,0x56,0xad,0x52,0xba,0x0c,0x9e,0x58,0x5c,0xaa,0x20,0x8d, - 0x03,0x0a,0x48,0x1b,0x5a,0xe6,0x4c,0xc8,0xa4,0x9d,0x95,0x0b,0x20,0x8d, - 0x03,0x0a,0x49,0x1a,0xdd,0x4d,0x98,0x5e,0xef,0x70,0x45,0x90,0x20,0x8d, - 0x03,0x0a,0x49,0x5c,0x4e,0x97,0x52,0x16,0x5c,0x92,0xbf,0x7a,0x20,0x8d, - 0x03,0x0a,0x49,0x84,0x64,0x79,0x5a,0x7d,0xdc,0xe4,0x76,0x1b,0x20,0x8d, - 0x03,0x0a,0x49,0xc0,0xd0,0x6b,0x92,0xd8,0xf2,0xa4,0x4f,0x2f,0x20,0x8d, - 0x03,0x0a,0x4a,0x26,0x6a,0x2c,0x3a,0xe3,0x2a,0x58,0x44,0x66,0x20,0x8d, - 0x03,0x0a,0x4a,0x69,0x5b,0x05,0x25,0xca,0xd2,0xc6,0xfe,0x7b,0x20,0x8d, - 0x03,0x0a,0x4a,0xc4,0x57,0x30,0xd1,0xed,0xca,0x4b,0x81,0x05,0x20,0x8d, - 0x03,0x0a,0x4a,0xc8,0x79,0x7b,0x01,0x0e,0xbd,0x05,0xb5,0xa0,0x20,0x8d, - 0x03,0x0a,0x4a,0xd3,0x9c,0xf2,0x6c,0x0c,0x23,0x78,0x6e,0x1d,0x20,0x8d, - 0x03,0x0a,0x4b,0x85,0xc7,0x40,0x44,0x20,0xd4,0x6f,0xfe,0xa5,0x20,0x8d, - 0x03,0x0a,0x4c,0x7b,0x8f,0x35,0x34,0x08,0x83,0x5f,0x1b,0x7f,0x20,0x8d, - 0x03,0x0a,0x4c,0x5c,0x07,0x4b,0xcb,0x07,0x2a,0x82,0x1d,0xdc,0x20,0x8d, - 0x03,0x0a,0x4c,0x98,0xf3,0x99,0x40,0xc7,0xd0,0x83,0x85,0x51,0x20,0x8d, - 0x03,0x0a,0x4c,0xd5,0x26,0xb9,0x54,0x90,0x72,0xc9,0x7e,0xcb,0x20,0x8d, - 0x03,0x0a,0x4d,0x3a,0x3a,0x3b,0x71,0xf3,0xfc,0x34,0x65,0xa2,0x20,0x8d, - 0x03,0x0a,0x4d,0xbd,0x9c,0x32,0xe2,0x69,0x02,0x03,0xd2,0x89,0x20,0x8d, - 0x03,0x0a,0x4d,0xc0,0xba,0x9c,0xbf,0xb7,0xec,0x4a,0xc3,0x36,0x20,0x8d, - 0x03,0x0a,0x4e,0x32,0x72,0x6d,0x06,0xe7,0x10,0x25,0x62,0x41,0x20,0x8d, - 0x03,0x0a,0x4e,0x49,0xea,0x29,0xbc,0x40,0xe2,0x7e,0x70,0x8e,0x20,0x8d, - 0x03,0x0a,0x57,0x0c,0xb7,0x4d,0x77,0x6b,0x27,0x30,0xf8,0x53,0x20,0x8d, - 0x03,0x0a,0x57,0xe9,0x8d,0xa2,0xcc,0xa9,0xa9,0x9c,0x18,0x7a,0x20,0x8d, - 0x03,0x0a,0x52,0xf5,0xb7,0x14,0x06,0xdd,0x14,0x1f,0x1e,0xeb,0x20,0x8d, - 0x03,0x0a,0x53,0x95,0x3d,0x42,0x3e,0x1f,0x1e,0xcc,0x07,0x43,0x20,0x8d, - 0x03,0x0a,0x53,0xc0,0xba,0x6c,0xfd,0xc0,0xd4,0xe0,0x22,0xb2,0x20,0x8d, - 0x03,0x0a,0x54,0x46,0xf0,0x8e,0xb3,0x85,0xba,0x2e,0xac,0x84,0x20,0x8d, - 0x03,0x0a,0x54,0x51,0x6f,0x2b,0x29,0xc8,0x23,0x93,0x07,0x66,0x20,0x8d, - 0x03,0x0a,0x54,0x5f,0xa9,0x9c,0x4c,0xb4,0x5f,0x27,0x50,0x9e,0x20,0x8d, - 0x03,0x0a,0x55,0x71,0x51,0xd9,0x36,0x98,0x09,0xd6,0x3b,0xff,0x20,0x8d, - 0x03,0x0a,0x56,0x23,0x78,0xa3,0xb1,0x0c,0x7c,0x87,0xd2,0x32,0x20,0x8d, - 0x03,0x0a,0x56,0x76,0xeb,0x9b,0xff,0xe7,0x47,0x79,0xfb,0x50,0x20,0x8d, - 0x03,0x0a,0x5e,0xed,0xd2,0x89,0x48,0xd5,0x83,0x17,0x6a,0x01,0x20,0x8d, - 0x03,0x0a,0x5f,0x38,0x14,0x4a,0x97,0x39,0xff,0x12,0x07,0xb0,0x20,0x8d, - 0x03,0x0a,0x5f,0x7d,0xd3,0x77,0x5b,0x23,0x12,0x40,0xd2,0x49,0x20,0x8d, - 0x03,0x0a,0x5f,0xad,0xd5,0x0c,0x88,0x35,0xa4,0x66,0x97,0xb3,0x20,0x8d, - 0x03,0x0a,0x5f,0xc1,0xc2,0x32,0x38,0x2d,0xd4,0x93,0x31,0x81,0x20,0x8d, - 0x03,0x0a,0x5f,0xe4,0xb7,0x48,0x49,0x84,0x02,0x82,0x8a,0x56,0x20,0x8d, - 0x03,0x0a,0x58,0x00,0x54,0xc2,0xb3,0x71,0xbe,0x34,0x95,0x7a,0x20,0x8d, - 0x03,0x0a,0x58,0x0f,0xef,0xf9,0x57,0x09,0x82,0x6b,0x6e,0x9a,0x20,0x8d, - 0x03,0x0a,0x58,0x61,0xa0,0x7d,0xed,0x7b,0x2a,0x8b,0x6a,0x0e,0x20,0x8d, - 0x03,0x0a,0x58,0xb9,0x66,0xbe,0x0b,0xd7,0xeb,0x86,0x23,0x7d,0x20,0x8d, - 0x03,0x0a,0x58,0xdc,0x52,0x84,0xaf,0x56,0xd3,0xe1,0x7f,0x1f,0x20,0x8d, - 0x03,0x0a,0x59,0x0e,0xf6,0x19,0x6a,0x45,0x5c,0x18,0x6a,0x0e,0x20,0x8d, - 0x03,0x0a,0x59,0x89,0x67,0xa7,0x3f,0x41,0x3e,0x30,0x42,0x11,0x20,0x8d, - 0x03,0x0a,0x59,0x95,0x50,0xd6,0x2e,0xf7,0xd2,0xe6,0x3a,0x56,0x20,0x8d, - 0x03,0x0a,0x5a,0x2d,0xdc,0xf1,0xa6,0x40,0xbc,0x1f,0xd5,0xb5,0x20,0x8d, - 0x03,0x0a,0x5a,0x65,0xf3,0x5a,0x2c,0x66,0x41,0xe8,0x78,0xc0,0x20,0x8d, - 0x03,0x0a,0x5b,0x2a,0x0b,0xec,0x9e,0x05,0x81,0x7a,0x9e,0x08,0x20,0x8d, - 0x03,0x0a,0x5c,0x73,0xff,0x8e,0xc5,0xfe,0x21,0xc1,0x19,0xb3,0x20,0x8d, - 0x03,0x0a,0x5d,0x41,0xde,0x3d,0xa1,0x86,0x9b,0x26,0x27,0x11,0x20,0x8d, - 0x03,0x0a,0x5d,0xc5,0xaa,0x3c,0xf7,0xc6,0x2e,0x55,0x9d,0xa5,0x20,0x8d, - 0x03,0x0a,0x5e,0x13,0x80,0x8e,0x3c,0x3b,0x13,0xb0,0xc0,0x01,0x20,0x8d, - 0x03,0x0a,0x5e,0x75,0x95,0xb5,0x98,0xc3,0x6d,0x33,0x58,0xba,0x20,0x8d, - 0x03,0x0a,0x67,0x8e,0x26,0xbd,0x0a,0x43,0x30,0x7d,0xff,0x0f,0x20,0x8d, - 0x03,0x0a,0x60,0xfd,0xbe,0xb9,0x89,0x6c,0x4c,0x72,0x10,0x7b,0x20,0x8d, - 0x03,0x0a,0x60,0xc3,0xb7,0x51,0xf6,0x2f,0x0b,0xa8,0x61,0x21,0x20,0x8d, - 0x03,0x0a,0x61,0x3c,0x3e,0x12,0x57,0xfb,0x8e,0x36,0xdd,0xa4,0x20,0x8d, - 0x03,0x0a,0x61,0x04,0x55,0x21,0x5d,0x12,0x39,0xfb,0x09,0x49,0x20,0x8d, - 0x03,0x0a,0x61,0x63,0x52,0x55,0xbf,0xb7,0xa3,0x69,0x3f,0x91,0x20,0x8d, - 0x03,0x0a,0x62,0x19,0x4a,0x4d,0x64,0xb7,0x65,0x19,0x8e,0x8a,0x20,0x8d, - 0x03,0x0a,0x63,0x71,0x25,0x6d,0x19,0xbd,0x62,0x0d,0x9e,0x95,0x20,0x8d, - 0x03,0x0a,0x64,0x29,0xe3,0x42,0x71,0x3b,0x3d,0x7c,0xda,0xc7,0x20,0x8d, - 0x03,0x0a,0x65,0xa8,0x2f,0x55,0xcc,0xe3,0x4c,0x84,0xcc,0x3b,0x20,0x8d, - 0x03,0x0a,0x65,0xc7,0x38,0xa4,0xe4,0xd6,0x0b,0x2b,0xed,0xe6,0x20,0x8d, - 0x03,0x0a,0x6f,0x10,0x12,0x4f,0x8f,0x44,0x85,0x5d,0x69,0xa9,0x20,0x8d, - 0x03,0x0a,0x6f,0x87,0xcf,0x54,0x39,0xbf,0x36,0x12,0x55,0x61,0x20,0x8d, - 0x03,0x0a,0x6f,0xa7,0xe5,0x14,0xd9,0x5d,0x5d,0x9b,0x9c,0xac,0x20,0x8d, - 0x03,0x0a,0x6f,0xe3,0x17,0x08,0xf6,0x24,0x4b,0xa8,0x5f,0x24,0x20,0x8d, - 0x03,0x0a,0x68,0xa4,0x34,0x41,0x8d,0xb9,0xda,0xd4,0x86,0x59,0x20,0x8d, - 0x03,0x0a,0x6a,0x27,0x7b,0x6d,0x0b,0x29,0x5a,0x67,0xd1,0x95,0x20,0x8d, - 0x03,0x0a,0x6a,0x57,0x2a,0xd0,0x28,0x58,0xc8,0x75,0xd2,0xd1,0x20,0x8d, - 0x03,0x0a,0x6a,0x64,0xb2,0xc9,0x15,0xc6,0x0e,0x8b,0x86,0x4f,0x20,0x8d, - 0x03,0x0a,0x6a,0x8b,0xd2,0x78,0x3f,0x7a,0xf8,0x92,0x8f,0x80,0x20,0x8d, - 0x03,0x0a,0x6a,0x9e,0xf9,0x07,0x73,0xd8,0xe8,0x24,0x93,0xcc,0x20,0x8d, - 0x03,0x0a,0x6a,0xcb,0x6c,0x41,0x52,0x61,0x20,0x4e,0x77,0x39,0x20,0x8d, - 0x03,0x0a,0x6a,0xf0,0x96,0x3c,0x4c,0x78,0x33,0xd0,0xf0,0x00,0x20,0x8d, - 0x03,0x0a,0x6b,0x59,0x5f,0xe7,0xdd,0x57,0xba,0xc1,0x12,0x51,0x20,0x8d, - 0x03,0x0a,0x6c,0x62,0x5b,0x0d,0x91,0x66,0xd0,0xca,0x10,0x2d,0x20,0x8d, - 0x03,0x0a,0x6c,0x62,0xc5,0x19,0x94,0x5b,0xcd,0x20,0xd9,0x73,0x20,0x8d, - 0x03,0x0a,0x6d,0xb8,0x7f,0xac,0x82,0x55,0x27,0xf2,0x01,0xf5,0x20,0x8d, - 0x03,0x0a,0x6d,0x95,0x8d,0xd8,0x7b,0x41,0xdc,0x81,0xd4,0x3d,0x20,0x8d, - 0x03,0x0a,0x6e,0x38,0xa5,0x11,0x8c,0x64,0x2b,0xc5,0xbe,0x6c,0x20,0x8d, - 0x03,0x0a,0x76,0xbb,0x65,0x0a,0xdf,0x23,0xa2,0x6d,0x4d,0xc8,0x20,0x8d, - 0x03,0x0a,0x76,0x8d,0x46,0x54,0x2a,0xb7,0x9e,0xce,0x74,0x45,0x20,0x8d, - 0x03,0x0a,0x77,0x30,0x99,0x1c,0x76,0x58,0x64,0x7c,0x2e,0x16,0x20,0x8d, - 0x03,0x0a,0x71,0x6f,0xc8,0x1a,0xde,0x5b,0xde,0xda,0xcc,0xd5,0x20,0x8d, - 0x03,0x0a,0x72,0x89,0x34,0x3d,0x7c,0x33,0x47,0x01,0x02,0x92,0x20,0x8d, - 0x03,0x0a,0x74,0x3b,0x0e,0x42,0x30,0x42,0x63,0xa5,0x3e,0x8d,0x20,0x8d, - 0x03,0x0a,0x74,0x2d,0xb6,0x15,0xc8,0x70,0x60,0x25,0x2e,0xe7,0x20,0x8d, - 0x03,0x0a,0x74,0x65,0x8d,0x57,0xdb,0x20,0xa2,0xc1,0xa7,0xbd,0x20,0x8d, - 0x03,0x0a,0x74,0xf9,0x3c,0xb3,0x2d,0xc2,0x18,0xc5,0xcb,0x2a,0x20,0x8d, - 0x03,0x0a,0x75,0x95,0xe1,0x69,0x25,0x99,0xec,0xac,0x00,0xe4,0x20,0x8d, - 0x03,0x0a,0x76,0x3f,0x29,0x6c,0xec,0xd3,0x95,0x7e,0x4e,0x8d,0x20,0x8d, - 0x03,0x0a,0x7e,0x9e,0x2f,0x58,0x20,0x23,0xea,0x34,0x78,0x44,0x20,0x8d, - 0x03,0x0a,0x7e,0xaf,0xae,0x18,0x67,0x04,0x98,0x61,0x2f,0xa9,0x20,0x8d, - 0x03,0x0a,0x7f,0x84,0xea,0x51,0x31,0xd3,0x46,0x75,0xae,0xbb,0x20,0x8d, - 0x03,0x0a,0x78,0x3e,0x3b,0x74,0x2b,0x6f,0x57,0x06,0x53,0xbb,0x20,0x8d, - 0x03,0x0a,0x78,0x24,0xc1,0x1e,0x6e,0x73,0x93,0xa5,0x08,0xe3,0x20,0x8d, - 0x03,0x0a,0x78,0xc0,0xf5,0x28,0xea,0xf3,0xc2,0x2c,0x6a,0x69,0x20,0x8d, - 0x03,0x0a,0x79,0x0f,0xd0,0x25,0xd4,0xa5,0xbc,0xcb,0x72,0x51,0x20,0x8d, - 0x03,0x0a,0x7a,0xa9,0x41,0x75,0xf6,0x5f,0x6f,0x83,0x58,0xf1,0x20,0x8d, - 0x03,0x0a,0x7c,0x39,0x64,0xaf,0xf5,0x37,0xe7,0x22,0xe0,0x42,0x20,0x8d, - 0x03,0x0a,0x7c,0xc3,0x68,0x1e,0x92,0x7c,0xbb,0x04,0x12,0x0b,0x20,0x8d, - 0x03,0x0a,0x7c,0xec,0xf0,0xdb,0x09,0xea,0xdb,0x82,0x5b,0x45,0x20,0x8d, - 0x03,0x0a,0x7d,0x3f,0x6d,0xa4,0xb8,0x8e,0x5f,0xf9,0x5e,0x48,0x20,0x8d, - 0x03,0x0a,0x7d,0xb0,0xb0,0xe2,0xa5,0xa0,0xbd,0xa3,0x9e,0xb7,0x20,0x8d, - 0x03,0x0a,0x86,0x8a,0x76,0xb7,0x13,0xe8,0x74,0x0c,0x54,0x44,0x20,0x8d, - 0x03,0x0a,0x86,0xd1,0xb0,0x3e,0x88,0x73,0x42,0x0c,0xb0,0xa4,0x20,0x8d, - 0x03,0x0a,0x80,0xfc,0x51,0x3e,0x9b,0x7d,0x42,0x5d,0x63,0x77,0x20,0x8d, - 0x03,0x0a,0x81,0x49,0x6a,0xef,0x1f,0x06,0xdf,0xc4,0x6c,0x23,0x20,0x8d, - 0x03,0x0a,0x81,0xf1,0x31,0xce,0x65,0x59,0xc2,0x2e,0x46,0x47,0x20,0x8d, - 0x03,0x0a,0x82,0x9b,0xbe,0xc4,0x3b,0xbe,0x8d,0x70,0xda,0x1c,0x20,0x8d, - 0x03,0x0a,0x82,0xea,0xb2,0x5e,0x5f,0x7d,0x80,0x2d,0x17,0x81,0x20,0x8d, - 0x03,0x0a,0x83,0x8c,0x28,0x22,0x33,0xa4,0xc1,0xe8,0xae,0xe6,0x20,0x8d, - 0x03,0x0a,0x84,0x73,0x02,0xdd,0x47,0x8b,0x29,0xda,0xf6,0x2e,0x20,0x8d, - 0x03,0x0a,0x84,0xb0,0x90,0x4a,0x1c,0xf0,0x75,0x2c,0x23,0x12,0x20,0x8d, - 0x03,0x0a,0x85,0x29,0xc0,0xeb,0x29,0x0b,0x63,0xaa,0x13,0x98,0x20,0x8d, - 0x03,0x0a,0x85,0x30,0x22,0xa7,0x56,0x23,0x73,0xe0,0x97,0x03,0x20,0x8d, - 0x03,0x0a,0x85,0x47,0x8d,0x89,0x8e,0x13,0x57,0x5e,0xd7,0xe2,0x20,0x8d, - 0x03,0x0a,0x85,0x6c,0x77,0xc3,0x06,0x03,0x75,0x75,0x63,0xa7,0x20,0x8d, - 0x03,0x0a,0x86,0x29,0x3b,0x0b,0x5e,0xa2,0xd7,0x44,0x80,0xa1,0x20,0x8d, - 0x03,0x0a,0x8f,0x3b,0x03,0x68,0x7e,0x45,0x8a,0x33,0xc2,0xcb,0x20,0x8d, - 0x03,0x0a,0x8f,0x2f,0x41,0xc7,0xd4,0xe4,0x7a,0xdc,0x18,0x1c,0x20,0x8d, - 0x03,0x0a,0x8f,0x80,0xf0,0x76,0x52,0xa2,0x6e,0x1b,0x0f,0x7c,0x20,0x8d, - 0x03,0x0a,0x8f,0xb3,0xa3,0x0a,0x54,0xdf,0xd5,0xb3,0x00,0x07,0x20,0x8d, - 0x03,0x0a,0x88,0x62,0x93,0x14,0x42,0x07,0xab,0xd0,0xff,0x0e,0x20,0x8d, - 0x03,0x0a,0x88,0x90,0x5b,0xa0,0x20,0xb4,0x27,0xe8,0xdf,0x39,0x20,0x8d, - 0x03,0x0a,0x88,0xdd,0xbb,0x8a,0x6a,0xde,0x55,0x94,0xd5,0x6d,0x20,0x8d, - 0x03,0x0a,0x88,0xea,0xb2,0x3f,0x1e,0x31,0xcc,0xf0,0x3f,0x2e,0x20,0x8d, - 0x03,0x0a,0x89,0x05,0x2d,0x83,0x5f,0x11,0xeb,0xa5,0x9b,0xdd,0x20,0x8d, - 0x03,0x0a,0x89,0x58,0x14,0x63,0xb5,0xcc,0xea,0xdf,0x1f,0x0d,0x20,0x8d, - 0x03,0x0a,0x8a,0xd1,0xd5,0x85,0x24,0xe2,0xbf,0xf4,0x37,0x36,0x20,0x8d, - 0x03,0x0a,0x8b,0xa1,0x66,0xb0,0x8f,0x12,0x79,0xdd,0xd4,0xa7,0x20,0x8d, - 0x03,0x0a,0x8b,0xc2,0xdb,0xf7,0x90,0x6a,0x11,0x58,0xb0,0xfb,0x20,0x8d, - 0x03,0x0a,0x8c,0xab,0x57,0x1a,0x03,0x5a,0x12,0xff,0xfc,0xf5,0x20,0x8d, - 0x03,0x0a,0x8d,0x99,0xd1,0xf0,0xe6,0xd9,0xc5,0xff,0xa8,0x73,0x20,0x8d, - 0x03,0x0a,0x96,0xf0,0x45,0xaa,0xa2,0xe9,0x7b,0x72,0x62,0x56,0x20,0x8d, - 0x03,0x0a,0x97,0xa3,0x7e,0xe8,0xe8,0x9b,0x1e,0xfe,0x2c,0xc4,0x20,0x8d, - 0x03,0x0a,0x90,0x3c,0xcd,0xc6,0xb8,0x12,0x1e,0x62,0x31,0x58,0x20,0x8d, - 0x03,0x0a,0x90,0x2a,0x40,0x90,0x92,0x62,0x91,0x56,0x14,0x2e,0x20,0x8d, - 0x03,0x0a,0x90,0x70,0x98,0xf5,0xaf,0x56,0x98,0xb6,0x16,0xdf,0x20,0x8d, - 0x03,0x0a,0x91,0x23,0x64,0xf3,0x49,0x61,0x3b,0x73,0x9d,0x96,0x20,0x8d, - 0x03,0x0a,0x91,0xb9,0x56,0x50,0x35,0xd8,0xd3,0x1c,0xd6,0x87,0x20,0x8d, - 0x03,0x0a,0x91,0xe4,0x65,0x49,0x74,0xcf,0x92,0xa3,0x3f,0xc6,0x20,0x8d, - 0x03,0x0a,0x92,0x71,0x96,0x5a,0xd4,0xf0,0xd0,0x84,0x4f,0x71,0x20,0x8d, - 0x03,0x0a,0x92,0xb6,0x46,0xee,0x24,0xa0,0xcd,0xb9,0x0c,0xdd,0x20,0x8d, - 0x03,0x0a,0x92,0x9c,0x82,0xbf,0x8e,0x4f,0xd7,0xc7,0x4a,0x9d,0x20,0x8d, - 0x03,0x0a,0x92,0xc9,0xa1,0x01,0xeb,0x52,0xdb,0xbd,0x93,0xf8,0x20,0x8d, - 0x03,0x0a,0x93,0x06,0x3f,0xc3,0xe6,0x73,0x40,0x91,0xb1,0x30,0x20,0x8d, - 0x03,0x0a,0x93,0xd8,0x7a,0x5d,0x21,0xd0,0x87,0xf5,0x92,0x8d,0x20,0x8d, - 0x03,0x0a,0x94,0x54,0x6c,0x57,0xa4,0x1b,0x74,0xf0,0x7d,0x0b,0x20,0x8d, - 0x03,0x0a,0x94,0x96,0xd4,0xa4,0xed,0x65,0x96,0xbc,0x4a,0xbc,0x20,0x8d, - 0x03,0x0a,0x95,0x3d,0xec,0x1a,0x20,0x97,0xa2,0xa1,0xcd,0xab,0x20,0x8d, - 0x03,0x0a,0x95,0x1a,0x3a,0xb0,0x29,0x8c,0xcc,0x32,0x80,0xf7,0x20,0x8d, - 0x03,0x0a,0x95,0x47,0xee,0xab,0xa9,0x78,0x17,0xa7,0xed,0x73,0x20,0x8d, - 0x03,0x0a,0x95,0x68,0x0e,0x9d,0x10,0x5d,0x2d,0xf7,0x6a,0x56,0x20,0x8d, - 0x03,0x0a,0x95,0xe0,0x9a,0x05,0x94,0x67,0x22,0xc2,0x99,0xf4,0x20,0x8d, - 0x03,0x0a,0x9e,0xb9,0xda,0xa3,0xfc,0xd4,0xd1,0xb9,0xb5,0x40,0x20,0x8d, - 0x03,0x0a,0x9f,0x0a,0x17,0x56,0xa6,0xcb,0xda,0x86,0x0f,0x4f,0x20,0x8d, - 0x03,0x0a,0x9f,0x17,0xcb,0x57,0x64,0x8a,0x8e,0xf1,0x93,0x4f,0x20,0x8d, - 0x03,0x0a,0x9f,0x60,0x23,0xd8,0x31,0xf5,0x3b,0x5d,0x00,0xca,0x20,0x8d, - 0x03,0x0a,0x9f,0xd2,0xb0,0x27,0xc6,0x36,0x2f,0xf9,0x76,0xb8,0x20,0x8d, - 0x03,0x0a,0x98,0x3d,0x24,0x92,0x18,0x0e,0xbe,0x5e,0x37,0x80,0x20,0x8d, - 0x03,0x0a,0x98,0x2b,0xfa,0x4d,0xf6,0xe3,0xcb,0x8f,0xa7,0xca,0x20,0x8d, - 0x03,0x0a,0x98,0x2e,0x6e,0xe7,0x52,0xb9,0x59,0xd1,0x70,0x7e,0x20,0x8d, - 0x03,0x0a,0x99,0x15,0x6a,0xb4,0x2e,0x18,0x73,0x15,0xd0,0xb2,0x20,0x8d, - 0x03,0x0a,0x99,0xb9,0x4b,0x45,0x2c,0x9c,0x74,0x95,0x85,0x38,0x20,0x8d, - 0x03,0x0a,0x99,0xf8,0x24,0xd4,0xa5,0x4c,0xed,0xea,0xb9,0x94,0x20,0x8d, - 0x03,0x0a,0x99,0xfc,0x5b,0xe1,0x93,0xb3,0x4a,0x82,0xc0,0x94,0x20,0x8d, - 0x03,0x0a,0x99,0xe6,0x23,0x9d,0x7a,0xed,0x35,0xe6,0x99,0x70,0x20,0x8d, - 0x03,0x0a,0x9a,0x10,0x03,0xfc,0x52,0xa3,0x94,0xb1,0x55,0x1e,0x20,0x8d, - 0x03,0x0a,0x9a,0xbd,0xbb,0xf4,0xaa,0xde,0xf7,0xfc,0xee,0x83,0x20,0x8d, - 0x03,0x0a,0x9a,0x8c,0xe7,0x4c,0x13,0xf0,0xa0,0xdf,0xd7,0x18,0x20,0x8d, - 0x03,0x0a,0x9b,0x53,0xdf,0x76,0xd6,0x86,0x7b,0x67,0xa6,0xb2,0x20,0x8d, - 0x03,0x0a,0x9c,0xbd,0x0b,0xef,0xec,0x63,0xe9,0xe6,0xa7,0xb8,0x20,0x8d, - 0x03,0x0a,0x9c,0xd2,0x89,0x56,0xf8,0x19,0x83,0x37,0xf7,0xc5,0x20,0x8d, - 0x03,0x0a,0x9d,0x9b,0xde,0x57,0xf1,0x06,0xae,0x93,0x0f,0xbd,0x20,0x8d, - 0x03,0x0a,0x9d,0xc8,0xce,0xb0,0x94,0x36,0xb8,0x6d,0x13,0x23,0x20,0x8d, - 0x03,0x0a,0x9e,0x11,0x46,0xb7,0x7e,0x5b,0x0a,0x28,0x75,0x71,0x20,0x8d, - 0x03,0x0a,0x9e,0x2b,0xdf,0x5e,0x5e,0x37,0x9a,0x3c,0xc2,0x97,0x20,0x8d, - 0x03,0x0a,0xa7,0x3e,0x5d,0x9e,0xf6,0x87,0xbb,0x23,0x4b,0x8e,0x20,0x8d, - 0x03,0x0a,0xa7,0x23,0xf2,0xb4,0xee,0x5c,0x47,0x6b,0x2d,0xa8,0x20,0x8d, - 0x03,0x0a,0xa7,0x7b,0xe7,0x14,0x3b,0x66,0x01,0x10,0x16,0xcd,0x20,0x8d, - 0x03,0x0a,0xa7,0x61,0xb3,0x07,0x3c,0x83,0xf3,0xcb,0x55,0x71,0x20,0x8d, - 0x03,0x0a,0xa0,0x14,0xbc,0x6f,0x03,0x89,0x2b,0x57,0xde,0xc8,0x20,0x8d, - 0x03,0x0a,0xa1,0xbc,0x70,0x3d,0x1c,0x84,0xc8,0xac,0x8b,0xf5,0x20,0x8d, - 0x03,0x0a,0xa2,0x3b,0xdd,0xc1,0xd3,0x1f,0xa2,0xe6,0xee,0x25,0x20,0x8d, - 0x03,0x0a,0xa2,0x23,0xf2,0xee,0xcb,0x9b,0x94,0x0f,0x04,0x21,0x20,0x8d, - 0x03,0x0a,0xa2,0xa2,0x94,0x9e,0xce,0x1a,0xf9,0xcb,0x31,0xc5,0x20,0x8d, - 0x03,0x0a,0xa2,0xfa,0x66,0x69,0x17,0xc7,0xd5,0x01,0x96,0xc6,0x20,0x8d, - 0x03,0x0a,0xa3,0x46,0x3f,0xc6,0x49,0xe3,0xc8,0xdd,0xd9,0xdc,0x20,0x8d, - 0x03,0x0a,0xa3,0xa3,0x67,0xe4,0xa4,0x3c,0xf0,0xa8,0x9b,0x9b,0x20,0x8d, - 0x03,0x0a,0xa3,0xab,0x27,0xeb,0x0b,0x9b,0x40,0xe4,0xc3,0xcb,0x20,0x8d, - 0x03,0x0a,0xa4,0x81,0x96,0x0c,0x52,0xde,0x9b,0x8d,0x70,0x78,0x20,0x8d, - 0x03,0x0a,0xa4,0x81,0x99,0x7c,0xcb,0x67,0xcc,0x4c,0x5d,0x4b,0x20,0x8d, - 0x03,0x0a,0xa4,0xa5,0xa5,0x10,0x66,0xfc,0x15,0x63,0x0e,0x3d,0x20,0x8d, - 0x03,0x0a,0xa4,0xcd,0x88,0xd6,0xdf,0xed,0xab,0xa6,0xe1,0x88,0x20,0x8d, - 0x03,0x0a,0xa6,0x6c,0x01,0x32,0x5f,0x56,0x32,0x72,0x1c,0x2b,0x20,0x8d, - 0x03,0x0a,0xae,0x94,0x31,0x12,0x75,0x92,0xd8,0x32,0x8a,0xd1,0x20,0x8d, - 0x03,0x0a,0xaf,0x56,0x76,0xe7,0x35,0xf3,0x5a,0x62,0x9b,0xa3,0x20,0x8d, - 0x03,0x0a,0xa8,0xb9,0xc3,0x07,0x95,0x23,0xde,0xe0,0xc6,0x7b,0x20,0x8d, - 0x03,0x0a,0xa9,0x6d,0x83,0xa6,0x9c,0xdd,0xae,0x7c,0xd6,0x97,0x20,0x8d, - 0x03,0x0a,0xa9,0xa8,0x9a,0x15,0x5d,0xda,0xe1,0x87,0x2d,0x0e,0x20,0x8d, - 0x03,0x0a,0xa9,0xc8,0x44,0xc2,0x1a,0xaf,0x46,0xa0,0xf2,0xf1,0x20,0x8d, - 0x03,0x0a,0xaa,0x7d,0xc2,0x0c,0x95,0xe2,0x5b,0x02,0x8e,0x41,0x20,0x8d, - 0x03,0x0a,0xab,0x00,0x8e,0xd1,0x06,0x26,0x63,0xa5,0x1d,0x49,0x20,0x8d, - 0x03,0x0a,0xab,0x29,0x85,0x0f,0xf2,0xb8,0x58,0x8f,0xdb,0xbf,0x20,0x8d, - 0x03,0x0a,0xab,0x98,0x40,0x0a,0x73,0x43,0x6f,0xb6,0x3d,0x8b,0x20,0x8d, - 0x03,0x0a,0xab,0xdd,0x6d,0x5d,0xc5,0x36,0xcb,0x6c,0xc8,0x70,0x20,0x8d, - 0x03,0x0a,0xac,0x81,0x65,0xa3,0x8b,0xea,0x0b,0x71,0xe4,0x16,0x20,0x8d, - 0x03,0x0a,0xad,0x1b,0x40,0xc1,0x45,0x64,0xbf,0x24,0x15,0xca,0x20,0x8d, - 0x03,0x0a,0xad,0x1c,0xc0,0xb4,0x95,0xb5,0x17,0xc0,0xc2,0x41,0x20,0x8d, - 0x03,0x0a,0xad,0xc4,0xfa,0x8d,0xa6,0xf7,0x40,0x42,0xe7,0xd3,0x20,0x8d, - 0x03,0x0a,0xae,0x2e,0xe4,0x64,0x79,0x05,0x5f,0xb7,0x04,0x14,0x20,0x8d, - 0x03,0x0a,0xb0,0x48,0xe6,0xe8,0x48,0xfa,0xca,0x87,0x78,0x18,0x20,0x8d, - 0x03,0x0a,0xb0,0x6c,0x4a,0x92,0xde,0xd3,0x0d,0x28,0xc4,0x79,0x20,0x8d, - 0x03,0x0a,0xb1,0x41,0x81,0xac,0xde,0xce,0x0b,0x94,0x8a,0x9d,0x20,0x8d, - 0x03,0x0a,0xb1,0x73,0xdf,0x4b,0xab,0xc3,0x7a,0x3c,0x48,0x99,0x20,0x8d, - 0x03,0x0a,0xb1,0xb6,0xc8,0x72,0x86,0xc6,0x34,0x6b,0xef,0x41,0x20,0x8d, - 0x03,0x0a,0xb1,0xd5,0x8e,0xf0,0x22,0x9a,0x8b,0xa6,0xf1,0xfb,0x20,0x8d, - 0x03,0x0a,0xb1,0xd8,0x90,0x36,0x0e,0xc6,0x51,0x9c,0x8b,0x93,0x20,0x8d, - 0x03,0x0a,0xb2,0xce,0xea,0x6a,0xd7,0x34,0x30,0x8d,0xdf,0x65,0x20,0x8d, - 0x03,0x0a,0xb2,0xea,0xa2,0xc5,0xeb,0x2a,0x10,0xec,0xeb,0x4e,0x20,0x8d, - 0x03,0x0a,0xbe,0xda,0x60,0xee,0xa0,0xf8,0xdd,0x5a,0x11,0xb6,0x20,0x8d, - 0x03,0x0a,0xbf,0x7f,0x7f,0x68,0x2c,0x63,0x70,0xba,0xbb,0xf1,0x20,0x8d, - 0x03,0x0a,0xb9,0xaa,0xce,0xfd,0x87,0x35,0x7b,0xee,0x0d,0x40,0x20,0x8d, - 0x03,0x0a,0xb9,0xe5,0xb3,0x2c,0xb6,0x6d,0x91,0x46,0x22,0xad,0x20,0x8d, - 0x03,0x0a,0xba,0x49,0xd2,0xda,0xb8,0x28,0xe8,0x4d,0x53,0xca,0x20,0x8d, - 0x03,0x0a,0xba,0xcd,0x40,0x9b,0x0b,0xc6,0x82,0xba,0xc8,0xdd,0x20,0x8d, - 0x03,0x0a,0xbb,0x57,0x4d,0xce,0xa0,0x53,0x4d,0x8f,0xcd,0x4f,0x20,0x8d, - 0x03,0x0a,0xbb,0xba,0xc0,0x45,0x0b,0x3d,0x30,0xef,0x86,0x93,0x20,0x8d, - 0x03,0x0a,0xbc,0x80,0x0b,0xa0,0xe3,0xc1,0x9b,0x6b,0xc5,0x17,0x20,0x8d, - 0x03,0x0a,0xbd,0x3a,0xc5,0xd0,0xc3,0x93,0x32,0x55,0x57,0x27,0x20,0x8d, - 0x03,0x0a,0xbd,0x63,0x78,0x09,0xf3,0x85,0x50,0x42,0x0c,0x3a,0x20,0x8d, - 0x03,0x0a,0xbd,0xb2,0x78,0xc7,0x06,0x2c,0xe1,0xb8,0x72,0xdc,0x20,0x8d, - 0x03,0x0a,0xc7,0x25,0x66,0x48,0x17,0x18,0x9d,0x2d,0x05,0xb4,0x20,0x8d, - 0x03,0x0a,0xc7,0x66,0xbe,0x2e,0x08,0xdf,0xba,0xf7,0xae,0x83,0x20,0x8d, - 0x03,0x0a,0xc7,0x6d,0x92,0x43,0x00,0x24,0xe5,0xd6,0x83,0xd3,0x20,0x8d, - 0x03,0x0a,0xc7,0xf7,0x05,0x69,0x99,0x52,0x54,0x77,0x2b,0x1f,0x20,0x8d, - 0x03,0x0a,0xc7,0xdd,0x9d,0xe0,0x6d,0xaa,0x03,0xcb,0x9c,0x21,0x20,0x8d, - 0x03,0x0a,0xc0,0x0f,0xf8,0x18,0xb0,0x84,0x66,0x47,0x08,0xe4,0x20,0x8d, - 0x03,0x0a,0xc0,0x41,0xc0,0xc5,0x9d,0xef,0x46,0x46,0xae,0x7f,0x20,0x8d, - 0x03,0x0a,0xc1,0x89,0x05,0x1b,0x88,0x6b,0xd7,0x20,0x08,0x9b,0x20,0x8d, - 0x03,0x0a,0xc2,0x4e,0xd2,0xd3,0xfd,0x58,0x32,0x14,0x6f,0x87,0x20,0x8d, - 0x03,0x0a,0xc2,0x6d,0xf5,0x40,0x0f,0xbd,0xfb,0x53,0x19,0xc9,0x20,0x8d, - 0x03,0x0a,0xc3,0x3e,0x86,0xb1,0xd5,0x0c,0x5a,0x0e,0x18,0x4e,0x20,0x8d, - 0x03,0x0a,0xc4,0x3a,0x2a,0x49,0xb4,0x72,0xa4,0x2c,0x7b,0x99,0x20,0x8d, - 0x03,0x0a,0xc4,0x44,0x04,0x3a,0x11,0x84,0x47,0x67,0x2a,0x13,0x20,0x8d, - 0x03,0x0a,0xc4,0x45,0x1f,0xbc,0xc9,0xa0,0x32,0x01,0xeb,0xbc,0x20,0x8d, - 0x03,0x0a,0xc4,0x55,0x2a,0xb9,0xbb,0x9b,0x2a,0xe7,0x1c,0x75,0x20,0x8d, - 0x03,0x0a,0xc4,0xdf,0x49,0x72,0xb7,0xed,0xbe,0x9f,0x59,0xfa,0x20,0x8d, - 0x03,0x0a,0xc4,0xe0,0x24,0x19,0x5a,0x39,0xc6,0xbe,0x74,0xee,0x20,0x8d, - 0x03,0x0a,0xc5,0xdc,0x95,0xee,0xec,0x4d,0x25,0xb1,0xa1,0x5a,0x20,0x8d, - 0x03,0x0a,0xc6,0x47,0x01,0xca,0x17,0xe1,0x47,0x46,0x9b,0xd6,0x20,0x8d, - 0x03,0x0a,0xce,0xf6,0xda,0x2a,0x7f,0x69,0x90,0xad,0x89,0xe4,0x20,0x8d, - 0x03,0x0a,0xce,0xf1,0x60,0x80,0x76,0xe7,0x9a,0x36,0xdc,0xc7,0x20,0x8d, - 0x03,0x0a,0xcf,0x98,0x18,0x43,0xeb,0x5d,0xd7,0x16,0xf1,0x50,0x20,0x8d, - 0x03,0x0a,0xc8,0x76,0xb8,0x89,0x52,0x6f,0x23,0x93,0xe5,0x24,0x20,0x8d, - 0x03,0x0a,0xc9,0x3e,0xe1,0xbf,0xef,0xc8,0x22,0x97,0xae,0x51,0x20,0x8d, - 0x03,0x0a,0xc9,0x82,0xc3,0xcc,0x29,0x07,0x0b,0x8d,0x6f,0xfb,0x20,0x8d, - 0x03,0x0a,0xc9,0xfe,0x7a,0x81,0x62,0x35,0x52,0xf7,0x02,0x0c,0x20,0x8d, - 0x03,0x0a,0xca,0x33,0x3a,0xdc,0x87,0x62,0x7a,0xc2,0x1d,0xe6,0x20,0x8d, - 0x03,0x0a,0xca,0x50,0x8d,0xe0,0x82,0x1c,0x59,0x0f,0xef,0x1b,0x20,0x8d, - 0x03,0x0a,0xca,0xa3,0x66,0x19,0x34,0xac,0xb2,0x0f,0x60,0x9a,0x20,0x8d, - 0x03,0x0a,0xcb,0xb3,0xa0,0x39,0xf6,0x46,0xec,0x5a,0x42,0xc6,0x20,0x8d, - 0x03,0x0a,0xcc,0xc6,0x22,0xb4,0xfc,0xf7,0xff,0xb0,0xa2,0xb4,0x20,0x8d, - 0x03,0x0a,0xcd,0x2e,0x71,0x78,0x7b,0x6d,0x9e,0x61,0x70,0x05,0x20,0x8d, - 0x03,0x0a,0xcd,0x31,0x38,0x94,0x95,0xca,0x44,0xf4,0x65,0x68,0x20,0x8d, - 0x03,0x0a,0xcd,0x61,0xe1,0xbe,0x7b,0x46,0x9c,0x51,0xbf,0x66,0x20,0x8d, - 0x03,0x0a,0xcd,0xac,0xcf,0x18,0x1f,0xa6,0x8f,0x02,0x6a,0x43,0x20,0x8d, - 0x03,0x0a,0xce,0x20,0x1e,0x2c,0x8d,0x2c,0x9e,0xd9,0xa7,0xac,0x20,0x8d, 0x04,0x20,0xd1,0xbb,0x02,0x8d,0x4d,0xd5,0x6a,0x20,0xc0,0xf9,0x16,0x2b,0x84,0x22,0x66,0xe0,0x89,0x45,0x60,0x37,0x52,0xe2,0x0b,0xa5,0xb4,0xf8,0x26,0xb3,0x8f,0x5a,0x30,0xed,0x20,0x8d, 0x04,0x20,0xd2,0x59,0x3b,0xd7,0x14,0x7e,0xd0,0x98,0xfe,0x9e,0xa5,0x69,0xf4,0x26,0x6d,0x72,0x6f,0xc3,0x76,0xce,0x1d,0x40,0x41,0xa2,0xa1,0xaf,0xf9,0x6e,0x57,0x2d,0x9d,0xc3,0x20,0x8d, 0x04,0x20,0xdf,0xd9,0xed,0x59,0xbf,0x1e,0x77,0x48,0x3c,0x13,0x3b,0xc5,0xc8,0x15,0x86,0x88,0x68,0xf0,0x08,0xe9,0xee,0x9b,0x3d,0xa4,0x33,0x0a,0x68,0x67,0x86,0x9d,0xe2,0x83,0x20,0x8d, diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 6ccfd7fc20..88f93321f9 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -112,14 +112,11 @@ public: //! Get wallet address list. virtual std::vector<WalletAddress> getAddresses() = 0; - //! Add dest data. - virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0; + //! Get receive requests. + virtual std::vector<std::string> getAddressReceiveRequests() = 0; - //! Erase dest data. - virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0; - - //! Get dest values with prefix. - virtual std::vector<std::string> getDestValues(const std::string& prefix) = 0; + //! Save or remove receive request. + virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0; //! Lock coin. virtual void lockCoin(const COutPoint& output) = 0; diff --git a/src/key_io.cpp b/src/key_io.cpp index dbcbfa1f29..615f4c9312 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -54,6 +54,14 @@ public: return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data); } + std::string operator()(const WitnessV1Taproot& tap) const + { + std::vector<unsigned char> data = {1}; + data.reserve(53); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, tap.begin(), tap.end()); + return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data); + } + std::string operator()(const WitnessUnknown& id) const { if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { @@ -135,6 +143,13 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return CNoDestination(); } + if (version == 1 && data.size() == WITNESS_V1_TAPROOT_SIZE) { + static_assert(WITNESS_V1_TAPROOT_SIZE == WitnessV1Taproot::size()); + WitnessV1Taproot tap; + std::copy(data.begin(), data.end(), tap.begin()); + return tap; + } + if (version > 16) { error_str = "Invalid Bech32 address witness version"; return CNoDestination(); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 352abae298..1ea3969978 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -32,14 +32,7 @@ CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const case NET_IPV6: return BIP155Network::IPV6; case NET_ONION: - switch (m_addr.size()) { - case ADDR_TORV2_SIZE: - return BIP155Network::TORV2; - case ADDR_TORV3_SIZE: - return BIP155Network::TORV3; - default: - assert(false); - } + return BIP155Network::TORV3; case NET_I2P: return BIP155Network::I2P; case NET_CJDNS: @@ -72,14 +65,6 @@ bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t addre throw std::ios_base::failure( strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size, ADDR_IPV6_SIZE)); - case BIP155Network::TORV2: - if (address_size == ADDR_TORV2_SIZE) { - m_net = NET_ONION; - return true; - } - throw std::ios_base::failure( - strprintf("BIP155 TORv2 address with length %u (should be %u)", address_size, - ADDR_TORV2_SIZE)); case BIP155Network::TORV3: if (address_size == ADDR_TORV3_SIZE) { m_net = NET_ONION; @@ -130,7 +115,7 @@ void CNetAddr::SetIP(const CNetAddr& ipIn) assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE); break; case NET_ONION: - assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE || ipIn.m_addr.size() == ADDR_TORV3_SIZE); + assert(ipIn.m_addr.size() == ADDR_TORV3_SIZE); break; case NET_I2P: assert(ipIn.m_addr.size() == ADDR_I2P_SIZE); @@ -161,9 +146,12 @@ void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6) m_net = NET_IPV4; skip = sizeof(IPV4_IN_IPV6_PREFIX); } else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) { - // TORv2-in-IPv6 - m_net = NET_ONION; - skip = sizeof(TORV2_IN_IPV6_PREFIX); + // TORv2-in-IPv6 (unsupported). Unserialize as !IsValid(), thus ignoring them. + // Mimic a default-constructed CNetAddr object which is !IsValid() and thus + // will not be gossiped, but continue reading next addresses from the stream. + m_net = NET_IPV6; + m_addr.assign(ADDR_IPV6_SIZE, 0x0); + return; } else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) { // Internal-in-IPv6 m_net = NET_INTERNAL; @@ -254,12 +242,7 @@ bool CNetAddr::SetTor(const std::string& addr) return false; } - switch (input.size()) { - case ADDR_TORV2_SIZE: - m_net = NET_ONION; - m_addr.assign(input.begin(), input.end()); - return true; - case torv3::TOTAL_LEN: { + if (input.size() == torv3::TOTAL_LEN) { Span<const uint8_t> input_pubkey{input.data(), ADDR_TORV3_SIZE}; Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN}; Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)}; @@ -279,7 +262,6 @@ bool CNetAddr::SetTor(const std::string& addr) m_addr.assign(input_pubkey.begin(), input_pubkey.end()); return true; } - } return false; } @@ -528,7 +510,6 @@ bool CNetAddr::IsAddrV1Compatible() const case NET_INTERNAL: return true; case NET_ONION: - return m_addr.size() == ADDR_TORV2_SIZE; case NET_I2P: case NET_CJDNS: return false; @@ -613,33 +594,26 @@ static std::string IPv6ToString(Span<const uint8_t> a, uint32_t scope_id) return r; } +static std::string OnionToString(Span<const uint8_t> addr) +{ + uint8_t checksum[torv3::CHECKSUM_LEN]; + torv3::Checksum(addr, checksum); + // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion" + prevector<torv3::TOTAL_LEN, uint8_t> address{addr.begin(), addr.end()}; + address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN); + address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION)); + return EncodeBase32(address) + ".onion"; +} + std::string CNetAddr::ToStringIP() const { switch (m_net) { case NET_IPV4: return IPv4ToString(m_addr); - case NET_IPV6: { + case NET_IPV6: return IPv6ToString(m_addr, m_scope_id); - } case NET_ONION: - switch (m_addr.size()) { - case ADDR_TORV2_SIZE: - return EncodeBase32(m_addr) + ".onion"; - case ADDR_TORV3_SIZE: { - - uint8_t checksum[torv3::CHECKSUM_LEN]; - torv3::Checksum(m_addr, checksum); - - // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion" - prevector<torv3::TOTAL_LEN, uint8_t> address{m_addr.begin(), m_addr.end()}; - address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN); - address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION)); - - return EncodeBase32(address) + ".onion"; - } - default: - assert(false); - } + return OnionToString(m_addr); case NET_I2P: return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p"; case NET_CJDNS: diff --git a/src/netaddress.h b/src/netaddress.h index 897ce46cda..0d04ab88fb 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -97,9 +97,6 @@ static constexpr size_t ADDR_IPV4_SIZE = 4; /// Size of IPv6 address (in bytes). static constexpr size_t ADDR_IPV6_SIZE = 16; -/// Size of TORv2 address (in bytes). -static constexpr size_t ADDR_TORV2_SIZE = 10; - /// Size of TORv3 address (in bytes). This is the length of just the address /// as used in BIP155, without the checksum and the version byte. static constexpr size_t ADDR_TORV3_SIZE = 32; @@ -260,8 +257,7 @@ class CNetAddr /** * Parse a Tor address and set this object to it. * @param[in] addr Address to parse, must be a valid C string, for example - * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or - * 6hzph5hv6337r6p2.onion. + * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion. * @returns Whether the operation was successful. * @see CNetAddr::IsTor() */ @@ -303,7 +299,7 @@ class CNetAddr /** * Get the BIP155 network id of this address. * Must not be called for IsInternal() objects. - * @returns BIP155 network id + * @returns BIP155 network id, except TORV2 which is no longer supported. */ BIP155Network GetBIP155Network() const; @@ -334,23 +330,14 @@ class CNetAddr memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size); memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); return; - case NET_ONION: - if (m_addr.size() == ADDR_TORV3_SIZE) { - break; - } - prefix_size = sizeof(TORV2_IN_IPV6_PREFIX); - assert(prefix_size + m_addr.size() == sizeof(arr)); - memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size); - memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); - return; case NET_INTERNAL: prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX); assert(prefix_size + m_addr.size() == sizeof(arr)); memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size); memcpy(arr + prefix_size, m_addr.data(), m_addr.size()); return; + case NET_ONION: case NET_I2P: - break; case NET_CJDNS: break; case NET_UNROUTABLE: @@ -358,7 +345,7 @@ class CNetAddr assert(false); } // no default case, so the compiler can warn about missing cases - // Serialize TORv3, I2P and CJDNS as all-zeros. + // Serialize ONION, I2P and CJDNS as all-zeros. memset(arr, 0x0, V1_SERIALIZATION_SIZE); } diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 334acb454e..51cc826b00 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -180,6 +180,12 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes) std::copy(bytes.begin(), bytes.end(), m_keydata.begin()); } +bool XOnlyPubKey::IsFullyValid() const +{ + secp256k1_xonly_pubkey pubkey; + return secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data()); +} + bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const { assert(sigbytes.size() == 64); @@ -188,13 +194,45 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey); } -bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const +static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak"); + +uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const +{ + if (merkle_root == nullptr) { + // We have no scripts. The actual tweak does not matter, but follow BIP341 here to + // allow for reproducible tweaking. + return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256(); + } else { + return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256(); + } +} + +bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const +{ + secp256k1_xonly_pubkey internal_key; + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false; + uint256 tweak = internal.ComputeTapTweakHash(&merkle_root); + return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin()); +} + +std::optional<std::pair<XOnlyPubKey, bool>> XOnlyPubKey::CreateTapTweak(const uint256* merkle_root) const { secp256k1_xonly_pubkey base_point; - if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false; - return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin()); + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, data())) return std::nullopt; + secp256k1_pubkey out; + uint256 tweak = ComputeTapTweakHash(merkle_root); + if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_verify, &out, &base_point, tweak.data())) return std::nullopt; + int parity = -1; + std::pair<XOnlyPubKey, bool> ret; + secp256k1_xonly_pubkey out_xonly; + if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_verify, &out_xonly, &parity, &out)) return std::nullopt; + secp256k1_xonly_pubkey_serialize(secp256k1_context_verify, ret.first.begin(), &out_xonly); + assert(parity == 0 || parity == 1); + ret.second = parity; + return ret; } + bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { if (!IsValid()) return false; diff --git a/src/pubkey.h b/src/pubkey.h index 1af1187006..152a48dd18 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -13,6 +13,7 @@ #include <uint256.h> #include <cstring> +#include <optional> #include <vector> const unsigned int BIP32_EXTKEY_SIZE = 74; @@ -222,19 +223,56 @@ private: uint256 m_keydata; public: + /** Construct an empty x-only pubkey. */ + XOnlyPubKey() = default; + + XOnlyPubKey(const XOnlyPubKey&) = default; + XOnlyPubKey& operator=(const XOnlyPubKey&) = default; + + /** Determine if this pubkey is fully valid. This is true for approximately 50% of all + * possible 32-byte arrays. If false, VerifySchnorr and CreatePayToContract will always + * fail. */ + bool IsFullyValid() const; + /** Construct an x-only pubkey from exactly 32 bytes. */ explicit XOnlyPubKey(Span<const unsigned char> bytes); + /** Construct an x-only pubkey from a normal pubkey. */ + explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span<const unsigned char>(pubkey.begin() + 1, pubkey.begin() + 33)) {} + /** Verify a Schnorr signature against this public key. * * sigbytes must be exactly 64 bytes. */ bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const; - bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const; + + /** Compute the Taproot tweak as specified in BIP341, with *this as internal + * key: + * - if merkle_root == nullptr: H_TapTweak(xonly_pubkey) + * - otherwise: H_TapTweak(xonly_pubkey || *merkle_root) + * + * Note that the behavior of this function with merkle_root != nullptr is + * consensus critical. + */ + uint256 ComputeTapTweakHash(const uint256* merkle_root) const; + + /** Verify that this is a Taproot tweaked output point, against a specified internal key, + * Merkle root, and parity. */ + bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const; + + /** Construct a Taproot tweaked output point with this point as internal key. */ + std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const; const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } const unsigned char* data() const { return m_keydata.begin(); } - size_t size() const { return m_keydata.size(); } + static constexpr size_t size() { return decltype(m_keydata)::size(); } + const unsigned char* begin() const { return m_keydata.begin(); } + const unsigned char* end() const { return m_keydata.end(); } + unsigned char* begin() { return m_keydata.begin(); } + unsigned char* end() { return m_keydata.end(); } + bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; } + bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; } + bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; } }; struct CExtPubKey { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 69948402d0..9e6cf56d31 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -60,6 +60,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); Q_IMPORT_PLUGIN(QMacStylePlugin); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e5dde88bd1..50f6e739e8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -105,6 +105,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty { /** Create wallet frame and make it the central widget */ walletFrame = new WalletFrame(_platformStyle, this); + connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] { + auto activity = new CreateWalletActivity(getWalletController(), this); + connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater); + activity->create(); + }); setCentralWidget(walletFrame); } else #endif // ENABLE_WALLET @@ -671,7 +676,10 @@ WalletController* BitcoinGUI::getWalletController() void BitcoinGUI::addWallet(WalletModel* walletModel) { if (!walletFrame) return; - if (!walletFrame->addWallet(walletModel)) return; + + WalletView* wallet_view = new WalletView(platformStyle, walletFrame); + if (!walletFrame->addWallet(walletModel, wallet_view)) return; + rpcConsole->addWallet(walletModel); if (m_wallet_selector->count() == 0) { setWalletActionsEnabled(true); @@ -681,6 +689,18 @@ void BitcoinGUI::addWallet(WalletModel* walletModel) } const QString display_name = walletModel->getDisplayName(); m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel)); + + connect(wallet_view, &WalletView::outOfSyncWarningClicked, walletFrame, &WalletFrame::outOfSyncWarningClicked); + connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage); + connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage); + connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) { + this->message(title, message, style); + }); + connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus); + connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction); + connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus); + connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy); + wallet_view->setPrivacy(isPrivacyModeActivated()); } void BitcoinGUI::removeWallet(WalletModel* walletModel) diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 7a25ba907e..15e0d3fad9 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -470,13 +470,7 @@ </spacer> </item> <item> - <widget class="QPushButton" name="fontSmallerButton"> - <property name="maximumSize"> - <size> - <width>24</width> - <height>24</height> - </size> - </property> + <widget class="QToolButton" name="fontSmallerButton"> <property name="toolTip"> <string>Decrease font size</string> </property> @@ -489,26 +483,14 @@ </property> <property name="iconSize"> <size> - <width>24</width> - <height>16</height> + <width>22</width> + <height>22</height> </size> </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>true</bool> - </property> </widget> </item> <item> - <widget class="QPushButton" name="fontBiggerButton"> - <property name="maximumSize"> - <size> - <width>24</width> - <height>24</height> - </size> - </property> + <widget class="QToolButton" name="fontBiggerButton"> <property name="toolTip"> <string>Increase font size</string> </property> @@ -521,26 +503,14 @@ </property> <property name="iconSize"> <size> - <width>24</width> - <height>16</height> + <width>22</width> + <height>22</height> </size> </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>true</bool> - </property> </widget> </item> <item> - <widget class="QPushButton" name="clearButton"> - <property name="maximumSize"> - <size> - <width>24</width> - <height>24</height> - </size> - </property> + <widget class="QToolButton" name="clearButton"> <property name="toolTip"> <string>Clear console</string> </property> @@ -554,15 +524,15 @@ <iconset resource="../bitcoin.qrc"> <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> </property> + <property name="iconSize"> + <size> + <width>22</width> + <height>22</height> + </size> + </property> <property name="shortcut"> <string notr="true">Ctrl+L</string> </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>true</bool> - </property> </widget> </item> </layout> diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 11441481bb..fff079d5db 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -132,6 +132,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const } else if (role == Qt::TextAlignmentRole) { switch (column) { case NetNodeId: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); case Address: return {}; case ConnectionType: diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 1ecc2f67ef..ec3d970a7f 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -10,7 +10,10 @@ #include <qt/walletmodel.h> #include <clientversion.h> +#include <interfaces/wallet.h> +#include <key_io.h> #include <streams.h> +#include <util/string.h> #include <utility> @@ -21,10 +24,9 @@ RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) { // Load entries from wallet - std::vector<std::string> vReceiveRequests; - parent->loadReceiveRequests(vReceiveRequests); - for (const std::string& request : vReceiveRequests) + for (const std::string& request : parent->wallet().getAddressReceiveRequests()) { addNewRequest(request); + } /* These columns must match the indices in the ColumnIndex enumeration */ columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle(); @@ -150,7 +152,7 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex for (int i = 0; i < count; ++i) { const RecentRequestEntry* rec = &list[row+i]; - if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, "")) + if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(rec->recipient.address.toStdString()), ToString(rec->id), "")) return false; } @@ -179,7 +181,7 @@ void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient CDataStream ss(SER_DISK, CLIENT_VERSION); ss << newEntry; - if (!walletModel->saveReceiveRequest(recipient.address.toStdString(), newEntry.id, ss.str())) + if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(recipient.address.toStdString()), ToString(newEntry.id), ss.str())) return; addNewRequest(newEntry); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 548b8cb34d..e6e767c5af 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -34,9 +34,12 @@ #include <wallet/wallet.h> #endif +#include <QAbstractButton> #include <QDateTime> #include <QFont> #include <QKeyEvent> +#include <QLatin1String> +#include <QLocale> #include <QMenu> #include <QMessageBox> #include <QScreen> @@ -44,9 +47,10 @@ #include <QSettings> #include <QString> #include <QStringList> +#include <QStyledItemDelegate> #include <QTime> #include <QTimer> - +#include <QVariant> const int CONSOLE_HISTORY = 50; const int INITIAL_TRAFFIC_GRAPH_MINS = 30; @@ -128,6 +132,20 @@ public: } }; +class PeerIdViewDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit PeerIdViewDelegate(QObject* parent = nullptr) + : QStyledItemDelegate(parent) {} + + QString displayText(const QVariant& value, const QLocale& locale) const override + { + // Additional spaces should visually separate right-aligned content + // from the next column to the right. + return value.toString() + QLatin1String(" "); + } +}; #include <qt/rpcconsole.moc> @@ -469,6 +487,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray()); } + m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray(); + m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray(); + constexpr QChar nonbreaking_hyphen(8209); const std::vector<QString> CONNECTION_TYPE_DOC{ tr("Inbound: initiated by peer"), @@ -513,9 +534,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty ui->lineEdit->setMaxLength(16 * 1024 * 1024); ui->messagesWidget->installEventFilter(this); - connect(ui->clearButton, &QPushButton::clicked, [this] { clear(); }); - connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger); - connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller); + connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); }); + connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger); + connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller); connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear); // disable the wallet selector by default @@ -552,6 +573,9 @@ RPCConsole::~RPCConsole() settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState()); } + settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state); + settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state); + m_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; @@ -640,10 +664,14 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu); - ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); - ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); - ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); + + if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) { + ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); + ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); + ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); + } ui->peerWidget->horizontalHeader()->setStretchLastSection(true); + ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this)); // create peer table context menu peersTableContextMenu = new QMenu(this); @@ -664,8 +692,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection); ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu); - ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH); - ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH); + + if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) { + ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH); + ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH); + } ui->banlistWidget->horizontalHeader()->setStretchLastSection(true); // create ban table context menu @@ -1171,6 +1202,11 @@ void RPCConsole::showEvent(QShowEvent *event) void RPCConsole::hideEvent(QHideEvent *event) { + // It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of + // the columns of QTableView child widgets will have zero width at that moment. + m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState(); + m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState(); + QWidget::hideEvent(event); if (!clientModel || !clientModel->getPeerTableModel()) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 75f466642b..2412ae543c 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -10,9 +10,10 @@ #include <net.h> -#include <QWidget> +#include <QByteArray> #include <QCompleter> #include <QThread> +#include <QWidget> class ClientModel; class PlatformStyle; @@ -167,6 +168,8 @@ private: QThread thread; WalletModel* m_last_wallet_model{nullptr}; bool m_is_executing{false}; + QByteArray m_peer_widget_header_state; + QByteArray m_banlist_widget_header_state; /** Update UI with latest network info from model. */ void updateNetworkState(); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 26f568b16a..ea35f80cf5 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -224,6 +224,7 @@ void TestGUI(interfaces::Node& node) int initialRowCount = requestTableModel->rowCount({}); QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton"); requestPaymentButton->click(); + QString address; for (QWidget* widget : QApplication::topLevelWidgets()) { if (widget->inherits("ReceiveRequestDialog")) { ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget); @@ -232,6 +233,9 @@ void TestGUI(interfaces::Node& node) QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text(); QCOMPARE(uri.count("bitcoin:"), 2); QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:")); + QVERIFY(address.isEmpty()); + address = receiveRequestDialog->QObject::findChild<QLabel*>("address_content")->text(); + QVERIFY(!address.isEmpty()); QCOMPARE(uri.count("amount=0.00000001"), 2); QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:")); @@ -258,12 +262,30 @@ void TestGUI(interfaces::Node& node) int currentRowCount = requestTableModel->rowCount({}); QCOMPARE(currentRowCount, initialRowCount+1); + // Check addition to wallet + std::vector<std::string> requests = walletModel.wallet().getAddressReceiveRequests(); + QCOMPARE(requests.size(), size_t{1}); + RecentRequestEntry entry; + CDataStream{MakeUCharSpan(requests[0]), SER_DISK, CLIENT_VERSION} >> entry; + QCOMPARE(entry.nVersion, int{1}); + QCOMPARE(entry.id, int64_t{1}); + QVERIFY(entry.date.isValid()); + QCOMPARE(entry.recipient.address, address); + QCOMPARE(entry.recipient.label, QString{"TEST_LABEL_1"}); + QCOMPARE(entry.recipient.amount, CAmount{1}); + QCOMPARE(entry.recipient.message, QString{"TEST_MESSAGE_1"}); + QCOMPARE(entry.recipient.sPaymentRequest, std::string{}); + QCOMPARE(entry.recipient.authenticatedMerchant, QString{}); + // Check Remove button QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView"); table->selectRow(currentRowCount-1); QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton"); removeRequestButton->click(); QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); + + // Check removal from wallet + QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(), size_t{0}); } } // namespace diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 02b3c62867..0f2ea99685 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -4,10 +4,7 @@ #include <qt/walletframe.h> -#include <qt/bitcoingui.h> -#include <qt/createwalletdialog.h> #include <qt/overviewpage.h> -#include <qt/walletcontroller.h> #include <qt/walletmodel.h> #include <qt/walletview.h> @@ -19,9 +16,8 @@ #include <QPushButton> #include <QVBoxLayout> -WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui) - : QFrame(_gui), - gui(_gui), +WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent) + : QFrame(parent), platformStyle(_platformStyle), m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint()) { @@ -42,11 +38,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui) // A button for create wallet dialog QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack); - connect(create_wallet_button, &QPushButton::clicked, [this] { - auto activity = new CreateWalletActivity(gui->getWalletController(), this); - connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater); - activity->create(); - }); + connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked); no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop); no_wallet_group->setLayout(no_wallet_layout); @@ -66,17 +58,15 @@ void WalletFrame::setClientModel(ClientModel *_clientModel) } } -bool WalletFrame::addWallet(WalletModel *walletModel) +bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView) { - if (!gui || !clientModel || !walletModel) return false; + if (!clientModel || !walletModel) return false; if (mapWalletViews.count(walletModel) > 0) return false; - WalletView *walletView = new WalletView(platformStyle, this); walletView->setClientModel(clientModel); walletView->setWalletModel(walletModel); walletView->showOutOfSyncWarning(bOutOfSync); - walletView->setPrivacy(gui->isPrivacyModeActivated()); WalletView* current_wallet_view = currentWalletView(); if (current_wallet_view) { @@ -88,17 +78,6 @@ bool WalletFrame::addWallet(WalletModel *walletModel) walletStack->addWidget(walletView); mapWalletViews[walletModel] = walletView; - connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked); - connect(walletView, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage); - connect(walletView, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage); - connect(walletView, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) { - gui->message(title, message, style); - }); - connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus); - connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction); - connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus); - connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy); - return true; } diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index f57f8678d6..844ed121a0 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -8,7 +8,6 @@ #include <QFrame> #include <QMap> -class BitcoinGUI; class ClientModel; class PlatformStyle; class SendCoinsRecipient; @@ -31,12 +30,12 @@ class WalletFrame : public QFrame Q_OBJECT public: - explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = nullptr); + explicit WalletFrame(const PlatformStyle* platformStyle, QWidget* parent); ~WalletFrame(); void setClientModel(ClientModel *clientModel); - bool addWallet(WalletModel *walletModel); + bool addWallet(WalletModel* walletModel, WalletView* walletView); void setCurrentWallet(WalletModel* wallet_model); void removeWallet(WalletModel* wallet_model); void removeAllWallets(); @@ -51,9 +50,10 @@ Q_SIGNALS: /** Notify that the user has requested more information about the out-of-sync warning */ void requestedSyncWarningInfo(); + void createWalletButtonClicked(); + private: QStackedWidget *walletStack; - BitcoinGUI *gui; ClientModel *clientModel; QMap<WalletModel*, WalletView*> mapWalletViews; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index cc6db8d33e..7c58b8afd2 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -466,25 +466,6 @@ void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs) rhs.relock = false; } -void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests) -{ - vReceiveRequests = m_wallet->getDestValues("rr"); // receive request -} - -bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) -{ - CTxDestination dest = DecodeDestination(sAddress); - - std::stringstream ss; - ss << nId; - std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata - - if (sRequest.empty()) - return m_wallet->eraseDestData(dest, key); - else - return m_wallet->addDestData(dest, key, sRequest); -} - bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) { CCoinControl coin_control; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 4ca8643444..b2ce5d69fb 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -135,9 +135,6 @@ public: UnlockContext requestUnlock(); - void loadReceiveRequests(std::vector<std::string>& vReceiveRequests); - bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest); - bool bumpFee(uint256 hash, uint256& new_hash); static bool isWalletEnabled(); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index c4d6e3d546..3013c76825 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -939,7 +939,7 @@ static RPCHelpMan addpeeraddress() bool success{false}; if (LookupHost(addr_string, net_addr, false)) { - CAddress address{CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK | NODE_WITNESS))}; + CAddress address{{net_addr, port}, ServiceFlags{NODE_NETWORK | NODE_WITNESS}}; address.nTime = GetAdjustedTime(); // The source address is set equal to the address. This is equivalent to the peer // announcing itself. diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 7cf25e0c82..2059628b54 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -301,6 +301,16 @@ public: return obj; } + UniValue operator()(const WitnessV1Taproot& tap) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 1); + obj.pushKV("witness_program", HexStr(tap)); + return obj; + } + UniValue operator()(const WitnessUnknown& id) const { UniValue obj(UniValue::VOBJ); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index b54ba204f0..51cf8a7d62 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -241,9 +241,10 @@ public: class ConstPubkeyProvider final : public PubkeyProvider { CPubKey m_pubkey; + bool m_xonly; public: - ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey) : PubkeyProvider(exp_index), m_pubkey(pubkey) {} + ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly = false) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {} bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override { key = m_pubkey; @@ -254,7 +255,7 @@ public: } bool IsRange() const override { return false; } size_t GetSize() const override { return m_pubkey.size(); } - std::string ToString() const override { return HexStr(m_pubkey); } + std::string ToString() const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); } bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { CKey key; @@ -505,6 +506,7 @@ protected: public: DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args() {} DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {} + DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::vector<std::unique_ptr<DescriptorImpl>> scripts, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(std::move(scripts)) {} bool IsSolvable() const override { @@ -638,6 +640,20 @@ public: std::optional<OutputType> GetOutputType() const override { return std::nullopt; } }; +static std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) { + if (std::holds_alternative<PKHash>(dest) || + std::holds_alternative<ScriptHash>(dest)) { + return OutputType::LEGACY; + } + if (std::holds_alternative<WitnessV0KeyHash>(dest) || + std::holds_alternative<WitnessV0ScriptHash>(dest) || + std::holds_alternative<WitnessV1Taproot>(dest) || + std::holds_alternative<WitnessUnknown>(dest)) { + return OutputType::BECH32; + } + return std::nullopt; +} + /** A parsed addr(A) descriptor. */ class AddressDescriptor final : public DescriptorImpl { @@ -651,15 +667,7 @@ public: std::optional<OutputType> GetOutputType() const override { - switch (m_destination.index()) { - case 1 /* PKHash */: - case 2 /* ScriptHash */: return OutputType::LEGACY; - case 3 /* WitnessV0ScriptHash */: - case 4 /* WitnessV0KeyHash */: - case 5 /* WitnessUnknown */: return OutputType::BECH32; - case 0 /* CNoDestination */: - default: return std::nullopt; - } + return OutputTypeFromDestination(m_destination); } bool IsSingleType() const final { return true; } }; @@ -679,15 +687,7 @@ public: { CTxDestination dest; ExtractDestination(m_script, dest); - switch (dest.index()) { - case 1 /* PKHash */: - case 2 /* ScriptHash */: return OutputType::LEGACY; - case 3 /* WitnessV0ScriptHash */: - case 4 /* WitnessV0KeyHash */: - case 5 /* WitnessUnknown */: return OutputType::BECH32; - case 0 /* CNoDestination */: - default: return std::nullopt; - } + return OutputTypeFromDestination(dest); } bool IsSingleType() const final { return true; } }; @@ -695,10 +695,20 @@ public: /** A parsed pk(P) descriptor. */ class PKDescriptor final : public DescriptorImpl { +private: + const bool m_xonly; protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override + { + if (m_xonly) { + CScript script = CScript() << ToByteVector(XOnlyPubKey(keys[0])) << OP_CHECKSIG; + return Vector(std::move(script)); + } else { + return Vector(GetScriptForRawPubKey(keys[0])); + } + } public: - PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pk") {} + PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {} bool IsSingleType() const final { return true; } }; @@ -816,6 +826,56 @@ public: bool IsSingleType() const final { return true; } }; +/** A parsed tr(...) descriptor. */ +class TRDescriptor final : public DescriptorImpl +{ + std::vector<int> m_depths; +protected: + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override + { + TaprootBuilder builder; + assert(m_depths.size() == scripts.size()); + for (size_t pos = 0; pos < m_depths.size(); ++pos) { + builder.Add(m_depths[pos], scripts[pos], TAPROOT_LEAF_TAPSCRIPT); + } + if (!builder.IsComplete()) return {}; + assert(keys.size() == 1); + XOnlyPubKey xpk(keys[0]); + if (!xpk.IsFullyValid()) return {}; + builder.Finalize(xpk); + return Vector(GetScriptForDestination(builder.GetOutput())); + } + bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override + { + if (m_depths.empty()) return true; + std::vector<bool> path; + for (size_t pos = 0; pos < m_depths.size(); ++pos) { + if (pos) ret += ','; + while ((int)path.size() <= m_depths[pos]) { + if (path.size()) ret += '{'; + path.push_back(false); + } + std::string tmp; + if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false; + ret += std::move(tmp); + while (!path.empty() && path.back()) { + if (path.size() > 1) ret += '}'; + path.pop_back(); + } + if (!path.empty()) path.back() = true; + } + return true; + } +public: + TRDescriptor(std::unique_ptr<PubkeyProvider> internal_key, std::vector<std::unique_ptr<DescriptorImpl>> descs, std::vector<int> depths) : + DescriptorImpl(Vector(std::move(internal_key)), std::move(descs), "tr"), m_depths(std::move(depths)) + { + assert(m_subdescriptor_args.size() == m_depths.size()); + } + std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; } + bool IsSingleType() const final { return true; } +}; + //////////////////////////////////////////////////////////////////////////// // Parser // //////////////////////////////////////////////////////////////////////////// @@ -825,6 +885,7 @@ enum class ParseScriptContext { P2SH, //!< Inside sh() (script becomes P2SH redeemScript) P2WPKH, //!< Inside wpkh() (no script, pubkey only) P2WSH, //!< Inside wsh() (script becomes v0 witness script) + P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf) }; /** Parse a key path, being passed a split list of elements (the first element is ignored). */ @@ -873,6 +934,13 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S error = "Uncompressed keys are not allowed"; return nullptr; } + } else if (data.size() == 32 && ctx == ParseScriptContext::P2TR) { + unsigned char fullkey[33] = {0x02}; + std::copy(data.begin(), data.end(), fullkey + 1); + pubkey.Set(std::begin(fullkey), std::end(fullkey)); + if (pubkey.IsFullyValid()) { + return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true); + } } error = strprintf("Pubkey '%s' is invalid", str); return nullptr; @@ -960,13 +1028,16 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); if (!pubkey) return nullptr; ++key_exp_index; - return std::make_unique<PKDescriptor>(std::move(pubkey)); + return std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR); } - if (Func("pkh", expr)) { + if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && Func("pkh", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); if (!pubkey) return nullptr; ++key_exp_index; return std::make_unique<PKHDescriptor>(std::move(pubkey)); + } else if (Func("pkh", expr)) { + error = "Can only have pkh at top level, in sh(), or in wsh()"; + return nullptr; } if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error); @@ -977,7 +1048,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const error = "Can only have combo() at top level"; return nullptr; } - if ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr)) { + if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr))) { auto threshold = Expr(expr); uint32_t thres; std::vector<std::unique_ptr<PubkeyProvider>> providers; @@ -1022,6 +1093,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } } return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi); + } else if (Func("sortedmulti", expr) || Func("multi", expr)) { + error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()"; + return nullptr; } if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) { auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error); @@ -1059,6 +1133,67 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const error = "Can only have addr() at top level"; return nullptr; } + if (ctx == ParseScriptContext::TOP && Func("tr", expr)) { + auto arg = Expr(expr); + auto internal_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error); + if (!internal_key) return nullptr; + ++key_exp_index; + std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions + std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts) + if (expr.size()) { + if (!Const(",", expr)) { + error = strprintf("tr: expected ',', got '%c'", expr[0]); + return nullptr; + } + /** The path from the top of the tree to what we're currently processing. + * branches[i] == false: left branch in the i'th step from the top; true: right branch. + */ + std::vector<bool> branches; + // Loop over all provided scripts. In every iteration exactly one script will be processed. + // Use a do-loop because inside this if-branch we expect at least one script. + do { + // First process all open braces. + while (Const("{", expr)) { + branches.push_back(false); // new left branch + if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT) { + error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT); + return nullptr; + } + } + // Process the actual script expression. + auto sarg = Expr(expr); + subscripts.emplace_back(ParseScript(key_exp_index, sarg, ParseScriptContext::P2TR, out, error)); + if (!subscripts.back()) return nullptr; + depths.push_back(branches.size()); + // Process closing braces; one is expected for every right branch we were in. + while (branches.size() && branches.back()) { + if (!Const("}", expr)) { + error = strprintf("tr(): expected '}' after script expression"); + return nullptr; + } + branches.pop_back(); // move up one level after encountering '}' + } + // If after that, we're at the end of a left branch, expect a comma. + if (branches.size() && !branches.back()) { + if (!Const(",", expr)) { + error = strprintf("tr(): expected ',' after script expression"); + return nullptr; + } + branches.back() = true; // And now we're in a right branch. + } + } while (branches.size()); + // After we've explored a whole tree, we must be at the end of the expression. + if (expr.size()) { + error = strprintf("tr(): expected ')' after script expression"); + return nullptr; + } + } + assert(TaprootBuilder::ValidDepths(depths)); + return std::make_unique<TRDescriptor>(std::move(internal_key), std::move(subscripts), std::move(depths)); + } else if (Func("tr", expr)) { + error = "Can only have tr at top level"; + return nullptr; + } if (ctx == ParseScriptContext::TOP && Func("raw", expr)) { std::string str(expr.begin(), expr.end()); if (!IsHex(str)) { diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index dc0f165be0..3c3c3ac1a8 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1484,9 +1484,8 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash"); -static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf"); -static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch"); -static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak"); +const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf"); +const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch"); static bool HandleMissingData(MissingDataBehavior mdb) { @@ -1869,10 +1868,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c } k = ss_branch.GetSHA256(); } - // Compute the tweak from the Merkle root and the internal pubkey. - k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256(); // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. - return q.CheckPayToContract(p, k, control[0] & 1); + return q.CheckTapTweak(p, k, control[0] & 1); } static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 212de17c7b..fa4ee83e04 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_INTERPRETER_H #define BITCOIN_SCRIPT_INTERPRETER_H +#include <hash.h> #include <script/script_error.h> #include <span.h> #include <primitives/transaction.h> @@ -218,6 +219,9 @@ static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32; static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128; static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; +extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it. +extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it. + template <class T> uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 364fac3c84..a4b11cc0a9 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -6,8 +6,11 @@ #include <script/standard.h> #include <crypto/sha256.h> +#include <hash.h> #include <pubkey.h> +#include <script/interpreter.h> #include <script/script.h> +#include <util/strencodings.h> #include <string> @@ -155,15 +158,14 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { - vSolutionsRet.push_back(witnessprogram); + vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V0_KEYHASH; } if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { - vSolutionsRet.push_back(witnessprogram); + vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V0_SCRIPTHASH; } if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) { - vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V1_TAPROOT; } @@ -242,8 +244,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) addressRet = hash; return true; } - case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: { + WitnessV1Taproot tap; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), tap.begin()); + addressRet = tap; + return true; + } + case TxoutType::WITNESS_UNKNOWN: { WitnessUnknown unk; unk.version = vSolutions[0][0]; std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); @@ -329,6 +336,11 @@ public: return CScript() << OP_0 << ToByteVector(id); } + CScript operator()(const WitnessV1Taproot& tap) const + { + return CScript() << OP_1 << ToByteVector(tap); + } + CScript operator()(const WitnessUnknown& id) const { return CScript() << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length); @@ -361,3 +373,99 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) bool IsValidDestination(const CTxDestination& dest) { return dest.index() != 0; } + +/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b) +{ + NodeInfo ret; + /* Lexicographically sort a and b's hash, and compute parent hash. */ + if (a.hash < b.hash) { + ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256(); + } else { + ret.hash = (CHashWriter(HASHER_TAPBRANCH) << b.hash << a.hash).GetSHA256(); + } + return ret; +} + +void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth) +{ + assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT); + /* We cannot insert a leaf at a lower depth while a deeper branch is unfinished. Doing + * so would mean the Add() invocations do not correspond to a DFS traversal of a + * binary tree. */ + if ((size_t)depth + 1 < m_branch.size()) { + m_valid = false; + return; + } + /* As long as an entry in the branch exists at the specified depth, combine it and propagate up. + * The 'node' variable is overwritten here with the newly combined node. */ + while (m_valid && m_branch.size() > (size_t)depth && m_branch[depth].has_value()) { + node = Combine(std::move(node), std::move(*m_branch[depth])); + m_branch.pop_back(); + if (depth == 0) m_valid = false; /* Can't propagate further up than the root */ + --depth; + } + if (m_valid) { + /* Make sure the branch is big enough to place the new node. */ + if (m_branch.size() <= (size_t)depth) m_branch.resize((size_t)depth + 1); + assert(!m_branch[depth].has_value()); + m_branch[depth] = std::move(node); + } +} + +/*static*/ bool TaprootBuilder::ValidDepths(const std::vector<int>& depths) +{ + std::vector<bool> branch; + for (int depth : depths) { + // This inner loop corresponds to effectively the same logic on branch + // as what Insert() performs on the m_branch variable. Instead of + // storing a NodeInfo object, just remember whether or not there is one + // at that depth. + if (depth < 0 || (size_t)depth > TAPROOT_CONTROL_MAX_NODE_COUNT) return false; + if ((size_t)depth + 1 < branch.size()) return false; + while (branch.size() > (size_t)depth && branch[depth]) { + branch.pop_back(); + if (depth == 0) return false; + --depth; + } + if (branch.size() <= (size_t)depth) branch.resize((size_t)depth + 1); + assert(!branch[depth]); + branch[depth] = true; + } + // And this check corresponds to the IsComplete() check on m_branch. + return branch.size() == 0 || (branch.size() == 1 && branch[0]); +} + +TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version) +{ + assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0); + if (!IsValid()) return *this; + /* Construct NodeInfo object with leaf hash. */ + NodeInfo node; + node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256(); + /* Insert into the branch. */ + Insert(std::move(node), depth); + return *this; +} + +TaprootBuilder& TaprootBuilder::AddOmitted(int depth, const uint256& hash) +{ + if (!IsValid()) return *this; + /* Construct NodeInfo object with the hash directly, and insert it into the branch. */ + NodeInfo node; + node.hash = hash; + Insert(std::move(node), depth); + return *this; +} + +TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key) +{ + /* Can only call this function when IsComplete() is true. */ + assert(IsComplete()); + m_internal_key = internal_key; + auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash); + assert(ret.has_value()); + std::tie(m_output_key, std::ignore) = *ret; + return *this; +} + +WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; } diff --git a/src/script/standard.h b/src/script/standard.h index 12ab9979a8..d7ea5cef27 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_STANDARD_H #define BITCOIN_SCRIPT_STANDARD_H +#include <pubkey.h> #include <script/interpreter.h> #include <uint256.h> #include <util/hash_type.h> @@ -113,6 +114,12 @@ struct WitnessV0KeyHash : public BaseHash<uint160> }; CKeyID ToKeyID(const WitnessV0KeyHash& key_hash); +struct WitnessV1Taproot : public XOnlyPubKey +{ + WitnessV1Taproot() : XOnlyPubKey() {} + explicit WitnessV1Taproot(const XOnlyPubKey& xpk) : XOnlyPubKey(xpk) {} +}; + //! CTxDestination subtype to encode any future Witness version struct WitnessUnknown { @@ -142,11 +149,11 @@ struct WitnessUnknown * * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH) * * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH) * * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH) - * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???) - * (taproot outputs do not require their own type as long as no wallet support exists) + * * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR) + * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???) * A CTxDestination is the internal data type encoded in a bitcoin address */ -using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>; +using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); @@ -202,4 +209,82 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey); /** Generate a multisig script. */ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys); +/** Utility class to construct Taproot outputs from internal key and script tree. */ +class TaprootBuilder +{ +private: + /** Information associated with a node in the Merkle tree. */ + struct NodeInfo + { + /** Merkle hash of this node. */ + uint256 hash; + }; + /** Whether the builder is in a valid state so far. */ + bool m_valid = true; + + /** The current state of the builder. + * + * For each level in the tree, one NodeInfo object may be present. m_branch[0] + * is information about the root; further values are for deeper subtrees being + * explored. + * + * For every right branch taken to reach the position we're currently + * working in, there will be a (non-nullopt) entry in m_branch corresponding + * to the left branch at that level. + * + * For example, imagine this tree: - N0 - + * / \ + * N1 N2 + * / \ / \ + * A B C N3 + * / \ + * D E + * + * Initially, m_branch is empty. After processing leaf A, it would become + * {nullopt, nullopt, A}. When processing leaf B, an entry at level 2 already + * exists, and it would thus be combined with it to produce a level 1 one, + * resulting in {nullopt, N1}. Adding C and D takes us to {nullopt, N1, C} + * and {nullopt, N1, C, D} respectively. When E is processed, it is combined + * with D, and then C, and then N1, to produce the root, resulting in {N0}. + * + * This structure allows processing with just O(log n) overhead if the leaves + * are computed on the fly. + * + * As an invariant, there can never be nullopt entries at the end. There can + * also not be more than 128 entries (as that would mean more than 128 levels + * in the tree). The depth of newly added entries will always be at least + * equal to the current size of m_branch (otherwise it does not correspond + * to a depth-first traversal of a tree). m_branch is only empty if no entries + * have ever be processed. m_branch having length 1 corresponds to being done. + */ + std::vector<std::optional<NodeInfo>> m_branch; + + XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing. + XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */ + + /** Combine information about a parent Merkle tree node from its child nodes. */ + static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b); + /** Insert information about a node at a certain depth, and propagate information up. */ + void Insert(NodeInfo&& node, int depth); + +public: + /** Add a new script at a certain depth in the tree. Add() operations must be called + * in depth-first traversal order of binary tree. */ + TaprootBuilder& Add(int depth, const CScript& script, int leaf_version); + /** Like Add(), but for a Merkle node with a given hash to the tree. */ + TaprootBuilder& AddOmitted(int depth, const uint256& hash); + /** Finalize the construction. Can only be called when IsComplete() is true. + internal_key.IsFullyValid() must be true. */ + TaprootBuilder& Finalize(const XOnlyPubKey& internal_key); + + /** Return true if so far all input was valid. */ + bool IsValid() const { return m_valid; } + /** Return whether there were either no leaves, or the leaves form a Huffman tree. */ + bool IsComplete() const { return m_valid && (m_branch.size() == 0 || (m_branch.size() == 1 && m_branch[0].has_value())); } + /** Compute scriptPubKey (after Finalize()). */ + WitnessV1Taproot GetOutput(); + /** Check if a list of depths is legal (will lead to IsComplete()). */ + static bool ValidDepths(const std::vector<int>& depths); +}; + #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 7a122bd8b0..46f88c1282 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -318,15 +318,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic) BOOST_CHECK(!addr.IsBindAny()); BOOST_CHECK_EQUAL(addr.ToString(), link_local); - // TORv2 - BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion")); - BOOST_REQUIRE(addr.IsValid()); - BOOST_REQUIRE(addr.IsTor()); - - BOOST_CHECK(!addr.IsI2P()); - BOOST_CHECK(!addr.IsBindAny()); - BOOST_CHECK(addr.IsAddrV1Compatible()); - BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion"); + // TORv2, no longer supported + BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion")); // TORv3 const char* torv3_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"; @@ -470,10 +463,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v1) BOOST_CHECK_EQUAL(HexStr(s), "1a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b"); s.clear(); - BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion")); - s << addr; - BOOST_CHECK_EQUAL(HexStr(s), "fd87d87eeb43f1f2f3f4f5f6f7f8f9fa"); - s.clear(); + // TORv2, no longer supported + BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion")); BOOST_REQUIRE(addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")); s << addr; @@ -508,10 +499,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v2) BOOST_CHECK_EQUAL(HexStr(s), "02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b"); s.clear(); - BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion")); - s << addr; - BOOST_CHECK_EQUAL(HexStr(s), "030af1f2f3f4f5f6f7f8f9fa"); - s.clear(); + // TORv2, no longer supported + BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion")); BOOST_REQUIRE(addr.SetSpecial("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion")); s << addr; @@ -617,26 +606,14 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2) BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); - // Valid TORv2. + // TORv2, no longer supported. s << MakeSpan(ParseHex("03" // network type (TORv2) "0a" // address length "f1f2f3f4f5f6f7f8f9fa")); // address s >> addr; - BOOST_CHECK(addr.IsValid()); - BOOST_CHECK(addr.IsTor()); - BOOST_CHECK(addr.IsAddrV1Compatible()); - BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion"); + BOOST_CHECK(!addr.IsValid()); BOOST_REQUIRE(s.empty()); - // Invalid TORv2, with bogus length. - s << MakeSpan(ParseHex("03" // network type (TORv2) - "07" // address length - "00")); // address - BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, - HasReason("BIP155 TORv2 address with length 7 (should be 10)")); - BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input. - s.clear(); - // Valid TORv3. s << MakeSpan(ParseHex("04" // network type (TORv3) "20" // address length diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 3c47cf83e2..687d2f6747 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -44,13 +44,12 @@ static CNetAddr CreateInternal(const std::string& host) BOOST_AUTO_TEST_CASE(netbase_networks) { - BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE); - BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE); - BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); - BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); - BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION); - BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL); - + BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE); + BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE); + BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4); + BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6); + BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").GetNetwork() == NET_ONION); + BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL); } BOOST_AUTO_TEST_CASE(netbase_properties) @@ -73,7 +72,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties) BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343()); BOOST_CHECK(ResolveIP("FE80::").IsRFC4862()); BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052()); - BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor()); + BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").IsTor()); BOOST_CHECK(ResolveIP("127.0.0.1").IsLocal()); BOOST_CHECK(ResolveIP("::1").IsLocal()); BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable()); @@ -133,18 +132,6 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric) BOOST_CHECK(TestParse("[fd6c:88c0:8724:1:2:3:4:5]", "[fd6c:88c0:8724:1:2:3:4:5]:65535")); } -BOOST_AUTO_TEST_CASE(onioncat_test) -{ - // values from https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat - CNetAddr addr1(ResolveIP("5wyqrzbvrdsumnok.onion")); - CNetAddr addr2(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca")); - BOOST_CHECK(addr1 == addr2); - BOOST_CHECK(addr1.IsTor()); - BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion"); - BOOST_CHECK(addr1.IsRoutable()); - -} - BOOST_AUTO_TEST_CASE(embedded_test) { CNetAddr addr1(ResolveIP("1.2.3.4")); @@ -338,7 +325,6 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052 BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964 BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380 - BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6 @@ -481,10 +467,10 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters) BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret)); BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret)); - BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret)); - BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret)); - BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret)); - BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret)); + BOOST_CHECK(LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"s, ret)); + BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0"s, ret)); + BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com"s, ret)); + BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com\0"s, ret)); } // Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 44fbfa5970..a01d3fa03a 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -3,10 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <key.h> +#include <key_io.h> #include <script/script.h> #include <script/signingprovider.h> #include <script/standard.h> #include <test/util/setup_common.h> +#include <util/strencodings.h> #include <boost/test/unit_test.hpp> @@ -111,9 +113,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s.clear(); s << OP_1 << ToByteVector(uint256::ZERO); BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V1_TAPROOT); - BOOST_CHECK_EQUAL(solutions.size(), 2U); - BOOST_CHECK(solutions[0] == std::vector<unsigned char>{1}); - BOOST_CHECK(solutions[1] == ToByteVector(uint256::ZERO)); + BOOST_CHECK_EQUAL(solutions.size(), 1U); + BOOST_CHECK(solutions[0] == ToByteVector(uint256::ZERO)); // TxoutType::WITNESS_UNKNOWN s.clear(); @@ -379,4 +380,70 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) BOOST_CHECK(result == expected); } +BOOST_AUTO_TEST_CASE(script_standard_taproot_builder) +{ + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,2}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,1}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,0}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,1}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2}), false); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2,3,4,5,6,7,8,9,10,11,12,14,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,31,31,31,31,31,31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({128,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({129,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), false); + + XOnlyPubKey key_inner{ParseHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")}; + XOnlyPubKey key_1{ParseHex("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5")}; + XOnlyPubKey key_2{ParseHex("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")}; + CScript script_1 = CScript() << ToByteVector(key_1) << OP_CHECKSIG; + CScript script_2 = CScript() << ToByteVector(key_2) << OP_CHECKSIG; + uint256 hash_3 = uint256S("31fe7061656bea2a36aa60a2f7ef940578049273746935d296426dc0afd86b68"); + + TaprootBuilder builder; + BOOST_CHECK(builder.IsValid() && builder.IsComplete()); + builder.Add(2, script_2, 0xc0); + BOOST_CHECK(builder.IsValid() && !builder.IsComplete()); + builder.AddOmitted(2, hash_3); + BOOST_CHECK(builder.IsValid() && !builder.IsComplete()); + builder.Add(1, script_1, 0xc0); + BOOST_CHECK(builder.IsValid() && builder.IsComplete()); + builder.Finalize(key_inner); + BOOST_CHECK(builder.IsValid() && builder.IsComplete()); + BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/uint256.h b/src/uint256.h index fadf2320af..d4917d0eac 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -75,7 +75,7 @@ public: return &m_data[WIDTH]; } - unsigned int size() const + static constexpr unsigned int size() { return sizeof(m_data); } diff --git a/src/util/spanparsing.cpp b/src/util/spanparsing.cpp index 0f68254f2c..e2e2782bec 100644 --- a/src/util/spanparsing.cpp +++ b/src/util/spanparsing.cpp @@ -34,11 +34,11 @@ Span<const char> Expr(Span<const char>& sp) int level = 0; auto it = sp.begin(); while (it != sp.end()) { - if (*it == '(') { + if (*it == '(' || *it == '{') { ++level; - } else if (level && *it == ')') { + } else if (level && (*it == ')' || *it == '}')) { --level; - } else if (level == 0 && (*it == ')' || *it == ',')) { + } else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) { break; } ++it; diff --git a/src/validation.cpp b/src/validation.cpp index 4c861599fd..e6e6aadb17 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1401,8 +1401,9 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew) } // Same as InvalidChainFound, above, except not called directly from InvalidateBlock, -// which does its own setBlockIndexCandidates manageent. -void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) { +// which does its own setBlockIndexCandidates management. +void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state) +{ if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus |= BLOCK_FAILED_VALID; m_blockman.m_failed_blocks.insert(pindex); @@ -1819,8 +1820,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, // may have let in a block that violates the rule prior to updating the // software, and we would NOT be enforcing the rule here. Fully solving // upgrade from one software version to the next after a consensus rule - // change is potentially tricky and issue-specific (see RewindBlockIndex() - // for one general approach that was used for BIP 141 deployment). + // change is potentially tricky and issue-specific (see NeedsRedownload() + // for one approach that was used for BIP 141 deployment). // Also, currently the rule against blocks more than 2 hours in the future // is enforced in ContextualCheckBlockHeader(); we wouldn't want to // re-enforce that rule here (at least until we make it impossible for @@ -3605,8 +3606,11 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s // Therefore, the following critical section must include the CheckBlock() call as well. LOCK(cs_main); - // Ensure that CheckBlock() passes before calling AcceptBlock, as - // belt-and-suspenders. + // Skipping AcceptBlock() for CheckBlock() failures means that we will never mark a block as invalid if + // CheckBlock() fails. This is protective against consensus failure if there are any unknown forms of block + // malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and + // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is + // not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial. bool ret = CheckBlock(*block, state, chainparams.GetConsensus()); if (ret) { // Store to disk @@ -5079,24 +5083,20 @@ bool ChainstateManager::PopulateAndValidateSnapshot( LOCK(::cs_main); // Fake various pieces of CBlockIndex state: - // - // - nChainTx: so that we accurately report IBD-to-tip progress - // - nTx: so that LoadBlockIndex() loads assumed-valid CBlockIndex entries - // (among other things) - // - nStatus & BLOCK_OPT_WITNESS: so that RewindBlockIndex() doesn't zealously - // unwind the assumed-valid chain. - // CBlockIndex* index = nullptr; for (int i = 0; i <= snapshot_chainstate.m_chain.Height(); ++i) { index = snapshot_chainstate.m_chain[i]; + // Fake nTx so that LoadBlockIndex() loads assumed-valid CBlockIndex + // entries (among other things) if (!index->nTx) { index->nTx = 1; } + // Fake nChainTx so that GuessVerificationProgress reports accurately index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1; - // We need to fake this flag so that CChainState::RewindBlockIndex() - // won't try to rewind the entire assumed-valid chain on startup. + // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload() + // won't ask to rewind the entire assumed-valid chain on startup. if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) { index->nStatus |= BLOCK_OPT_WITNESS; } diff --git a/src/validation.h b/src/validation.h index 9c718b3d63..359a6c779f 100644 --- a/src/validation.h +++ b/src/validation.h @@ -555,7 +555,7 @@ enum class CoinsCacheSizeState * * Anything that is contingent on the current tip of the chain is stored here, * whereas block information and metadata independent of the current tip is - * kept in `BlockMetadataManager`. + * kept in `BlockManager`. */ class CChainState { diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 64ce09d1d1..aca52964ee 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -197,22 +197,14 @@ public: } return result; } - bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override - { + std::vector<std::string> getAddressReceiveRequests() override { LOCK(m_wallet->cs_wallet); - WalletBatch batch{m_wallet->GetDatabase()}; - return m_wallet->AddDestData(batch, dest, key, value); + return m_wallet->GetAddressReceiveRequests(); } - bool eraseDestData(const CTxDestination& dest, const std::string& key) override - { + bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) override { LOCK(m_wallet->cs_wallet); WalletBatch batch{m_wallet->GetDatabase()}; - return m_wallet->EraseDestData(batch, dest, key); - } - std::vector<std::string> getDestValues(const std::string& prefix) override - { - LOCK(m_wallet->cs_wallet); - return m_wallet->GetDestValues(prefix); + return m_wallet->SetAddressReceiveRequest(batch, dest, id, value); } void lockCoin(const COutPoint& output) override { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f270e1ad05..534c974178 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3735,6 +3735,7 @@ public: return obj; } + UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); } UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); } }; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 34bb29f79f..6a791748b4 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -385,11 +385,11 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests) CTxDestination dest = PKHash(); LOCK(m_wallet.cs_wallet); WalletBatch batch{m_wallet.GetDatabase()}; - m_wallet.AddDestData(batch, dest, "misc", "val_misc"); - m_wallet.AddDestData(batch, dest, "rr0", "val_rr0"); - m_wallet.AddDestData(batch, dest, "rr1", "val_rr1"); + m_wallet.SetAddressUsed(batch, dest, true); + m_wallet.SetAddressReceiveRequest(batch, dest, "0", "val_rr0"); + m_wallet.SetAddressReceiveRequest(batch, dest, "1", "val_rr1"); - auto values = m_wallet.GetDestValues("rr"); + auto values = m_wallet.GetAddressReceiveRequests(); BOOST_CHECK_EQUAL(values.size(), 2U); BOOST_CHECK_EQUAL(values[0], "val_rr0"); BOOST_CHECK_EQUAL(values[1], "val_rr1"); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9986a02fc1..4b6630de3c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -814,12 +814,11 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned CTxDestination dst; if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) { if (IsMine(dst)) { - if (used && !GetDestData(dst, "used", nullptr)) { - if (AddDestData(batch, dst, "used", "p")) { // p for "present", opposite of absent (null) + if (used != IsAddressUsed(dst)) { + if (used) { tx_destinations.insert(dst); } - } else if (!used && GetDestData(dst, "used", nullptr)) { - EraseDestData(batch, dst, "used"); + SetAddressUsed(batch, dst, used); } } } @@ -835,7 +834,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const if (!ExtractDestination(srctx->tx->vout[n].scriptPubKey, dest)) { return false; } - if (GetDestData(dest, "used", nullptr)) { + if (IsAddressUsed(dest)) { return true; } if (IsLegacy()) { @@ -843,15 +842,15 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const assert(spk_man != nullptr); for (const auto& keyid : GetAffectedKeys(srctx->tx->vout[n].scriptPubKey, *spk_man)) { WitnessV0KeyHash wpkh_dest(keyid); - if (GetDestData(wpkh_dest, "used", nullptr)) { + if (IsAddressUsed(wpkh_dest)) { return true; } ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest)); - if (GetDestData(sh_wpkh_dest, "used", nullptr)) { + if (IsAddressUsed(sh_wpkh_dest)) { return true; } PKHash pkh_dest(keyid); - if (GetDestData(pkh_dest, "used", nullptr)) { + if (IsAddressUsed(pkh_dest)) { return true; } } @@ -2387,45 +2386,45 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const return nTimeSmart; } -bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value) +bool CWallet::SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) { + const std::string key{"used"}; if (std::get_if<CNoDestination>(&dest)) return false; + if (!used) { + if (auto* data = util::FindKey(m_address_book, dest)) data->destdata.erase(key); + return batch.EraseDestData(EncodeDestination(dest), key); + } + + const std::string value{"1"}; m_address_book[dest].destdata.insert(std::make_pair(key, value)); return batch.WriteDestData(EncodeDestination(dest), key, value); } -bool CWallet::EraseDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key) -{ - if (!m_address_book[dest].destdata.erase(key)) - return false; - return batch.EraseDestData(EncodeDestination(dest), key); -} - void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) { m_address_book[dest].destdata.insert(std::make_pair(key, value)); } -bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const +bool CWallet::IsAddressUsed(const CTxDestination& dest) const { + const std::string key{"used"}; std::map<CTxDestination, CAddressBookData>::const_iterator i = m_address_book.find(dest); if(i != m_address_book.end()) { CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); if(j != i->second.destdata.end()) { - if(value) - *value = j->second; return true; } } return false; } -std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const +std::vector<std::string> CWallet::GetAddressReceiveRequests() const { + const std::string prefix{"rr"}; std::vector<std::string> values; for (const auto& address : m_address_book) { for (const auto& data : address.second.destdata) { @@ -2437,6 +2436,20 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const return values; } +bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) +{ + const std::string key{"rr" + id}; // "rr" prefix = "receive request" in destdata + CAddressBookData& data = m_address_book.at(dest); + if (value.empty()) { + if (!batch.EraseDestData(EncodeDestination(dest), key)) return false; + data.destdata.erase(key); + } else { + if (!batch.WriteDestData(EncodeDestination(dest), key, value)) return false; + data.destdata[key] = value; + } + return true; +} + std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string) { // Do some checking on wallet path. It should be either a: diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4a3e0f7054..788a901f95 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -479,19 +479,8 @@ public: bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = 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<std::string> 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}; @@ -705,6 +694,12 @@ public: bool DelAddressBook(const CTxDestination& address); + bool IsAddressUsed(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + std::vector<std::string> GetAddressReceiveRequests() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! signify that a particular wallet feature is now used. |