From 063b76b9247c1e019f580ce410118053a58b56ab Mon Sep 17 00:00:00 2001 From: "K.Matsuzawa" <49175372+k-matsuzawa@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:01:27 +0900 Subject: [PATCH] update to v0.3.0 (#24) update from cryptogarageinc v0.3.8 --- .github/workflows/check_pre-merge_master.yml | 3 +- .github/workflows/check_pre-merge_sprint.yml | 38 +- .github/workflows/code_scanner.yml | 5 - README.md | 73 +- cmake/CfdCommonOption.cmake | 1 + cmake/CfdWallyOption.cmake | 3 +- cmake/Cpp11Setting.cmake | 5 + external/CMakeLists.txt | 2 +- include/cfdcore/Makefile.srclist | 2 + include/cfdcore/cfdcore_address.h | 400 +- include/cfdcore/cfdcore_amount.h | 293 +- include/cfdcore/cfdcore_bytedata.h | 450 ++- include/cfdcore/cfdcore_coin.h | 75 +- include/cfdcore/cfdcore_common.h | 28 +- include/cfdcore/cfdcore_descriptor.h | 68 +- include/cfdcore/cfdcore_elements_address.h | 99 +- include/cfdcore/cfdcore_elements_script.h | 38 +- .../cfdcore/cfdcore_elements_transaction.h | 744 ++-- include/cfdcore/cfdcore_exception.h | 78 +- include/cfdcore/cfdcore_hdwallet.h | 522 ++- include/cfdcore/cfdcore_iterator.h | 28 +- include/cfdcore/cfdcore_json_mapping_base.h | 203 +- include/cfdcore/cfdcore_json_writer.h | 195 +- include/cfdcore/cfdcore_key.h | 187 +- include/cfdcore/cfdcore_logger_interface.h | 12 +- include/cfdcore/cfdcore_psbt.h | 731 ++++ include/cfdcore/cfdcore_schnorrsig.h | 89 +- include/cfdcore/cfdcore_script.h | 611 ++- include/cfdcore/cfdcore_taproot.h | 346 ++ include/cfdcore/cfdcore_transaction.h | 328 +- include/cfdcore/cfdcore_transaction_common.h | 416 +- include/cfdcore/cfdcore_util.h | 536 ++- package.json | 2 +- src/Makefile.srclist | 2 + src/cfdcore_address.cpp | 280 +- src/cfdcore_amount.cpp | 3 +- src/cfdcore_bytedata.cpp | 427 ++- src/cfdcore_coin.cpp | 21 +- src/cfdcore_descriptor.cpp | 263 +- src/cfdcore_elements_address.cpp | 23 +- src/cfdcore_elements_script.cpp | 3 +- src/cfdcore_elements_transaction.cpp | 134 +- src/cfdcore_hdwallet.cpp | 419 ++- src/cfdcore_key.cpp | 106 +- src/cfdcore_logger.cpp | 3 +- src/cfdcore_manager.cpp | 3 +- src/cfdcore_manager.h | 25 +- src/cfdcore_psbt.cpp | 3349 +++++++++++++++++ src/cfdcore_schnorrsig.cpp | 128 +- src/cfdcore_script.cpp | 365 +- src/cfdcore_secp256k1.cpp | 3 +- src/cfdcore_secp256k1.h | 53 +- src/cfdcore_taproot.cpp | 685 ++++ src/cfdcore_transaction.cpp | 415 +- src/cfdcore_transaction_common.cpp | 3 +- src/cfdcore_transaction_internal.h | 31 + src/cfdcore_util.cpp | 314 +- src/cfdcore_wally_util.h | 3 +- src/include/cfdcore/cfdcore_logger.h | 3 +- test/Makefile.srclist | 6 + test/test_address.cpp | 134 +- test/test_cryptoutil.cpp | 21 + test/test_descriptor.cpp | 126 + test/test_deserializer.cpp | 32 + .../test_elements_confidentialtransaction.cpp | 70 + test/test_elements_confidentialtxin.cpp | 72 +- test/test_extprivkey.cpp | 19 + test/test_extpubkey.cpp | 10 + test/test_hashutil.cpp | 76 +- test/test_hdwallet.cpp | 21 + test/test_keydata.cpp | 789 ++++ test/test_privkey.cpp | 3 + test/test_psbt.cpp | 1228 ++++++ test/test_pubkey.cpp | 7 + test/test_schnorrsig.cpp | 31 + test/test_script.cpp | 70 + test/test_scriptbuilder.cpp | 66 + test/test_scripthash.cpp | 1 - test/test_scriptoperator.cpp | 1 - test/test_scriptutil.cpp | 102 +- test/test_serializer.cpp | 25 + test/test_sighashtype.cpp | 2 + test/test_taproot_merkletree.cpp | 559 +++ test/test_taproot_util.cpp | 291 ++ test/test_transaction.cpp | 119 + test/test_txin_txinreference.cpp | 22 +- tools/create_opsuccess.js | 47 + tools/format.bat | 7 + tools/format.sh | 4 + 89 files changed, 15130 insertions(+), 2506 deletions(-) create mode 100644 include/cfdcore/cfdcore_psbt.h create mode 100644 include/cfdcore/cfdcore_taproot.h create mode 100644 src/cfdcore_psbt.cpp create mode 100644 src/cfdcore_taproot.cpp create mode 100644 src/cfdcore_transaction_internal.h create mode 100644 test/test_deserializer.cpp create mode 100644 test/test_keydata.cpp create mode 100644 test/test_psbt.cpp create mode 100644 test/test_serializer.cpp create mode 100644 test/test_taproot_merkletree.cpp create mode 100644 test/test_taproot_util.cpp create mode 100644 tools/create_opsuccess.js create mode 100644 tools/format.bat create mode 100755 tools/format.sh diff --git a/.github/workflows/check_pre-merge_master.yml b/.github/workflows/check_pre-merge_master.yml index 397d2e79..9dae4b05 100644 --- a/.github/workflows/check_pre-merge_master.yml +++ b/.github/workflows/check_pre-merge_master.yml @@ -40,7 +40,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-10.15, macos-11.0] + os: [macos-10.15] +# os: [macos-10.15, macos-11.0] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/check_pre-merge_sprint.yml b/.github/workflows/check_pre-merge_sprint.yml index 7b537f4f..43ed6b79 100644 --- a/.github/workflows/check_pre-merge_sprint.yml +++ b/.github/workflows/check_pre-merge_sprint.yml @@ -40,7 +40,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-10.15, macos-11.0] + os: [macos-10.15] + # os: [macos-10.15, macos-11.0] steps: - uses: actions/checkout@v2 @@ -104,6 +105,41 @@ jobs: name: output-lcov-cfdcore-${{ matrix.os }} path: ./build/lcov_cfdcore_output.zip + ubuntu-valgrind: + name: valgrind-ubuntu + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04] + shared: [on] + + steps: + - uses: actions/checkout@v2 + - name: dump version + run: | + cmake --version + gcc --version + - name: ubuntu-apt-install + run: | + cat /etc/os-release + sudo apt-get update + sudo apt-get install -y valgrind + - name: cmake-build + run: | + cmake --version + cmake -S . -B build -G "Unix Makefiles" + cmake -DENABLE_SHARED=${{ matrix.shared }} -DCMAKE_BUILD_TYPE=Debug -DTARGET_RPATH=./build/Debug --build build + cmake --build build --config Debug --parallel 4 + - name: valgrind + run: | + # --valgrind-stacksize=1048576 --num-callers=12 + valgrind -v --tool=memcheck --leak-check=full --valgrind-stacksize=10485760 --log-file=./valgrind.log --time-stamp=yes ./build/Debug/cfdcore_test + - name: upload coverage + uses: actions/upload-artifact@v1 + with: + name: valgrind-log + path: ./valgrind.log + doxygen-ubuntu: name: doxygen-check runs-on: ubuntu-18.04 diff --git a/.github/workflows/code_scanner.yml b/.github/workflows/code_scanner.yml index 97a986d0..43048bdb 100644 --- a/.github/workflows/code_scanner.yml +++ b/.github/workflows/code_scanner.yml @@ -14,11 +14,6 @@ on: - '**.h' - '**/code_scanner.yml' - '**/external_project_local_setting.config' - pull_request: - branches: - - master - - develop - - features/sprint* jobs: analyze-CodeQL: diff --git a/README.md b/README.md index 028afa3e..38501d2f 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,53 @@ # Crypto Finance Development Kit Core (CFD-CORE) -core moduels for cfd libraries - - +This library is development kit for crypto finance application. +Useful when developing applications for cryptocurrencies. + +### Target Network + +- Bitcoin +- Liquid Network + +### Support function by cfd-core + +- Bitcoin + - Bitcoin Script (builder, viewer) + - Transaction + - PSBT (v0) + - ECDSA Pubkey/Privkey (TweakAdd/Mul, Negate, Sign, Verify) + - BIP32, BIP39 + - Output Descriptor (contains miniscript parser) + - Schnorr/Taproot + - Bitcoin Address (Segwit-v0, Segwit-v1, P2PKH/P2SH) +- Liquid Network + - Confidential Transaction + - Blind, Unblind + - Issuance, Reissuance + - PegIn, PegOut + - Confidential Address + +### Libraries for each language + +- C++ : cfd-core + - Core library. Definition base class. +- C/C++ : cfd + - Extend the cfd-core library. Defines the C language API and extension classes. +- Libraries to link cfd library: + - JavaScript : cfd-js + - WebAssembly : cfd-js-wasm + - Python : cfd-python + - C# : cfd-csharp + - Go : cfd-go + - Rust : cfd-rust ## Dependencies - C/C++ Compiler -  - can compile c++11 + - can compile c++11 - CMake (3.14.3 or higher) - When using npm scripts and cmake-js - node.js (stable version) @@ -21,6 +57,7 @@ core moduels for cfd libraries ### Windows download and install files. + - [CMake](https://cmake.org/) (3.14.3 or higher) - Compiler or development environment (One of the following) - MSVC @@ -55,7 +92,7 @@ apt-get install -y build-essential cmake python nodejs ``` cmake version 3.14.2 or lower, download from website and install cmake. -(https://cmake.org/download/) +() --- @@ -88,7 +125,7 @@ cmake -D ENABLE_SHARED=1 -DCMAKE_BUILD_TYPE=Release --build build cmake --build build ``` -**CMake options** +### CMake options - `-DENABLE_ELEMENTS`: Enable functionalies for elements sidechain. [ON/OFF] (default:ON) - `-DENABLE_SHARED`: Enable building a shared library. [ON/OFF] (default:OFF) @@ -118,6 +155,7 @@ npm cmake_make_install ``` cmake version is 3.15 or higher: + ```Shell npm cmake_install (Enter the password when prompted to use the sudo command.) @@ -135,6 +173,7 @@ cd build && sudo ninja install cmake version is 3.15 or higher: `cmake --install build` ### uninstall + ```Shell (uninstall by using makefile) cd build && sudo make uninstall @@ -167,6 +206,7 @@ npm run ctest ### using library - [libwally-core](https://github.com/cryptogarageinc/libwally-core/tree/cfd-develop) (forked from [ElementsProject/libwally-core](https://github.com/ElementsProject/libwally-core)) + - [secp256k1-zkp](https://github.com/cryptogarageinc/secp256k1-zkp/tree/cfd-develop) (forked from [ElementsProject/secp256k1-zkp](https://github.com/ElementsProject/secp256k1-zkp)) - [univalue](https://github.com/jgarzik/univalue) (for JSON encoding and decoding) - [googletest](https://github.com/google/googletest) (for testing) @@ -199,26 +239,29 @@ npm run ctest ## Note -### Git connection: +### Git connection Git repository connections default to HTTPS. However, depending on the connection settings of GitHub, you may only be able to connect via SSH. As a countermeasure, forcibly establish SSH connection by setting `CFD_CMAKE_GIT_SSH=1` in the environment variable. - Windows: (On the command line. Or set from the system setting screen.) -``` + +```Bat set CFD_CMAKE_GIT_SSH=1 ``` - MacOS & Linux(Ubuntu): -``` + +```Shell export CFD_CMAKE_GIT_SSH=1 ``` -### Ignore git update for CMake External Project: +### Ignore git update for CMake External Project Depending on your git environment, you may get the following error when checking out external: -``` + +```Shell Performing update step for 'libwally-core-download' Current branch cmake_build is up to date. No stash entries found. @@ -237,11 +280,13 @@ This phenomenon is due to the `git update` related command. Please set an environment variable that skips update processing. - Windows: (On the command line. Or set from the system setting screen.) -``` + +```Bat set CFD_CMAKE_GIT_SKIP_UPDATE=1 ``` - MacOS & Linux(Ubuntu): -``` + +```Shell export CFD_CMAKE_GIT_SKIP_UPDATE=1 ``` diff --git a/cmake/CfdCommonOption.cmake b/cmake/CfdCommonOption.cmake index 73a61cef..848197f8 100644 --- a/cmake/CfdCommonOption.cmake +++ b/cmake/CfdCommonOption.cmake @@ -6,6 +6,7 @@ endif() option(ENABLE_ELEMENTS "enable elements code (ON or OFF. default:ON)" ON) option(ENABLE_TESTS "enable code tests (ON or OFF. default:ON)" ON) option(ENABLE_EMSCRIPTEN "enable EMSCRIPTEN (ON or OFF. default:OFF)" OFF) +option(STD_CPP_VERSION "c++ version (11/14/17. default:11)" "11") # use "cmake -DCMAKE_BUILD_TYPE=Debug" or "cmake-js -D" # option(ENABLE_DEBUG "enable debugging (ON or OFF. default:OFF)" OFF) diff --git a/cmake/CfdWallyOption.cmake b/cmake/CfdWallyOption.cmake index 63df95d0..8dc2cade 100644 --- a/cmake/CfdWallyOption.cmake +++ b/cmake/CfdWallyOption.cmake @@ -6,4 +6,5 @@ option(ENABLE_JS_WRAPPER "enable the Javascript interface wrappers (ON or OFF. d else() set(ENABLE_JS_WRAPPER OFF) endif() -set(GENERATE_WALLY ON) +option(GENERATE_WALLY "generate the wally.xxx library (ON or OFF. default:ON)" ON) +option(EXCLUDE_WALLYCORE_LIB "exclude wallycore lib (ON or OFF. default:OFF)" OFF) diff --git a/cmake/Cpp11Setting.cmake b/cmake/Cpp11Setting.cmake index c6beeab8..b92ed3e8 100644 --- a/cmake/Cpp11Setting.cmake +++ b/cmake/Cpp11Setting.cmake @@ -13,6 +13,11 @@ if(${USE_EMSCRIPTEN}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") endif() +if(${STD_CPP_VERSION}) +set(CMAKE_CXX_STANDARD ${STD_CPP_VERSION}) +message(STATUS "[STD_CPP_VERSION] ${STD_CPP_VERSION}") +else() set(CMAKE_CXX_STANDARD 11) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 45041394..3317a148 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -55,7 +55,7 @@ if(LIBWALLY_TARGET_VERSION) set(LIBWALLY_TARGET_TAG ${LIBWALLY_TARGET_VERSION}) message(STATUS "[external project local] libwally-core target=${LIBWALLY_TARGET_VERSION}") else() -set(LIBWALLY_TARGET_TAG refs/tags/cfd-0.2.4) +set(LIBWALLY_TARGET_TAG refs/tags/cfd-0.3.4) endif() if(${USE_GIT_SSH}) diff --git a/include/cfdcore/Makefile.srclist b/include/cfdcore/Makefile.srclist index 0f78b3d7..0a262061 100644 --- a/include/cfdcore/Makefile.srclist +++ b/include/cfdcore/Makefile.srclist @@ -20,10 +20,12 @@ CFDCORE_PKGINCLUDE_FILES = \ cfdcore_key.h \ cfdcore_script.h \ cfdcore_descriptor.h \ + cfdcore_psbt.h \ cfdcore_exception.h \ cfdcore_iterator.h \ cfdcore_logger_interface.h \ cfdcore_schnorrsig.h \ cfdcore_ecdsa_adaptor.h \ + cfdcore_taproot.h \ $(CFDCORE_ELEMENTS_PKGINCLUDE_FILES) diff --git a/include/cfdcore/cfdcore_address.h b/include/cfdcore/cfdcore_address.h index 556b6c0a..c7d28813 100644 --- a/include/cfdcore/cfdcore_address.h +++ b/include/cfdcore/cfdcore_address.h @@ -2,7 +2,7 @@ /** * @file cfdcore_address.h * - * @brief Addressクラス定義 + * @brief Bitcoin address definition file. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ADDRESS_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ADDRESS_H_ @@ -13,7 +13,9 @@ #include "cfdcore/cfdcore_common.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" namespace cfd { namespace core { @@ -45,69 +47,68 @@ constexpr const char* const kPrefixBlindBech32Hrp = "blech32"; /** * @class AddressFormatData - * @brief \~japanese Address format dataクラス - * \~english class for showing format data of address + * @brief class for showing format data of address */ class CFD_CORE_EXPORT AddressFormatData { public: /** - * @brief コンストラクタ + * @brief constructor. */ AddressFormatData(); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] default_format_name default format name */ explicit AddressFormatData(const std::string& default_format_name); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] map_data prefix setting map */ explicit AddressFormatData( const std::map& map_data); /** - * @brief 文字列情報を取得する. + * @brief Get string value. * @param[in] key mapping key * @return value */ std::string GetString(const std::string& key) const; /** - * @brief 文字列情報を数値型に変換して取得する. + * @brief Get numeric value from string. * @param[in] key mapping key * @return uint32_t value */ uint32_t GetValue(const std::string& key) const; /** - * @brief P2pkhのprefix値を取得する. + * @brief Get P2pkh prefix. * @return P2pkh prefix */ uint8_t GetP2pkhPrefix() const; /** - * @brief P2shのprefix値を取得する. + * @brief Get P2sh prefix. * @return P2sh prefix */ uint8_t GetP2shPrefix() const; /** - * @brief bech32のhrpを取得する. + * @brief Get hrp on bech32. * @return Bech32 hrp */ std::string GetBech32Hrp() const; /** - * @brief netTypeを取得する. + * @brief Get network type. * @return network type */ NetType GetNetType() const; /** - * @brief json文字列情報からAddress format dataを取得する. + * @brief Get Address format data from json string. * @param[in] json_data json string * @return Address format data */ static AddressFormatData ConvertFromJson(const std::string& json_data); /** - * @brief json文字列情報からAddress format data一覧を取得する. + * @brief Get Address format data list from json string. * @param[in] json_data json string * @return Address format data list */ @@ -119,76 +120,64 @@ class CFD_CORE_EXPORT AddressFormatData { }; /** - * @brief Bitcoin のデフォルトのアドレスフォーマットリストを取得する. - * @return Bitcoinデフォルトのアドレスフォーマットリスト + * @brief Get address format list by Bitcoin default. + * @return Address format list by Bitcoin default. */ CFD_CORE_API std::vector GetBitcoinAddressFormatList(); /** * @typedef AddressType - * @brief Address種別の定義 + * @brief Address type. */ enum AddressType { - kP2shAddress = 1, //!< Legacy address (Script Hash) - kP2pkhAddress, //!< Legacy address (PublicKey Hash) - kP2wshAddress, //!< Native segwit address (Script Hash) - kP2wpkhAddress, //!< Native segwit address (PublicKey Hash) - kP2shP2wshAddress, //!< P2sh wrapped address (Script Hash) - kP2shP2wpkhAddress //!< P2sh wrapped address (Pubkey Hash) -}; - -/** - * @typedef WitnessVersion - * @brief Witnessバージョンの定義 - */ -enum WitnessVersion { - kVersionNone = -1, //!< Missing WitnessVersion - kVersion0 = 0, //!< version 0 - kVersion1, //!< version 1 (for future use) - kVersion2, //!< version 2 (for future use) - kVersion3, //!< version 3 (for future use) - kVersion4, //!< version 4 (for future use) - kVersion5, //!< version 5 (for future use) - kVersion6, //!< version 6 (for future use) - kVersion7, //!< version 7 (for future use) - kVersion8, //!< version 8 (for future use) - kVersion9, //!< version 9 (for future use) - kVersion10, //!< version 10 (for future use) - kVersion11, //!< version 11 (for future use) - kVersion12, //!< version 12 (for future use) - kVersion13, //!< version 13 (for future use) - kVersion14, //!< version 14 (for future use) - kVersion15, //!< version 15 (for future use) - kVersion16 //!< version 16 (for future use) + kP2shAddress = 1, //!< Legacy address (Script Hash) + kP2pkhAddress, //!< Legacy address (PublicKey Hash) + kP2wshAddress, //!< Native segwit address (Script Hash) + kP2wpkhAddress, //!< Native segwit address (PublicKey Hash) + kP2shP2wshAddress, //!< P2sh wrapped address (Script Hash) + kP2shP2wpkhAddress, //!< P2sh wrapped address (Pubkey Hash) + kTaprootAddress, //!< Taproot (segwit v1) address + kWitnessUnknown //!< witness unknown address }; /** * @class Address - * @brief アドレスの生成クラス + * @brief address class. */ class CFD_CORE_EXPORT Address { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor. */ Address(); + /** + * @brief copy constructor. + * @param[in] object object + */ + Address(const Address& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Address& operator=(const Address& object); /** - * @brief コンストラクタ(hex文字列からの復元) - * @param[in] address_string アドレス文字列 + * @brief Constructor. (for string) + * @param[in] address_string address string */ explicit Address(const std::string& address_string); /** - * @brief コンストラクタ(hex文字列からの復元) - * @param[in] address_string アドレス文字列 + * @brief Constructor. (for string) + * @param[in] address_string address string * @param[in] network_parameters network parameter list */ explicit Address( const std::string& address_string, const std::vector& network_parameters); /** - * @brief コンストラクタ(hex文字列からの復元) - * @param[in] address_string アドレス文字列 + * @brief Constructor. (for string) + * @param[in] address_string address string * @param[in] network_parameter network parameter */ explicit Address( @@ -196,20 +185,20 @@ class CFD_CORE_EXPORT Address { const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2PKH用) + * @brief Constructor. (for P2PKH) * @param[in] type NetType * @param[in] pubkey PublicKey */ Address(NetType type, const Pubkey& pubkey); /** - * @brief コンストラクタ(P2PKH用) + * @brief Constructor. (for P2PKH) * @param[in] type NetType * @param[in] pubkey PublicKey * @param[in] prefix p2pkh prefix */ Address(NetType type, const Pubkey& pubkey, uint8_t prefix); /** - * @brief コンストラクタ(P2PKH用) + * @brief Constructor. (for P2PKH) * @param[in] type NetType * @param[in] pubkey PublicKey * @param[in] network_parameter network parameter @@ -218,7 +207,7 @@ class CFD_CORE_EXPORT Address { NetType type, const Pubkey& pubkey, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2PKH用) + * @brief Constructor. (for P2PKH) * @param[in] type NetType * @param[in] pubkey PublicKey * @param[in] network_parameters network parameter list @@ -228,16 +217,16 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief コンストラクタ(P2WPKH用) + * @brief Constructor. (for P2WPKH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] pubkey PublicKey */ Address(NetType type, WitnessVersion witness_ver, const Pubkey& pubkey); /** - * @brief コンストラクタ(P2WPKH用) + * @brief Constructor. (for P2WPKH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] pubkey PublicKey * @param[in] bech32_hrp bech32 hrp */ @@ -245,9 +234,9 @@ class CFD_CORE_EXPORT Address { NetType type, WitnessVersion witness_ver, const Pubkey& pubkey, const std::string& bech32_hrp); /** - * @brief コンストラクタ(P2WPKH用) + * @brief Constructor. (for P2WPKH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] pubkey PublicKey * @param[in] network_parameter network parameter */ @@ -255,9 +244,9 @@ class CFD_CORE_EXPORT Address { NetType type, WitnessVersion witness_ver, const Pubkey& pubkey, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2WPKH用) + * @brief Constructor. (for P2WPKH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] pubkey PublicKey * @param[in] network_parameters network parameter list */ @@ -266,20 +255,20 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief コンストラクタ(P2SH用) + * @brief Constructor. (for P2SH) * @param[in] type NetType * @param[in] redeem_script Redeem Script */ Address(NetType type, const Script& redeem_script); /** - * @brief コンストラクタ(P2SH用) + * @brief Constructor. (for P2SH) * @param[in] type NetType * @param[in] redeem_script Redeem Script * @param[in] prefix p2sh prefix */ Address(NetType type, const Script& redeem_script, uint8_t prefix); /** - * @brief コンストラクタ(P2SH用) + * @brief Constructor. (for P2SH) * @param[in] type NetType * @param[in] redeem_script Redeem Script * @param[in] network_parameter network parameter @@ -288,7 +277,7 @@ class CFD_CORE_EXPORT Address { NetType type, const Script& redeem_script, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2SH用) + * @brief Constructor. (for P2SH) * @param[in] type NetType * @param[in] redeem_script Redeem Script * @param[in] network_parameters network parameter list @@ -298,17 +287,17 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief コンストラクタ(P2WSH用) + * @brief Constructor. (for P2WSH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] redeem_script Redeem Script */ Address( NetType type, WitnessVersion witness_ver, const Script& redeem_script); /** - * @brief コンストラクタ(P2WSH用) + * @brief Constructor. (for P2WSH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] redeem_script Redeem Script * @param[in] bech32_hrp bech32 hrp */ @@ -316,9 +305,9 @@ class CFD_CORE_EXPORT Address { NetType type, WitnessVersion witness_ver, const Script& redeem_script, const std::string& bech32_hrp); /** - * @brief コンストラクタ(P2WSH用) + * @brief Constructor. (for P2WSH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] redeem_script Redeem Script * @param[in] network_parameter network parameter */ @@ -326,9 +315,9 @@ class CFD_CORE_EXPORT Address { NetType type, WitnessVersion witness_ver, const Script& redeem_script, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2WSH用) + * @brief Constructor. (for P2WSH) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン + * @param[in] witness_ver Witness version * @param[in] redeem_script Redeem Script * @param[in] network_parameters network parameter list */ @@ -337,27 +326,112 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief コンストラクタ(P2PKH/P2SH用。AddressType明示) + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] pubkey PublicKey + */ + Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] pubkey PublicKey + * @param[in] bech32_hrp bech32 hrp + */ + Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const std::string& bech32_hrp); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] pubkey PublicKey + * @param[in] network_parameter network parameter + */ + Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const AddressFormatData& network_parameter); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] pubkey PublicKey + * @param[in] network_parameters network parameter list + */ + Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const std::vector& network_parameters); + + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] tree tapscript tree + * @param[in] internal_pubkey internal PublicKey + */ + Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] tree tapscript tree + * @param[in] internal_pubkey internal PublicKey + * @param[in] bech32_hrp bech32 hrp + */ + Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, const std::string& bech32_hrp); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] tree tapscript tree + * @param[in] internal_pubkey internal PublicKey + * @param[in] network_parameter network parameter + */ + Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const AddressFormatData& network_parameter); + /** + * @brief Constructor. (for Taproot) + * @param[in] type NetType + * @param[in] witness_ver Witness version + * @param[in] tree tapscript tree + * @param[in] internal_pubkey internal PublicKey + * @param[in] network_parameters network parameter list + */ + Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const std::vector& network_parameters); + + /** + * @brief Constructor. (for P2PKH/P2SH) * @param[in] type NetType - * @param[in] addr_type 種別 - * @param[in] hash ハッシュ化済みの値 + * @param[in] addr_type Address type + * @param[in] hash hashed data */ Address(NetType type, AddressType addr_type, const ByteData160& hash); /** - * @brief コンストラクタ(P2PKH/P2SH用。AddressType明示) + * @brief Constructor. (for P2PKH/P2SH) * @param[in] type NetType - * @param[in] addr_type 種別 - * @param[in] hash ハッシュ化済みの値 + * @param[in] addr_type Address type + * @param[in] hash hashed data * @param[in] network_parameter network parameter */ Address( NetType type, AddressType addr_type, const ByteData160& hash, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(P2PKH/P2SH用。AddressType明示) + * @brief Constructor. (for P2PKH/P2SH) * @param[in] type NetType - * @param[in] addr_type 種別 - * @param[in] hash ハッシュ化済みの値 + * @param[in] addr_type Address type + * @param[in] hash hashed data * @param[in] network_parameters network parameter list */ Address( @@ -365,27 +439,27 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief コンストラクタ(ハッシュ化済みの値用) + * @brief Constructor. (for hashed data) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン - * @param[in] hash ハッシュ化済みの値 + * @param[in] witness_ver Witness version + * @param[in] hash hashed data */ Address(NetType type, WitnessVersion witness_ver, const ByteData& hash); /** - * @brief コンストラクタ(ハッシュ化済みの値用) + * @brief Constructor. (for hashed data) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン - * @param[in] hash ハッシュ化済みの値 + * @param[in] witness_ver Witness version + * @param[in] hash hashed data * @param[in] network_parameter network parameter */ Address( NetType type, WitnessVersion witness_ver, const ByteData& hash, const AddressFormatData& network_parameter); /** - * @brief コンストラクタ(ハッシュ化済みの値用) + * @brief Constructor. (for hashed data) * @param[in] type NetType - * @param[in] witness_ver Witnessバージョン - * @param[in] hash ハッシュ化済みの値 + * @param[in] witness_ver Witness version + * @param[in] hash hashed data * @param[in] network_parameters network parameter list */ Address( @@ -393,92 +467,103 @@ class CFD_CORE_EXPORT Address { const std::vector& network_parameters); /** - * @brief アドレスのhex文字列を取得する. - * @return アドレス文字列 + * @brief Get address string. + * @return address string */ std::string GetAddress() const; /** - * @brief AddressのNetTypeを取得する. + * @brief Get network type. * @return NetType */ NetType GetNetType() const { return type_; } /** - * @brief Address種別を取得する. - * @return Address種別 + * @brief Get address type. + * @return Address type */ AddressType GetAddressType() const { return addr_type_; } /** - * @brief Witnessバージョンを取得する. - * @return Witnessバージョン + * @brief Get witness version. + * @return Witness version */ WitnessVersion GetWitnessVersion() const { return witness_ver_; } /** - * @brief アドレスHashを取得する. - * @return アドレスHashのByteDataインスタンス + * @brief Get address Hash. + * @return address hash */ ByteData GetHash() const { return hash_; } /** - * @brief PublicKeyを取得する. - * @return Pubkeyオブジェクト + * @brief Get PublicKey. + * @return Pubkey */ Pubkey GetPubkey() const { return pubkey_; } /** - * @brief Redeem Scriptを取得する. - * @return Scriptオブジェクト + * @brief Get Schnorr PublicKey. + * @return Schnorr Pubkey + */ + SchnorrPubkey GetSchnorrPubkey() const { return schnorr_pubkey_; } + /** + * @brief Get taproot script tree. + * @return taproot script tree + */ + TaprootScriptTree GetScriptTree() const { return script_tree_; } + + /** + * @brief Get Redeem Script. + * @return Script */ Script GetScript() const { return redeem_script_; } /** - * @brief AddressFormatDataを取得する. - * @return AddressFormatDataオブジェクト + * @brief Get AddressFormatData. + * @return AddressFormatData */ AddressFormatData GetAddressFormatData() const { return format_data_; } /** - * @brief LockingScriptを取得する + * @brief Get LockingScript * @return locking script */ Script GetLockingScript() const; private: /** - * @brief P2SH Addressの情報を算出する. + * @brief calculate P2SH Address * @param[in] prefix p2sh prefix */ void CalculateP2SH(uint8_t prefix = 0); /** - * @brief P2SH Addressの情報を算出する. - * @param[in] hash_data ハッシュ化済みRedeem script + * @brief calculate P2SH Address. + * @param[in] hash_data RedeemScript hash * @param[in] prefix p2sh prefix */ void CalculateP2SH(const ByteData160& hash_data, uint8_t prefix = 0); /** - * @brief P2PKH Addressの情報を算出する. + * @brief calculate P2PKH Address * @param[in] prefix p2pkh prefix */ void CalculateP2PKH(uint8_t prefix = 0); /** - * @brief P2PKH Addressの情報を算出する. - * @param[in] hash_data ハッシュ化済みPubkey + * @brief calculate P2PKH Address + * @param[in] hash_data Pubkey hash * @param[in] prefix p2pkh prefix */ void CalculateP2PKH(const ByteData160& hash_data, uint8_t prefix = 0); /** - * @brief P2WSH Addressの情報を算出する. + * @brief calculate P2WSH Address * @param[in] bech32_hrp bech32 hrp */ void CalculateP2WSH(const std::string& bech32_hrp = ""); /** - * @brief P2WSH Addressの情報を算出する. - * @param[in] hash_data ハッシュ化済みRedeemScript + * @brief calculate P2WSH Address + * @param[in] hash_data RedeemScript hash * @param[in] bech32_hrp bech32 hrp */ void CalculateP2WSH( @@ -486,93 +571,88 @@ class CFD_CORE_EXPORT Address { const std::string& bech32_hrp = ""); /** - * @brief P2WPKH Address算出() + * @brief calculate P2WPKH Address * @param[in] bech32_hrp bech32 hrp */ void CalculateP2WPKH(const std::string& bech32_hrp = ""); /** - * @brief P2WPKH Addressの情報を算出する. - * @param[in] hash_data ハッシュ化済みPubkey + * @brief calculate P2WPKH Address + * @param[in] hash_data pubkey hash * @param[in] bech32_hrp bech32 hrp */ void CalculateP2WPKH( const ByteData160& hash_data, // pubkey hash const std::string& bech32_hrp = ""); - /* Segwit Address Format - * - * 例:bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 - * - * "bc"or"tb" - * :Human-readable part(bc=mainnet/tb=testnet) - * "1" :Separator 1固定 - * "v8f3t4" - * :checksum - * - * "qw508d6qejxtdg4y5r3zarvary0c5xw7k"をbase32Decode - * -> "0014751e76e8199196d454941c45d1b3a323f1433bd6" - * "00" :witness version - * "14" :data長(P2WPKHは20byte/P2WSHは32byte) - * "751e76e8199196d454941c45d1b3a323f1433bd6" - * :witness program(PubkeyHash or ScriptHash) - */ - /** - * @brief Hex文字列をdecodeする. - * @param[in] bs58 デコードするアドレスのbase58文字列 + /** + * @brief calculate Taproot Address + * @param[in] bech32_hrp bech32 hrp + */ + void CalculateTaproot(const std::string& bech32_hrp = ""); + /** + * @brief calculate Bech32m Address + * @param[in] hash_data hash data + * @param[in] bech32_hrp bech32 hrp + */ + void CalculateBech32m( + const ByteData& hash_data, // hash data + const std::string& bech32_hrp = ""); + + /** + * @brief decode address from address string. + * @param[in] address_string address string * @param[in] network_parameters network parameter list */ void DecodeAddress( - std::string bs58, // LF + std::string address_string, // LF const std::vector* network_parameters); /** - * @brief NetType設定用のWrapper関数. + * @brief set network type. * @param[in] format_data Address format data */ void SetNetType(const AddressFormatData& format_data); /** - * @brief AddressType設定用のWrapper関数. + * @brief set address type. * @param[in] addr_type Address type */ void SetAddressType(AddressType addr_type); /** - * \~english * @brief Get AddressFormatData with NetType from format list. * @param[in] network_parameters Address format data list - * \~japanese - * @brief Address format data一覧から指定NetTypeの情報を取得する. - * @param[in] network_parameters AddressFormatData一覧 - * \~ * @param[in] type NetType * @return Address format data */ static AddressFormatData GetTargetFormatData( const std::vector& network_parameters, NetType type); - //! アドレスのNetType + //! address's NetType NetType type_; - //! アドレス種別 + //! address type AddressType addr_type_; - //! Witnessバージョン + //! Witness version WitnessVersion witness_ver_; - //! アドレス文字列 + //! address string std::string address_; - //! アドレスHash + //! address Hash ByteData hash_; //! PublicKey Pubkey pubkey_; + SchnorrPubkey schnorr_pubkey_; //!< Schnorr PublicKey + TaprootScriptTree script_tree_; //!< Taproot ScriptTree + //! Redeem Script Script redeem_script_; - //! チェックサム + //! checksum uint8_t checksum_[4]; //! address prefix format data diff --git a/include/cfdcore/cfdcore_amount.h b/include/cfdcore/cfdcore_amount.h index feec9a12..ea171ab8 100644 --- a/include/cfdcore/cfdcore_amount.h +++ b/include/cfdcore/cfdcore_amount.h @@ -2,7 +2,7 @@ /** * @file cfdcore_amount.h * - * @brief Amount関連クラス定義 + * @brief The amount related class definition. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_AMOUNT_H_ @@ -14,35 +14,31 @@ #include "cfdcore/cfdcore_common.h" /** - * @brief cfd名前空間 + * @brief cfd namespace */ namespace cfd { /** - * @brief cfd::core名前空間 + * @brief cfd::core namespace */ namespace core { -//! bitcoinとsatoshi単位の変換に用いる因数(10^8) +//! Factors used to convert bitcoin and satoshi units(10^8) static constexpr int64_t kCoinBase = 100000000; /** - * @brief satoshi単位の最大値 - * @details 厳密には流通通貨の最大値とは異なるが、bitcoin coreに合わせて限度額を設定 + * @brief Maximum value in satoshi unit. + * @details Strictly speaking, it is different from the maximum \ + * value of the currency in circulation, but the limit is \ + * set according to the bitcoin core. * @see https://github.com/bitcoin/bitcoin/blob/e756eca9e8bf39f0a891f1760df0a317ecb7fee8/src/amount.h#L25 */ static constexpr int64_t kMaxAmount = 21000000 * kCoinBase; /** - * @brief BitcoinのAmountを表現するクラス + * @brief A class that represents Bitcoin's Amount. */ class CFD_CORE_EXPORT Amount { public: /** - * \~japanese - * @brief satoshi単位のAmountからAmountインスタンスを生成する. - * @param[in] amount satoshi単位のAmount - * @return Amountインスタンス - * @exception CfdException 不正な値が渡された場合 - * \~english * @brief Create Amount instance by amount satoshi units * @param[in] amount amount in satoshi units * @return instance of Amount class @@ -51,12 +47,6 @@ class CFD_CORE_EXPORT Amount { static Amount CreateBySatoshiAmount(int64_t amount); /** - * \~japanese - * @brief bitcoin単位のAmountからAmountインスタンスを生成する - * @param[in] coin_amount bitcoin単位のAmount - * @return Amountインスタンス - * @exception CfdException 不正な値が渡された場合 - * \~english * @brief Create Amount instance by amount bitcoin units * @param[in] coin_amount amount in bitcoin units * @return instance of Amount class @@ -97,108 +87,105 @@ class CFD_CORE_EXPORT Amount { explicit Amount(int64_t amount, bool ignore_check); /** - * @brief 自身のインスタンスからsatoshi単位のAmount額を取得する. - * @return satoshi単位のAmountの数値 + * @brief Get the Amount amount in satoshi units from object. + * @return Amount value in satoshi units. */ int64_t GetSatoshiValue() const; /** - * @brief 自身のインスタンスからbitcoin単位のAmount額を取得する. - * @details double精度の誤差が生じる可能性があるため注意. - * @return bitcoin単位のAmountの数値 + * @brief Get the Amount amount in bitcoin units from object. + * @details Note that double precision errors may occur. + * @return Amount value in bitcoin units. */ double GetCoinValue() const; /** - * @brief ByteDataをBigEndianで取得する. - * @return satoshiのByteData + * @brief Get ByteData by BigEndian. + * @return satoshi's ByteData */ ByteData GetByteData() const; // operator overloading /** - * @brief 等価比較オペレータ - * @param[in] amount 被演算子 比較対象とするAmountインスタンス - * @retval true 等価 - * @retval false 不等価 - * @return 等価であればtrue, それ以外はfalse + * @brief Equals operator. + * @param[in] amount Amount instance + * @retval true equals + * @retval false not equals */ bool operator==(const Amount &amount) const; /** - * @brief 等価比較オペレータ - * @param[in] satoshi_amount 被演算子 比較対象とするsatoshi単位のAmount額 - * @retval true 等価 - * @retval false 不等価 + * @brief Equals operator. + * @param[in] satoshi_amount amount in satoshi units + * @retval true equals + * @retval false not equals */ bool operator==(const int64_t satoshi_amount) const; /** - * @brief 不等価比較オペレータ - * @param[in] amount 被演算子 比較対象とするAmountインスタンス - * @retval true 不等価 - * @retval false 等価 + * @brief not equals operator. + * @param[in] amount Amount instance + * @retval true not equals + * @retval false equals */ bool operator!=(const Amount &amount) const; /** - * @brief 不等価比較オペレータ - * @param[in] satoshi_amount 被演算子 比較対象とするsatoshi単位のAmount額 - * @retval true 不等価 - * @retval false 等価 + * @brief not equals operator. + * @param[in] satoshi_amount amount in satoshi units + * @retval true not equals + * @retval false equals */ bool operator!=(const int64_t satoshi_amount) const; /** - * @brief 加算代入オペレータ(Amount += Amount) - * @param[in] amount 加数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Addition operator (Amount += Amount) + * @param[in] amount Amount instance + * @return Amount instance */ Amount operator+=(const Amount &amount); /** - * @brief 加算代入オペレータ(Amount += int64_t) - * @param[in] satoshi_amount 加数 satoshi単位のAmount額 - * @return 計算結果 Amountインスタンス + * @brief Addition operator(Amount += int64_t) + * @param[in] satoshi_amount 加数 amount in satoshi units + * @return Amount instance */ Amount operator+=(const int64_t satoshi_amount); /** - * @brief 減算代入オペレータ(Amount -= Amount) - * @param[in] amount 減数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Subtraction operator(Amount -= Amount) + * @param[in] amount Amount instance + * @return Amount instance */ Amount operator-=(const Amount &amount); /** - * @brief 減算代入オペレータ(Amount -= int64_t) - * @param[in] satoshi_amount 減数 satoshi単位のAmount額 - * @return 計算結果 Amountインスタンス + * @brief Subtraction operator(Amount -= int64_t) + * @param[in] satoshi_amount amount in satoshi units + * @return Amount instance */ Amount operator-=(const int64_t satoshi_amount); /** - * @brief 乗算代入オペレータ(Amount *= int64_t) - * @param[in] value 乗数 - * @return 計算結果 Amountインスタンス + * @brief Multiplication operator(Amount *= int64_t) + * @param[in] value multiplier + * @return Amount instance */ Amount operator*=(const int64_t value); /** - * @brief 除算代入オペレータ(Amount /= int64_t) - * @param[in] value 除数 - * @return 計算結果 Amountインスタンス + * @brief Division operator(Amount /= int64_t) + * @param[in] value divisor + * @return Amount instance */ Amount operator/=(const int64_t value); /** - * @brief コンストラクタ. - * - * リスト要素指定時の初期化用. + * @brief default constructor. */ Amount(); private: - //! satoshi単位のAmount + //! amount in satoshi units int64_t amount_; //! ignore valid check flag. bool ignore_check_; /** - * @brief 引数で与えられたAmount額が不正なものでないかを検証する. - * @param[in] amount satoshi単位のAmount - * @retval true 正常Amount額 - * @retval false 不正Amount額 + * @brief Verify that the amount is not invalid. + * @param[in] amount amount in satoshi units + * @retval true valid + * @retval false invalid */ static bool IsValidAmount(int64_t amount) { return (amount >= 0 && amount <= kMaxAmount); @@ -206,193 +193,193 @@ class CFD_CORE_EXPORT Amount { /** * @brief 引数で与えられたsatoshi単位のAmountが不正な値でないかを検証する. - * @param[in] satoshi_amount satoshi単位のAmount - * @exception CfdException 不正な値が渡された場合 + * @param[in] satoshi_amount amount in satoshi units + * @exception CfdException If an invalid value is passed */ static void CheckValidAmount(int64_t satoshi_amount); }; /** - * @brief 等価比較オペレータ - * @param[in] satoshi_amount satoshi単位のAmount額 - * @param[in] amount 比較対象Amountインスタンス - * @retval true 等価 - * @retval false 不等価 + * @brief Equals operator. + * @param[in] satoshi_amount amount in satoshi units + * @param[in] amount Amount instance + * @retval true equals + * @retval false not equals */ CFD_CORE_EXPORT bool operator==( const int64_t satoshi_amount, const Amount &amount); /** - * @brief 不等価比較オペレータ - * @param[in] satoshi_amount satoshi単位のAmount額 - * @param[in] amount 比較対象Amountインスタンス - * @retval true 不等価 - * @retval false 等価 + * @brief not equals operator. + * @param[in] satoshi_amount amount in satoshi units + * @param[in] amount Amount instance + * @retval true not equals + * @retval false equals */ CFD_CORE_EXPORT bool operator!=( const int64_t satoshi_amount, const Amount &amount); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is less than rhs * @retval false lhs is greater than or equal rhs */ CFD_CORE_EXPORT bool operator<(const Amount &lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(int64_t) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(int64_t) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is less than rhs * @retval false lhs is greater than or equal rhs */ CFD_CORE_EXPORT bool operator<(const int64_t lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(int64_t) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(int64_t) * @retval true lhs is less than rhs * @retval false lhs is greater than or equal rhs */ CFD_CORE_EXPORT bool operator<(const Amount &lhs, const int64_t rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is greater than rhs * @retval false lhs is less than or equal rhs */ CFD_CORE_EXPORT bool operator>(const Amount &lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(int64_t) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(int64_t) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is greater than rhs * @retval false lhs is less than or equal rhs */ CFD_CORE_EXPORT bool operator>(const int64_t lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(int64_t) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(int64_t) * @retval true lhs is greater than rhs * @retval false lhs is less than or equal rhs */ CFD_CORE_EXPORT bool operator>(const Amount &lhs, const int64_t rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is less than or equal rhs * @retval false lhs is greater than rhs */ CFD_CORE_EXPORT bool operator<=(const Amount &lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(int64_t) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(int64_t) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is less than or equal rhs * @retval false lhs is greater than rhs */ CFD_CORE_EXPORT bool operator<=(const int64_t lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(int64_t) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(int64_t) * @retval true lhs is less than or equal rhs * @retval false lhs is greater than rhs */ CFD_CORE_EXPORT bool operator<=(const Amount &lhs, const int64_t rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is greater than or equal rhs * @retval false lhs is less than rhs */ CFD_CORE_EXPORT bool operator>=(const Amount &lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(int64_t) - * @param[in] rhs 比較数(Amount) + * @brief comparison operator + * @param[in] lhs Value to be compared(int64_t) + * @param[in] rhs Value to compare(Amount) * @retval true lhs is greater than or equal rhs * @retval false lhs is less than rhs */ CFD_CORE_EXPORT bool operator>=(const int64_t lhs, const Amount &rhs); /** - * @brief 二方比較オペレータ - * @param[in] lhs 被比較数(Amount) - * @param[in] rhs 比較数(int64_t) + * @brief comparison operator + * @param[in] lhs Value to be compared(Amount) + * @param[in] rhs Value to compare(int64_t) * @retval true lhs is greater than or equal rhs * @retval false lhs is less than rhs */ CFD_CORE_EXPORT bool operator>=(const Amount &lhs, const int64_t rhs); /** - * @brief 加算オペレータ(Amount + Amount) - * @param[in] left_amount 被加数 Amountインスタンス - * @param[in] right_amount 加数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Addition operator(Amount + Amount) + * @param[in] left_amount Amount instance + * @param[in] right_amount Amount instance + * @return Amount instance */ CFD_CORE_EXPORT Amount operator+(const Amount &left_amount, const Amount &right_amount); /** - * @brief 加算オペレータ(Amount + int64_t) - * @param[in] amount 被加数 Amountインスタンス - * @param[in] satoshi_amount 加数 satoshi単位のAmount額 - * @return 計算結果 Amountインスタンス + * @brief Addition operator(Amount + int64_t) + * @param[in] amount Amount instance + * @param[in] satoshi_amount amount in satoshi units + * @return Amount instance */ CFD_CORE_EXPORT Amount operator+(const Amount &amount, const int64_t satoshi_amount); /** - * @brief 加算オペレータ(int64_t + Amount) - * @param[in] satoshi_amount 被加数 satoshi単位のAmount額 - * @param[in] amount 加数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Addition operator(int64_t + Amount) + * @param[in] satoshi_amount amount in satoshi units + * @param[in] amount Amount instance + * @return Amount instance */ CFD_CORE_EXPORT Amount operator+(const int64_t satoshi_amount, const Amount &amount); /** - * @brief 減算オペレータ(Amount - Amount) - * @param[in] left_amount 被減数 Amountインスタンス - * @param[in] right_amount 減数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Subtraction operator(Amount - Amount) + * @param[in] left_amount Amount instance + * @param[in] right_amount Amount instance + * @return Amount instance */ CFD_CORE_EXPORT Amount operator-(const Amount &left_amount, const Amount &right_amount); /** - * @brief 減算オペレータ(int64_t - Amount) - * @param[in] amount 被減数 Amountインスタンス - * @param[in] satoshi_amount 減数 satoshi単位のAmount額 - * @return 計算結果 Amountインスタンス + * @brief Subtraction operator(int64_t - Amount) + * @param[in] amount Amount instance + * @param[in] satoshi_amount amount in satoshi units + * @return Amount instance */ CFD_CORE_EXPORT Amount operator-(const Amount &amount, const int64_t satoshi_amount); /** - * @brief 減算オペレータ(Amount - int64_t) - * @param[in] satoshi_amount 被減数 satoshi単位のAmount額 - * @param[in] amount 減数 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Subtraction operator(Amount - int64_t) + * @param[in] satoshi_amount amount in satoshi units + * @param[in] amount Amount instance + * @return Amount instance */ CFD_CORE_EXPORT Amount operator-(const int64_t satoshi_amount, const Amount &amount); /** - * @brief 乗算オペレータ(Amount * int64_t) - * @param[in] amount 被乗数 Amountインスタンス - * @param[in] value 乗数 - * @return 計算結果 Amountインスタンス + * @brief Multiplication operator(Amount * int64_t) + * @param[in] amount Amount instance + * @param[in] value multiplier + * @return Amount instance */ CFD_CORE_EXPORT Amount operator*(const Amount &amount, const int64_t value); /** - * @brief 乗算オペレータ(int64_t * Amount) - * @param[in] value 乗数 - * @param[in] amount 被演算子 Amountインスタンス - * @return 計算結果 Amountインスタンス + * @brief Multiplication operator(int64_t * Amount) + * @param[in] value multiplier + * @param[in] amount Amount instance + * @return Amount instance */ CFD_CORE_EXPORT Amount operator*(const int64_t value, const Amount &amount); /** - * @brief 除算オペレータ(Amount / int64_t) - * @param[in] amount 被除数 Amountインスタンス - * @param[in] value 除数 - * @return 計算結果 Amountインスタンス + * @brief Division operator(Amount / int64_t) + * @param[in] amount Amount instance + * @param[in] value divisor + * @return Amount instance */ CFD_CORE_EXPORT Amount operator/(const Amount &amount, const int64_t value); diff --git a/include/cfdcore/cfdcore_bytedata.h b/include/cfdcore/cfdcore_bytedata.h index 6500f6d2..35b30ae6 100644 --- a/include/cfdcore/cfdcore_bytedata.h +++ b/include/cfdcore/cfdcore_bytedata.h @@ -2,7 +2,7 @@ /** * @file cfdcore_bytedata.h * - * @brief ByteData関連クラス定義 + * @brief The ByteData related class definition. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_BYTEDATA_H_ @@ -22,24 +22,24 @@ class ByteData256; /** * @class ByteData - * @brief 可変サイズのByte配列データクラス + * @brief The variable size byte array data class. */ class CFD_CORE_EXPORT ByteData { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor. */ ByteData(); /** - * @brief コンストラクタ - * @param[in] vector 格納Byte配列 + * @brief constructor + * @param[in] vector byte array. */ ByteData(const std::vector& vector); // NOLINT /** - * @brief コンストラクタ - * @param[in] hex Byteデータ HEX文字列 + * @brief constructor + * @param[in] hex hex string. */ explicit ByteData(const std::string& hex); @@ -51,42 +51,48 @@ class CFD_CORE_EXPORT ByteData { explicit ByteData(const uint8_t* buffer, uint32_t size); /** - * @brief HEX文字列を取得する. - * @return HEX文字列 + * @brief constructor + * @param[in] single_byte 1-Byte data + */ + explicit ByteData(const uint8_t single_byte); + + /** + * @brief Get a hex string. + * @return hex string. */ std::string GetHex() const; /** - * @brief Byte配列を取得する. - * @return Byte配列 + * @brief Get a byte array. + * @return byte array. */ std::vector GetBytes() const; /** - * @brief データサイズを取得する. - * @return Byte配列サイズ + * @brief Get a byte data size. + * @return byte data size. */ size_t GetDataSize() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. * @deprecated replace to IsEmpty . */ bool Empty() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. */ bool IsEmpty() const; /** - * @brief ByteData比較 - * @param bytedata 比較対象のオブジェクト - * @retval true 一致した場合 - * @retval false 不一致の場合 + * @brief Check equals. + * @param bytedata compare target object. + * @retval true equals. + * @retval false not equals. */ bool Equals(const ByteData& bytedata) const; @@ -98,13 +104,13 @@ class CFD_CORE_EXPORT ByteData { uint8_t GetHeadData() const; /** - * @brief byte data情報をserializeする. + * @brief Serialize byte data. * @return serialize data */ ByteData Serialize() const; /** - * @brief serializeされたbyte dataのサイズを取得する。 + * @brief Get the serialized size. * @return serialize data size */ size_t GetSerializeSize() const; @@ -178,65 +184,70 @@ class CFD_CORE_EXPORT ByteData { /** * @brief Push to back. * @param[in] back_insert_data back insert data. - * @return joined byte data. */ void Push(const ByteData& back_insert_data); /** * @brief Push to back. * @param[in] back_insert_data back insert data. - * @return joined byte data. */ void Push(const ByteData160& back_insert_data); /** * @brief Push to back. * @param[in] back_insert_data back insert data. - * @return joined byte data. */ void Push(const ByteData256& back_insert_data); /** - * @brief 可変長サイズ情報(バッファ)を取得する. + * @brief Equals operator. + * @param[in] object target object. + * @retval true equals + * @retval false not equals + */ + bool operator==(const ByteData& object) const; + + /** + * @brief Get the variable integer buffer. * @param[in] value size value * @return variable size buffer */ static ByteData GetVariableInt(uint64_t value); /** - * @brief 指定された2つのバッファのHEX値を比較する. + * @brief Compare the HEX values ​​of the two specified buffers. * @param[in] source source target * @param[in] destination destination target - * @retval true 大きい - * @retval false 小さい + * @retval true Large + * @retval false Small or equals. */ static bool IsLarge(const ByteData& source, const ByteData& destination); private: /** - * @brief データ格納Byte配列 + * @brief データbyte array. */ std::vector data_; }; /** * @class ByteData160 - * @brief サイズ固定(20byte)のByte配列データクラス + * @brief Fixed size (20 bytes) Byte array data class */ class CFD_CORE_EXPORT ByteData160 { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor */ ByteData160(); /** - * @brief コンストラクタ - * @param[in] vector 20byteデータ格納Byte配列 + * @brief constructor + * @param[in] vector byte array(20byte). */ ByteData160(const std::vector& vector); // NOLINT /** - * @brief コンストラクタ - * @param[in] hex ByteデータHEX文字列 + * @brief constructor + * @param[in] hex hex string. */ explicit ByteData160(const std::string& hex); @@ -247,41 +258,41 @@ class CFD_CORE_EXPORT ByteData160 { explicit ByteData160(const ByteData& byte_data); /** - * @brief HEX文字列を取得する. - * @return HEX文字列 + * @brief Get a hex string. + * @return hex string. */ std::string GetHex() const; /** - * @brief Byte配列を取得する. - * @return Byte配列 + * @brief Get a byte array. + * @return byte array. */ std::vector GetBytes() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. * @deprecated replace to IsEmpty . */ bool Empty() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. */ bool IsEmpty() const; /** - * @brief ByteData比較 - * @param bytedata 比較対象のオブジェクト - * @retval true 一致した場合 - * @retval false 不一致の場合 + * @brief Check equals. + * @param bytedata compare target object. + * @retval true equals. + * @retval false not equals. */ bool Equals(const ByteData160& bytedata) const; /** - * @brief byte data情報を取得する. + * @brief Get a byte data object. * @return byte data */ ByteData GetData() const; @@ -360,38 +371,46 @@ class CFD_CORE_EXPORT ByteData160 { } /** - * @brief byte data情報をserializeする. + * @brief Serialize byte data. * @return serialize data */ ByteData Serialize() const; + /** + * @brief Equals operator. + * @param[in] object target object. + * @retval true equals + * @retval false not equals + */ + bool operator==(const ByteData160& object) const; + private: /** - * @brief 20byte固定データ格納Byte配列 + * @brief 20byte fixed data. */ std::vector data_; }; /** * @class ByteData256 - * @brief サイズ固定(32byte)のByte配列データクラス + * @brief Fixed size (32 bytes) Byte array data class. */ class CFD_CORE_EXPORT ByteData256 { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor */ ByteData256(); /** - * @brief コンストラクタ - * @param[in] vector 32byteデータ格納Byte配列 + * @brief constructor + * @param[in] vector byte array(32byte). */ ByteData256(const std::vector& vector); // NOLINT /** - * @brief コンストラクタ - * @param[in] hex ByteデータHEX文字列 + * @brief constructor + * @param[in] hex hex string. */ explicit ByteData256(const std::string& hex); @@ -402,41 +421,41 @@ class CFD_CORE_EXPORT ByteData256 { explicit ByteData256(const ByteData& byte_data); /** - * @brief HEX文字列を取得する. - * @return HEX文字列 + * @brief Get a hex string. + * @return hex string. */ std::string GetHex() const; /** - * @brief Byte配列を取得する. - * @return Byte配列 + * @brief Get a byte array. + * @return byte array. */ std::vector GetBytes() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. * @deprecated replace to IsEmpty . */ bool Empty() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check is data empty. + * @retval true empty. + * @retval false not empty. */ bool IsEmpty() const; /** - * @brief ByteData比較 - * @param bytedata 比較対象のオブジェクト - * @retval true 一致した場合 - * @retval false 不一致の場合 + * @brief Check equals. + * @param bytedata compare target object. + * @retval true equals. + * @retval false not equals. */ bool Equals(const ByteData256& bytedata) const; /** - * @brief byte data情報を取得する. + * @brief Get a byte data object. * @return byte data */ ByteData GetData() const; @@ -515,18 +534,291 @@ class CFD_CORE_EXPORT ByteData256 { } /** - * @brief byte data情報をserializeする. + * @brief Serialize byte data. * @return serialize data */ ByteData Serialize() const; + /** + * @brief Equals operator. + * @param[in] object target object. + * @retval true equals + * @retval false not equals + */ + bool operator==(const ByteData256& object) const; + private: /** - * @brief 32byte固定データ格納Byte配列 + * @brief 32byte fixed data. */ std::vector data_; }; +/** + * @class Serializer + * @brief A class that serializes a byte array. + */ +class CFD_CORE_EXPORT Serializer { + public: + static constexpr uint8_t kViTag16 = 253; //!< VarInt16 + static constexpr uint8_t kViTag32 = 254; //!< VarInt32 + static constexpr uint8_t kViTag64 = 255; //!< VarInt64 + static constexpr uint8_t kViMax8 = 252; //!< VarInt8 + + /** + * @brief get variable integer size. + * @param[in] value value + * @return variable integer size + */ + static uint32_t GetVariableIntSize(uint64_t value); + + /** + * @brief constructor. + */ + Serializer(); + /** + * @brief constructor. + * @param[in] initial_size initial buffer size. + */ + explicit Serializer(uint32_t initial_size); + /** + * @brief destructor. + */ + virtual ~Serializer() {} + /** + * @brief copy constructor. + * @param[in] object object + */ + Serializer(const Serializer& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Serializer& operator=(const Serializer& object); + + /** + * @brief add variable integer. + * @param[in] value value + */ + void AddVariableInt(uint64_t value); + + /** + * @brief add variable buffer. + * @param[in] buffer buffer + */ + void AddVariableBuffer(const ByteData& buffer); + /** + * @brief add prefix buffer. + * @param[in] prefix prefix + * @param[in] buffer buffer + */ + void AddPrefixBuffer(uint64_t prefix, const ByteData& buffer); + /** + * @brief add direct byte array. + * @param[in] buffer buffer + */ + void AddDirectBytes(const ByteData& buffer); + /** + * @brief add direct byte array. + * @param[in] buffer buffer + */ + void AddDirectBytes(const ByteData256& buffer); + + /** + * @brief add variable buffer. + * @param[in] buffer buffer + * @param[in] buffer_size buffer size + */ + void AddVariableBuffer(const uint8_t* buffer, uint32_t buffer_size); + /** + * @brief add prefix buffer. + * @param[in] prefix prefix + * @param[in] buffer buffer + * @param[in] buffer_size buffer size + */ + void AddPrefixBuffer( + uint64_t prefix, const uint8_t* buffer, uint32_t buffer_size); + /** + * @brief add direct byte array. + * @param[in] buffer buffer + * @param[in] buffer_size buffer size + */ + void AddDirectBytes(const uint8_t* buffer, uint32_t buffer_size); + + /** + * @brief add direct byte data. + * @param[in] byte_data byte data + */ + void AddDirectByte(uint8_t byte_data); + /** + * @brief add direct number. + * @param[in] number value + */ + void AddDirectNumber(uint32_t number); + /** + * @brief add direct number. + * @param[in] number value + */ + void AddDirectNumber(uint64_t number); + /** + * @brief add direct number. + * @param[in] number value + */ + void AddDirectNumber(int64_t number); + + /** + * @brief add direct byte array. + * @param[in] buffer buffer + * @return serializer object. + */ + Serializer& operator<<(const ByteData& buffer); + /** + * @brief add direct byte array. + * @param[in] buffer buffer + * @return serializer object. + */ + Serializer& operator<<(const ByteData256& buffer); + /** + * @brief add direct byte data. + * @param[in] byte_data byte data + * @return serializer object. + */ + Serializer& operator<<(uint8_t byte_data); + /** + * @brief add direct number. + * @param[in] number value + * @return serializer object. + */ + Serializer& operator<<(uint32_t number); + /** + * @brief add direct number. + * @param[in] number value + * @return serializer object. + */ + Serializer& operator<<(uint64_t number); + /** + * @brief add direct number. + * @param[in] number value + * @return serializer object. + */ + Serializer& operator<<(int64_t number); + + /** + * @brief Output byte array. + * @return byte array. + */ + ByteData Output(); + + protected: + std::vector buffer_; //!< buffer + uint32_t offset_; //!< offset + + /** + * @brief check need buffer size. + * @param[in] need_size need buffer size + */ + void CheckNeedSize(uint32_t need_size); +}; + +/** + * @class Deserializer + * @brief A class that analyze a serialized byte array. + */ +class CFD_CORE_EXPORT Deserializer { + public: + /** + * @brief constructor. + */ + Deserializer() : offset_(0) {} + /** + * @brief constructor. + * @param[in] buffer buffer + */ + explicit Deserializer(const std::vector& buffer); + /** + * @brief constructor. + * @param[in] buffer buffer + */ + explicit Deserializer(const ByteData& buffer); + /** + * @brief destructor. + */ + virtual ~Deserializer() {} + /** + * @brief copy constructor. + * @param[in] object object + */ + Deserializer(const Deserializer& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Deserializer& operator=(const Deserializer& object); + + /** + * @brief read uint64. + * @return uint64 + */ + uint64_t ReadUint64(); + /** + * @brief read uint32. + * @return uint32 + */ + uint32_t ReadUint32(); + /** + * @brief read uint8. + * @return uint8 + */ + uint8_t ReadUint8(); + + /** + * @brief read variable integer. + * @return uint64 + */ + uint64_t ReadVariableInt(); + /** + * @brief read buffer. + * @param[in] size read size. + * @return buffer + */ + std::vector ReadBuffer(uint32_t size); + /** + * @brief read array. + * @param[in,out] output write array. + * @param[in] size read size. + */ + void ReadArray(uint8_t* output, size_t size); + + /** + * @brief read variable buffer. + * @return buffer + */ + std::vector ReadVariableBuffer(); + /** + * @brief read variable buffer. + * @return buffer + */ + ByteData ReadVariableData(); + + /** + * @brief get all read size. + * @return size (offset) + */ + uint32_t GetReadSize(); + + protected: + std::vector buffer_; //!< buffer + uint32_t offset_; //!< offset + + /** + * @brief check read offset size. + * @param[in] size need size + */ + void CheckReadSize(uint64_t size); +}; + } // namespace core } // namespace cfd diff --git a/include/cfdcore/cfdcore_coin.h b/include/cfdcore/cfdcore_coin.h index a202a315..ab4b855e 100644 --- a/include/cfdcore/cfdcore_coin.h +++ b/include/cfdcore/cfdcore_coin.h @@ -2,7 +2,7 @@ /** * @file cfdcore_coin.h * - * @brief Coin(UTXO)関連クラス定義 + * @brief Coin (UTXO) related class definition. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_COIN_H_ @@ -18,44 +18,56 @@ namespace cfd { namespace core { /** - * @brief transaction idクラス + * @brief transaction id class. */ class CFD_CORE_EXPORT Txid { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor */ Txid(); /** - * @brief コンストラクタ - * @param[in] hex Hex文字列 + * @brief constructor + * @param[in] hex hex string */ explicit Txid(const std::string& hex); /** - * @brief コンストラクタ - * @param[in] data ByteData256インスタンス + * @brief constructor + * @param[in] data ByteData256 instance */ explicit Txid(const ByteData256& data); /** - * @brief デストラクタ + * @brief destructor. */ virtual ~Txid() { // do nothing } /** - * @brief Hex文字列を取得する. - * @return Hex文字列 + * @brief copy constructor. + * @param[in] object object + */ + Txid(const Txid& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Txid& operator=(const Txid& object); + /** + * @brief Get a hex string. + * @return hex string */ const std::string GetHex() const; /** - * @brief ByteDataを取得する. - * @return ByteDataオブジェクト + * @brief Get a ByteData object. + * @return ByteData object. */ const ByteData GetData() const; /** - * @brief Txid比較 - * @param txid 比較対象のオブジェクト - * @return true:一致/false:不一致 + * @brief compare Txid. + * @param txid compare target. + * @retval true equals. + * @retval false not equals. */ bool Equals(const Txid& txid) const; /** @@ -70,40 +82,51 @@ class CFD_CORE_EXPORT Txid { }; /** - * @brief block hashクラス + * @brief block hash class. */ class CFD_CORE_EXPORT BlockHash { public: /** - * @brief デフォルトコンストラクタ + * @brief default constructor */ BlockHash() { // do nothing } /** - * @brief コンストラクタ - * @param[in] hex Hex文字列 + * @brief constructor + * @param[in] hex hex string */ explicit BlockHash(const std::string& hex); /** - * @brief コンストラクタ - * @param[in] data ByteData256インスタンス + * @brief constructor + * @param[in] data ByteData256 object. */ explicit BlockHash(const ByteData256& data); /** - * @brief デストラクタ + * @brief destructor. */ virtual ~BlockHash() { // do nothing } /** - * @brief Hex文字列を取得する. - * @return Hex文字列 + * @brief copy constructor. + * @param[in] object object + */ + BlockHash(const BlockHash& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + BlockHash& operator=(const BlockHash& object); + /** + * @brief Get a hex string. + * @return hex string */ const std::string GetHex() const; /** - * @brief ByteDataを取得する. - * @return ByteDataオブジェクト + * @brief Get a ByteData object. + * @return ByteData object. */ const ByteData GetData() const; /** diff --git a/include/cfdcore/cfdcore_common.h b/include/cfdcore/cfdcore_common.h index 40288f24..0fd0cafa 100644 --- a/include/cfdcore/cfdcore_common.h +++ b/include/cfdcore/cfdcore_common.h @@ -1,7 +1,7 @@ // Copyright 2019 CryptoGarage /** * @file cfdcore_common.h - * @brief cfdcoreの共通定義ファイル。 + * @brief Common definition file for cfdcore. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_COMMON_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_COMMON_H_ @@ -9,7 +9,7 @@ #include /** - * @brief APIのDLLエクスポート定義 + * @brief API DLL export definition */ #ifndef CFD_CORE_API #if defined(_WIN32) @@ -28,7 +28,7 @@ #endif /** - * @brief クラスのDLLエクスポート定義 + * @brief DLL export definition for class */ #ifndef CFD_CORE_EXPORT #if defined(_WIN32) @@ -47,19 +47,19 @@ #endif /** - * @brief cfd名前空間 + * @brief cfd namespace */ namespace cfd { /** - * @brief cfd::core名前空間 + * @brief cfd::core namespace */ namespace core { -/// cfdcoreのハンドル値。 +/// Handle value of cfdcore. using CfdCoreHandle = void*; /** - * @brief ライブラリがサポートしている機能の定義値 + * @brief Definition value of the function supported by the library. */ enum LibraryFunction { kEnableBitcoin = 0x0001, //!< enable bitcoin function @@ -68,19 +68,19 @@ enum LibraryFunction { // API /** - * @brief ライブラリがサポートしている機能の値を取得する。 - * @return LibraryFunctionのビットフラグ + * @brief Get the value of the function supported by the library. + * @return Library Function bit flag */ CFD_CORE_API uint64_t GetSupportedFunction(); /** - * @brief cfdcoreの初期化を行う。 - * @param[out] handle ハンドル値。 + * @brief Initialize cfdcore. + * @param[out] handle Handle value. */ CFD_CORE_API void Initialize(CfdCoreHandle* handle); /** - * @brief cfdcoreの終了処理を行う。 - * @param[in] handle ハンドル値。 - * @param[in] is_finish_process プロセス終了時かどうか + * @brief Performs cfdcore termination processing. + * @param[in] handle Handle value. + * @param[in] is_finish_process Whether at the end of the process. */ CFD_CORE_API void Finalize( const CfdCoreHandle handle, bool is_finish_process = false); diff --git a/include/cfdcore/cfdcore_descriptor.h b/include/cfdcore/cfdcore_descriptor.h index 9d7a8238..c55f0a33 100644 --- a/include/cfdcore/cfdcore_descriptor.h +++ b/include/cfdcore/cfdcore_descriptor.h @@ -2,7 +2,7 @@ /** * @file cfdcore_descriptor.h * - * @brief Output Descriptor関連クラス定義 + * @brief The Output Descriptor related class definition. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_DESCRIPTOR_H_ @@ -28,7 +28,7 @@ namespace core { constexpr const char* const kArgumentBaseExtkey = "base"; /** - * @brief DescriptorNode の種別定義. + * @brief DescriptorNode type definition. */ enum DescriptorNodeType { kDescriptorTypeNull, //!< null @@ -38,7 +38,7 @@ enum DescriptorNodeType { }; /** - * @brief DescriptorNode のScript種別定義. + * @brief Script type definition of DescriptorNode. */ enum DescriptorScriptType { kDescriptorScriptNull, //!< null @@ -56,7 +56,7 @@ enum DescriptorScriptType { }; /** - * @brief DescriptorNode のKey種別定義. + * @brief Key type definition of DescriptorNode. */ enum DescriptorKeyType { kDescriptorKeyNull, //!< null @@ -66,7 +66,7 @@ enum DescriptorKeyType { }; /** - * @brief key型descriptorの情報クラスです. + * @brief Information class of key type descriptor. */ class CFD_CORE_EXPORT DescriptorKeyInfo { public: @@ -216,7 +216,7 @@ class CFD_CORE_EXPORT DescriptorKeyInfo { }; /** - * @brief key型descriptorの参照クラスです. + * @brief It is a reference class of key type descriptor. */ class CFD_CORE_EXPORT DescriptorKeyReference { public: @@ -243,6 +243,13 @@ class CFD_CORE_EXPORT DescriptorKeyReference { */ explicit DescriptorKeyReference( const ExtPubkey& ext_pubkey, const std::string* arg = nullptr); + /** + * @brief constructor. + * @param[in] key key data + * @param[in] arg argument + */ + explicit DescriptorKeyReference( + const KeyData& key, const std::string* arg = nullptr); /** * @brief copy constructor. * @param[in] object DescriptorKeyReference object @@ -289,6 +296,11 @@ class CFD_CORE_EXPORT DescriptorKeyReference { * @return ext-pubkey */ ExtPubkey GetExtPubkey() const; + /** + * @brief getting key data. + * @return key data + */ + KeyData GetKeyData() const; /** * @brief getting key type. * @return key type @@ -300,11 +312,12 @@ class CFD_CORE_EXPORT DescriptorKeyReference { Pubkey pubkey_; //!< pubkey ExtPrivkey extprivkey_; //!< ext privkey ExtPubkey extpubkey_; //!< ext pubkey + KeyData key_data_; //!< key data std::string argument_; //!< argument }; /** - * @brief Script型descriptorの参照クラスです. + * @brief Script type descriptor is a reference class. */ class CFD_CORE_EXPORT DescriptorScriptReference { public: @@ -474,7 +487,7 @@ class CFD_CORE_EXPORT DescriptorScriptReference { }; /** - * @brief Descriptor用Node定義クラス + * @brief Node definition class for Descriptor. */ class CFD_CORE_EXPORT DescriptorNode { public: @@ -520,21 +533,25 @@ class CFD_CORE_EXPORT DescriptorNode { /** * @brief get reference object. * @param[in] array_argument argument + * @param[in] parent parent object * @return reference object */ DescriptorScriptReference GetReference( - std::vector* array_argument) const; + std::vector* array_argument, + const DescriptorNode* parent = nullptr) const; /** * @brief get reference object list. * @param[in] array_argument argument + * @param[in] parent parent object * @return reference object list */ std::vector GetReferences( - std::vector* array_argument) const; + std::vector* array_argument, + const DescriptorNode* parent = nullptr) const; /** - * @brief argumentに必要な数を取得する。 + * @brief Get the number required for argument. * @return argument number. */ uint32_t GetNeedArgumentNum() const; @@ -547,12 +564,12 @@ class CFD_CORE_EXPORT DescriptorNode { std::string ToString(bool append_checksum = true) const; /** - * @brief DescriptorNodeの種別を取得する。 + * @brief Get the type of DescriptorNode. * @return DescriptorNodeType */ DescriptorNodeType GetNodeType() const { return node_type_; } /** - * @brief DescriptorNodeのScript種別を取得する。 + * @brief Get the Script type of DescriptorNode. * @return DescriptorScriptType */ DescriptorScriptType GetScriptType() const { return script_type_; } @@ -741,6 +758,31 @@ class CFD_CORE_EXPORT Descriptor { std::vector GetReferenceAll( const std::vector* array_argument = nullptr) const; + /** + * @brief getting key data. + * @return key data + */ + KeyData GetKeyData() const; + /** + * @brief getting key data. + * @param[in] argument argument + * @return key data + */ + KeyData GetKeyData(const std::string& argument) const; + /** + * @brief getting key data. + * @param[in] array_argument argument + * @return key data + */ + KeyData GetKeyData(const std::vector& array_argument) const; + /** + * @brief getting key data list. + * @param[in] array_argument argument + * @return key data + */ + std::vector GetKeyDataAll( + const std::vector* array_argument = nullptr) const; + /** * @brief getting output descriptor. * @param[in] append_checksum append checksum diff --git a/include/cfdcore/cfdcore_elements_address.h b/include/cfdcore/cfdcore_elements_address.h index 087b9ab3..554fdbc5 100644 --- a/include/cfdcore/cfdcore_elements_address.h +++ b/include/cfdcore/cfdcore_elements_address.h @@ -2,7 +2,7 @@ /** * @file cfdcore_elements_address.h * - * @brief Elements対応したAddressクラス定義 + * @brief The address class definition file used in Elements (liquid network). */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ELEMENTS_ADDRESS_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ELEMENTS_ADDRESS_H_ @@ -21,32 +21,32 @@ namespace cfd { namespace core { /** - * @brief ConfidentialKey(= Pubkey)の型定義 + * @brief Type definition of ConfidentialKey (= Pubkey) * @see Pubkey */ using ConfidentialKey = Pubkey; /** * @typedef ElementsNetType - * @brief Elements Networkの定義 + * @brief Elements Network definition */ using ElementsNetType = NetType; /** * @typedef ElementsAddressType - * @brief ElementsのAddress種別の定義 + * @brief Definition of Address type of Elements */ using ElementsAddressType = AddressType; /** - * @brief Elements のデフォルトのアドレスフォーマットリストを取得する. - * @return Elementsデフォルトのアドレスフォーマットリスト + * @brief Get the default address format list for Elements. + * @return default address format list for Elements. */ CFD_CORE_API std::vector GetElementsAddressFormatList(); /** * @class ElementsConfidentialAddress - * @brief ElementsのConfidentialアドレスを表現するクラス + * @brief A class that represents the Confidential address of Elements */ class CFD_CORE_EXPORT ElementsConfidentialAddress { public: @@ -60,91 +60,103 @@ class CFD_CORE_EXPORT ElementsConfidentialAddress { const Privkey& master_blinding_key, const Script& locking_script); /** - * @brief デフォルトコンストラクタ + * @brief default constructor. */ ElementsConfidentialAddress(); /** - * @brief コンストラクタ(UnblindedAddressからConfidentialAddress生成) - * @param unblinded_address UnblindedAddress インスタンス - * @param confidential_key ConfidentialKey インスタンス + * @brief constructor. (Generate Confidential Address from Unblinded Address) + * @param unblinded_address UnblindedAddress instance + * @param confidential_key ConfidentialKey instance */ - ElementsConfidentialAddress( + explicit ElementsConfidentialAddress( const Address& unblinded_address, const ConfidentialKey& confidential_key); /** - * @brief コンストラクタ(ConfidentialAddress文字列からのデコード) - * @param[in] confidential_address confidential アドレス文字列 + * @brief constructor. (Decoding from the ConfidentialAddress string) + * @param[in] confidential_address confidential address string. */ explicit ElementsConfidentialAddress( const std::string& confidential_address); /** - * @brief コンストラクタ(ConfidentialAddress文字列からのデコード) - * @param[in] confidential_address confidential アドレス文字列 + * @brief constructor. (Decoding from the ConfidentialAddress string) + * @param[in] confidential_address confidential address string. * @param[in] prefix_list address prefix list */ explicit ElementsConfidentialAddress( const std::string& confidential_address, const std::vector& prefix_list); + /** + * @brief copy constructor. + * @param[in] object object + */ + ElementsConfidentialAddress(const ElementsConfidentialAddress& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + ElementsConfidentialAddress& operator=( + const ElementsConfidentialAddress& object) &; /** - * @brief UnblindedAddressを取得 - * @return ConfidentialAddressに紐づくUnblindedAddressインスタンス + * @brief Get UnblindedAddress + * @return UnblindedAddress instance associated with ConfidentialAddress */ Address GetUnblindedAddress() const; /** - * @brief ConfidentialKeyを取得 - * @return ConfidentialAddressに紐づくConfidentialKeyインスタンス + * @brief Get Confidential Key + * @return Confidential Key instance associated with Confidential Address */ ConfidentialKey GetConfidentialKey() const; /** - * @brief アドレスのhex文字列を取得する. - * @return アドレス文字列 + * @brief Get the address string. + * @return address string. */ std::string GetAddress() const; /** - * @brief AddressのElementsNetTypeを取得する. + * @brief Get the ElementsNetType of Address. * @return ElementsNetType */ ElementsNetType GetNetType() const; /** - * @brief Address種別を取得する. - * @return Elements Address種別 + * @brief Get the Address type. + * @return Address type. */ ElementsAddressType GetAddressType() const; /** - * @brief アドレスHashを取得する. - * @return アドレスHashのByteDataインスタンス + * @brief Get the address Hash. + * @return address Hash. */ ByteData GetHash() const; /** - * @brief LockingScriptを取得する + * @brief Get LockingScript * @return locking script */ Script GetLockingScript() const; /** - * @brief 引数で指定されたアドレスがBlindされているアドレスであるかを判定する - * @param address アドレス(base58)文字列 - * @retval true Blindされているアドレスの場合 - * @retval false Blindされていないアドレスの場合 + * @brief Determines if the specified address is a Blinded address + * @param address address string + * @retval true has blind address + * @retval false not blind address */ static bool IsConfidentialAddress(const std::string& address); /** - * @brief 引数で指定されたアドレスがBlindされているアドレスであるかを判定する - * @param[in] address アドレス文字列 - * @param[in] prefix_list アドレス文字列 - * @retval true Blindされているアドレスの場合 - * @retval false Blindされていないアドレスの場合 + * @brief Determines if the specified address is a Blinded address + * @param[in] address address string + * @param[in] prefix_list address prefix list + * @retval true has blind address + * @retval false not blind address */ static bool IsConfidentialAddress( const std::string& address, @@ -152,7 +164,7 @@ class CFD_CORE_EXPORT ElementsConfidentialAddress { private: /** - * @brief confidentialアドレス文字列からモデルのデコードを行う + * @brief Decode the model from the confidential address string * @param[in] confidential_address confidential address string * @param[in] prefix_list address prefix list */ @@ -161,10 +173,11 @@ class CFD_CORE_EXPORT ElementsConfidentialAddress { const std::vector& prefix_list); /** - * @brief unblinded_addressとconfidential_keyから、confidential_addressを計算する. - * @details Elementsのネットワーク種別については、ublinded_addressと同種のネットワークで計算を行う. - * @param unblinded_address UnblindedAddressインスタンス - * @param confidential_key Blindに利用するConfidentialKeyインスタンス + * @brief Calculate the confidential_address from the unblinded_address and confidential_key. + * @details For the network type of Elements, the calculation is performed \ + * on the same type of network as connected_address. + * @param unblinded_address UnblindedAddress + * @param confidential_key Confidential Key instance used for Blind */ void CalculateAddress( const Address& unblinded_address, @@ -176,7 +189,7 @@ class CFD_CORE_EXPORT ElementsConfidentialAddress { /// Confidential Key ConfidentialKey confidential_key_; - /// アドレス文字列 + /// address string std::string address_; }; diff --git a/include/cfdcore/cfdcore_elements_script.h b/include/cfdcore/cfdcore_elements_script.h index 11f46944..82c3b21a 100644 --- a/include/cfdcore/cfdcore_elements_script.h +++ b/include/cfdcore/cfdcore_elements_script.h @@ -2,7 +2,7 @@ /** * @file cfdcore_elements_script.h * - * @brief Elements対応したScriptクラス定義 + * @brief The script class definition file used in Elements (liquid network). */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ELEMENTS_SCRIPT_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ELEMENTS_SCRIPT_H_ @@ -18,34 +18,34 @@ namespace core { /** * @class ContractHashUtil - * @brief ElementsのContractHash関連処理を行うクラス. + * @brief A class that performs ContractHash related processing of Elements. * * @details - * 本クラスではcfd_coreの機能として必要な最小限の処理のみ実装する。 - * 本クラスの対象外である下記の要素については、別途作成すること。 - * - Privkey: Privkey::GenerageRandomKey()でランダム生成する。 - * - Pubkey: PrivkeyのGeneratePubkey(true)で生成する。 - * - claim script: p2wpkh or p2pkhのlockingScriptを生成する。 - * - Elementsのデフォルトはbech32(p2wpkh)形式。 + * In this class, only the minimum processing required as a function of cfd_core is implemented. + * The following elements that are not covered by this class should be created separately. + * - Privkey: private key. + * - Pubkey: Generate using Privkey::GeneratePubkey(true). + * - claim script: Generate a lockingScript of p2wpkh or p2pkh. + * - Elements default to bech32 (p2wpkh) format. * (script: OP_0 <20-byte-key-hash>) - * - 生成方法: ScriptBuilder().AppendOperator(ScriptOperator::OP_0) + * - Generation: ScriptBuilder().AppendOperator(ScriptOperator::OP_0) * .AppendData(AddHashUtil::hash160(pubkey)).Build() - * - mainchain address: ContractScriptから生成する。 - * - Elementsのデフォルトはp2sh-segwit。 - * - 生成方法 \n - * 1. ContractScriptから、p2wshのlockingScriptを作成。 + * - mainchain address: Generated from ContractScript. + * - The default for Elements is p2sh-segwit. + * - Generation \n + * 1. Create a p2wsh locking Script from ContractScript. * (script: OP_0 <32-byte-script-hash>) \n * ScriptBuilder().AppendOperator(ScriptOperator::OP_0) * .AppendData(AddHashUtil::sha256(ContractScript)).Build() - * 2. 1を用いて、p2shのAddressを作成。 - * Address(NetType, 1のlockingScript) + * 2. Create a p2sh Address using 1's locking script. + * Address(NetType, 1's 'lockingScript) */ class CFD_CORE_EXPORT ContractHashUtil { public: /** - * @brief Pay-to-Contractスクリプトを生成する。 + * @brief Generate a Pay-to-Contract script. * @param[in] claim_script claim script - * @param[in] fedpeg_script elementsdのside chain設定スクリプト + * @param[in] fedpeg_script elementsd side chain configuration script * @return Pay-to-Contract script. */ static Script GetContractScript( @@ -53,7 +53,7 @@ class CFD_CORE_EXPORT ContractHashUtil { private: /** - * @brief liquidV1 watchman script形式かどうかをチェックする。 + * @brief Check if it is in liquidV1 watchman script format. * @param[in] script script * @retval true liquidV1 watchman script format * @retval false other script @@ -61,7 +61,7 @@ class CFD_CORE_EXPORT ContractHashUtil { static bool CheckLiquidV1Watchman(const Script& script); /** - * @brief コンストラクタ + * @brief constructor. */ ContractHashUtil(); }; diff --git a/include/cfdcore/cfdcore_elements_transaction.h b/include/cfdcore/cfdcore_elements_transaction.h index 0813e77b..d568fab1 100644 --- a/include/cfdcore/cfdcore_elements_transaction.h +++ b/include/cfdcore/cfdcore_elements_transaction.h @@ -2,7 +2,7 @@ /** * @file cfdcore_elements_transaction.h * - * @brief Elements Transaction関連クラスを定義する。 + * @brief Define Elements Transaction related classes. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ELEMENTS_TRANSACTION_H_ @@ -26,56 +26,65 @@ class BlindFactor; constexpr const int kDefaultBlindMinimumBits = 52; /** - * @brief nonce情報を保持するクラス + * @brief Class that holds nonce information */ class CFD_CORE_EXPORT ConfidentialNonce { public: /** - * @brief コンストラクタ. - * - * リスト定義等における初期化のため、定義する。 + * @brief constructor. */ ConfidentialNonce(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] hex_string hex string. */ explicit ConfidentialNonce(const std::string& hex_string); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] byte_data byte array data. */ explicit ConfidentialNonce(const ByteData& byte_data); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] pubkey pubkey. */ explicit ConfidentialNonce(const Pubkey& pubkey); /** - * @brief デストラクタ. + * @brief destructor. */ virtual ~ConfidentialNonce() { // do nothing } + /** + * @brief copy constructor. + * @param[in] object object + */ + ConfidentialNonce(const ConfidentialNonce& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + ConfidentialNonce& operator=(const ConfidentialNonce& object); /** - * @brief バイトデータを取得する. + * @brief Get byte data. * @return byte array data. */ ByteData GetData() const; /** - * @brief HEX文字列を取得する. + * @brief Get the HEX string. * @return hex string */ std::string GetHex() const; /** - * @brief blind有無を取得する. + * @brief Get if it is blind. * @retval true blind * @retval false unblind */ bool HasBlinding() const; /** - * @brief 空かどうかを取得する. + * @brief Get if it's empty. * @retval true empty * @retval false exist value */ @@ -93,56 +102,65 @@ class CFD_CORE_EXPORT ConfidentialNonce { }; /** - * @brief AssetId情報を保持するクラス + * @brief Class that holds AssetId information */ class CFD_CORE_EXPORT ConfidentialAssetId { public: /** - * @brief コンストラクタ. - * - * リスト定義等における初期化のため、定義する。 + * @brief constructor. */ ConfidentialAssetId(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] hex_string hex string. */ explicit ConfidentialAssetId(const std::string& hex_string); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] byte_data byte array data. */ explicit ConfidentialAssetId(const ByteData& byte_data); /** - * @brief デストラクタ. + * @brief destructor. */ virtual ~ConfidentialAssetId() { // do nothing } + /** + * @brief copy constructor. + * @param[in] object object + */ + ConfidentialAssetId(const ConfidentialAssetId& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + ConfidentialAssetId& operator=(const ConfidentialAssetId& object); /** - * @brief バイトデータを取得する. + * @brief Get byte data. * @return byte array data. */ ByteData GetData() const; /** - * @brief HEX文字列を取得する. + * @brief Get the HEX string. * @return hex string (reverse data) */ std::string GetHex() const; /** - * @brief blind有無を取得する. + * @brief Get if it is blind. * @retval true blind * @retval false unblind */ bool HasBlinding() const; /** - * @brief unblind時、バイトデータを取得する. + * @brief Get unblinded byte data. * @return byte array data. */ ByteData GetUnblindedData() const; /** - * @brief 空かどうかを取得する. + * @brief Get if it's empty. * @retval true empty * @retval false exist value */ @@ -170,76 +188,85 @@ class CFD_CORE_EXPORT ConfidentialAssetId { }; /** - * @brief value情報を保持するクラス + * @brief Class that holds value information */ class CFD_CORE_EXPORT ConfidentialValue { public: /** - * @brief コンストラクタ. - * - * リスト定義等における初期化のため、定義する。 + * @brief constructor. */ ConfidentialValue(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] hex_string hex string. */ explicit ConfidentialValue(const std::string& hex_string); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] byte_data byte array data. */ explicit ConfidentialValue(const ByteData& byte_data); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] amount amount */ explicit ConfidentialValue(const Amount& amount); /** - * @brief デストラクタ. + * @brief destructor. */ virtual ~ConfidentialValue() { // do nothing } + /** + * @brief copy constructor. + * @param[in] object object + */ + ConfidentialValue(const ConfidentialValue& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + ConfidentialValue& operator=(const ConfidentialValue& object); /** - * @brief バイトデータを取得する. + * @brief Get byte data. * @return byte array data. */ ByteData GetData() const; /** - * @brief HEX文字列を取得する. + * @brief Get the HEX string. * @return hex string */ std::string GetHex() const; /** - * @brief Amountを取得する. + * @brief Get Amount. * - * なおblind状態では0を返却する。 + * In the blind state, 0 is returned. * @return Amount */ Amount GetAmount() const; /** - * @brief blind有無を取得する. + * @brief Get if it is blind. * @retval true blind * @retval false unblind */ bool HasBlinding() const; /** - * @brief 空かどうかを取得する. + * @brief Get if it's empty. * @retval true empty * @retval false exist value */ bool IsEmpty() const; /** - * @brief satoshiをConfidentialValueへと変換する. + * @brief Convert satoshi to Confidential Value. * @param[in] value amount value. * @return ConfidentialValue */ static ByteData ConvertToConfidentialValue(const Amount& value); /** - * @brief ConfidentialValueをsatoshiへと変換する. + * @brief Convert Confidential Value to satoshi. * @param[in] value ConfidentialValue. * @return amount value */ @@ -268,50 +295,59 @@ class CFD_CORE_EXPORT ConfidentialValue { }; /** - * @brief factor情報を保持するクラス + * @brief Class that holds blind factor information */ class CFD_CORE_EXPORT BlindFactor { public: /** - * @brief コンストラクタ. - * - * リスト定義等における初期化のため、定義する。 + * @brief constructor. */ BlindFactor(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] hex_string hex string. */ explicit BlindFactor(const std::string& hex_string); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] byte_data byte array data. */ explicit BlindFactor(const ByteData& byte_data); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] byte_data byte array data. */ explicit BlindFactor(const ByteData256& byte_data); /** - * @brief デストラクタ. + * @brief destructor. */ virtual ~BlindFactor() { // do nothing } + /** + * @brief copy constructor. + * @param[in] object object + */ + BlindFactor(const BlindFactor& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + BlindFactor& operator=(const BlindFactor& object); /** - * @brief バイトデータを取得する. + * @brief Get byte data. * @return byte array data. */ ByteData256 GetData() const; /** - * @brief HEX文字列を取得する. + * @brief Get the HEX string. * @return hex string (reverse data) */ std::string GetHex() const; /** - * @brief 空かどうかを取得する. + * @brief Get if it's empty. * @retval true empty * @retval false exist value */ @@ -322,7 +358,7 @@ class CFD_CORE_EXPORT BlindFactor { }; /** - * @brief TxIn情報を保持するクラス + * @brief Class that holds TxIn information */ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { public: @@ -383,37 +419,37 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { uint32_t* rangeproof_size = nullptr); /** - * @brief コンストラクタ. + * @brief constructor. */ ConfidentialTxIn(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) + * @param[in] index txout's index (vout) */ ConfidentialTxIn(const Txid& txid, uint32_t index); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index txout's index (vout) + * @param[in] sequence sequence */ ConfidentialTxIn(const Txid& txid, uint32_t index, uint32_t sequence); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index txout's index (vout) + * @param[in] sequence sequence * @param[in] unlocking_script unlocking script */ ConfidentialTxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index txout's index (vout) + * @param[in] sequence sequence * @param[in] unlocking_script unlocking script * @param[in] witness_stack witness stack * @param[in] blinding_nonce blinding nonce @@ -434,14 +470,14 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { const ByteData& inflation_keys_rangeproof, const ScriptWitness& pegin_witness); /** - * @brief デストラクタ + * @brief destructor */ virtual ~ConfidentialTxIn() { // do nothing } /** - * @brief 情報を更新する. + * @brief Update issuance information. * @param[in] blinding_nonce blinding nonce * @param[in] asset_entropy asset entropy * @param[in] issuance_amount issuance amount @@ -456,72 +492,72 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { const ByteData& issuance_amount_rangeproof, const ByteData& inflation_keys_rangeproof); /** - * @brief BlindingNonceを取得する - * @return BlindingNonceのByteData256インスタンス + * @brief Get Blinding Nonce + * @return BlindingNonce */ ByteData256 GetBlindingNonce() const { return blinding_nonce_; } /** - * @brief AssetEntropyを取得する - * @return AssetEntropyのByteData256インスタンス + * @brief Get Asset Entropy + * @return AssetEntropy */ ByteData256 GetAssetEntropy() const { return asset_entropy_; } /** - * @brief IssuanceAmountを取得する - * @return IssuanceAmountのByteDataインスタンス + * @brief Get IssuanceAmount + * @return IssuanceAmount */ ConfidentialValue GetIssuanceAmount() const { return issuance_amount_; } /** - * @brief InflationKeysを取得する - * @return InflationKeysのByteDataインスタンス + * @brief Get InflationKeys + * @return InflationKeys */ ConfidentialValue GetInflationKeys() const { return inflation_keys_; } /** - * @brief IssuanceAmountRangeproofを取得する - * @return IssuanceAmountRangeproofのByteDataインスタンス + * @brief Get IssuanceAmountRangeproof + * @return IssuanceAmountRangeproof */ ByteData GetIssuanceAmountRangeproof() const { return issuance_amount_rangeproof_; } /** - * @brief InflationKeysRangeproofを取得する - * @return InflationKeysRangeproofのByteDataインスタンス + * @brief Get InflationKeysRangeproof + * @return InflationKeysRangeproof */ ByteData GetInflationKeysRangeproof() const { return inflation_keys_rangeproof_; } /** - * @brief PeginWitnessを取得する - * @return PeginWitnessのScriptWitnessインスタンス + * @brief Get PeginWitness + * @return PeginWitness's 'ScriptWitness */ ScriptWitness GetPeginWitness() const { return pegin_witness_; } /** - * @brief pegin witnessの現在のstack数を取得する. - * @return pegin witnessのstack数 + * @brief Get the current stack count of pegin witness. + * @return stack count of pegin witness. */ uint32_t GetPeginWitnessStackNum() const { return pegin_witness_.GetWitnessNum(); } /** - * @brief pegin witnessにバイトデータを追加する. - * @param[in] data witness stack情報 - * @return pegin witnessのScriptWitnessインスタンス + * @brief Add byte data to pegin witness. + * @param[in] data witness stack data + * @return Script Witness instance of pegin witness */ ScriptWitness AddPeginWitnessStack(const ByteData& data); /** - * @brief pegin witnessにバイトデータを設定する. - * @param[in] index witness stackのindex値 - * @param[in] data witness stack情報 - * @return pegin witnessのScriptWitnessインスタンス + * @brief Set byte data to pegin witness. + * @param[in] index witness stack index + * @param[in] data witness stack data + * @return Script Witness instance of pegin witness */ ScriptWitness SetPeginWitnessStack(uint32_t index, const ByteData& data); /** - * @brief pegin witnessを全て削除する. + * @brief Remove all pegin witness. */ void RemovePeginWitnessStackAll(); /** - * @brief witness hashを取得する. + * @brief Get the witness hash. * @return witness hash */ ByteData256 GetWitnessHash() const; @@ -537,72 +573,70 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { }; /** - * @brief TxIn情報を参照するためのクラス + * @brief Class for referencing TxIn information */ class CFD_CORE_EXPORT ConfidentialTxInReference : public AbstractTxInReference { public: /** - * @brief コンストラクタ. - * @param[in] tx_in 参照するTxInインスタンス + * @brief constructor. + * @param[in] tx_in TxIn */ explicit ConfidentialTxInReference(const ConfidentialTxIn& tx_in); /** - * @brief デフォルトコンストラクタ. - * - * リスト作成用。 + * @brief default constructor. */ ConfidentialTxInReference(); /** - * @brief デストラクタ + * @brief destructor */ virtual ~ConfidentialTxInReference() { // do nothing } /** - * @brief BlindingNonceを取得する - * @return BlindingNonceのByteData256インスタンス + * @brief Get Blinding Nonce + * @return BlindingNonce */ ByteData256 GetBlindingNonce() const { return blinding_nonce_; } /** - * @brief AssetEntropyを取得する - * @return AssetEntropyのByteData256インスタンス + * @brief Get AssetEntropy + * @return AssetEntropy */ ByteData256 GetAssetEntropy() const { return asset_entropy_; } /** - * @brief IssuanceAmountを取得する - * @return IssuanceAmountのByteDataインスタンス + * @brief Get IssuanceAmount + * @return IssuanceAmount */ ConfidentialValue GetIssuanceAmount() const { return issuance_amount_; } /** - * @brief InflationKeysを取得する - * @return InflationKeysのByteDataインスタンス + * @brief Get InflationKeys + * @return InflationKeys */ ConfidentialValue GetInflationKeys() const { return inflation_keys_; } /** - * @brief IssuanceAmountRangeproofを取得する - * @return IssuanceAmountRangeproofのByteDataインスタンス + * @brief Get IssuanceAmountRangeproof + * @return IssuanceAmountRangeproof */ ByteData GetIssuanceAmountRangeproof() const { return issuance_amount_rangeproof_; } /** - * @brief InflationKeysRangeproofを取得する - * @return InflationKeysRangeproofのByteDataインスタンス + * @brief Get InflationKeysRangeproof + * @return InflationKeysRangeproof */ ByteData GetInflationKeysRangeproof() const { return inflation_keys_rangeproof_; } /** - * @brief PeginWitnessを取得する - * @return PeginWitnessのScriptWitnessインスタンス + * @brief Get PeginWitness + * @return ScriptWitness instance of PeginWitness */ ScriptWitness GetPeginWitness() const { return pegin_witness_; } /** - * @brief pegin witnessの現在のstack数を取得する. - * @return pegin witnessのstack数 + * @brief Get the current stack count of pegin witness. + * @return stack count of pegin witness. */ uint32_t GetPeginWitnessStackNum() const { return pegin_witness_.GetWitnessNum(); @@ -674,16 +708,16 @@ struct RangeProofInfo { }; /** - * @brief Confidential TransactionのTxOut情報を保持するクラス + * @brief Class that holds TxOut information of Confidential Transaction */ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { public: /** - * @brief コンストラクタ + * @brief constructor */ ConfidentialTxOut(); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] locking_script locking script. * @param[in] asset asset. * @param[in] confidential_value value by confidential transaction. @@ -692,9 +726,9 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { const Script& locking_script, const ConfidentialAssetId& asset, const ConfidentialValue& confidential_value); /** - * @brief コンストラクタ. + * @brief constructor. * - * blind後の情報登録用. + * For information registration after blind. * @param[in] locking_script locking script. * @param[in] asset asset. * @param[in] confidential_value value by confidential transaction. @@ -708,9 +742,9 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { const ConfidentialNonce& nonce, const ByteData& surjection_proof, const ByteData& range_proof); /** - * @brief コンストラクタ. + * @brief constructor. * - * fee追加用. + * For additional fee. * @param[in] asset asset. * @param[in] confidential_value value by confidential transaction. */ @@ -718,15 +752,15 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { const ConfidentialAssetId& asset, const ConfidentialValue& confidential_value); /** - * @brief コンストラクタ. + * @brief constructor. * - * fee追加用. + * For additional fee. * @param[in] asset asset. * @param[in] amount amount. */ ConfidentialTxOut(const ConfidentialAssetId& asset, const Amount& amount); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] address address. * @param[in] asset asset. * @param[in] amount amount. @@ -735,7 +769,7 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { const Address& address, const ConfidentialAssetId& asset, const Amount& amount); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] confidential_address confidential address. * @param[in] asset asset. * @param[in] amount amount. @@ -744,7 +778,7 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { const ElementsConfidentialAddress& confidential_address, const ConfidentialAssetId& asset, const Amount& amount); /** - * @brief デストラクタ + * @brief destructor */ virtual ~ConfidentialTxOut() { // do nothing @@ -772,18 +806,18 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { */ void SetNonce(const ConfidentialNonce& nonce); /** - * @brief valueを設定する。 + * @brief set amount value. * @param[in] value amount value. */ virtual void SetValue(const Amount& value); /** - * @brief assetを取得する。 + * @brief set asset. * @return asset */ ConfidentialAssetId GetAsset() const { return asset_; } /** - * @brief confidential valueを取得する。 + * @brief Get confidential value. * @return confidential value */ ConfidentialValue GetConfidentialValue() const { @@ -791,25 +825,25 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { } /** - * @brief nonceを取得する。 + * @brief Get nonce * @return nonce */ ConfidentialNonce GetNonce() const { return nonce_; } /** - * @brief surjection proofを取得する。 + * @brief Get surjection proof * @return surjection proof */ ByteData GetSurjectionProof() const { return surjection_proof_; } /** - * @brief range proofを取得する。 + * @brief Get range proof * @return range proof */ ByteData GetRangeProof() const { return range_proof_; } /** - * @brief witness hashを取得する. + * @brief Get witness hash * @return witness hash */ ByteData256 GetWitnessHash() const; @@ -840,40 +874,38 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { }; /** - * @brief Confidential TransactionのTxOut情報を参照するためのクラス + * @brief Class for referencing TxOut information of Confidential Transaction */ class CFD_CORE_EXPORT ConfidentialTxOutReference : public AbstractTxOutReference { public: /** - * @brief コンストラクタ + * @brief constructor * @param[in] tx_out Confidential Transaction's TxOut. */ explicit ConfidentialTxOutReference(const ConfidentialTxOut& tx_out); /** - * @brief デフォルトコンストラクタ. - * - * リスト作成用。 + * @brief default constructor. */ ConfidentialTxOutReference() : ConfidentialTxOutReference(ConfidentialTxOut()) { // do nothing } /** - * @brief デストラクタ + * @brief destructor */ virtual ~ConfidentialTxOutReference() { // do nothing } /** - * @brief assetを取得する。 + * @brief Get asset * @return asset */ ConfidentialAssetId GetAsset() const { return asset_; } /** - * @brief confidential valueを取得する。 + * @brief Get confidential value * @return confidential value */ ConfidentialValue GetConfidentialValue() const { @@ -881,19 +913,19 @@ class CFD_CORE_EXPORT ConfidentialTxOutReference } /** - * @brief nonceを取得する。 + * @brief Get nonce * @return nonce */ ConfidentialNonce GetNonce() const { return nonce_; } /** - * @brief surjection proofを取得する。 + * @brief Get surjection proof * @return surjection proof */ ByteData GetSurjectionProof() const { return surjection_proof_; } /** - * @brief range proofを取得する。 + * @brief Get range proof * @return range proof */ ByteData GetRangeProof() const { return range_proof_; } @@ -945,7 +977,7 @@ class CFD_CORE_EXPORT ConfidentialTxOutReference }; /** - * @brief Issuance出力情報構造体 + * @brief Issuance output information structure */ struct IssuanceParameter { BlindFactor entropy; //!< entropy @@ -954,7 +986,7 @@ struct IssuanceParameter { }; /** - * @brief Unblind出力情報構造体 + * @brief Unblind output information structure */ struct UnblindParameter { ConfidentialAssetId asset; //!< confidential asset @@ -964,7 +996,7 @@ struct UnblindParameter { }; /** - * @brief Blind用情報構造体 + * @brief Information structure for Blind */ using BlindParameter = UnblindParameter; @@ -983,7 +1015,7 @@ struct BlindData { }; /** - * @brief Issuance confidentialKeyペア構造体 + * @brief Issuance confidentialKey pair structure */ struct IssuanceBlindingKeyPair { Privkey asset_key; //!< asset blinding key @@ -991,7 +1023,7 @@ struct IssuanceBlindingKeyPair { }; /** - * @brief PegOut Key情報構造体 + * @brief PegOut Key Information Structure */ struct PegoutKeyData { Pubkey btc_pubkey_bytes; //!< bitcoin pubkey byte data @@ -999,28 +1031,26 @@ struct PegoutKeyData { }; /** - * @brief Confidential Transaction情報クラス + * @brief Confidential Transaction information class */ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { public: - /// ElementsTransactionの最小サイズ + /// Minimum size of Elements Transaction static constexpr size_t kElementsTransactionMinimumSize = 11; /** - * @brief コンストラクタ. - * - * リスト作成用。 + * @brief constructor. */ ConfidentialTransaction(); /** - * @brief コンストラクタ + * @brief constructor * @param[in] version version * @param[in] lock_time lock time */ explicit ConfidentialTransaction(int32_t version, uint32_t lock_time); /** - * @brief コンストラクタ - * @param[in] hex_string txバイトデータのHEX文字列 + * @brief constructor + * @param[in] hex_string tx hex string */ explicit ConfidentialTransaction(const std::string& hex_string); /** @@ -1029,155 +1059,155 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { */ explicit ConfidentialTransaction(const ByteData& byte_data); /** - * @brief コンストラクタ - * @param[in] transaction トランザクション情報 + * @brief copy constructor + * @param[in] transaction transaction object */ explicit ConfidentialTransaction(const ConfidentialTransaction& transaction); /** - * @brief デストラクタ + * @brief destructor */ virtual ~ConfidentialTransaction() { // do nothing } /** - * @brief コピーコンストラクタ. - * @param[in] transaction トランザクション情報 - * @return Confidential Transactionオブジェクト + * @brief copy constructor. + * @param[in] transaction transaction object + * @return Confidential Transaction */ ConfidentialTransaction& operator=( const ConfidentialTransaction& transaction) &; /** - * @brief TxInを取得する. - * @param[in] index 取得するindex位置 - * @return 指定indexのTxInインスタンス + * @brief Get TxIn. + * @param[in] index index + * @return ConfidentialTxInReference */ const ConfidentialTxInReference GetTxIn(uint32_t index) const; /** - * @brief TxInのindexを取得する. - * @param[in] txid 取得するTxInのtxid - * @param[in] vout 取得するTxInのvout - * @return 条件に合致するTxInのindex番号 + * @brief Get TxIn index. + * @param[in] txid txid + * @param[in] vout vout + * @return index */ virtual uint32_t GetTxInIndex(const Txid& txid, uint32_t vout) const; /** - * @brief TxOutのindexを取得する. + * @brief Get the index of TxOut. * @param[in] locking_script locking script - * @return 条件に合致するTxOutのindex番号 + * @return TxOut index */ virtual uint32_t GetTxOutIndex(const Script& locking_script) const; /** - * @brief TxOutのindexを一括取得する. + * @brief Get the indexes of TxOut. * @param[in] locking_script locking script - * @return 条件に合致するTxOutのindex番号の一覧 + * @return TxOut index list. */ virtual std::vector GetTxOutIndexList( const Script& locking_script) const; /** - * @brief 保持しているTxInの数を取得する. - * @return TxIn数 + * @brief Get the count of TxIns. + * @return count of TxIns. */ uint32_t GetTxInCount() const; /** - * @brief TxIn一覧を取得する. - * @return TxInReference一覧 + * @brief Get TxIn list. + * @return TxInReference list */ const std::vector GetTxInList() const; /** - * @brief TxInを追加する. + * @brief Add TxIn. * @param[in] txid txid * @param[in] index vout * @param[in] sequence sequence - * @param[in] unlocking_script unlocking script (未指定時はEmptyを設定する. default Script::Empty) - * @return 追加したTxInのindex位置 + * @param[in] unlocking_script unlocking script + * @return Added TxIn index */ uint32_t AddTxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script = Script::Empty); /** - * @brief TxIn情報を削除する. - * @param[in] index 削除するindex位置 + * @brief Delete TxIn + * @param[in] index txin index */ void RemoveTxIn(uint32_t index); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking script (Push Op Only) + * @brief Set unlocking script. + * @param[in] tx_in_index TxIn index + * @param[in] unlocking_script unlocking script (Push Op Only) */ void SetUnlockingScript( uint32_t tx_in_index, const Script& unlocking_script); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking scriptの構成要素リスト + * @brief Set unlocking script. + * @param[in] tx_in_index TxIn index + * @param[in] unlocking_script Unlocking script component list */ void SetUnlockingScript( uint32_t tx_in_index, const std::vector& unlocking_script); /** - * @brief witness stackの現在の個数を取得する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @return witness stackの個数 + * @brief Get the count of witness stacks. + * @param[in] tx_in_index TxIn index + * @return count of witness stacks. */ uint32_t GetScriptWitnessStackNum(uint32_t tx_in_index) const; /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData160& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData256& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData160& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData256& data); /** - * @brief script witnessを全て削除する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @brief Remove all script witness. + * @param[in] tx_in_index TxIn index */ void RemoveScriptWitnessStackAll(uint32_t tx_in_index); /** * @brief 情報を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @param[in] tx_in_index TxIn index * @param[in] blinding_nonce blinding nonce * @param[in] asset_entropy asset entropy * @param[in] issuance_amount issuance amount @@ -1193,69 +1223,69 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ByteData inflation_keys_rangeproof); /** * @brief witness stackの現在の個数を取得する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @param[in] tx_in_index TxIn index * @return witness stackの個数 */ uint32_t GetPeginWitnessStackNum(uint32_t tx_in_index) const; /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddPeginWitnessStack( uint32_t tx_in_index, const ByteData& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddPeginWitnessStack( uint32_t tx_in_index, const ByteData160& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness AddPeginWitnessStack( uint32_t tx_in_index, const ByteData256& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetPeginWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetPeginWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData160& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetPeginWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData256& data); /** - * @brief script witnessを全て削除する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @brief Remove all script witness. + * @param[in] tx_in_index TxIn index */ void RemovePeginWitnessStackAll(uint32_t tx_in_index); /** - * @brief IssueAssetの情報を設定する. + * @brief Set the Issue Asset information. * @param[in] tx_in_index Txin index * @param[in] asset_amount issuance amount * @param[in] asset_locking_script asset locking script @@ -1274,7 +1304,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ConfidentialNonce& token_nonce, bool is_blind, const ByteData256& contract_hash); /** - * @brief IssueAssetの情報を設定する. + * @brief Set the Issue Asset information. * @param[in] tx_in_index Txin index * @param[in] asset_amount issuance amount * @param[in] asset_output_amount_list asset output list @@ -1300,7 +1330,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ByteData256& contract_hash); /** - * @brief ReissueAssetの情報を設定する. + * @brief ReSet the Issue Asset information. * @param[in] tx_in_index Txin index * @param[in] asset_amount reissuance amount * @param[in] asset_locking_script asset locking script @@ -1315,7 +1345,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ConfidentialNonce& asset_blind_nonce, const BlindFactor& asset_blind_factor, const BlindFactor& entropy); /** - * @brief ReissueAssetの情報を設定する. + * @brief ReSet the Issue Asset information. * @param[in] tx_in_index Txin index * @param[in] asset_amount reissuance amount * @param[in] asset_output_amount_list asset output list @@ -1333,61 +1363,61 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const BlindFactor& asset_blind_factor, const BlindFactor& entropy); /** - * @brief TxOutを取得する. - * @param[in] index 取得するindex位置 - * @return TxOutReference + * @brief Get TxOut. + * @param[in] index index + * @return ConfidentialTxOutReference */ const ConfidentialTxOutReference GetTxOut(uint32_t index) const; /** - * @brief 保持しているTxOutの数を取得する. - * @return TxOut数 + * @brief Get the count of TxOuts. + * @return count of TxOuts. */ uint32_t GetTxOutCount() const; /** - * @brief TxOut一覧を取得する. - * @return TxOutReference一覧 + * @brief Get TxOut list. + * @return ConfidentialTxOutReference list */ const std::vector GetTxOutList() const; /** - * @brief TxOut情報を追加する. + * @brief Add TxOut information. * @param[in] value amount * @param[in] asset asset * @param[in] locking_script locking script - * @return 追加したTxOutのindex位置 + * @return Added TxOut index */ uint32_t AddTxOut( const Amount& value, const ConfidentialAssetId& asset, const Script& locking_script); /** - * @brief TxOut情報を追加する. + * @brief Add TxOut information. * @param[in] value amount * @param[in] asset asset * @param[in] locking_script locking script * @param[in] nonce nonce - * @return 追加したTxOutのindex位置 + * @return Added TxOut index */ uint32_t AddTxOut( const Amount& value, const ConfidentialAssetId& asset, const Script& locking_script, const ConfidentialNonce& nonce); /** - * @brief TxOut情報を追加する. + * @brief Add TxOut information. * @param[in] value amount * @param[in] asset asset * @param[in] locking_script locking script * @param[in] nonce nonce. * @param[in] surjection_proof surjection proof. * @param[in] range_proof range proof. - * @return 追加したTxOutのindex位置 + * @return Added TxOut index */ uint32_t AddTxOut( const Amount& value, const ConfidentialAssetId& asset, const Script& locking_script, const ConfidentialNonce& nonce, const ByteData& surjection_proof, const ByteData& range_proof); /** - * @brief TxOut情報としてfeeを追加する. + * @brief Add fee as TxOut information. * @param[in] value amount * @param[in] asset asset - * @return 追加したTxOutのindex位置 + * @return Added TxOut index */ uint32_t AddTxOutFee(const Amount& value, const ConfidentialAssetId& asset); /** @@ -1397,8 +1427,8 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { */ void SetTxOutValue(uint32_t index, const Amount& value); /** - * @brief TxOut情報を更新する. - * @param[in] index index位置 + * @brief Update TxOut information. + * @param[in] index index * @param[in] asset asset * @param[in] value amount * @param[in] nonce nonce. @@ -1410,10 +1440,27 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ConfidentialValue& value, const ConfidentialNonce& nonce, const ByteData& surjection_proof, const ByteData& range_proof); /** - * @brief TxOut情報を削除する. - * @param[in] index 取得するindex位置 + * @brief Delete the TxOut information. + * @param[in] index index */ void RemoveTxOut(uint32_t index); + + /** + * @brief Get the total byte size of Transaction. + * @return Total byte size + */ + virtual uint32_t GetTotalSize() const; + /** + * @brief Get vsize information of Transaction. + * @return vsize + */ + virtual uint32_t GetVsize() const; + /** + * @brief Get the Weight information of Transaction. + * @return weight + */ + virtual uint32_t GetWeight() const; + /** * @brief Blinding transaction. * @param[in] txin_info_list txin blind info list. @@ -1435,7 +1482,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { int minimum_bits = kDefaultBlindMinimumBits, std::vector* blinder_list = nullptr); /** - * @brief TransactionのTxOutのblindingを行う. + * @brief Blinding TxOut of Transaction. * @param[in] txin_info_list txin blind info list. * @param[in] txout_confidential_keys blinding pubkey list. * @param[in] minimum_range_value rangeproof minimum value. @@ -1453,39 +1500,39 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { int minimum_bits = kDefaultBlindMinimumBits, std::vector* blinder_list = nullptr); /** - * @brief indexで指定されたInputに対して、unblind処理を行う. - * @param tx_in_index TxInのindex値 - * @param blinding_key blinding key(秘密鍵) - * @param token_blinding_key token blinding key(秘密鍵). - * @return unblindの出力データを格納したUnblindParameter構造体 + * @brief Performs unblind processing for the specified Input. + * @param tx_in_index TxIn index + * @param blinding_key blinding key(private key) + * @param token_blinding_key token blinding key(private key). + * @return UnblindParameter structure containing unblind output data list */ std::vector UnblindTxIn( uint32_t tx_in_index, const Privkey& blinding_key, const Privkey token_blinding_key = Privkey()); /** - * @brief indexで指定されたOutputに対して、unblind処理を行う. - * @param tx_out_index TxOutのindex値 - * @param blinding_key blinding key(秘密鍵) - * @return unblindの出力データを格納したUnblindParameter構造体 + * @brief Performs unblind processing for the specified Output. + * @param tx_out_index TxOut index + * @param blinding_key blinding key(private key) + * @return UnblindParameter structure containing unblind output data */ UnblindParameter UnblindTxOut( uint32_t tx_out_index, const Privkey& blinding_key); /** - * @brief Transactionの全てのOutputに対して、unblind処理を行う. - * @param[in] blinding_keys blinding key(秘密鍵) list - * @return unblindの出力データを格納したUnblindParameter構造体リスト + * @brief Unblind processing is performed for all Outputs of Transaction. + * @param[in] blinding_keys blinding key(private key) list + * @return UnblindParameter structure containing unblind output data list */ std::vector UnblindTxOut( const std::vector& blinding_keys); /** - * @brief Elements用signatureハッシュを取得する. - * @param[in] txin_index TxInのindex値 - * @param[in] script_data unlocking script もしくは witness_program. + * @brief Get the signature hash for Confidential Transaction. + * @param[in] txin_index TxIn index + * @param[in] script_data unlocking script or witness program. * @param[in] sighash_type SigHashType(@see cfdcore_util.h) - * @param[in] value TxInのAmount/amountcommitment値. + * @param[in] value TxIn Amount/amountcommitment. * @param[in] version Witness version - * @return signatureハッシュ + * @return signature hash */ ByteData256 GetElementsSignatureHash( uint32_t txin_index, const ByteData& script_data, @@ -1494,32 +1541,32 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { WitnessVersion version = WitnessVersion::kVersionNone) const; /** - * @brief TxOutの順序をランダムソートする. - * @details ブラインド前のみ実施可能. + * @brief Randomly sort the order of TxOut. + * @details Can only be done before the blinds. */ void RandomSortTxOut(); /** - * @brief witness情報のみのHashを取得する. + * @brief Get a Hash of witness information only. * @return witness only hash */ ByteData256 GetWitnessOnlyHash() const; /** - * @brief witness情報かどうかを取得する. - * @retval true witness - * @retval false witnessではない + * @brief Get if it have witness information. + * @retval true witness exist + * @retval false witness not found */ virtual bool HasWitness() const; /** - * @brief libwally処理用フラグを取得する。 - * @return libwally用フラグ + * @brief libwally Get the processing flag. + * @return libwally flag */ virtual uint32_t GetWallyFlag() const; /** - * @brief Bitcoin Transaction情報を取得する。 + * @brief Get Bitcoin Transaction information. * @param[in] bitcoin_tx_data bitcoin transaction data * @param[in] is_remove_witness remove witness flag * @return transaction data. @@ -1527,7 +1574,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { static ByteData GetBitcoinTransaction( const ByteData& bitcoin_tx_data, bool is_remove_witness = false); /** - * @brief asset entropyの情報を算出する. + * @brief Calculate asset entropy information. * @param[in] txid utxo txid * @param[in] vout utxo vout * @param[in] contract_hash asset entropy @@ -1536,13 +1583,13 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { static BlindFactor CalculateAssetEntropy( const Txid& txid, const uint32_t vout, const ByteData256& contract_hash); /** - * @brief assetの情報を算出する. + * @brief Calculate asset information. * @param[in] entropy asset entropy * @return asset id (ConfidentialAssetId). */ static ConfidentialAssetId CalculateAsset(const BlindFactor& entropy); /** - * @brief reissuance tokenの情報を算出する. + * @brief Calculate the reissuance token information. * @param[in] entropy asset entropy * @param[in] is_blind asset is blinded or not * @return reissuance token (ConfidentialAssetId). @@ -1550,7 +1597,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { static ConfidentialAssetId CalculateReissuanceToken( const BlindFactor& entropy, bool is_blind); /** - * @brief IssueAssetの情報を設定する. + * @brief Set the Issue Asset information. * @param[in] txid utxo txid * @param[in] vout utxo vout * @param[in] is_blind blinding issuance @@ -1562,7 +1609,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const Txid& txid, uint32_t vout, bool is_blind, const ByteData256& contract_hash, const ByteData256& asset_entropy); /** - * @brief issuance/reissuanceのblinding keyを取得する. + * @brief Get the blinding key of issuance / reissuance. * @param[in] master_blinding_key master blindingKey * @param[in] txid issuance utxo txid * @param[in] vout issuance utxo vout @@ -1571,7 +1618,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { static Privkey GetIssuanceBlindingKey( const Privkey& master_blinding_key, const Txid& txid, uint32_t vout); /** - * @brief pegoutで使用するpubkey情報を取得する. + * @brief Get the pubkey information used by pegout. * @param[in] online_pubkey online pubkey * @param[in] master_online_key online privkey * @param[in] bitcoin_descriptor bip32 pubkey (m/0/\*) @@ -1593,107 +1640,106 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { Address* descriptor_derive_address = nullptr); protected: - std::vector vin_; ///< TxIn配列 - std::vector vout_; ///< TxOut配列 + std::vector vin_; ///< TxIn array + std::vector vout_; ///< TxOut array /** - * @brief HEX文字列からTransaction情報を設定する. - * @param[in] hex_string TransactionバイトデータのHEX文字列 + * @brief Set Transaction information from HEX string. + * @param[in] hex_string HEX string. */ void SetFromHex(const std::string& hex_string); private: /** - * @brief TxIn配列のIndex範囲をチェックする. - * @param[in] index TxIn配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @brief check TxIn array range. + * @param[in] index TxIn Index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxInIndex( uint32_t index, int line, const char* caller) const; /** - * @brief TxOut配列のIndex範囲をチェックする. * @brief check TxOut array range. - * @param[in] index TxOut配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @param[in] index TxOut Index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxOutIndex( uint32_t index, int line, const char* caller) const; /** - * @brief witness stackに情報を追加する. - * @param[in] tx_in_index TxIn配列のindex値 - * @param[in] data witness stackに追加するバイトデータ + * @brief Add information to the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data data to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const std::vector& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const std::vector& data); /** - * @brief witness stackに情報を追加する. - * @param[in] tx_in_index TxIn配列のindex値 - * @param[in] data witness stackに追加するバイトデータ + * @brief Add information to the pegin witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data data to add to the witness stack * @return witness stack */ const ScriptWitness AddPeginWitnessStack( uint32_t tx_in_index, const std::vector& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the pegin witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Information to add to the witness stack * @return witness stack */ const ScriptWitness SetPeginWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const std::vector& data); /** - * @brief Transactionのバイトデータを取得する. - * @param[in] has_witness witnessを含めるかのフラグ - * @return バイトデータ + * @brief Get the byte data of Transaction. + * @param[in] has_witness Flag to include witness + * @return ByteData */ ByteData GetByteData(bool has_witness) const; /** - * @brief ElementsのTx状態フラグ(libwally値)を設定する。 + * @brief Set the Tx status flag (libwally value) of Elements. */ void SetElementsTxState(); /** - * @brief 配列をByteDataへと変換する. + * @brief Convert the array to ByteData. * @param[in] data buffer * @param[in] size size * @return ByteData */ static ByteData ConvertToByteData(const uint8_t* data, size_t size); /** - * @brief ConfidentialNonce情報をコピーする。 - * @param[in] buffer コピー元のバッファ - * @param[in] buffer_size バッファサイズ - * @param[in] explicit_size unblind時のサイズ - * @param[in] address コピー先のアドレス - * @return 移動後のアドレス + * @brief Copy the Confidential Nonce information. + * @param[in] buffer Copy source buffer + * @param[in] buffer_size Copy source buffer size + * @param[in] explicit_size Unblind size + * @param[in] address Copy destination address + * @return Address after moving */ static uint8_t* CopyConfidentialCommitment( const void* buffer, size_t buffer_size, size_t explicit_size, uint8_t* address); /** - * blindされたデータに対して、unblind処理をかける - * @param[in] nonce nonce値 - * @param[in] blinding_key blindingした際の秘密鍵 - * @param[in] rangeproof asset amountの検証に用いる検証値 - * @param[in] value_commitment blindされたvalueのcommitement値 - * @param[in] extra unblindに必要な情報 + * @brief Unblind processing is applied to blinded data + * @param[in] nonce nonce + * @param[in] blinding_key blinding private key + * @param[in] rangeproof asset amount rangeproof + * @param[in] value_commitment blind value commitement + * @param[in] extra unblind need data * @param[in] asset confidential asset id - * @return Unblindされた際に出力されたUnblindParameter構造体 + * @return UnblindParameter structure output when unblinded */ static UnblindParameter CalculateUnblindData( const ConfidentialNonce& nonce, const Privkey& blinding_key, @@ -1701,13 +1747,13 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const Script& extra, const ConfidentialAssetId& asset); /** - * blindされたIssueデータに対して、unblind処理をかける - * @param[in] blinding_key blindingした際の秘密鍵 - * @param[in] rangeproof asset amountの検証に用いる検証値 - * @param[in] value_commitment blindされたvalueのcommitement値 - * @param[in] extra unblindに必要な情報 + * @brief Unblind processing is applied to the blinded Issue data + * @param[in] blinding_key blinding private key + * @param[in] rangeproof asset amount rangeproof + * @param[in] value_commitment blind value commitement + * @param[in] extra unblind need data * @param[in] asset confidential asset id - * @return Unblindされた際に出力されたUnblindParameter構造体 + * @return UnblindParameter structure output when unblinded */ static UnblindParameter CalculateUnblindIssueData( const Privkey& blinding_key, const ByteData& rangeproof, @@ -1715,7 +1761,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const ConfidentialAssetId& asset); /** - * @brief rangeProofなどを生成する。 + * @brief Generate rangeProof etc. * @param[in] value amount * @param[in] pubkey public key * @param[in] privkey private key @@ -1741,7 +1787,7 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { std::vector* commitment, std::vector* range_proof); /** - * @brief Descriptor情報から拡張Keyを生成する. + * @brief Generate an extended Key from Descriptor information. * @param[in] bitcoin_descriptor descriptor * @param[in] bip32_counter bip32 counter * @param[in] prefix extend pubkey prefix diff --git a/include/cfdcore/cfdcore_exception.h b/include/cfdcore/cfdcore_exception.h index c11a2bc5..760c9d76 100644 --- a/include/cfdcore/cfdcore_exception.h +++ b/include/cfdcore/cfdcore_exception.h @@ -1,7 +1,7 @@ // Copyright 2019 CryptoGarage /** * @file cfdcore_exception.h - * @brief cfdの例外クラス定義ファイルです。 + * @brief The cfd exception class definition file. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_EXCEPTION_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_EXCEPTION_H_ @@ -14,11 +14,11 @@ #include "cfdcore/cfdcore_common.h" #ifndef _GLIBCXX_TXN_SAFE_DYN -/// インクルードガード(_GLIBCXX_TXN_SAFE_DYN) +/// Include guard (_GLIBCXX_TXN_SAFE_DYN) #define _GLIBCXX_TXN_SAFE_DYN #endif #ifndef _GLIBCXX_USE_NOEXCEPT -/// インクルードガード(_GLIBCXX_USE_NOEXCEPT) +/// Include guard (_GLIBCXX_USE_NOEXCEPT) #define _GLIBCXX_USE_NOEXCEPT noexcept #endif @@ -26,78 +26,78 @@ namespace cfd { namespace core { /** - * @brief エラーコード定義 + * @brief Error code definition. */ typedef enum { - kCfdSuccess = 0, //!< 正常終了 - kCfdUnknownError = -1, //!< 不明なエラー - kCfdInternalError = -2, //!< 内部エラー - kCfdMemoryFullError = -3, //!< メモリ確保エラー - kCfdIllegalArgumentError = 1, //!< 引数不正 - kCfdIllegalStateError = 2, //!< 状態不正 - kCfdOutOfRangeError = 3, //!< 範囲外の値 - kCfdInvalidSettingError = 4, //!< 設定不正 - kCfdConnectionError = 5, //!< 接続エラー - kCfdDiskAccessError = 6 //!< ディスクアクセスエラー + kCfdSuccess = 0, //!< Successful completion + kCfdUnknownError = -1, //!< Unknown error + kCfdInternalError = -2, //!< Internal error + kCfdMemoryFullError = -3, //!< Memory allocation error + kCfdIllegalArgumentError = 1, //!< Invalid argument + kCfdIllegalStateError = 2, //!< Illegal state + kCfdOutOfRangeError = 3, //!< Out of range value + kCfdInvalidSettingError = 4, //!< Improper settings + kCfdConnectionError = 5, //!< Connection error + kCfdDiskAccessError = 6 //!< Disk access error } CfdError; -/// @brief エラーメッセージ:不明なエラー +/// @brief Error message: Unknown error const char kCfdUnknownErrorMessage[] = "Unknown error occurred."; /** - * @brief CFD例外クラス + * @brief CFD exception class. */ class CfdException : public std::exception { public: /** - * @brief コンストラクタ + * @brief Constructor. */ CfdException() : error_code_(kCfdUnknownError), message_(kCfdUnknownErrorMessage) {} /** - * @brief コンストラクタ - * @param[in] message エラーメッセージ + * @brief Constructor. + * @param[in] message Error message. */ explicit CfdException(const std::string& message) : error_code_(kCfdUnknownError), message_(message) {} /** - * @brief コンストラクタ - * @param[in] error_code エラーコード + * @brief Constructor. + * @param[in] error_code Error code. */ explicit CfdException(CfdError error_code) : error_code_(error_code), message_(kCfdUnknownErrorMessage) {} /** - * @brief コンストラクタ - * @param[in] error_code エラーコード - * @param[in] message エラーメッセージ + * @brief Constructor. + * @param[in] error_code Error code. + * @param[in] message Error message. */ CfdException(CfdError error_code, const std::string& message) : error_code_(error_code), message_(message) {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~CfdException(void) _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT { // do nothing } /** - * @brief エラーメッセージを取得する. - * @return エラーメッセージ + * @brief Get an error message. + * @return Error message. */ virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT { return message_.c_str(); } /** - * @brief エラーコードを取得する. - * @return エラーコード + * @brief Get an error code. + * @return Error code. */ virtual CfdError GetErrorCode() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT { return error_code_; } /** - * @brief エラーコードに即したエラータイプを取得する. - * @return エラータイプ文字列 + * @brief Get the error type according to the error code. + * @return Error type string. */ virtual std::string GetErrorType() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT { @@ -116,38 +116,38 @@ class CfdException : public std::exception { } protected: - CfdError error_code_; ///< エラーコード - std::string message_; ///< エラーメッセージ + CfdError error_code_; ///< error code + std::string message_; ///< error message }; // ----------------------------------------------------------------------------- // InvalidScriptException // ----------------------------------------------------------------------------- -/// @brief スクリプト例外メッセージ +/// @brief Script exception message const char kCfdInvalidScriptMessage[] = "invalid script error."; /** - * @brief スクリプト例外クラス. + * @brief Script exception class. */ class InvalidScriptException : public CfdException { public: /** - * @brief コンストラクタ. + * @brief Constructor. */ InvalidScriptException() : CfdException(kCfdIllegalArgumentError, kCfdInvalidScriptMessage) { // do nothing } /** - * @brief コンストラクタ. - * @param[in] message エラーメッセージ + * @brief Constructor. + * @param[in] message error message */ explicit InvalidScriptException(const std::string& message) : CfdException(kCfdIllegalArgumentError, message) { // do nothing } /** - * @brief デストラクタ. + * @brief Destructor. */ virtual ~InvalidScriptException(void) _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT { diff --git a/include/cfdcore/cfdcore_hdwallet.h b/include/cfdcore/cfdcore_hdwallet.h index 63a46588..19368935 100644 --- a/include/cfdcore/cfdcore_hdwallet.h +++ b/include/cfdcore/cfdcore_hdwallet.h @@ -1,8 +1,8 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfdcore_hdwallet.h * - * @brief BIP32/BIP39/BIP44関連クラス + * @brief definition for BIP32/BIP39/BIP44 class. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_HDWALLET_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_HDWALLET_H_ @@ -19,9 +19,10 @@ namespace core { class ExtPrivkey; class ExtPubkey; +class KeyData; /** - * @brief HDWalletを表現するデータクラス + * @brief A data class that represents an HD Wallet. */ class CFD_CORE_EXPORT HDWallet { public: @@ -39,41 +40,41 @@ class CFD_CORE_EXPORT HDWallet { static constexpr uint32_t kSeed512Size = 64; // BIP32_ENTROPY_LEN_512 /** - * @brief デフォルトコンストラクタ + * @brief constructor. */ HDWallet(); /** - * @brief コンストラクタ - * @param[in] seed シード値 + * @brief constructor. + * @param[in] seed seed */ explicit HDWallet(const ByteData& seed); /** - * @brief コンストラクタ - * @param[in] mnemonic ニーモニック文字列配列 - * @param[in] passphrase パスフレーズ - * @param[in] use_ideographic_space 全角スペース利用フラグ(default: false) + * @brief constructor. + * @param[in] mnemonic mnemonic + * @param[in] passphrase passphrase + * @param[in] use_ideographic_space ideographic space use flag. (default: false) */ HDWallet( std::vector mnemonic, std::string passphrase, bool use_ideographic_space = false); /** - * @brief seedを取得する - * @return seed値ByteData + * @brief Get seed value. + * @return seed */ ByteData GetSeed() const; /** - * @brief 拡張秘密鍵を生成する。 + * @brief Generate an extended privkey. * @param[in] network_type network type * @return extended privkey * @throws CfdException If invalid seed. */ ExtPrivkey GeneratePrivkey(NetType network_type) const; /** - * @brief 拡張秘密鍵を生成する。 + * @brief Generate an extended privkey. * @param[in] network_type network type * @param[in] child_num child number * @return extended privkey @@ -81,7 +82,7 @@ class CFD_CORE_EXPORT HDWallet { */ ExtPrivkey GeneratePrivkey(NetType network_type, uint32_t child_num) const; /** - * @brief 拡張秘密鍵を生成する。 + * @brief Generate an extended privkey. * @param[in] network_type network type * @param[in] path child number path * @return extended privkey @@ -90,7 +91,7 @@ class CFD_CORE_EXPORT HDWallet { ExtPrivkey GeneratePrivkey( NetType network_type, const std::vector& path) const; /** - * @brief 拡張秘密鍵を生成する。 + * @brief Generate an extended privkey. * @param[in] network_type network type * @param[in] string_path child number string path * @return extended privkey @@ -100,14 +101,33 @@ class CFD_CORE_EXPORT HDWallet { NetType network_type, const std::string& string_path) const; /** - * @brief 拡張公開鍵を生成する。 + * @brief Generate an extended privkey data. + * @param[in] network_type network type + * @param[in] path child number path + * @return extended privkey + * @throws CfdException If invalid seed. + */ + KeyData GeneratePrivkeyData( + NetType network_type, const std::vector& path) const; + /** + * @brief Generate an extended privkey data. + * @param[in] network_type network type + * @param[in] string_path child number string path + * @return extended privkey + * @throws CfdException If invalid seed. + */ + KeyData GeneratePrivkeyData( + NetType network_type, const std::string& string_path) const; + + /** + * @brief Generate an extended pubkey. * @param[in] network_type network type * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey GeneratePubkey(NetType network_type) const; /** - * @brief 拡張公開鍵を生成する。 + * @brief Generate an extended pubkey. * @param[in] network_type network type * @param[in] child_num child number * @return extended pubkey @@ -115,7 +135,7 @@ class CFD_CORE_EXPORT HDWallet { */ ExtPubkey GeneratePubkey(NetType network_type, uint32_t child_num) const; /** - * @brief 拡張公開鍵を生成する。 + * @brief Generate an extended pubkey. * @param[in] network_type network type * @param[in] path child number path * @return extended pubkey @@ -124,7 +144,7 @@ class CFD_CORE_EXPORT HDWallet { ExtPubkey GeneratePubkey( NetType network_type, const std::vector& path) const; /** - * @brief 拡張公開鍵を生成する。 + * @brief Generate an extended pubkey. * @param[in] network_type network type * @param[in] string_path child number string path * @return extended pubkey @@ -134,29 +154,48 @@ class CFD_CORE_EXPORT HDWallet { NetType network_type, const std::string& string_path) const; /** - * @brief Mnemonic で利用できる Wordlist を取得する. - * @param[in] language 取得するWordlistの言語 - * @return Wordlist配列 - * @throws CfdException 非対応の言語が渡された場合 + * @brief Generate an extended pubkey data. + * @param[in] network_type network type + * @param[in] path child number path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData GeneratePubkeyData( + NetType network_type, const std::vector& path) const; + /** + * @brief Generate an extended pubkey data. + * @param[in] network_type network type + * @param[in] string_path child number string path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData GeneratePubkeyData( + NetType network_type, const std::string& string_path) const; + + /** + * @brief Get the Wordlist available in Mnemonic. + * @param[in] language language to Wordlist + * @return Wordlist vector + * @throws CfdException If invalid language passed. */ static std::vector GetMnemonicWordlist( const std::string& language); /** - * @brief Mnemonic で利用できる Wordlist を取得する. - * @param[in] entropy Mnemonic生成のエントロピー値 - * @param[in] language Mnemonicの言語 - * @return ニーモニック配列 - * @throws CfdException 非対応の言語が渡された場合 + * @brief Get the Wordlist available in Mnemonic. + * @param[in] entropy Entropy value for Mnemonic generation + * @param[in] language language to mnemonic + * @return mnemonic vector + * @throws CfdException If invalid language passed. */ static std::vector ConvertEntropyToMnemonic( const ByteData& entropy, const std::string& language); /** - * @brief Mnemonic から Entropy へ変換する. - * @param[in] mnemonic エントロピーを導出するニーモニック配列 - * @param[in] language ニーモニックの言語 - * @return エントロピー値バイトデータ + * @brief Convert from Mnemonic to Entropy. + * @param[in] mnemonic mnemonic vector + * @param[in] language language to mnemonic + * @return entropy data * @throws CfdException If invalid language passed. */ static ByteData ConvertMnemonicToEntropy( @@ -167,7 +206,7 @@ class CFD_CORE_EXPORT HDWallet { * @param[in] mnemonic mnemonic vector to check valid * @param[in] language language to verify * @retval true mnemonic checksum is valid - * @retval true mnemonic checksum is invalid + * @retval false mnemonic checksum is invalid */ static bool CheckValidMnemonic( const std::vector& mnemonic, const std::string& language); @@ -176,7 +215,7 @@ class CFD_CORE_EXPORT HDWallet { ByteData seed_; //!< seed /** - * @brief Mnemonic でサポートしている言語であるかを判定する. + * @brief Determine if the language is supported by Mnemonic. * @param[in] language language used by mnemonic. * @retval true If language is supported. * @retval false If language is not supported. @@ -184,11 +223,11 @@ class CFD_CORE_EXPORT HDWallet { static bool CheckSupportedLanguages(const std::string& language); /** - * @brief mnemonic と passphrase から seed を生成する. - * @param[in] mnemonic ニーモニック配列 - * @param[in] passphrase パスフレーズ - * @param[in] use_ideographic_space 全角スペースで区切るかのフラグ - * @return シード値バイトデータ + * @brief Generate seed from mnemonic and passphrase. + * @param[in] mnemonic mnemonic vector + * @param[in] passphrase passphrase + * @param[in] use_ideographic_space Flag to separate with double-byte space + * @return seed */ static ByteData ConvertMnemonicToSeed( const std::vector& mnemonic, const std::string& passphrase, @@ -196,7 +235,7 @@ class CFD_CORE_EXPORT HDWallet { }; /** - * @brief 拡張秘密鍵を表現するデータクラス + * @brief A data class that represents an extended private key. */ class CFD_CORE_EXPORT ExtPrivkey { public: @@ -205,7 +244,7 @@ class CFD_CORE_EXPORT ExtPrivkey { */ static constexpr uint32_t kSerializeSize = 78; // BIP32_SERIALIZED_LEN /** - * @brief 強化鍵定義 + * @brief hardened key definition */ static constexpr uint32_t kHardenedKey = 0x80000000; /** @@ -218,41 +257,41 @@ class CFD_CORE_EXPORT ExtPrivkey { static constexpr uint32_t kVersionTestnetPrivkey = 0x04358394; /** - * @brief デフォルトコンストラクタ + * @brief constructor. */ ExtPrivkey(); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] seed seed byte * @param[in] network_type network type */ explicit ExtPrivkey(const ByteData& seed, NetType network_type); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] serialize_data serialize data */ explicit ExtPrivkey(const ByteData& serialize_data); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] serialize_data serialize data * @param[in] tweak_sum tweak sum */ explicit ExtPrivkey( const ByteData& serialize_data, const ByteData256& tweak_sum); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] base58_data base58 data */ explicit ExtPrivkey(const std::string& base58_data); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] base58_data base58 data * @param[in] tweak_sum tweak sum */ explicit ExtPrivkey( const std::string& base58_data, const ByteData256& tweak_sum); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_key parent privkey * @param[in] parent_chain_code parent chain code @@ -264,7 +303,7 @@ class CFD_CORE_EXPORT ExtPrivkey { const ByteData256& parent_chain_code, uint8_t parent_depth, uint32_t child_num); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_key parent privkey * @param[in] privkey privkey @@ -276,7 +315,7 @@ class CFD_CORE_EXPORT ExtPrivkey { NetType network_type, const Privkey& parent_key, const Privkey& privkey, const ByteData256& chain_code, uint8_t depth, uint32_t child_num); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_fingerprint parent fingerprint(4byte) * @param[in] privkey privkey @@ -290,37 +329,37 @@ class CFD_CORE_EXPORT ExtPrivkey { uint32_t child_num); /** - * @brief 拡張keyのSerialize情報を取得する. + * @brief Get Serialize information of extension key. * @return serialize data */ ByteData GetData() const; /** - * @brief 拡張keyのBase58文字列を取得する. + * @brief Get the Base58 string of the extended key. * @return base58 string */ std::string ToString() const; /** - * @brief Privkeyインスタンスを取得する. - * @return Privkeyインスタンス + * @brief Get a privkey. + * @return Privkey */ Privkey GetPrivkey() const; /** - * @brief 指定階層の拡張秘密鍵を取得する。 + * @brief Acquires the extended private key of the specified hierarchy. * @param[in] child_num child number * @return extended pubprivkeykey * @throws CfdException If invalid seed. */ ExtPrivkey DerivePrivkey(uint32_t child_num) const; /** - * @brief 指定階層の拡張秘密鍵を取得する。 + * @brief Acquires the extended private key of the specified hierarchy. * @param[in] path child number path * @return extended privkey * @throws CfdException If invalid seed. */ ExtPrivkey DerivePrivkey(const std::vector& path) const; /** - * @brief 指定階層の拡張秘密鍵を取得する。 + * @brief Acquires the extended private key of the specified hierarchy. * @param[in] string_path child number string path * @return extended pubkey * @throws CfdException If invalid seed. @@ -328,27 +367,42 @@ class CFD_CORE_EXPORT ExtPrivkey { ExtPrivkey DerivePrivkey(const std::string& string_path) const; /** - * @brief 同一階層の拡張公開鍵を取得する。 + * @brief Derive extended privkey. + * @param[in] path child number path + * @return extended privkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePrivkeyData(const std::vector& path) const; + /** + * @brief Derive ext-privkey. + * @param[in] string_path child number string path + * @return extended privkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePrivkeyData(const std::string& string_path) const; + + /** + * @brief Obtain the extended public key of the same layer. * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey GetExtPubkey() const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] child_num child number * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey DerivePubkey(uint32_t child_num) const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] path child number path * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey DerivePubkey(const std::vector& path) const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] string_path child number string path * @return extended pubkey * @throws CfdException If invalid seed. @@ -356,54 +410,69 @@ class CFD_CORE_EXPORT ExtPrivkey { ExtPubkey DerivePubkey(const std::string& string_path) const; /** - * @brief 状態が正常であるかを返却する. - * @retval true 正常 - * @retval false 不正 + * @brief Derive ext-pubkey. + * @param[in] path child number path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePubkeyData(const std::vector& path) const; + /** + * @brief Derive extended pubkey. + * @param[in] string_path child number string path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePubkeyData(const std::string& string_path) const; + + /** + * @brief Check if the data format is correct. + * @retval true valid + * @retval false invalid */ bool IsValid() const; /** - * @brief 拡張keyのDepthを取得する. + * @brief Get depth. * @return depth value */ uint8_t GetDepth() const; /** - * @brief version部を取得する. + * @brief Get veresion. * @return version data (4byte) */ uint32_t GetVersion() const; /** - * @brief child number部を取得する. + * @brief Get veresion. + * @return version data (4byte) + */ + ByteData GetVersionData() const; + /** + * @brief Get child number. * @return child number (4byte) */ uint32_t GetChildNum() const; /** - * @brief chain code部を取得する. + * @brief Get chain code. * @return chain code (32byte) */ ByteData256 GetChainCode() const; /** - * @brief version部を取得する. - * @return version data (4byte) - */ - ByteData GetVersionData() const; - /** - * @brief fingerprint部を取得する. + * @brief Get fingerprint. * @return fingerprint data (4byte) */ uint32_t GetFingerprint() const; /** - * @brief fingerprint部を取得する. + * @brief Get fingerprint. * @return fingerprint data (4byte) */ ByteData GetFingerprintData() const; /** - * @brief 派生Pubkeyを生成する過程で生成されたtweak値の合成値を取得する。 + * @brief Get the composite value of the tweak value generated in the process of generating the derived Pubkey. * @return tweak sum */ ByteData256 GetPubTweakSum() const; /** - * @brief get network type. + * @brief Get network type. * @return network type. */ NetType GetNetworkType() const; @@ -420,7 +489,7 @@ class CFD_CORE_EXPORT ExtPrivkey { }; /** - * @brief 拡張公開鍵を表現するデータクラス + * @brief A data class that represents an extended public key. */ class CFD_CORE_EXPORT ExtPubkey { public: @@ -434,35 +503,35 @@ class CFD_CORE_EXPORT ExtPubkey { static constexpr uint32_t kVersionTestnetPubkey = 0x043587cf; /** - * @brief デフォルトコンストラクタ + * @brief constructor. */ ExtPubkey(); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] serialize_data serialize data */ explicit ExtPubkey(const ByteData& serialize_data); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] serialize_data serialize data * @param[in] tweak_sum tweak sum */ explicit ExtPubkey( const ByteData& serialize_data, const ByteData256& tweak_sum); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] base58_data base58 data */ explicit ExtPubkey(const std::string& base58_data); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] base58_data base58 data * @param[in] tweak_sum tweak sum */ explicit ExtPubkey( const std::string& base58_data, const ByteData256& tweak_sum); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_key parent pubkey * @param[in] parent_chain_code parent chain code @@ -474,7 +543,7 @@ class CFD_CORE_EXPORT ExtPubkey { const ByteData256& parent_chain_code, uint8_t parent_depth, uint32_t child_num); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_key parent pubkey * @param[in] pubkey pubkey @@ -486,7 +555,7 @@ class CFD_CORE_EXPORT ExtPubkey { NetType network_type, const Pubkey& parent_key, const Pubkey& pubkey, const ByteData256& chain_code, uint8_t depth, uint32_t child_num); /** - * @brief コンストラクタ + * @brief constructor. * @param[in] network_type network type * @param[in] parent_fingerprint parent fingerprint(4byte) * @param[in] pubkey pubkey @@ -500,37 +569,37 @@ class CFD_CORE_EXPORT ExtPubkey { uint32_t child_num); /** - * @brief 拡張keyのSerialize情報を取得する. + * @brief Get Serialize information of extended key. * @return serialize data */ ByteData GetData() const; /** - * @brief 拡張keyのBase58文字列を取得する. + * @brief Get the Base58 string of the extended key. * @return base58 string */ std::string ToString() const; /** - * @brief Pubkeyインスタンスを取得する. - * @return Pubkeyインスタンス + * @brief Get pubkey. + * @return Pubkey */ Pubkey GetPubkey() const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] child_num child number * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey DerivePubkey(uint32_t child_num) const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] path child number path * @return extended pubkey * @throws CfdException If invalid seed. */ ExtPubkey DerivePubkey(const std::vector& path) const; /** - * @brief 指定階層の拡張公開鍵を取得する。 + * @brief Obtain the extended public key of the specified hierarchy. * @param[in] string_path child number string path * @return extended pubkey * @throws CfdException If invalid seed. @@ -538,61 +607,76 @@ class CFD_CORE_EXPORT ExtPubkey { ExtPubkey DerivePubkey(const std::string& string_path) const; /** - * @brief 派生Pubkeyを生成する過程で生成されたtweak値の合成値を取得する。 + * @brief Derive extended pubkey. + * @param[in] path child number path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePubkeyData(const std::vector& path) const; + /** + * @brief Derive extended pubkey. + * @param[in] string_path child number string path + * @return extended pubkey + * @throws CfdException If invalid seed. + */ + KeyData DerivePubkeyData(const std::string& string_path) const; + + /** + * @brief Get the tweak value generated in the process of generating the derived Pubkey. * @param[in] path child number path * @return tweak sum */ ByteData256 DerivePubTweak(const std::vector& path) const; /** - * @brief 派生Pubkeyを生成する過程で生成されたtweak値の合成値を取得する。 + * @brief Get the composite value of the tweak value generated in the process of generating the derived Pubkey. * @return tweak sum */ ByteData256 GetPubTweakSum() const; /** - * @brief 状態が正常であるかを返却する. - * @retval true 正常 - * @retval false 不正 + * @brief Check if the data format is correct. + * @retval true valid + * @retval false invalid */ bool IsValid() const; /** - * @brief 拡張keyのDepthを取得する. + * @brief Get depth. * @return depth value */ uint8_t GetDepth() const; /** - * @brief version部を取得する. + * @brief Get version. * @return version data (4byte) */ uint32_t GetVersion() const; /** - * @brief child number部を取得する. + * @brief Get version. + * @return version data (4byte) + */ + ByteData GetVersionData() const; + /** + * @brief Get child number. * @return child number (4byte) */ uint32_t GetChildNum() const; /** - * @brief chain code部を取得する. + * @brief Get chain code. * @return chain code (32byte) */ ByteData256 GetChainCode() const; /** - * @brief version部を取得する. - * @return version data (4byte) - */ - ByteData GetVersionData() const; - /** - * @brief fingerprint部を取得する. + * @brief Get fingerprint. * @return fingerprint data (4byte) */ uint32_t GetFingerprint() const; /** - * @brief fingerprint部を取得する. + * @brief Get fingerprint. * @return fingerprint data (4byte) */ ByteData GetFingerprintData() const; /** - * @brief get network type. + * @brief Get network type. * @return network type. */ NetType GetNetworkType() const; @@ -608,6 +692,222 @@ class CFD_CORE_EXPORT ExtPubkey { ByteData256 tweak_sum_; //!< tweak sum }; +/** + * @brief hardened string type. + */ +enum HardenedType { + kApostrophe = 0, //!< apostrophe + kLargeH = 1, //!< 'H' + kSmallH = 2, //!< 'h' + kNumber = 3, //!< number only (0x80000000) +}; + +/** + * @brief key and bip32 path information class. + */ +class CFD_CORE_EXPORT KeyData { + public: + /** + * @brief constructor. + */ + KeyData(); + /** + * @brief Get key text information from ext-privkey. + * @param[in] ext_privkey privkey + * @param[in] child_path bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const ExtPrivkey& ext_privkey, const std::string& child_path, + const ByteData& finterprint); + /** + * @brief Get key text information from ext-pubkey. + * @param[in] ext_pubkey pubkey + * @param[in] child_path bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const ExtPubkey& ext_pubkey, const std::string& child_path, + const ByteData& finterprint); + /** + * @brief Get key text information from privkey. + * @param[in] privkey privkey + * @param[in] child_path bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const Privkey& privkey, const std::string& child_path, + const ByteData& finterprint); + /** + * @brief Get key text information from pubkey. + * @param[in] pubkey pubkey + * @param[in] child_path bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const Pubkey& pubkey, const std::string& child_path, + const ByteData& finterprint); + /** + * @brief Get key text information from ext-privkey. + * @param[in] ext_privkey privkey + * @param[in] child_num_list bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const ExtPrivkey& ext_privkey, + const std::vector& child_num_list, + const ByteData& finterprint); + /** + * @brief Get key text information from ext-pubkey. + * @param[in] ext_pubkey pubkey + * @param[in] child_num_list bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const ExtPubkey& ext_pubkey, const std::vector& child_num_list, + const ByteData& finterprint); + /** + * @brief Get key text information from privkey. + * @param[in] privkey privkey + * @param[in] child_num_list bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const Privkey& privkey, const std::vector& child_num_list, + const ByteData& finterprint); + /** + * @brief Get key text information from pubkey. + * @param[in] pubkey pubkey + * @param[in] child_num_list bip32 path for child. + * @param[in] finterprint master-pubkey fingerprint + */ + explicit KeyData( + const Pubkey& pubkey, const std::vector& child_num_list, + const ByteData& finterprint); + /** + * @brief Get key text information from string. + * @param[in] path_info key-path info. + * @param[in] child_num child number to use if an asterisk is used. + */ + explicit KeyData(const std::string& path_info, int32_t child_num = -1); + + /** + * @brief exist ext-privkey. + * @retval true exist + * @retval false not exist + */ + bool HasExtPrivkey() const; + /** + * @brief exist ext-pubkey. + * @retval true exist + * @retval false not exist + */ + bool HasExtPubkey() const; + /** + * @brief exist privkey. + * @retval true exist + * @retval false not exist + */ + bool HasPrivkey() const; + /** + * @brief getting pubkey. + * @return pubkey + */ + Pubkey GetPubkey() const; + /** + * @brief getting privkey. + * @return privkey + */ + Privkey GetPrivkey() const; + /** + * @brief getting ext-privkey. + * @details need ext-privkey exists. + * @return ext-privkey + */ + ExtPrivkey GetExtPrivkey() const; + /** + * @brief getting ext-pubkey. + * @details need ext-pubkey exists. + * @return ext-pubkey + */ + ExtPubkey GetExtPubkey() const; + + /** + * @brief Derive ext-privkey. + * @param[in] path path + * @param[in] has_rebase_path rebase path/fingerprint base + * @return path info with ext-privkey. + */ + KeyData DerivePrivkey( + std::vector path, bool has_rebase_path) const; + /** + * @brief Derive ext-privkey. + * @param[in] path path + * @param[in] has_rebase_path rebase path/fingerprint base + * @return path info with ext-privkey. + */ + KeyData DerivePrivkey(std::string path, bool has_rebase_path) const; + /** + * @brief Derive ext-pubkey. + * @param[in] path path + * @param[in] has_rebase_path rebase path/fingerprint base + * @return path info with ext-pubkey. + */ + KeyData DerivePubkey(std::vector path, bool has_rebase_path) const; + /** + * @brief Derive ext-pubkey. + * @param[in] path path + * @param[in] has_rebase_path rebase path/fingerprint base + * @return path info with ext-pubkey. + */ + KeyData DerivePubkey(std::string path, bool has_rebase_path) const; + + /** + * @brief getting bip32 path. + * @param[in] hardened_type hardened string type + * @param[in] has_hex using hex string + * @return bip32 path + */ + std::string GetBip32Path( + HardenedType hardened_type = HardenedType::kApostrophe, + bool has_hex = false) const; + /** + * @brief get message string. + * @param[in] has_pubkey displays the pubkey string. + * @param[in] hardened_type hardened string type + * @param[in] has_hex using hex string + * @return message string. + */ + std::string ToString( + bool has_pubkey = true, + HardenedType hardened_type = HardenedType::kApostrophe, + bool has_hex = false) const; + /** + * @brief get fingerprint. + * @return fingerprint. + */ + ByteData GetFingerprint() const; + /** + * @brief get child number array. + * @return child number array. + */ + std::vector GetChildNumArray() const; + /** + * @brief check valid. + * @retval true valid + * @retval false invalid + */ + bool IsValid() const; + + private: + Pubkey pubkey_; //!< pubkey + Privkey privkey_; //!< privkey + ExtPrivkey extprivkey_; //!< ext privkey + ExtPubkey extpubkey_; //!< ext pubkey + std::vector path_; //!< bip32 path + ByteData fingerprint_; //!< fingerprint by key string +}; + } // namespace core } // namespace cfd diff --git a/include/cfdcore/cfdcore_iterator.h b/include/cfdcore/cfdcore_iterator.h index 43acb3b7..7ffa48f4 100644 --- a/include/cfdcore/cfdcore_iterator.h +++ b/include/cfdcore/cfdcore_iterator.h @@ -2,7 +2,7 @@ /** * @file cfdcore_iterator.h * - * @brief Iterator Wrapperクラス定義 + * @brief The Iterator Wrapper class definition */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ITERATOR_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_ITERATOR_H_ @@ -19,14 +19,14 @@ namespace cfd { namespace core { /** - * @brief IteratorのWrapperクラス + * @brief Iterator's Wrapper class. */ template class IteratorWrapper { public: /** * @brief constructor. - * @details 引数で指定されたvectorのiterator wrapperインスタンスの作成をする. + * @details Create an iterator wrapper instance of the vector specified by the argument. * @param vector * @param error_message * @param is_reverse @@ -48,9 +48,10 @@ class IteratorWrapper { // do nothing } /** - * @brief 次の要素が取得可能であるかを返却する. - * @retval true 次の要素が取得可能であるとき - * @retval true 次の要素が取得できないとき(イテレータが末端を指しているとき) + * @brief Returns whether the next element is available. + * @retval true When the next element is available + * @retval false When the next element cannot be obtained \ + * (when the iterator points to the end) */ bool hasNext() const { if (reverse_) { @@ -59,9 +60,10 @@ class IteratorWrapper { return iterator_ != vector_.cend(); } /** - * @brief 前の要素が取得可能であるかを返却する. - * @retval true 前の要素が取得可能であるとき - * @retval true 前の要素が取得できないとき(イテレータが始端を指しているとき) + * @brief Returns whether the previous element is available. + * @retval true When the previous element is available + * @retval false When the previous element cannot be obtained \ + * (when the iterator points to the beginning) */ bool hasBack() const { if (reverse_) { @@ -70,8 +72,8 @@ class IteratorWrapper { return iterator_ != vector_.cbegin(); } /** - * @brief 次の要素を取得する. - * @return 現在のイテレータの次の要素 + * @brief Get the next element. + * @return The next element of the current iterator */ T next() { if (!hasNext()) { @@ -88,8 +90,8 @@ class IteratorWrapper { return *(iterator_++); } /** - * @brief 前の要素を取得する. - * @return 現在のイテレータの次の要素 + * @brief Get the previous element. + * @return The next element of the current iterator */ T back() { if (!hasBack()) { diff --git a/include/cfdcore/cfdcore_json_mapping_base.h b/include/cfdcore/cfdcore_json_mapping_base.h index 55bd830a..bbb971a0 100644 --- a/include/cfdcore/cfdcore_json_mapping_base.h +++ b/include/cfdcore/cfdcore_json_mapping_base.h @@ -2,9 +2,9 @@ /** * @file cfdcore_json_mapping_base.h * - * @brief JSON-クラスマッピング処理を定義するファイル。 + * @brief JSON-A file that defines the class mapping process. * - * 継承クラスを作成して利用する。 + * Create and use an inherited class. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_JSON_MAPPING_BASE_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_JSON_MAPPING_BASE_H_ @@ -30,10 +30,10 @@ namespace cfd { namespace core { // ----------------------------------------------------------------------------- -// クラス定義 +// Class definition // ----------------------------------------------------------------------------- /** - * @brief Get/Set/Type処理用のテンプレート構造体 + * @brief Template structure for Get / Set / Type processing. */ template struct CLASS_FUNCTION_TABLE { @@ -43,15 +43,15 @@ struct CLASS_FUNCTION_TABLE { }; /** - * @brief Json型処理を行うためのマッピングテンプレート + * @brief Mapping template for Json type processing */ template using JsonTableMap = std::map>; /** - * @brief 文字列変換を行います。 - * @param[in] value 変換元の値 - * @return 文字列変換後の値 + * @brief Performs character string conversion. + * @param[in] value Source value + * @return Value after string conversion */ inline std::string ConvertToString(const uint32_t& value) { // NOLINT UniValue json_value(static_cast(value)); @@ -60,18 +60,18 @@ inline std::string ConvertToString(const uint32_t& value) { // NOLINT } /** - * @brief 文字列変換を行います。 - * @param[in] value 変換元の値 - * @return 文字列変換後の値 + * @brief Performs character string conversion. + * @param[in] value Source value + * @return Value after string conversion */ inline std::string ConvertToString(const uint64_t& value) { // NOLINT return std::to_string(value); } /** - * @brief 文字列変換を行います。 - * @param[in] value 変換元の値 - * @return 文字列変換後の値 + * @brief Performs character string conversion. + * @param[in] value Source value + * @return Value after string conversion */ template inline std::string ConvertToString(const T& value) { // NOLINT @@ -81,9 +81,9 @@ inline std::string ConvertToString(const T& value) { // NOLINT } /** - * @brief UniValueオブジェクトからstring型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to string type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ inline void ConvertFromUniValue( std::string& value, // NOLINT @@ -102,9 +102,9 @@ inline void ConvertFromUniValue( } /** - * @brief UniValueオブジェクトからbool型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to bool type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ inline void ConvertFromUniValue( bool& value, const UniValue& json_value) { // NOLINT @@ -122,9 +122,9 @@ inline void ConvertFromUniValue( } /** - * @brief UniValueオブジェクトからdouble型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to double type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ inline void ConvertFromUniValue( double& value, const UniValue& json_value) { // NOLINT @@ -142,9 +142,9 @@ inline void ConvertFromUniValue( } /** - * @brief UniValueオブジェクトからfloat型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to float type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ inline void ConvertFromUniValue( float& value, const UniValue& json_value) { // NOLINT @@ -162,9 +162,9 @@ inline void ConvertFromUniValue( } /** - * @brief UniValueオブジェクトからfloat型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to unsigned 64bit type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ inline void ConvertFromUniValue( uint64_t& value, const UniValue& json_value) { // NOLINT @@ -203,9 +203,9 @@ inline void ConvertFromUniValue( } /** - * @brief UniValueオブジェクトから指定された型に変換します。 - * @param[out] value 変換後の設定値 - * @param[in] json_value UniValueオブジェクト + * @brief Convert from UniValue object to template type. + * @param[out] value Set value after conversion + * @param[in] json_value UniValue object */ template inline void ConvertFromUniValue( @@ -259,51 +259,51 @@ inline void ConvertFromUniValue( } } -// テンプレートクラス(JSON処理のためテンプレートに) +// Template class (in template for JSON processing) /** - * @brief Jsonマッピング変換クラスのベースクラス。 + * @brief Base class for Json mapping transformation class. * - * 本ファイル下部のマクロを利用して継承クラスを定義する。 + * Define the inherited class using the macro at the bottom of this file. */ template class JsonClassBase { public: /** - * @brief コンストラクタ + * @brief Constructor. */ JsonClassBase() {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~JsonClassBase() {} /** - * @brief シリアライズ開始前にコールされる関数。 + * @brief A function that is called before serialization begins. * - * 必要に応じて継承クラス側でオーバーライドする。 + * Override on the inherited class side if necessary. */ virtual void PreSerialize() const {} /** - * @brief シリアライズ終了時にコールされる関数。 + * @brief A function called at the end of serialization. * - * 必要に応じて継承クラス側でオーバーライドする。 + * Override on the inherited class side if necessary. */ virtual void PostSerialize() const {} /** - * @brief デシリアライズ開始前にコールされる関数。 + * @brief A function that is called before deserialization begins. * - * 必要に応じて継承クラス側でオーバーライドする。 + * Override on the inherited class side if necessary. */ virtual void PreDeserialize() {} /** - * @brief デシリアライズ終了時にコールされる関数。 + * @brief A function called at the end of deserialization. * - * 必要に応じて継承クラス側でオーバーライドする。 + * Override on the inherited class side if necessary. */ virtual void PostDeserialize() {} /** - * @brief シリアライズ処理(JSON文字列化)を行う。 - * @return JSON文字列 + * @brief Performs serialization processing (JSON character string conversion). + * @return JSON string */ virtual std::string Serialize() const { PreSerialize(); @@ -339,8 +339,8 @@ class JsonClassBase { } /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value JSON文字列 + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value JSON string. */ virtual void Deserialize(const std::string& value) { UniValue object; @@ -349,12 +349,12 @@ class JsonClassBase { } /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value UniValueオブジェクト + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value UniValue object. */ virtual void DeserializeUniValue(const UniValue& value) { if (value.isArray()) { - // rootがリスト1つの場合、子クラスに引継ぎ + // If root is one list, take over to child class JsonTableMap mapper = GetJsonMapper(); if (mapper.size() == 1) { PreDeserialize(); @@ -386,51 +386,54 @@ class JsonClassBase { protected: /** - * @brief JSONマッピングオブジェクトを取得する。 + * @brief Get the JSON mapping object. * - * テンプレートを用いる関係上、実態を有する継承クラス側で実装する。 - * @return JSONマッピングオブジェクト + * Since the template is used, it is implemented on the \ + * inherited class side that has the actual situation. + * @return JSON mapping object. */ virtual const JsonTableMap& GetJsonMapper() const = 0; /** - * @brief JSONマッピングのアイテム一覧を取得する。 + * @brief Get the JSON mapping item list. * - * 対象の変数名を、定義順序に従い一覧取得する。 - * テンプレートを用いる関係上、実態を有する継承クラス側で実装する。 - * @return JSONマッピングのアイテム一覧 + * Get a list of target variable names according to the definition order. + * Since the template is used, it is implemented on the inherited class \ + * side that has the actual situation. + * @return JSON mapping item list. */ virtual const std::vector& GetJsonItemList() const = 0; /** - * @brief JSONマッピング時に無視するアイテム一覧を取得する。 + * @brief Get a list of items to ignore during JSON mapping. * - * Serialize時に対象の変数を無視する。 - * テンプレートを用いる関係上、実態を有する継承クラス側で実装する。 - * @return JSONマッピング時に無視するアイテム一覧 + * Ignore the target variable when serializing. + * Since the template is used, it is implemented on the \ + * inherited class side that has the actual situation. + * @return Item list to ignore when JSON mapping */ virtual const std::set& GetIgnoreItem() const = 0; }; /** - * @brief Jsonマッピング変換リストクラスのベースクラス。 + * @brief Base class for Json mapping transformation list class. * - * 本ファイル下部のマクロを利用して継承クラスを定義する。 + * Define the inherited class using the macro at the bottom of this file. */ template class JsonVector : public std::vector { public: /** - * @brief コンストラクタ + * @brief Constructor. */ JsonVector() {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~JsonVector() {} /** - * @brief オペレーター(代入) - * @param[in] obj 代入する側のインスタンス - * @return 代入される側のインスタンス + * @brief Substitution operator. + * @param[in] obj instance. + * @return setting object. */ TYPE& operator=(const TYPE& obj) { std::string serialize_string = obj.Serialize(); @@ -439,14 +442,14 @@ class JsonVector : public std::vector { } /** - * @brief シリアライズ処理(JSON文字列化)を行う。 - * @return JSON文字列 + * @brief Performs serialization processing (JSON character string conversion). + * @return JSON string */ virtual std::string Serialize() const = 0; /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value JSON文字列 + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value JSON string */ virtual void Deserialize(const std::string& value) { UniValue object; @@ -455,32 +458,32 @@ class JsonVector : public std::vector { } /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value UniValueオブジェクト + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value UniValue object */ virtual void DeserializeUniValue(const UniValue& value) = 0; }; /** - * @brief 設定値用のJsonマッピング変換リストクラスのベースクラス。 + * @brief Base class of Json mapping transformation list class for settings. * - * 本ファイル下部のマクロを利用して継承クラスを定義する。 + * Define the inherited class using the macro at the bottom of this file. */ template class JsonValueVector : public JsonVector { public: /** - * @brief コンストラクタ + * @brief Constructor. */ JsonValueVector() {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~JsonValueVector() {} /** - * @brief シリアライズ処理(JSON文字列化)を行う。 - * @return JSON文字列 + * @brief Performs serialization processing (JSON character string conversion). + * @return JSON string. */ virtual std::string Serialize() const { std::string result; @@ -505,8 +508,8 @@ class JsonValueVector : public JsonVector { } /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value UniValueオブジェクト + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value UniValue object. */ virtual void DeserializeUniValue(const UniValue& value) { if (!value.isArray()) { @@ -523,8 +526,8 @@ class JsonValueVector : public JsonVector { } /** - * @brief Struct情報からの変換処理を行う。 - * @param[in] list リスト情報 + * @brief Performs conversion processing from Struct information. + * @param[in] list List information */ void ConvertFromStruct(const std::vector& list) { for (const auto& element : list) { @@ -533,8 +536,8 @@ class JsonValueVector : public JsonVector { } /** - * @brief Struct情報への変換処理を行う。 - * @return 変換済みリスト情報 + * @brief Performs conversion processing to Struct information. + * @return Converted list information */ std::vector ConvertToStruct() const { std::vector result; @@ -547,25 +550,25 @@ class JsonValueVector : public JsonVector { }; /** - * @brief クラスオブジェクト用のJsonマッピング変換リストクラスのベースクラス。 + * @brief Base class for Json mapping transformation list classes for class objects. * - * 本ファイル下部のマクロを利用して継承クラスを定義する。 + * Define the inherited class using the macro at the bottom of this file. */ template class JsonObjectVector : public JsonVector { public: /** - * @brief コンストラクタ + * @brief Constructor. */ JsonObjectVector() {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~JsonObjectVector() {} /** - * @brief シリアライズ処理(JSON文字列化)を行う。 - * @return JSON文字列 + * @brief Performs serialization processing (JSON character string conversion). + * @return JSON string. */ virtual std::string Serialize() const { std::string result; @@ -590,8 +593,8 @@ class JsonObjectVector : public JsonVector { } /** - * @brief デシリアライズ処理(JSONオブジェクト化)を行う。 - * @param[in] value UniValueオブジェクト + * @brief Perform deserialization processing (JSON objectization). + * @param[in] value UniValue object */ virtual void DeserializeUniValue(const UniValue& value) { if (!value.isArray()) { @@ -608,8 +611,8 @@ class JsonObjectVector : public JsonVector { } /** - * @brief Struct情報からの変換処理を行う。 - * @param[in] list リスト情報 + * @brief Performs conversion processing from Struct information. + * @param[in] list List information */ void ConvertFromStruct(const std::vector& list) { for (const auto& element : list) { @@ -620,8 +623,8 @@ class JsonObjectVector : public JsonVector { } /** - * @brief Struct情報への変換処理を行う。 - * @return 変換済みリスト情報 + * @brief Performs conversion processing to Struct information. + * @return Converted list information */ std::vector ConvertToStruct() const { std::vector result; diff --git a/include/cfdcore/cfdcore_json_writer.h b/include/cfdcore/cfdcore_json_writer.h index c7f87ef1..a0366193 100644 --- a/include/cfdcore/cfdcore_json_writer.h +++ b/include/cfdcore/cfdcore_json_writer.h @@ -2,7 +2,7 @@ /** * @file cfdcore_json_writer.h * - * @brief JSON文字列の生成時に使用にするクラスを定義する。 + * @brief Define the class to be used when generating the JSON string. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_JSON_WRITER_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_JSON_WRITER_H_ @@ -16,75 +16,76 @@ namespace cfd { namespace core { /** - * @brief Json生成時に利用するクラス。 + * @brief Class used when generating Json. */ class JsonElement { public: /** - * @brief コンストラクタ - * @param[in] key キー値 + * @brief Constructor. + * @param[in] key key */ explicit JsonElement(std::string key) : key_(key), value_() {} /** - * @brief コンストラクタ - * @param[in] key キー値 - * @param[in] object エレメントオブジェクト + * @brief Constructor. + * @param[in] key key + * @param[in] object element object */ JsonElement(std::string key, const JsonElement& object) : key_(key), value_(UniValue::VOBJ) { value_.push_back(object.GetUnivalue()); } /** - * @brief コンストラクタ - * @param[in] value UniValueオブジェクト + * @brief Constructor. + * @param[in] value UniValue object */ explicit JsonElement(const UniValue& value) : key_(), value_(value) {} /** - * @brief コンストラクタ - * @param[in] key キー値 - * @param[in] value UniValueオブジェクト + * @brief Constructor. + * @param[in] key key + * @param[in] value UniValue object */ JsonElement(std::string key, const UniValue& value) : key_(key), value_(value) {} /** - * @brief コンストラクタ - * @param[in] key キー値 - * @param[in] value 設定値 + * @brief Constructor. + * @param[in] key key + * @param[in] value value */ template JsonElement(std::string key, TYPE value) : key_(key), value_(value) {} /** - * @brief デストラクタ + * @brief destructor. */ virtual ~JsonElement() {} /** - * @brief キー値を設定する。 - * @param[in] key キー値 + * @brief Set the key. + * @param[in] key key */ void SetKey(const std::string& key) { key_ = key; } /** - * @brief キー値を取得する。 - * @return キー値 + * @brief Get the key. + * @return key */ const std::string& GetKey() const { return key_; } /** - * @brief UniValue値を取得する。 - * @return UniValueオブジェクト + * @brief Get the UniValue object. + * @return UniValue object */ const UniValue& GetUnivalue() const { return value_; } protected: - std::string key_; ///< キー値 - UniValue value_; ///< UniValueオブジェクト + std::string key_; ///< key + UniValue value_; ///< UniValue object }; /** - * @brief Json生成用クラス。 + * @brief Json generation class. * - * Json生成のみ対応。Json文字列からの変換には対応せず。 + * Only Json generation is supported. + * Does not support conversion from Json strings. * - * イメージ(テスト実装) + * Image (test implementation): * @code * JsonBuilder jb; * jb.Set( @@ -110,24 +111,24 @@ class JsonElement { */ class JsonBuilder { /** - * @brief JsonElementの初期化子リスト + * @brief JsonElement initializer list */ using JsonElementInitialize = std::initializer_list; public: /** - * @brief コンストラクタ + * @brief Constructor. */ JsonBuilder() : root_() {} /** - * @brief デストラクタ + * @brief Destructor. */ virtual ~JsonBuilder() {} /** - * @brief ルートに指定されたエレメントを設定する。 - * @param[in] value JsonElementオブジェクト - * @param[in] args 可変引数(JsonElementオブジェクト) + * @brief Set the element specified in the root. + * @param[in] value JsonElement object + * @param[in] args Variadic argument (JsonElement object) */ template void Set(const JsonElement& value, Args&&... args) { @@ -148,49 +149,53 @@ class JsonBuilder { } /** - * @brief JSON文字列を生成する。 - * @param[in] indent Indent値。0は整形なし(1行出力)。1以上は整形+指定値分Indentする。 - * @return JSON文字列 + * @brief Generate a JSON string. + * @param[in] indent Indent value. \ + * 0 is no formatting (1 line output). \ + * 1 or more is shaped + indented by the specified value. + * @return JSON string */ std::string Build(int indent = 0) { - // IndentLevel(writeの第二引数)はどうも((indent-1)*IndentLevel)分だけ2行目以降のIndentに加算するので、変えない方がいい。 - // 固定値加算ならともかく、なぜに乗算。 + // IndentLevel (second argument of write function) is added to \ + // Indent on the second and subsequent lines by \ + // ((indent-1) * IndentLevel), so it is better not to change it. + // Why is it multiplying rather than adding at a fixed value. return root_.write(indent); } /** - * @brief 文字列型を生成する。 - * @param[in] key キー値 - * @param[in] value JsonElementオブジェクト - * @return 文字列型を設定したJsonElement + * @brief Generate a string type. + * @param[in] key key + * @param[in] value JsonElement object + * @return JsonElement with string type set */ JsonElement Str(std::string key, const JsonElement& value) { return JsonElement(key, value.GetUnivalue()); } /** - * @brief 文字列型を生成する。 - * @param[in] key キー値 - * @param[in] value stringオブジェクト - * @return 文字列型を設定したJsonElement + * @brief Generate a string type. + * @param[in] key key + * @param[in] value string object + * @return JsonElement with string type set */ JsonElement Str(std::string key, const std::string& value) { return JsonElement(key, value); } /** - * @brief 数値型を生成する。 - * @param[in] key キー値 - * @param[in] value JsonElementオブジェクト - * @return 数値型を設定したJsonElement + * @brief Generate a numeric type. + * @param[in] key key + * @param[in] value JsonElement object + * @return JsonElement with numeric type */ JsonElement Num(std::string key, const JsonElement& value) { return JsonElement(key, value.GetUnivalue()); } /** - * @brief 数値列型を生成する。 - * @param[in] key キー値 - * @param[in] value 数値型の値 - * @return 数値型を設定したJsonElement + * @brief Generate a numeric type. + * @param[in] key key + * @param[in] value Numeric value + * @return JsonElement with numeric type */ template JsonElement Num(std::string key, TYPE value) { @@ -198,29 +203,29 @@ class JsonBuilder { } /** - * @brief bool型を生成する。 - * @param[in] key キー値 - * @param[in] value JsonElementオブジェクト - * @return bool型を設定したJsonElement + * @brief Generate a bool type. + * @param[in] key key + * @param[in] value JsonElement object + * @return JsonElement with bool type */ JsonElement Bool(std::string key, const JsonElement& value) { return JsonElement(key, value.GetUnivalue()); } /** - * @brief bool型を生成する。 - * @param[in] key キー値 - * @param[in] is_true bool値 - * @return bool型を設定したJsonElement + * @brief Generate a bool type. + * @param[in] key key + * @param[in] is_true bool value + * @return JsonElement with bool type */ JsonElement Bool(std::string key, bool is_true) { return JsonElement(key, is_true); } /** - * @brief オブジェクト型を生成する。 - * @param[in] key キー値 - * @param[in] args オブジェクト型に設定するJsonElementオブジェクト - * @return オブジェクト型を設定したJsonElement + * @brief Generate an object type. + * @param[in] key key + * @param[in] args JsonElement object to set to object type + * @return JsonElement with object type set */ template JsonElement Object(std::string key, Args&&... args) { @@ -231,10 +236,10 @@ class JsonBuilder { } /** - * @brief Array型を生成する。 - * @param[in] key キー値 - * @param[in] args Array型に設定するJsonElementオブジェクト - * @return Array型を設定したJsonElement + * @brief Generate Array type. + * @param[in] key key + * @param[in] args JsonElement object set to Array type + * @return JsonElement with Array type set */ template JsonElement Array(std::string key, Args&&... args) { @@ -245,28 +250,28 @@ class JsonBuilder { } /** - * @brief 文字列型を生成する。 - * @param[in] value JsonElementオブジェクト - * @return 文字列型を設定したJsonElement + * @brief Generate a string type. + * @param[in] value JsonElement object + * @return JsonElement with string type set */ JsonElement StrV(const JsonElement& value) { return Str("", value); } /** - * @brief 文字列型を生成する。 - * @param[in] value stringオブジェクト - * @return 文字列型を設定したJsonElement + * @brief Generate a string type. + * @param[in] value string object + * @return JsonElement with string type set */ JsonElement StrV(const std::string& value) { return Str("", value); } /** - * @brief 数値型を生成する。 - * @param[in] value JsonElementオブジェクト - * @return 数値型を設定したJsonElement + * @brief Generate a numeric type. + * @param[in] value JsonElement object + * @return JsonElement with numeric type */ JsonElement NumV(const JsonElement& value) { return Num("", value); } /** - * @brief 数値列型を生成する。 - * @param[in] value 数値型の値 - * @return 数値型を設定したJsonElement + * @brief Generate a numeric type. + * @param[in] value Numeric value + * @return JsonElement with numeric type */ template JsonElement NumV(TYPE value) { @@ -274,22 +279,22 @@ class JsonBuilder { } /** - * @brief bool型を生成する。 - * @param[in] value JsonElementオブジェクト - * @return bool型を設定したJsonElement + * @brief Generate a bool type. + * @param[in] value JsonElement object + * @return JsonElement with bool type */ JsonElement BoolV(const JsonElement& value) { return Bool("", value); } /** - * @brief bool型を生成する。 - * @param[in] is_true bool値 - * @return bool型を設定したJsonElement + * @brief Generate a bool type. + * @param[in] is_true bool value + * @return JsonElement with bool type */ JsonElement BoolV(bool is_true) { return Bool("", is_true); } /** - * @brief オブジェクト型を生成する。 - * @param[in] args オブジェクト型に設定するJsonElementオブジェクト - * @return オブジェクト型を設定したJsonElement + * @brief Generate an object type. + * @param[in] args JsonElement object set to object type + * @return JsonElement with object type set */ template JsonElement ObjectV(Args&&... args) { @@ -300,9 +305,9 @@ class JsonBuilder { } /** - * @brief Array型を生成する。 - * @param[in] args Array型に設定するJsonElementオブジェクト - * @return Array型を設定したJsonElement + * @brief Generate Array type. + * @param[in] args JsonElement object set to Array type + * @return JsonElement with Array type set */ template JsonElement ArrayV(Args&&... args) { @@ -313,7 +318,7 @@ class JsonBuilder { } private: - UniValue root_; ///< UniValueオブジェクト + UniValue root_; ///< UniValue object }; } // namespace core diff --git a/include/cfdcore/cfdcore_key.h b/include/cfdcore/cfdcore_key.h index fcc2acde..d930e842 100644 --- a/include/cfdcore/cfdcore_key.h +++ b/include/cfdcore/cfdcore_key.h @@ -1,8 +1,8 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfdcore_key.h * - * @brief Pubkey/Privkey関連クラス定義 + * @brief definition for Pubkey/Privkey class */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_KEY_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_KEY_H_ @@ -18,7 +18,7 @@ namespace core { /** * @typedef NetType - * @brief Bitcoin networkの定義 + * @brief definition for Bitcoin/Liquid network. */ typedef enum { kMainnet = 0, //!< MAINNET @@ -31,7 +31,7 @@ typedef enum { } NetType; /** - * @brief PublicKeyを表現するデータクラス + * @brief Data class representing PublicKey */ class CFD_CORE_EXPORT Pubkey { public: @@ -45,37 +45,38 @@ class CFD_CORE_EXPORT Pubkey { static constexpr uint32_t kCompressedPubkeySize = 33; /** - * @brief デフォルトコンストラクタ + * @brief constructor */ Pubkey(); /** - * @brief コンストラクタ - * @param[in] byte_data 公開鍵のByteDataインスタンス + * @brief constructor + * @param[in] byte_data Public key ByteData instance */ explicit Pubkey(ByteData byte_data); /** - * @brief HEX文字列からPublicKeyモデルを復元するコンストラクタ. - * @param[in] hex_string PublicKeyのHEX文字列 + * @brief constructor + * @param[in] hex_string Public Key HEX string */ explicit Pubkey(const std::string &hex_string); /** - * @brief 自身のByteDataからHEX文字列を取得する. - * @return 公開鍵のHEX文字列 + * @brief Get HEX string. + * @return HEX string. */ std::string GetHex() const; /** - * @brief 自身のByteDataを取得する. - * @return 公開鍵のByteData + * @brief Get ByteData instance. + * @return ByteData */ ByteData GetData() const; /** - * @brief 公開鍵がCompress形式であるかを返却する. - * @return Compressであればtrue, Uncompressであればfalse + * @brief Returns whether the public key is in Compress format. + * @retval true compressed key. + * @retval false uncompressed key. */ bool IsCompress() const; @@ -87,32 +88,39 @@ class CFD_CORE_EXPORT Pubkey { bool IsParity() const; /** - * @brief 公開鍵として正しい形式であるかを検証する. - * @retval true 正常フォーマット - * @retval false 不正フォーマット + * @brief Verify that the public key is in the correct format. + * @retval true valid format + * @retval false invalid format */ bool IsValid() const; /** - * @brief 公開鍵が一致するかチェックする. - * @param[in] pubkey 比較対象Pubkey - * @retval true 一致 - * @retval false 不一致 + * @brief Check if the public keys match. + * @param[in] pubkey check target Pubkey + * @retval true equal + * @retval false not equal */ bool Equals(const Pubkey &pubkey) const; /** - * @brief 合成Pubkeyを生成する. - * @param[in] pubkeys 合成元Pubkey list - * @return 合成したPubkeyインスタンス + * @brief Get fingerprint. + * @param[in] get_size get fingerprint size. + * @return fingerprint + */ + ByteData GetFingerprint(uint32_t get_size = 4) const; + + /** + * @brief Combine pubkeys. + * @param[in] pubkeys Pubkey list + * @return Combined pubkey */ static Pubkey CombinePubkey(const std::vector &pubkeys); /** - * @brief 合成Pubkeyを生成する. - * @param[in] pubkey 合成元Pubkey - * @param[in] message_key 合成するmessage Pubkey - * @return 合成したPubkeyインスタンス + * @brief Combine pubkey. + * @param[in] pubkey base pubkey + * @param[in] message_key combine pubkey + * @return Combined pubkey */ static Pubkey CombinePubkey(const Pubkey &pubkey, const Pubkey &message_key); @@ -164,19 +172,19 @@ class CFD_CORE_EXPORT Pubkey { const ByteData256 &signature_hash, const ByteData &signature) const; /** - * @brief 公開鍵として正しい形式であるかを検証する. - * @param[in] byte_data 公開鍵のByteData - * @retval true 正常フォーマット - * @retval false 不正フォーマット + * @brief Verify that the public key is in the correct format. + * @param[in] byte_data pubkey bytedata + * @retval true valid format + * @retval false invalid format */ static bool IsValid(const ByteData &byte_data); /** - * @brief 指定された2つの公開鍵のHEX値を比較する. + * @brief Compare the HEX values ​​of the two specified public keys. * @param[in] source source target * @param[in] destination destination target - * @retval true 大きい - * @retval false 小さい + * @retval true Large + * @retval false Small */ static bool IsLarge(const Pubkey &source, const Pubkey &destination); @@ -213,7 +221,7 @@ class CFD_CORE_EXPORT Pubkey { }; /** - * @brief Private Keyを表現するデータクラス + * @brief Data class representing Private Key */ class CFD_CORE_EXPORT Privkey { public: @@ -222,52 +230,69 @@ class CFD_CORE_EXPORT Privkey { */ static constexpr uint32_t kPrivkeySize = 32; // EC_PRIVATE_KEY_LEN /** - * @brief デフォルトコンストラクタ + * @brief default constructor. */ Privkey(); /** - * @brief コンストラクタ - * @param[in] byte_data 秘密鍵のByteDataインスタンス + * @brief constructor. + * @param[in] byte_data ByteData object. + * @param[in] net_type Mainnet or Testnet + * @param[in] is_compressed pubkey compressed flag. */ - explicit Privkey(const ByteData &byte_data); + explicit Privkey( + const ByteData &byte_data, NetType net_type = NetType::kMainnet, + bool is_compressed = true); /** - * @brief コンストラクタ - * @param[in] byte_data 秘密鍵のByteDataインスタンス + * @brief constructor. + * @param[in] byte_data ByteData object. + * @param[in] net_type Mainnet or Testnet + * @param[in] is_compressed pubkey compressed flag. */ - explicit Privkey(const ByteData256 &byte_data); + explicit Privkey( + const ByteData256 &byte_data, NetType net_type = NetType::kMainnet, + bool is_compressed = true); /** - * @brief 文字列からPrivateKeyモデルを復元するコンストラクタ. - * @param[in] hex_str PrivateKeyのHEX文字列 + * @brief constructor. + * @param[in] hex_str PrivateKey HEX string + * @param[in] net_type Mainnet or Testnet + * @param[in] is_compressed pubkey compressed flag. */ - explicit Privkey(const std::string &hex_str); + explicit Privkey( + const std::string &hex_str, NetType net_type = NetType::kMainnet, + bool is_compressed = true); /** - * @brief 自身のByteDataからHEX文字列を取得する. - * @return 秘密鍵のHEX文字列 + * @brief Get HEX string. + * @return HEX string */ std::string GetHex() const; /** - * @brief 自身のByteDataを取得する. - * @return 秘密鍵のByteData + * @brief Get ByteData instance. + * @return ByteData */ ByteData GetData() const; /** - * @brief WIFに変換する. + * @brief Convert to WIF. * @param[in] net_type Mainnet or Testnet - * @param[in] is_compressed privatekeyから導出するpubkeyのcompress有無 - * @return WIF文字列 + * @param[in] is_compressed pubkey compressed flag. + * @return WIF */ std::string ConvertWif(NetType net_type, bool is_compressed = true) const; + /** + * @brief Get Wallet Import Format from member value. + * @return WIF + */ + std::string GetWif() const; /** - * @brief Private keyからPubkeyインスタンスを生成する. - * @param[in] is_compressed privatekeyから導出するpubkeyのcompress有無 - * @return Pubkeyインスタンス + * @brief Generate pubkey to privkey. + * @param[in] is_compressed pubkey compressed flag. + * @return Pubkey */ Pubkey GeneratePubkey(bool is_compressed = true) const; @@ -320,17 +345,17 @@ class CFD_CORE_EXPORT Privkey { Privkey CreateNegate() const; /** - * @brief PrivateKeyの設定状態が不正であるかを返却する. - * @retval true 状態が不正 - * @retval false 状態は正常 - * @deprecated API整理時に削除予定 + * @brief Returns whether the private key setting status is invalid. + * @retval true invalid + * @retval false valid + * @deprecated Scheduled to be deleted when organizing API */ bool IsInvalid() const; /** - * @brief PrivateKeyの設定状態が正常であるかを返却する. - * @retval true 状態は正常 - * @retval false 状態が不正 + * @brief Returns whether the Private Key setting status is normal. + * @retval true valid + * @retval false invalid */ bool IsValid() const; @@ -356,22 +381,28 @@ class CFD_CORE_EXPORT Privkey { * @param[in] is_compressed pubkey compressed. */ void SetPubkeyCompressed(bool is_compressed); + /** + * @brief set network type. + * @param[in] net_type network type. + */ + void SetNetType(NetType net_type); /** - * @brief WIFからPrivKeyインスタンスを生成する. - * @param[in] wif WIF文字列 + * @brief Generate privkey from WIF. + * @param[in] wif WIF * @param[in] net_type Mainnet or Testnet - * @param[in] is_compressed privatekeyから導出するpubkeyのcompress有無 - * @return Privkeyインスタンス + * @param[in] is_compressed pubkey compress flag. + * @return Privkey */ static Privkey FromWif( - const std::string &wif, NetType net_type, bool is_compressed = true); + const std::string &wif, NetType net_type = NetType::kCustomChain, + bool is_compressed = true); /** - * @brief 乱数からPrivkeyインスタンスを生成する. + * @brief Generate Privkey from random numbers. * - * 生成できるまで繰り返すため、時間がかかる場合がある. - * @return Privkeyインスタンス + * It may take some time because it repeats until it can be generated. + * @return Privkey */ static Privkey GenerageRandomKey(); @@ -433,12 +464,16 @@ class CFD_CORE_EXPORT Privkey { * @brief pubkey compressed. */ bool is_compressed_ = true; + /** + * @brief network type. + */ + NetType net_type_ = NetType::kMainnet; /** - * @brief 秘密鍵として正しい形式であるかを検証する. - * @param[in] buffer 秘密鍵のByteData - * @retval true 正常フォーマット - * @retval false 不正フォーマット + * @brief Verify that it is in the correct format as a private key. + * @param[in] buffer ByteData of privkey. + * @retval true valid + * @retval false invalid */ static bool IsValid(const std::vector &buffer); }; diff --git a/include/cfdcore/cfdcore_logger_interface.h b/include/cfdcore/cfdcore_logger_interface.h index 4c97742e..70c957c9 100644 --- a/include/cfdcore/cfdcore_logger_interface.h +++ b/include/cfdcore/cfdcore_logger_interface.h @@ -2,7 +2,7 @@ /** * @file cfdcore_logger_interface.h * - * @brief ログ処理のインタフェースを定義するファイル。 + * @brief A file that defines the interface for log processing. */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_LOGGER_INTERFACE_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_LOGGER_INTERFACE_H_ @@ -13,19 +13,19 @@ namespace cfd { namespace core { /** - * @brief ログ出力用の関数ポインタを設定する。 - * @param[in] function_address 関数ポインタ + * @brief Set a function pointer for log output. + * @param[in] function_address Function pointer */ CFD_CORE_API void SetLogger(void* function_address); /** - * @brief ログ機能の初期化を行う。 + * @brief Initialize the log function. */ CFD_CORE_API void InitializeLogger(void); /** - * @brief ログ機能の終了処理を行う。 - * @param[in] is_finish_process プロセス終了時かどうか + * @brief Performs termination processing of the log function. + * @param[in] is_finish_process Whether at the end of the process */ CFD_CORE_API void FinalizeLogger(bool is_finish_process = false); diff --git a/include/cfdcore/cfdcore_psbt.h b/include/cfdcore/cfdcore_psbt.h new file mode 100644 index 00000000..bb8a24dc --- /dev/null +++ b/include/cfdcore/cfdcore_psbt.h @@ -0,0 +1,731 @@ +// Copyright 2020 CryptoGarage +/** + * @file cfdcore_psbt.h + * + * @brief This file is defines Partially Signed Bitcoin Transaction. + * + */ +#ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_PSBT_H_ +#define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_PSBT_H_ + +#include +#include +#include + +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_hdwallet.h" +#include "cfdcore/cfdcore_transaction.h" + +namespace cfd { +namespace core { + +/** + * @brief The class of Partially Signed Bitcoin Transaction. + */ +class CFD_CORE_EXPORT Psbt { + public: + //! PSBT_GLOBAL_UNSIGNED_TX + static constexpr uint8_t kPsbtGlobalUnsignedTx = 0x00; + //! PSBT_GLOBAL_XPUB + static constexpr uint8_t kPsbtGlobalXpub = 0x01; + //! PSBT_GLOBAL_VERSION + static constexpr uint8_t kPsbtGlobalVersion = 0xfb; + //! PSBT_GLOBAL_PROPRIETARY + static constexpr uint8_t kPsbtGlobalProprietary = 0xfc; + //! PSBT_IN_NON_WITNESS_UTXO + static constexpr uint8_t kPsbtInputNonWitnessUtxo = 0x00; + //! PSBT_IN_WITNESS_UTXO + static constexpr uint8_t kPsbtInputWitnessUtxo = 0x01; + //! PSBT_IN_PARTIAL_SIG + static constexpr uint8_t kPsbtInputPartialSig = 0x02; + //! PSBT_IN_SIGHASH_TYPE + static constexpr uint8_t kPsbtInputSighashType = 0x03; + //! PSBT_IN_REDEEM_SCRIPT + static constexpr uint8_t kPsbtInputRedeemScript = 0x04; + //! PSBT_IN_WITNESS_SCRIPT + static constexpr uint8_t kPsbtInputWitnessScript = 0x05; + //! PSBT_IN_BIP32_DERIVATION + static constexpr uint8_t kPsbtInputBip32Derivation = 0x06; + //! PSBT_IN_FINAL_SCRIPTSIG + static constexpr uint8_t kPsbtInputFinalScriptsig = 0x07; + //! PSBT_IN_FINAL_SCRIPTWITNESS + static constexpr uint8_t kPsbtInputFinalScriptWitness = 0x08; + //! PSBT_IN_POR_COMMITMENT + static constexpr uint8_t kPsbtInputPorCommitment = 0x09; + //! PSBT_IN_RIPEMD160 + static constexpr uint8_t kPsbtInputRipemd160 = 0x0a; + //! PSBT_IN_SHA256 + static constexpr uint8_t kPsbtInputSha256 = 0x0b; + //! PSBT_IN_HASH160 + static constexpr uint8_t kPsbtInputHash160 = 0x0c; + //! PSBT_IN_HASH256 + static constexpr uint8_t kPsbtInputHash256 = 0x0d; + //! PSBT_IN_PROPRIETARY + static constexpr uint8_t kPsbtInputProprietary = 0xfc; + //! PSBT_OUT_REDEEM_SCRIPT + static constexpr uint8_t kPsbtOutputRedeemScript = 0x00; + //! PSBT_OUT_WITNESS_SCRIPT + static constexpr uint8_t kPsbtOutputWitnessScript = 0x01; + //! PSBT_OUT_BIP32_DERIVATION + static constexpr uint8_t kPsbtOutputBip32Derivation = 0x02; + //! PSBT_OUT_PROPRIETARY + static constexpr uint8_t kPsbtOutputProprietary = 0xfc; + + /** + * @brief Get PSBT default version. + * @return PSBT version. + */ + static uint32_t GetDefaultVersion(); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @return key data. + */ + static ByteData CreateRecordKey(uint8_t type); + /** + * @brief Create psbt fix size record key. + * @param[in] type key type. + * @param[in] fixed_size_key fixed size key value. + * @return key data. + */ + static ByteData CreateFixRecordKey( + uint8_t type, const ByteData& fixed_size_key); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] key_bytes key value. + * @return key data. + */ + static ByteData CreateRecordKey(uint8_t type, const ByteData& key_bytes); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] key key value. + * @return key data. + */ + static ByteData CreateRecordKey(uint8_t type, const std::string& key); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] prefix key prefix. + * @param[in] sub_type sub field key type. + * @return key data. + */ + static ByteData CreateRecordKey( + uint8_t type, const ByteData& prefix, uint8_t sub_type); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] prefix key prefix. + * @param[in] sub_type sub field key type. + * @return key data. + */ + static ByteData CreateRecordKey( + uint8_t type, const std::string& prefix, uint8_t sub_type); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] prefix key prefix. + * @param[in] sub_type sub field key type. + * @param[in] sub_key_bytes sub field key value. + * @return key data. + */ + static ByteData CreateRecordKey( + uint8_t type, const ByteData& prefix, uint8_t sub_type, + const ByteData& sub_key_bytes); + /** + * @brief Create psbt record key. + * @param[in] type key type. + * @param[in] prefix key prefix. + * @param[in] sub_type sub field key type. + * @param[in] sub_key sub field key value. + * @return key data. + */ + static ByteData CreateRecordKey( + uint8_t type, const std::string& prefix, uint8_t sub_type, + const std::string& sub_key); + /** + * @brief Create psbt pubkey record key. + * @param[in] type key type. + * @param[in] pubkey pubkey value. + * @return key data. + */ + static ByteData CreatePubkeyRecordKey(uint8_t type, const Pubkey& pubkey); + + /** + * @brief constructor. + * + * for List. + */ + Psbt(); + /** + * @brief constructor + * @param[in] version tx version + * @param[in] lock_time lock time + */ + explicit Psbt(uint32_t version, uint32_t lock_time); + /** + * @brief constructor + * @param[in] psbt_version psbt version + * @param[in] version tx version + * @param[in] lock_time lock time + */ + explicit Psbt(uint32_t psbt_version, uint32_t version, uint32_t lock_time); + /** + * @brief constructor + * @param[in] base64 base64 string. + */ + explicit Psbt(const std::string& base64); + /** + * @brief constructor + * @param[in] byte_data byte data + */ + explicit Psbt(const ByteData& byte_data); + /** + * @brief constructor + * @param[in] transaction Transaction object. + */ + explicit Psbt(const Transaction& transaction); + /** + * @brief constructor + * @param[in] psbt_version psbt version + * @param[in] transaction Transaction object. + */ + explicit Psbt(uint32_t psbt_version, const Transaction& transaction); + /** + * @brief constructor + * @param[in] psbt Psbt object. + */ + Psbt(const Psbt& psbt); + /** + * @brief destructor + */ + virtual ~Psbt() { Psbt::FreeWallyPsbtAddress(wally_psbt_pointer_); } + + /** + * @brief copy constructor. + * @param[in] psbt Psbt object. + * @return Psbt object. + */ + Psbt& operator=(const Psbt& psbt) &; + + /** + * @brief Get base64 string. + * @return base64 string. + */ + std::string GetBase64() const; + + /** + * @brief Get binary data. + * @return binary data. + */ + ByteData GetData() const; + + /** + * @brief Get binary size. + * @return binary size. + */ + uint32_t GetDataSize() const; + + /** + * @brief check finalized. + * @retval true already finalized. + * @retval false not finalized. + */ + bool IsFinalized() const; + + /** + * @brief check finalized input. + * @param[in] index txin index. + * @retval true already finalized input. + * @retval false not finalized input. + */ + bool IsFinalizedInput(uint32_t index) const; + + /** + * @brief finalize input all. + * @details support hashtype is p2pkh, p2wpkh, p2sh-p2wpkh, multisig(p2sh, p2wsh, p2sh-p2wsh) + */ + void Finalize(); + + /** + * @brief extract transaction. + * @details need already finalized. + * @return binary transaction data. + */ + ByteData Extract() const; + + /** + * @brief extract transaction. + * @details need already finalized. + * @return transactiona. + */ + Transaction ExtractTransaction() const; + + /** + * @brief Get currently base transaction. + * @return base transaction. + */ + Transaction GetTransaction() const; + + /** + * @brief Join psbt transaction before sign. + * @param[in] transaction psbt transaction. + * @param[in] ignore_duplicate_error ignore duplicate parameter error. + */ + void Join(const Psbt& transaction, bool ignore_duplicate_error = false); + + /** + * @brief Sign psbt transaction. + * @param[in] privkey private key. + * @param[in] has_grind_r Grind-R option. + */ + void Sign(const Privkey& privkey, bool has_grind_r = true); + + /** + * @brief Combine signed psbt transaction. + * @param[in] transaction signed psbt transaction. + */ + void Combine(const Psbt& transaction); + + /** + * @brief Get txin count. + * @return txin count. + */ + uint32_t GetTxInCount() const; + + /** + * @brief Get txout count. + * @return txout count. + */ + uint32_t GetTxOutCount() const; + + /** + * @brief add base transaction input. + * @param[in] txin transaction input. + * @return added index. + */ + uint32_t AddTxIn(const TxIn& txin); + /** + * @brief add base transaction input. + * @param[in] txin transaction input. + * @return added index. + */ + uint32_t AddTxIn(const TxInReference& txin); + /** + * @brief add base transaction input. + * @param[in] txid utxo txid. + * @param[in] vout utxo vout. + * @param[in] sequence sequence. + * @return added index. + */ + uint32_t AddTxIn(const Txid& txid, uint32_t vout, uint32_t sequence); + + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] tx utxo transaction + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo(uint32_t index, const Transaction& tx, const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] tx utxo transaction + * @param[in] redeem_script utxo related script (only script hash) + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + uint32_t index, const Transaction& tx, const Script& redeem_script, + const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] tx utxo transaction + * @param[in] redeem_script utxo related script (only script hash) + * @param[in] key_list utxo related pubkey list + */ + void SetTxInUtxo( + uint32_t index, const Transaction& tx, const Script& redeem_script, + const std::vector& key_list); + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] txout utxo witness transaction output + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + uint32_t index, const TxOutReference& txout, const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] txout utxo witness transaction output + * @param[in] redeem_script utxo related script (only script hash) + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + uint32_t index, const TxOutReference& txout, const Script& redeem_script, + const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] index input index + * @param[in] txout utxo witness transaction output + * @param[in] redeem_script utxo related script (only script hash) + * @param[in] key_list utxo related pubkey list + */ + void SetTxInUtxo( + uint32_t index, const TxOutReference& txout, const Script& redeem_script, + const std::vector& key_list); + /** + * @brief set input utxo data on direct. + * @param[in] index input index + * @param[in] txout utxo witness transaction output + */ + void SetTxInWitnessUtxoDirect(uint32_t index, const TxOutReference& txout); + /** + * @brief set input bip32 key on direct. + * @param[in] index input index + * @param[in] key_data key data + */ + void SetTxInBip32KeyDirect(uint32_t index, const KeyData& key_data); + + /** + * @brief set input signature. + * @param[in] index input index + * @param[in] key utxo related pubkey + * @param[in] signature signature data + */ + void SetTxInSignature( + uint32_t index, const KeyData& key, const ByteData& signature); + /** + * @brief set input sighash type. + * @param[in] index input index + * @param[in] sighash_type sighash type + */ + void SetTxInSighashType(uint32_t index, const SigHashType& sighash_type); + /** + * @brief set input final script. + * @param[in] index input index + * @param[in] unlocking_script unlocking script data list. + */ + void SetTxInFinalScript( + uint32_t index, const std::vector& unlocking_script); + /** + * @brief set input record. + * @param[in] index input index + * @param[in] key key + * @param[in] value value + */ + void SetTxInRecord( + uint32_t index, const ByteData& key, const ByteData& value); + + /** + * @brief get input utxo full transaction. + * @param[in] index input index + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return utxo transaction + */ + Transaction GetTxInUtxoFull( + uint32_t index, bool ignore_error = false, + bool* is_witness = nullptr) const; + /** + * @brief get input utxo output data. + * @param[in] index input index + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return utxo transaction output + */ + TxOut GetTxInUtxo( + uint32_t index, bool ignore_error = false, + bool* is_witness = nullptr) const; + /** + * @brief get input redeem script. + * @param[in] index input index + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return redeem script (or witness script) + */ + Script GetTxInRedeemScript( + uint32_t index, bool ignore_error = false, + bool* is_witness = nullptr) const; + /** + * @brief get input redeem script. + * @param[in] index input index + * @param[in] ignore_error ignore error with empty data. + * @param[in] is_witness getting target witness. + * @return redeem script (or witness script) + */ + Script GetTxInRedeemScriptDirect( + uint32_t index, bool ignore_error = false, bool is_witness = true) const; + /** + * @brief get input key data list. + * @param[in] index input index + * @return key data list + */ + std::vector GetTxInKeyDataList(uint32_t index) const; + /** + * @brief get input key data (only list top data). + * @param[in] index input index + * @param[in] ignore_error ignore error with empty data. + * @return key data + */ + KeyData GetTxInKeyData(uint32_t index, bool ignore_error = false) const; + /** + * @brief get input key data list related to signature. + * @param[in] index input index + * @return key data list + */ + std::vector GetTxInSignaturePubkeyList(uint32_t index) const; + /** + * @brief get input signature related pubkey. + * @param[in] index input index + * @param[in] pubkey pubkey + * @return signature + */ + ByteData GetTxInSignature(uint32_t index, const Pubkey& pubkey) const; + /** + * @brief exist input signature related pubkey. + * @param[in] index input index + * @param[in] pubkey pubkey + * @retval true exist signature + * @retval false signature not found + */ + bool IsFindTxInSignature(uint32_t index, const Pubkey& pubkey) const; + /** + * @brief get input sighash type. + * @param[in] index input index + * @return sighash type + */ + SigHashType GetTxInSighashType(uint32_t index) const; + /** + * @brief exist input sighash type. + * @param[in] index input index + * @retval true exist sighash type + * @retval false sighash type not found + */ + bool IsFindTxInSighashType(uint32_t index) const; + /** + * @brief get input final script. + * @param[in] index input index + * @param[in] is_witness_stack target witness flag + * @return witness stack or scriptSig + */ + std::vector GetTxInFinalScript( + uint32_t index, bool is_witness_stack = true) const; + /** + * @brief get input record value. + * @param[in] index input index + * @param[in] key record key + * @return record value + */ + ByteData GetTxInRecord(uint32_t index, const ByteData& key) const; + /** + * @brief exist input record. + * @param[in] index input index + * @param[in] key record key + * @retval true exist record + * @retval false record not found + */ + bool IsFindTxInRecord(uint32_t index, const ByteData& key) const; + /** + * @brief get record key list. + * @param[in] index input index + * @return record key list + */ + std::vector GetTxInRecordKeyList(uint32_t index) const; + /** + * @brief clear input sign data. + * @details clear target is redeem script, signature, sighashtype. + * @param[in] index input index + */ + void ClearTxInSignData(uint32_t index); + + /** + * @brief add base transaction output. + * @param[in] txout transaction output. + * @return added index. + */ + uint32_t AddTxOut(const TxOut& txout); + /** + * @brief add base transaction output. + * @param[in] txout transaction output. + * @return added index. + */ + uint32_t AddTxOut(const TxOutReference& txout); + /** + * @brief add base transaction output. + * @param[in] locking_script locking script. + * @param[in] amount amount. + * @return added index. + */ + uint32_t AddTxOut(const Script& locking_script, const Amount& amount); + + /** + * @brief set output data. + * @param[in] index output index + * @param[in] key output related pubkey + */ + void SetTxOutData(uint32_t index, const KeyData& key); + /** + * @brief set output data. + * @param[in] index output index + * @param[in] redeem_script output redeem script (only script hash) + * @param[in] key output related pubkey + */ + void SetTxOutData( + uint32_t index, const Script& redeem_script, const KeyData& key); + /** + * @brief set output data. + * @param[in] index output index + * @param[in] redeem_script output redeem script (only script hash) + * @param[in] key_list output related pubkey list + */ + void SetTxOutData( + uint32_t index, const Script& redeem_script, + const std::vector& key_list); + /** + * @brief set output record. + * @param[in] index output index + * @param[in] key record key + * @param[in] value record value + */ + void SetTxOutRecord( + uint32_t index, const ByteData& key, const ByteData& value); + + /** + * @brief get output redeem script. + * @param[in] index output index + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return redeem script (or witness script) + */ + Script GetTxOutScript( + uint32_t index, bool ignore_error = false, + bool* is_witness = nullptr) const; + /** + * @brief get output key data (only list top data). + * @param[in] index output index + * @param[in] ignore_error ignore error with empty data. + * @return key data + */ + KeyData GetTxOutKeyData(uint32_t index, bool ignore_error = false) const; + /** + * @brief get output key data list related to redeem script. + * @param[in] index output index + * @return key data list + */ + std::vector GetTxOutKeyDataList(uint32_t index) const; + /** + * @brief get output record value. + * @param[in] index output index + * @param[in] key record key + * @return record value + */ + ByteData GetTxOutRecord(uint32_t index, const ByteData& key) const; + /** + * @brief exist output record. + * @param[in] index output index + * @param[in] key record key + * @retval true exist record + * @retval false record not found + */ + bool IsFindTxOutRecord(uint32_t index, const ByteData& key) const; + /** + * @brief get record key list. + * @param[in] index output index + * @return record key list + */ + std::vector GetTxOutRecordKeyList(uint32_t index) const; + + /** + * @brief Get the psbt version. + * @return psbt version + */ + uint32_t GetPsbtVersion() const; + /** + * @brief set global extpubkey. + * @param[in] key extkey data. + */ + void SetGlobalXpubkey(const KeyData& key); + /** + * @brief get global extpubkey. + * @param[in] key extpubkey + * @return key data + */ + KeyData GetGlobalXpubkeyBip32(const ExtPubkey& key) const; + /** + * @brief exist global extpubkey. + * @param[in] key extpubkey + * @retval true exist record + * @retval false record not found + */ + bool IsFindGlobalXpubkey(const ExtPubkey& key) const; + /** + * @brief get global key data list. + * @return key data list + */ + std::vector GetGlobalXpubkeyDataList() const; + /** + * @brief set global record. + * @param[in] key record key + * @param[in] value record value + */ + void SetGlobalRecord(const ByteData& key, const ByteData& value); + /** + * @brief get global record value. + * @param[in] key record key + * @return record value + */ + ByteData GetGlobalRecord(const ByteData& key) const; + /** + * @brief exist global record. + * @param[in] key record key + * @retval true exist record + * @retval false record not found + */ + bool IsFindGlobalRecord(const ByteData& key) const; + /** + * @brief get record key list. + * @return record key list + */ + std::vector GetGlobalRecordKeyList() const; + + protected: + void* wally_psbt_pointer_; ///< libwally psbt pointer + Transaction base_tx_; ///< base transaction + + /** + * @brief Free a heap address for libwally-core psbt object. + * @param[in] wally_psbt_pointer address + */ + static void FreeWallyPsbtAddress(const void* wally_psbt_pointer); + /** + * @brief Rebuild base transaction. + * @param[in] wally_psbt_pointer address + * @return Transaction + */ + static Transaction RebuildTransaction(const void* wally_psbt_pointer); + + /** + * @brief Check the index range of the TxIn array. + * @param[in] index input index + * @param[in] line file line + * @param[in] caller caller function name + */ + virtual void CheckTxInIndex( + uint32_t index, int line, const char* caller) const; + /** + * @brief Check the index range of the TxOut array. + * @param[in] index output index + * @param[in] line file line + * @param[in] caller caller function name + */ + virtual void CheckTxOutIndex( + uint32_t index, int line, const char* caller) const; +}; + +} // namespace core +} // namespace cfd + +#endif // CFD_CORE_INCLUDE_CFDCORE_CFDCORE_PSBT_H_ diff --git a/include/cfdcore/cfdcore_schnorrsig.h b/include/cfdcore/cfdcore_schnorrsig.h index 3f780ddf..c41f9809 100644 --- a/include/cfdcore/cfdcore_schnorrsig.h +++ b/include/cfdcore/cfdcore_schnorrsig.h @@ -1,12 +1,14 @@ // Copyright 2020 CryptoGarage +#ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_SCHNORRSIG_H_ +#define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_SCHNORRSIG_H_ + #include +#include #include "cfdcore/cfdcore_bytedata.h" #include "cfdcore/cfdcore_common.h" #include "cfdcore/cfdcore_key.h" - -#ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_SCHNORRSIG_H_ -#define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_SCHNORRSIG_H_ +#include "cfdcore/cfdcore_util.h" namespace cfd { namespace core { @@ -15,6 +17,7 @@ using cfd::core::ByteData; using cfd::core::ByteData256; using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SigHashType; class SchnorrSignature; @@ -60,6 +63,12 @@ class CFD_CORE_EXPORT SchnorrPubkey { * @return ByteData */ ByteData GetData() const; + /** + * @brief Get the underlying ByteData256 object + * + * @return ByteData256 + */ + ByteData256 GetByteData256() const; /** * @brief Get the hex string. * @@ -128,7 +137,7 @@ class CFD_CORE_EXPORT SchnorrPubkey { * @param[in] parity the parity of the pubkey. * @return pubkey */ - Pubkey CreatePubkey(bool parity) const; + Pubkey CreatePubkey(bool parity = false) const; /** * @brief get schnorr public key from private key. @@ -211,19 +220,39 @@ class CFD_CORE_EXPORT SchnorrSignature { * @param data the data representing the adaptor signature */ explicit SchnorrSignature(const std::string &data); + /** + * @brief Construct a new Schnorr Signature object from a Signature object. + * + * @param[in] object the data representing the adaptor signature + */ + SchnorrSignature(const SchnorrSignature &object); + /** + * @brief copy constructor. + * @param[in] object the data representing the adaptor signature + * @return object + */ + SchnorrSignature &operator=(const SchnorrSignature &object); /** * @brief Get the underlying ByteData object - * + * + * @param[in] append_sighash_type add sighash type. * @return ByteData */ - ByteData GetData() const; + ByteData GetData(bool append_sighash_type = false) const; /** * @brief Get the hex string. * + * @param[in] append_sighash_type add sighash type. * @return hex string. */ - std::string GetHex() const; + std::string GetHex(bool append_sighash_type = false) const; + /** + * @brief Get the sighash type. + * + * @return sighash type. + */ + SigHashType GetSigHashType() const; /** * @brief Return the nonce part of the signature. @@ -239,12 +268,31 @@ class CFD_CORE_EXPORT SchnorrSignature { */ Privkey GetPrivkey() const; + /** + * @brief Get the sighash type. + * @param[in] sighash_type sighash type + */ + void SetSigHashType(const SigHashType &sighash_type); + + /** + * @brief check valid sighash type. + * @param[in] sighash_type_value sighash type + * @retval true valid + * @retval false invalid + */ + static bool IsValidSigHashType(uint8_t sighash_type_value); + private: /** * @brief The underlying data * */ ByteData data_; + /** + * @brief The sighash type + * + */ + SigHashType sighash_type_; }; /** @@ -252,6 +300,16 @@ class CFD_CORE_EXPORT SchnorrSignature { */ class CFD_CORE_EXPORT SchnorrUtil { public: + /** + * @brief Create a schnorr signature over the given message using the given + * private key and auxiliary random data. + * + * @param msg the message to create the signature for. + * @param sk the secret key to create the signature with. + * @return SchnorrSignature + */ + static SchnorrSignature Sign(const ByteData256 &msg, const Privkey &sk); + /** * @brief Create a schnorr signature over the given message using the given * private key and auxiliary random data. @@ -288,6 +346,23 @@ class CFD_CORE_EXPORT SchnorrUtil { const ByteData256 &msg, const SchnorrPubkey &nonce, const SchnorrPubkey &pubkey); + /** + * @brief Compute the sum of signature points for a set of Schnorr signature. + * This enable reducing the number of EC multiplications done when computing the + * addition of multiple signature points. So instead of computing: + * S = (R_0 + X * H(X || R_0 || m_0)) + ... + (R_n + X * H(X || R_n || m_n)) + * This function computes: + * S = (R_0 + ... + R_n) + X * (H(X || R_0 || m_0) + ... + H(X || R_n || m_n)) + * + * @param msgs the set of messages that will be signed. + * @param nonces the public component of the nonces that will be used. + * @param pubkey the public key for which the signatures will be valid. + * @return Pubkey the signature point. + */ + static Pubkey ComputeSigPointBatch( + const std::vector &msgs, + const std::vector &nonces, const SchnorrPubkey &pubkey); + /** * @brief Verify a Schnorr signature. * diff --git a/include/cfdcore/cfdcore_script.h b/include/cfdcore/cfdcore_script.h index c4d96fac..4827ace4 100644 --- a/include/cfdcore/cfdcore_script.h +++ b/include/cfdcore/cfdcore_script.h @@ -2,7 +2,7 @@ /** * @file cfdcore_script.h * - * @brief Script関連クラス定義 + * @brief The script related class definition. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_SCRIPT_H_ @@ -21,19 +21,46 @@ namespace cfd { namespace core { -/// P2PKHのScriptサイズ(WALLY_SCRIPTPUBKEY_P2PKH_LEN) +/// Script size on P2PKH. constexpr size_t kScriptHashP2pkhLength = 25; -/// P2SHのScriptサイズ(WALLY_SCRIPTPUBKEY_P2SH_LEN) +/// Script size on P2SH. constexpr size_t kScriptHashP2shLength = 23; -/// P2WPKHのScriptサイズ(WALLY_SCRIPTPUBKEY_P2WPKH_LEN) +/// Script size on P2WPKH. constexpr size_t kScriptHashP2wpkhLength = 22; -/// P2WSHのScriptサイズ(WALLY_SCRIPTPUBKEY_P2WSH_LEN) +/// Script size on P2WSH. constexpr size_t kScriptHashP2wshLength = 34; -/// WitnessProgramの最小サイズ +/// Script size on Taproot. +constexpr size_t kScriptHashTaprootLength = 34; +/// WitnessProgram's minimum size. constexpr size_t kMinWitnessProgramLength = 4; -/// WitnessProgramの最大サイズ +/// WitnessProgram's maximum size. constexpr size_t kMaxWitnessProgramLength = 42; +/** + * @typedef WitnessVersion + * @brief Witness version + */ +enum WitnessVersion { + kVersionNone = -1, //!< Missing WitnessVersion + kVersion0 = 0, //!< version 0 + kVersion1, //!< version 1 (for future use) + kVersion2, //!< version 2 (for future use) + kVersion3, //!< version 3 (for future use) + kVersion4, //!< version 4 (for future use) + kVersion5, //!< version 5 (for future use) + kVersion6, //!< version 6 (for future use) + kVersion7, //!< version 7 (for future use) + kVersion8, //!< version 8 (for future use) + kVersion9, //!< version 9 (for future use) + kVersion10, //!< version 10 (for future use) + kVersion11, //!< version 11 (for future use) + kVersion12, //!< version 12 (for future use) + kVersion13, //!< version 13 (for future use) + kVersion14, //!< version 14 (for future use) + kVersion15, //!< version 15 (for future use) + kVersion16 //!< version 16 (for future use) +}; + /** * @brief script element type */ @@ -56,6 +83,7 @@ enum ScriptType { kOpPushData4 = 0x4e, //!< kOpPushData4 kOp1Negate = 0x4f, //!< kOp1Negate kOpReserved = 0x50, //!< kOpReserved + kOpSuccess80 = 0x50, //!< kOpSuccess80 (BIP-342) kOp_1 = 0x51, //!< kOp_1 kOpTrue = 0x51, //!< kOpTrue kOp_2 = 0x52, //!< kOp_2 @@ -75,6 +103,7 @@ enum ScriptType { kOp_16 = 0x60, //!< kOp_16 kOpNop = 0x61, //!< kOpNop kOpVer = 0x62, //!< kOpVer + kOpSuccess98 = 0x62, //!< kOpSuccess98 (BIP-342) kOpIf = 0x63, //!< kOpIf kOpNotIf = 0x64, //!< kOpNotIf kOpVerIf = 0x65, //!< kOpVerIf @@ -106,19 +135,31 @@ enum ScriptType { kOpSubstr = 0x7f, //!< kOpSubstr kOpLeft = 0x80, //!< kOpLeft kOpRight = 0x81, //!< kOpRight + kOpSuccess126 = 0x7e, //!< kOpSuccess126 (BIP-342) + kOpSuccess127 = 0x7f, //!< kOpSuccess127 (BIP-342) + kOpSuccess128 = 0x80, //!< kOpSuccess128 (BIP-342) + kOpSuccess129 = 0x81, //!< kOpSuccess129 (BIP-342) kOpSize = 0x82, //!< kOpSize kOpInvert = 0x83, //!< kOpInvert kOpAnd = 0x84, //!< kOpAnd kOpOr = 0x85, //!< kOpOr kOpXor = 0x86, //!< kOpXor + kOpSuccess131 = 0x83, //!< kOpSuccess131 (BIP-342) + kOpSuccess132 = 0x84, //!< kOpSuccess132 (BIP-342) + kOpSuccess133 = 0x85, //!< kOpSuccess133 (BIP-342) + kOpSuccess134 = 0x86, //!< kOpSuccess134 (BIP-342) kOpEqual = 0x87, //!< kOpEqual kOpEqualVerify = 0x88, //!< kOpEqualVerify kOpReserved1 = 0x89, //!< kOpReserved1 kOpReserved2 = 0x8a, //!< kOpReserved2 + kOpSuccess137 = 0x89, //!< kOpSuccess137 (BIP-342) + kOpSuccess138 = 0x8a, //!< kOpSuccess138 (BIP-342) kOp1Add = 0x8b, //!< kOp1Add kOp1Sub = 0x8c, //!< kOp1Sub kOp2Mul = 0x8d, //!< kOp2Mul kOp2Div = 0x8e, //!< kOp2Div + kOpSuccess141 = 0x8d, //!< kOpSuccess141 (BIP-342) + kOpSuccess142 = 0x8e, //!< kOpSuccess142 (BIP-342) kOpNegate = 0x8f, //!< kOpNegate kOpAbs = 0x90, //!< kOpAbs kOpNot = 0x91, //!< kOpNot @@ -130,6 +171,11 @@ enum ScriptType { kOpMod = 0x97, //!< kOpMod kOpLShift = 0x98, //!< kOpLShift kOpRShift = 0x99, //!< kOpRShift + kOpSuccess149 = 0x95, //!< kOpSuccess149 (BIP-342) + kOpSuccess150 = 0x96, //!< kOpSuccess150 (BIP-342) + kOpSuccess151 = 0x97, //!< kOpSuccess151 (BIP-342) + kOpSuccess152 = 0x98, //!< kOpSuccess152 (BIP-342) + kOpSuccess153 = 0x99, //!< kOpSuccess153 (BIP-342) kOpBoolAnd = 0x9a, //!< kOpBoolAnd kOpBoolOr = 0x9b, //!< kOpBoolOr kOpNumEqual = 0x9c, //!< kOpNumEqual @@ -164,6 +210,75 @@ enum ScriptType { kOpNop8 = 0xb7, //!< kOpNop8 kOpNop9 = 0xb8, //!< kOpNop9 kOpNop10 = 0xb9, //!< kOpNop10 + kOpCheckSigAdd = 0xba, //!< kOpCheckSigAdd (BIP-342) + kOpSuccess187 = 0xbb, //!< kOpSuccess187 (BIP-342) + kOpSuccess188 = 0xbc, //!< kOpSuccess188 (BIP-342) + kOpSuccess189 = 0xbd, //!< kOpSuccess189 (BIP-342) + kOpSuccess190 = 0xbe, //!< kOpSuccess190 (BIP-342) + kOpSuccess191 = 0xbf, //!< kOpSuccess191 (BIP-342) + kOpSuccess192 = 0xc0, //!< kOpSuccess192 (BIP-342) + kOpSuccess193 = 0xc1, //!< kOpSuccess193 (BIP-342) + kOpSuccess194 = 0xc2, //!< kOpSuccess194 (BIP-342) + kOpSuccess195 = 0xc3, //!< kOpSuccess195 (BIP-342) + kOpSuccess196 = 0xc4, //!< kOpSuccess196 (BIP-342) + kOpSuccess197 = 0xc5, //!< kOpSuccess197 (BIP-342) + kOpSuccess198 = 0xc6, //!< kOpSuccess198 (BIP-342) + kOpSuccess199 = 0xc7, //!< kOpSuccess199 (BIP-342) + kOpSuccess200 = 0xc8, //!< kOpSuccess200 (BIP-342) + kOpSuccess201 = 0xc9, //!< kOpSuccess201 (BIP-342) + kOpSuccess202 = 0xca, //!< kOpSuccess202 (BIP-342) + kOpSuccess203 = 0xcb, //!< kOpSuccess203 (BIP-342) + kOpSuccess204 = 0xcc, //!< kOpSuccess204 (BIP-342) + kOpSuccess205 = 0xcd, //!< kOpSuccess205 (BIP-342) + kOpSuccess206 = 0xce, //!< kOpSuccess206 (BIP-342) + kOpSuccess207 = 0xcf, //!< kOpSuccess207 (BIP-342) + kOpSuccess208 = 0xd0, //!< kOpSuccess208 (BIP-342) + kOpSuccess209 = 0xd1, //!< kOpSuccess209 (BIP-342) + kOpSuccess210 = 0xd2, //!< kOpSuccess210 (BIP-342) + kOpSuccess211 = 0xd3, //!< kOpSuccess211 (BIP-342) + kOpSuccess212 = 0xd4, //!< kOpSuccess212 (BIP-342) + kOpSuccess213 = 0xd5, //!< kOpSuccess213 (BIP-342) + kOpSuccess214 = 0xd6, //!< kOpSuccess214 (BIP-342) + kOpSuccess215 = 0xd7, //!< kOpSuccess215 (BIP-342) + kOpSuccess216 = 0xd8, //!< kOpSuccess216 (BIP-342) + kOpSuccess217 = 0xd9, //!< kOpSuccess217 (BIP-342) + kOpSuccess218 = 0xda, //!< kOpSuccess218 (BIP-342) + kOpSuccess219 = 0xdb, //!< kOpSuccess219 (BIP-342) + kOpSuccess220 = 0xdc, //!< kOpSuccess220 (BIP-342) + kOpSuccess221 = 0xdd, //!< kOpSuccess221 (BIP-342) + kOpSuccess222 = 0xde, //!< kOpSuccess222 (BIP-342) + kOpSuccess223 = 0xdf, //!< kOpSuccess223 (BIP-342) + kOpSuccess224 = 0xe0, //!< kOpSuccess224 (BIP-342) + kOpSuccess225 = 0xe1, //!< kOpSuccess225 (BIP-342) + kOpSuccess226 = 0xe2, //!< kOpSuccess226 (BIP-342) + kOpSuccess227 = 0xe3, //!< kOpSuccess227 (BIP-342) + kOpSuccess228 = 0xe4, //!< kOpSuccess228 (BIP-342) + kOpSuccess229 = 0xe5, //!< kOpSuccess229 (BIP-342) + kOpSuccess230 = 0xe6, //!< kOpSuccess230 (BIP-342) + kOpSuccess231 = 0xe7, //!< kOpSuccess231 (BIP-342) + kOpSuccess232 = 0xe8, //!< kOpSuccess232 (BIP-342) + kOpSuccess233 = 0xe9, //!< kOpSuccess233 (BIP-342) + kOpSuccess234 = 0xea, //!< kOpSuccess234 (BIP-342) + kOpSuccess235 = 0xeb, //!< kOpSuccess235 (BIP-342) + kOpSuccess236 = 0xec, //!< kOpSuccess236 (BIP-342) + kOpSuccess237 = 0xed, //!< kOpSuccess237 (BIP-342) + kOpSuccess238 = 0xee, //!< kOpSuccess238 (BIP-342) + kOpSuccess239 = 0xef, //!< kOpSuccess239 (BIP-342) + kOpSuccess240 = 0xf0, //!< kOpSuccess240 (BIP-342) + kOpSuccess241 = 0xf1, //!< kOpSuccess241 (BIP-342) + kOpSuccess242 = 0xf2, //!< kOpSuccess242 (BIP-342) + kOpSuccess243 = 0xf3, //!< kOpSuccess243 (BIP-342) + kOpSuccess244 = 0xf4, //!< kOpSuccess244 (BIP-342) + kOpSuccess245 = 0xf5, //!< kOpSuccess245 (BIP-342) + kOpSuccess246 = 0xf6, //!< kOpSuccess246 (BIP-342) + kOpSuccess247 = 0xf7, //!< kOpSuccess247 (BIP-342) + kOpSuccess248 = 0xf8, //!< kOpSuccess248 (BIP-342) + kOpSuccess249 = 0xf9, //!< kOpSuccess249 (BIP-342) + kOpSuccess250 = 0xfa, //!< kOpSuccess250 (BIP-342) + kOpSuccess251 = 0xfb, //!< kOpSuccess251 (BIP-342) + kOpSuccess252 = 0xfc, //!< kOpSuccess252 (BIP-342) + kOpSuccess253 = 0xfd, //!< kOpSuccess253 (BIP-342) + kOpSuccess254 = 0xfe, //!< kOpSuccess254 (BIP-342) kOpInvalidOpCode = 0xff, //!< kOpInvalidOpCode #ifndef CFD_DISABLE_ELEMENTS kOpDeterministricRandom = 0xc0, //!< kOpDeterministricRandom @@ -181,13 +296,13 @@ enum ScriptType { class Script; /** - * @brief Script操作定義クラス。 + * @brief Script Operation definition class. * @details - * OP_XXXXの定義値についてですが、使用時は以下に注意してください。 - * - static linkする場合は、グローバル変数の初期値に使用しないこと。 - * - 初期化順序の関係で、未初期化状態で設定されることがあります。 - * - グローバル変数の初期値に使う場合、 - * ScriptOperatorではなくScriptTypeを使用して下さい。 + * Regarding the definition value of OP_XXXX, please note the following when using it. + * - When statically linking, do not use it as the initial value of global variables. + * - Due to the initialization order, it may be set in the uninitialized state. + * - When using it as the initial value of a global variable, \ + * use ScriptType instead of ScriptOperator. */ class CFD_CORE_EXPORT ScriptOperator { public: @@ -282,8 +397,7 @@ class CFD_CORE_EXPORT ScriptOperator { static const ScriptOperator OP_LESSTHAN; //!< OP_LESSTHAN static const ScriptOperator OP_GREATERTHAN; //!< OP_GREATERTHAN static const ScriptOperator OP_LESSTHANOREQUAL; //!< OP_LESSTHANOREQUAL - static const ScriptOperator - OP_GREATERTHANOREQUAL; //!< OP_GREATERTHANOREQUAL //NOLINT + static const ScriptOperator OP_GREATERTHANOREQUAL; //!< OP_GREATERTHANOREQUAL //NOLINT static const ScriptOperator OP_MIN; //!< OP_MIN static const ScriptOperator OP_MAX; //!< OP_MAX static const ScriptOperator OP_WITHIN; //!< OP_WITHIN @@ -296,14 +410,11 @@ class CFD_CORE_EXPORT ScriptOperator { static const ScriptOperator OP_CHECKSIG; //!< OP_CHECKSIG static const ScriptOperator OP_CHECKSIGVERIFY; //!< OP_CHECKSIGVERIFY static const ScriptOperator OP_CHECKMULTISIG; //!< OP_CHECKMULTISIG - static const ScriptOperator - OP_CHECKMULTISIGVERIFY; //!< OP_CHECKMULTISIGVERIFY //NOLINT + static const ScriptOperator OP_CHECKMULTISIGVERIFY; //!< OP_CHECKMULTISIGVERIFY //NOLINT static const ScriptOperator OP_NOP1; //!< OP_NOP1 - static const ScriptOperator - OP_CHECKLOCKTIMEVERIFY; //!< OP_CHECKLOCKTIMEVERIFY //NOLINT + static const ScriptOperator OP_CHECKLOCKTIMEVERIFY; //!< OP_CHECKLOCKTIMEVERIFY //NOLINT static const ScriptOperator OP_NOP2; //!< OP_NOP2 - static const ScriptOperator - OP_CHECKSEQUENCEVERIFY; //!< OP_CHECKSEQUENCEVERIFY //NOLINT + static const ScriptOperator OP_CHECKSEQUENCEVERIFY; //!< OP_CHECKSEQUENCEVERIFY //NOLINT static const ScriptOperator OP_NOP3; //!< OP_NOP3 static const ScriptOperator OP_NOP4; //!< OP_NOP4 static const ScriptOperator OP_NOP5; //!< OP_NOP5 @@ -312,6 +423,7 @@ class CFD_CORE_EXPORT ScriptOperator { static const ScriptOperator OP_NOP8; //!< OP_NOP8 static const ScriptOperator OP_NOP9; //!< OP_NOP9 static const ScriptOperator OP_NOP10; //!< OP_NOP10 + static const ScriptOperator OP_CHECKSIGADD; //!< OP_CHECKSIGADD static const ScriptOperator OP_INVALIDOPCODE; //!< OP_INVALIDOPCODE #ifndef CFD_DISABLE_ELEMENTS static const ScriptOperator @@ -325,6 +437,93 @@ class CFD_CORE_EXPORT ScriptOperator { static const ScriptOperator OP_PUBKEYHASH; //!< OP_PUBKEYHASH static const ScriptOperator OP_PUBKEY; //!< OP_PUBKEY #endif // CFD_DISABLE_ELEMENTS + static const ScriptOperator OP_SUCCESS80; //!< OP_SUCCESS80 (BIP-342) + static const ScriptOperator OP_SUCCESS98; //!< OP_SUCCESS98 (BIP-342) + static const ScriptOperator OP_SUCCESS126; //!< OP_SUCCESS126 (BIP-342) + static const ScriptOperator OP_SUCCESS127; //!< OP_SUCCESS127 (BIP-342) + static const ScriptOperator OP_SUCCESS128; //!< OP_SUCCESS128 (BIP-342) + static const ScriptOperator OP_SUCCESS129; //!< OP_SUCCESS129 (BIP-342) + static const ScriptOperator OP_SUCCESS131; //!< OP_SUCCESS131 (BIP-342) + static const ScriptOperator OP_SUCCESS132; //!< OP_SUCCESS132 (BIP-342) + static const ScriptOperator OP_SUCCESS133; //!< OP_SUCCESS133 (BIP-342) + static const ScriptOperator OP_SUCCESS134; //!< OP_SUCCESS134 (BIP-342) + static const ScriptOperator OP_SUCCESS137; //!< OP_SUCCESS137 (BIP-342) + static const ScriptOperator OP_SUCCESS138; //!< OP_SUCCESS138 (BIP-342) + static const ScriptOperator OP_SUCCESS141; //!< OP_SUCCESS141 (BIP-342) + static const ScriptOperator OP_SUCCESS142; //!< OP_SUCCESS142 (BIP-342) + static const ScriptOperator OP_SUCCESS149; //!< OP_SUCCESS149 (BIP-342) + static const ScriptOperator OP_SUCCESS150; //!< OP_SUCCESS150 (BIP-342) + static const ScriptOperator OP_SUCCESS151; //!< OP_SUCCESS151 (BIP-342) + static const ScriptOperator OP_SUCCESS152; //!< OP_SUCCESS152 (BIP-342) + static const ScriptOperator OP_SUCCESS153; //!< OP_SUCCESS153 (BIP-342) + static const ScriptOperator OP_SUCCESS187; //!< OP_SUCCESS187 (BIP-342) + static const ScriptOperator OP_SUCCESS188; //!< OP_SUCCESS188 (BIP-342) + static const ScriptOperator OP_SUCCESS189; //!< OP_SUCCESS189 (BIP-342) + static const ScriptOperator OP_SUCCESS190; //!< OP_SUCCESS190 (BIP-342) + static const ScriptOperator OP_SUCCESS191; //!< OP_SUCCESS191 (BIP-342) + static const ScriptOperator OP_SUCCESS192; //!< OP_SUCCESS192 (BIP-342) + static const ScriptOperator OP_SUCCESS193; //!< OP_SUCCESS193 (BIP-342) + static const ScriptOperator OP_SUCCESS194; //!< OP_SUCCESS194 (BIP-342) + static const ScriptOperator OP_SUCCESS195; //!< OP_SUCCESS195 (BIP-342) + static const ScriptOperator OP_SUCCESS196; //!< OP_SUCCESS196 (BIP-342) + static const ScriptOperator OP_SUCCESS197; //!< OP_SUCCESS197 (BIP-342) + static const ScriptOperator OP_SUCCESS198; //!< OP_SUCCESS198 (BIP-342) + static const ScriptOperator OP_SUCCESS199; //!< OP_SUCCESS199 (BIP-342) + static const ScriptOperator OP_SUCCESS200; //!< OP_SUCCESS200 (BIP-342) + static const ScriptOperator OP_SUCCESS201; //!< OP_SUCCESS201 (BIP-342) + static const ScriptOperator OP_SUCCESS202; //!< OP_SUCCESS202 (BIP-342) + static const ScriptOperator OP_SUCCESS203; //!< OP_SUCCESS203 (BIP-342) + static const ScriptOperator OP_SUCCESS204; //!< OP_SUCCESS204 (BIP-342) + static const ScriptOperator OP_SUCCESS205; //!< OP_SUCCESS205 (BIP-342) + static const ScriptOperator OP_SUCCESS206; //!< OP_SUCCESS206 (BIP-342) + static const ScriptOperator OP_SUCCESS207; //!< OP_SUCCESS207 (BIP-342) + static const ScriptOperator OP_SUCCESS208; //!< OP_SUCCESS208 (BIP-342) + static const ScriptOperator OP_SUCCESS209; //!< OP_SUCCESS209 (BIP-342) + static const ScriptOperator OP_SUCCESS210; //!< OP_SUCCESS210 (BIP-342) + static const ScriptOperator OP_SUCCESS211; //!< OP_SUCCESS211 (BIP-342) + static const ScriptOperator OP_SUCCESS212; //!< OP_SUCCESS212 (BIP-342) + static const ScriptOperator OP_SUCCESS213; //!< OP_SUCCESS213 (BIP-342) + static const ScriptOperator OP_SUCCESS214; //!< OP_SUCCESS214 (BIP-342) + static const ScriptOperator OP_SUCCESS215; //!< OP_SUCCESS215 (BIP-342) + static const ScriptOperator OP_SUCCESS216; //!< OP_SUCCESS216 (BIP-342) + static const ScriptOperator OP_SUCCESS217; //!< OP_SUCCESS217 (BIP-342) + static const ScriptOperator OP_SUCCESS218; //!< OP_SUCCESS218 (BIP-342) + static const ScriptOperator OP_SUCCESS219; //!< OP_SUCCESS219 (BIP-342) + static const ScriptOperator OP_SUCCESS220; //!< OP_SUCCESS220 (BIP-342) + static const ScriptOperator OP_SUCCESS221; //!< OP_SUCCESS221 (BIP-342) + static const ScriptOperator OP_SUCCESS222; //!< OP_SUCCESS222 (BIP-342) + static const ScriptOperator OP_SUCCESS223; //!< OP_SUCCESS223 (BIP-342) + static const ScriptOperator OP_SUCCESS224; //!< OP_SUCCESS224 (BIP-342) + static const ScriptOperator OP_SUCCESS225; //!< OP_SUCCESS225 (BIP-342) + static const ScriptOperator OP_SUCCESS226; //!< OP_SUCCESS226 (BIP-342) + static const ScriptOperator OP_SUCCESS227; //!< OP_SUCCESS227 (BIP-342) + static const ScriptOperator OP_SUCCESS228; //!< OP_SUCCESS228 (BIP-342) + static const ScriptOperator OP_SUCCESS229; //!< OP_SUCCESS229 (BIP-342) + static const ScriptOperator OP_SUCCESS230; //!< OP_SUCCESS230 (BIP-342) + static const ScriptOperator OP_SUCCESS231; //!< OP_SUCCESS231 (BIP-342) + static const ScriptOperator OP_SUCCESS232; //!< OP_SUCCESS232 (BIP-342) + static const ScriptOperator OP_SUCCESS233; //!< OP_SUCCESS233 (BIP-342) + static const ScriptOperator OP_SUCCESS234; //!< OP_SUCCESS234 (BIP-342) + static const ScriptOperator OP_SUCCESS235; //!< OP_SUCCESS235 (BIP-342) + static const ScriptOperator OP_SUCCESS236; //!< OP_SUCCESS236 (BIP-342) + static const ScriptOperator OP_SUCCESS237; //!< OP_SUCCESS237 (BIP-342) + static const ScriptOperator OP_SUCCESS238; //!< OP_SUCCESS238 (BIP-342) + static const ScriptOperator OP_SUCCESS239; //!< OP_SUCCESS239 (BIP-342) + static const ScriptOperator OP_SUCCESS240; //!< OP_SUCCESS240 (BIP-342) + static const ScriptOperator OP_SUCCESS241; //!< OP_SUCCESS241 (BIP-342) + static const ScriptOperator OP_SUCCESS242; //!< OP_SUCCESS242 (BIP-342) + static const ScriptOperator OP_SUCCESS243; //!< OP_SUCCESS243 (BIP-342) + static const ScriptOperator OP_SUCCESS244; //!< OP_SUCCESS244 (BIP-342) + static const ScriptOperator OP_SUCCESS245; //!< OP_SUCCESS245 (BIP-342) + static const ScriptOperator OP_SUCCESS246; //!< OP_SUCCESS246 (BIP-342) + static const ScriptOperator OP_SUCCESS247; //!< OP_SUCCESS247 (BIP-342) + static const ScriptOperator OP_SUCCESS248; //!< OP_SUCCESS248 (BIP-342) + static const ScriptOperator OP_SUCCESS249; //!< OP_SUCCESS249 (BIP-342) + static const ScriptOperator OP_SUCCESS250; //!< OP_SUCCESS250 (BIP-342) + static const ScriptOperator OP_SUCCESS251; //!< OP_SUCCESS251 (BIP-342) + static const ScriptOperator OP_SUCCESS252; //!< OP_SUCCESS252 (BIP-342) + static const ScriptOperator OP_SUCCESS253; //!< OP_SUCCESS253 (BIP-342) + static const ScriptOperator OP_SUCCESS254; //!< OP_SUCCESS254 (BIP-342) // @formatter:on // clang-format on @@ -343,6 +542,14 @@ class CFD_CORE_EXPORT ScriptOperator { */ static ScriptOperator Get(const std::string &message); + /** + * @brief Check if it is OP_SUCCESSxx. + * @param[in] op_code OP Code + * @retval true OP_SUCCESSxx + * @retval false other + */ + static bool IsOpSuccess(ScriptType op_code); + /** * @brief get data type. * @return script data type @@ -397,54 +604,50 @@ class CFD_CORE_EXPORT ScriptOperator { ScriptOperator &operator=(const ScriptOperator &object); /** - * @brief 等価比較オペレータ - * @param[in] object 比較対象 - * @retval true 等価 - * @retval false 不等価 - * @return 等価であればtrue, それ以外はfalse + * @brief Equals operator. + * @param[in] object object + * @retval true equals + * @retval false not equals */ bool operator==(const ScriptOperator &object) const; /** - * @brief 不等価比較オペレータ - * @param[in] object 比較対象 - * @retval true 不等価 - * @retval false 等価 - * @return 不等価であればtrue, それ以外はfalse + * @brief Not Equals operator. + * @param[in] object object + * @retval true not equals + * @retval false equals */ bool operator!=(const ScriptOperator &object) const; /** - * @brief 比較オペレータ - * @param[in] object 比較対象 - * @retval true 条件に合致 - * @retval false 条件に合致せず + * @brief Compare operator. + * @param[in] object object + * @retval true match + * @retval false unmatch */ bool operator<(const ScriptOperator &object) const; /** - * @brief 比較オペレータ - * @param[in] object 比較対象 - * @retval true 条件に合致 - * @retval false 条件に合致せず + * @brief Compare operator. + * @param[in] object object + * @retval true match + * @retval false unmatch */ bool operator<=(const ScriptOperator &object) const; /** - * @brief 比較オペレータ - * @param[in] object 比較対象 - * @retval true 条件に合致 - * @retval false 条件に合致せず + * @brief Compare operator. + * @param[in] object object + * @retval true match + * @retval false unmatch */ bool operator>(const ScriptOperator &object) const; /** - * @brief 比較オペレータ - * @param[in] object 比較対象 - * @retval true 条件に合致 - * @retval false 条件に合致せず + * @brief Compare operator. + * @param[in] object object + * @retval true match + * @retval false unmatch */ bool operator>=(const ScriptOperator &object) const; /** * @brief default constructor. - * - * リスト型使用時のため. */ ScriptOperator() : data_type_(kOpInvalidOpCode), text_data_("OP_INVALIDOPCODE") { @@ -470,117 +673,122 @@ class CFD_CORE_EXPORT ScriptOperator { }; /** - * @brief Script要素保持クラス。 + * @brief Script element class. */ class CFD_CORE_EXPORT ScriptElement { public: /** - * @brief コンストラクタ. - * @param[in] element オブジェクト + * @brief constructor. + * @param[in] element object */ ScriptElement(const ScriptElement &element); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] type OP_CODE */ explicit ScriptElement(const ScriptType &type); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] op_code OP_CODE */ explicit ScriptElement(const ScriptOperator &op_code); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] binary_data binary data */ explicit ScriptElement(const ByteData &binary_data); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] value script number. */ explicit ScriptElement(int64_t value); /** - * @brief デストラクタ. + * @brief constructor. + * @param[in] value script number. + * @param[in] is_binary binary mode. + */ + explicit ScriptElement(int64_t value, bool is_binary); + /** + * @brief destructor. */ virtual ~ScriptElement() { // do nothing } /** - * @brief コピーコンストラクタ. - * @param[in] element オブジェクト - * @return オブジェクト + * @brief copy constructor. + * @param[in] element object + * @return object */ ScriptElement &operator=(const ScriptElement &element); /** - * @brief 要素種別を取得する. - * @return 要素種別. + * @brief Get the element type. + * @return element type. */ ScriptElementType GetType() const; /** - * @brief OP_CODEを取得する. + * @brief Get the OP_CODE. * @return OP_CODE */ const ScriptOperator &GetOpCode() const; /** - * @brief バイナリ値を取得する. - * @return バイナリ値 + * @brief Get a binary data. + * @return binary data. */ ByteData GetBinaryData() const; /** - * @brief 数値情報を取得する. - * @return 数値情報 + * @brief Get a numeric value. + * @return numeric value. */ int64_t GetNumber() const; /** - * @brief バイト配列を取得する. - * @return バイト配列 + * @brief Get a byte array. + * @return byte array. */ ByteData GetData() const; /** - * @brief 文字列情報を取得する. - * @return 文字列情報 + * @brief Get a stirng data. + * @return string data. */ std::string ToString() const; /** - * @brief OP_CODE型の情報かどうか判定する. + * @brief Determine if it is OP_CODE type information. * @retval true OP_CODE - * @retval false その他 + * @retval false other */ bool IsOpCode() const { return type_ == kElementOpCode; } /** - * @brief 数値型の情報かどうか判定する. - * @retval true 数値型 - * @retval false その他 + * @brief Determine if it is numeric type information. + * @retval true Numeric type + * @retval false other type */ bool IsNumber() const { - // 数値型明示 or 数値が入っている or OP_0 の何れかなら数値とみなす + // If either the numeric type is specified, the number is included, + // or OP_0 is specified, it is regarded as a number. return (type_ == kElementNumber) || (value_ != 0) || (op_code_.GetDataType() == kOp_0); } /** - * @brief バイナリ情報かどうか判定する. - * @retval true バイナリ情報 - * @retval false その他 + * @brief Determine if it is binary information. + * @retval true Binary information + * @retval false other */ bool IsBinary() const { return type_ == kElementBinary; } /** - * @brief バイナリ値から数値型に変換する. - * @param[out] int64_value 数値 - * @retval true 数値型変換OK - * @retval false 数値型変換NG + * @brief Convert from a binary value to a numeric type. + * @param[out] int64_value numeric + * @retval true conversion OK + * @retval false conversion fail. */ bool ConvertBinaryToNumber(int64_t *int64_value = nullptr) const; /** - * @brief デフォルトコンストラクタ. - * - * リスト作成のため. + * @brief default constructor. */ ScriptElement() : type_(kElementOpCode), op_code_(), binary_data_(), value_(0) { @@ -588,15 +796,15 @@ class CFD_CORE_EXPORT ScriptElement { } private: - ScriptElementType type_; ///< 要素種別 + ScriptElementType type_; ///< element type ScriptOperator op_code_; ///< OP_CODE - ByteData binary_data_; ///< バイナリ情報 - int64_t value_; ///< 数値 + ByteData binary_data_; ///< binary data + int64_t value_; ///< numeric value /** - * @brief Scriptに追加する数値をbyteデータに変換する - * @param[in] value scriptに追加する数値 - * @return numberをserializeしたbyteデータ + * @brief Convert the numerical value to be added to Script to byte data. + * @param[in] value Numerical value to add to script + * @return Byte data with serialized number */ static std::vector SerializeScriptNum(int64_t value); }; @@ -649,6 +857,8 @@ class CFD_CORE_EXPORT Script { static constexpr uint32_t kMaxScriptSize = 10000; //! maximum size of RedeemScript static constexpr uint32_t kMaxRedeemScriptSize = 520; + //! maximum size of multisig + static constexpr uint32_t kMaxMultisigPubkeyNum = 20; /** * @brief constructor. @@ -670,6 +880,17 @@ class CFD_CORE_EXPORT Script { virtual ~Script() { // do nothing } + /** + * @brief copy constructor. + * @param[in] object object + */ + Script(const Script &object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Script &operator=(const Script &object) &; /** * @brief get script. * @return script @@ -701,6 +922,13 @@ class CFD_CORE_EXPORT Script { * @retval false data exist. */ bool IsEmpty() const; + /** + * @brief check equal object. + * @param[in] script check target, + * @retval true equal + * @retval false differ + */ + bool Equals(const Script &script) const; /** * @brief get element list. * @return element list @@ -767,6 +995,13 @@ class CFD_CORE_EXPORT Script { */ bool IsP2wshScript() const; + /** + * @brief Check if the script is taproot script. + * @retval true script is taproot. + * @retval false not taproot. + */ + bool IsTaprootScript() const; + /** * @brief Check if the script is pegout script. * @retval true script is pegout script. @@ -774,6 +1009,12 @@ class CFD_CORE_EXPORT Script { */ bool IsPegoutScript() const; + /** + * @brief get witness version on locking script. + * @return witness version. + */ + WitnessVersion GetWitnessVersion() const; + private: /// script byte data ByteData script_data_; @@ -800,7 +1041,7 @@ class CFD_CORE_EXPORT Script { }; /** - * @brief script builderクラス. + * @brief script builder class. */ class CFD_CORE_EXPORT ScriptBuilder { public: @@ -886,6 +1127,67 @@ class CFD_CORE_EXPORT ScriptBuilder { // ScriptBuilder& AppendData(const ByteData& data, bool is_template); + /** + * @brief append string data. + * @param[in] message string data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const std::string &message); + /** + * @brief append script operator. + * @param[in] type ScriptType. + * @return script builder object. + */ + ScriptBuilder &operator<<(ScriptType type); + /** + * @brief append script operator. + * @param[in] operate_object operator object. + * @return script builder object. + */ + ScriptBuilder &operator<<(const ScriptOperator &operate_object); + /** + * @brief append script data. + * @param[in] data script data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const ByteData &data); + /** + * @brief append script data. + * @param[in] data script data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const ByteData160 &data); + /** + * @brief append script data. + * @param[in] data script data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const ByteData256 &data); + /** + * @brief append script data. + * @param[in] pubkey public key. + * @return script builder object. + */ + ScriptBuilder &operator<<(const Pubkey &pubkey); + /** + * @brief append script data. + * @param[in] script script data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const Script &script); + /** + * @brief append script data. + * @param[in] data script number. + * @return script builder object. + */ + ScriptBuilder &operator<<(const int64_t &data); + /** + * @brief append script element data. + * @param[in] element element data. + * @return script builder object. + */ + ScriptBuilder &operator<<(const ScriptElement &element); + /** * @brief build script. * @return script @@ -897,127 +1199,144 @@ class CFD_CORE_EXPORT ScriptBuilder { }; /** - * @brief Scriptを作成する関数群クラス + * @brief Utility class that creates Script. */ class CFD_CORE_EXPORT ScriptUtil { public: /** - * @brief P2PKのlocking scriptを作成する. - * @param[in] pubkey Pubkeyインスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2PK locking script. + * @param[in] pubkey Pubkey + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_CHECKSIG * @endcode */ static Script CreateP2pkLockingScript(const Pubkey &pubkey); /** - * @brief P2PKHのlocking scriptを作成する. - * @param[in] pubkey_hash pubkey hashが格納されたByteData160インスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2PKH locking script. + * @param[in] pubkey_hash pubkey hash + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG * @endcode */ static Script CreateP2pkhLockingScript(const ByteData160 &pubkey_hash); /** - * @brief P2PKHのlocking scriptを作成する. - * @param[in] pubkey Pubkeyインスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2PKH locking script. + * @param[in] pubkey Pubkey + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG * @endcode */ static Script CreateP2pkhLockingScript(const Pubkey &pubkey); /** - * @brief P2SHのlocking scriptを作成する. - * @param[in] script_hash script hashが格納されたByteData160インスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2SH locking script. + * @param[in] script_hash script hash + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_HASH160 OP_EQUAL * @endcode */ static Script CreateP2shLockingScript(const ByteData160 &script_hash); /** - * @brief P2SHのlocking scriptを作成する. - * @param[in] redeem_script redeem scriptのScriptインスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2SH locking script. + * @param[in] redeem_script redeem script + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_HASH160 OP_EQUAL * @endcode */ static Script CreateP2shLockingScript(const Script &redeem_script); /** - * @brief P2WPKHのlocking scriptを作成する. - * @param[in] pubkey_hash pubkey hashが格納されたByteData160インスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2WPKH locking script. + * @param[in] pubkey_hash pubkey hash + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_0 * @endcode */ static Script CreateP2wpkhLockingScript(const ByteData160 &pubkey_hash); /** - * @brief P2WPKHのlocking scriptを作成する. - * @param[in] pubkey Pubkeyインスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2WPKH locking script. + * @param[in] pubkey Pubkey + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_0 * @endcode */ static Script CreateP2wpkhLockingScript(const Pubkey &pubkey); /** - * @brief P2WSHのlocking scriptを作成する. - * @param[in] script_hash script hashのByteData256インスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2WSH locking script. + * @param[in] script_hash script hash + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_0 * @endcode */ static Script CreateP2wshLockingScript(const ByteData256 &script_hash); /** - * @brief P2WSHのlocking scriptを作成する. - * @param[in] redeem_script redeem scriptのScriptインスタンス - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create a P2WSH locking script. + * @param[in] redeem_script redeem script + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_0 * @endcode */ static Script CreateP2wshLockingScript(const Script &redeem_script); /** - * @brief RedeemScriptが有効なものであるかをチェックする. + * @brief Create locking script for taproot. + * @param[in] data witness program + * @return Script + * @details Create a Script with the following content. + * @code{.unparse} + * OP_1 <32-byte> + * @endcode + */ + static Script CreateTaprootLockingScript(const ByteData256 &data); + /** + * @brief Check if Redeem Script is valid. * @param[in] redeem_script redeem script - * @retval true 有効なredeem script - * @retval false 有効でないredeem script + * @retval true valid + * @retval false invalid */ static bool IsValidRedeemScript(const Script &redeem_script); /** - * @brief M-of-N Multisigのredeem scriptを作成する. - * @param[in] require_sig_num unlockingに必要なSignature数(Mに相当) - * @param[in] pubkeys 署名に対応するPubkey配列(Nに相当) - * @return Scriptインスタンス - * @details 下記の内容のScriptを作成する. + * @brief Create redeem script of the M-of-N Multisig. + * @param[in] require_sig_num \ + * Number of Signatures required for unlocking (equivalent to M) + * @param[in] pubkeys Pubkey array corresponding to the signature. \ + * (equivalent to N) + * @param[in] has_witness target is witness script. + * @return Script + * @details Create a Script with the following content. * @code{.unparse} * OP_ ... OP_n OP_CHECKMULTISIG * @endcode */ static Script CreateMultisigRedeemScript( - uint32_t require_sig_num, const std::vector &pubkeys); + uint32_t require_sig_num, const std::vector &pubkeys, + bool has_witness = true); + #ifndef CFD_DISABLE_ELEMENTS /** - * @brief Pegoutのlocking scriptを作成する. - * @param[in] genesisblock_hash mainchainのgenesisblock hash - * @param[in] parent_locking_script 送り先 bitcoin address の locking script - * @param[in] btc_pubkey_bytes DerivePubTweak関数で作られたpubkey情報 - * @param[in] whitelist_proof whitelistの証明 - * @return Scriptインスタンス + * @brief Create a Pegout locking script. + * @param[in] genesisblock_hash mainchain genesis block hash + * @param[in] parent_locking_script \ + * Destination bitcoin address locking script + * @param[in] btc_pubkey_bytes \ + * Pubkey information created by the DerivePubTweak function + * @param[in] whitelist_proof Proof of whitelist + * @return Script * @code{.unparse} * OP_RETURN * @endcode diff --git a/include/cfdcore/cfdcore_taproot.h b/include/cfdcore/cfdcore_taproot.h new file mode 100644 index 00000000..4aeceff6 --- /dev/null +++ b/include/cfdcore/cfdcore_taproot.h @@ -0,0 +1,346 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_taproot.h + * + * @brief This file defines the taproot utility class. + */ +#ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_TAPROOT_H_ +#define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_TAPROOT_H_ + +#include +#include + +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_util.h" + +namespace cfd { +namespace core { + +/** + * @brief This class implements a taproot script tree branch. + */ +class CFD_CORE_EXPORT TapBranch { + public: + /** + * @brief default constructor. + */ + TapBranch(); + /** + * @brief constructor. + * @param[in] commitment commitment + */ + explicit TapBranch(const ByteData256& commitment); + /** + * @brief copy constructor. + * @param[in] branch branch object + */ + TapBranch(const TapBranch& branch); + /** + * @brief destructor. + */ + virtual ~TapBranch() {} + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + TapBranch& operator=(const TapBranch& object); + + /** + * @brief Add branch. + * @param[in] pubkey schnorr pubkey + */ + virtual void AddBranch(const SchnorrPubkey& pubkey); + /** + * @brief Add branch. + * @param[in] commitment branch commitment. + */ + virtual void AddBranch(const ByteData256& commitment); + /** + * @brief Add branch. + * @param[in] branch branch. + */ + void AddBranch(const TapBranch& branch); + /** + * @brief Get a base hash. + * @return base hash. + */ + ByteData256 GetBaseHash() const; + /** + * @brief Get a current branch hash. + * @return branch hash. + */ + ByteData256 GetCurrentBranchHash() const; + /** + * @brief Get a branch hash. + * @param[in] depth depth + * @return branch hash. + */ + ByteData256 GetBranchHash(uint8_t depth = 255) const; + + /** + * @brief Exist a tapleaf. + * @retval true exist + * @retval false not exist + */ + bool HasTapLeaf() const; + /** + * @brief Get a leaf version. + * @return leaf version. + */ + uint8_t GetLeafVersion() const; + + /** + * @brief Get a tapscript. + * @return tapscript. + */ + Script GetScript() const; + /** + * @brief Get a node list. + * @return node list. + */ + std::vector GetBranchList() const; + /** + * @brief Get a node list. + * @return node list. + */ + virtual std::vector GetNodeList() const; + + /** + * @brief find tapscript in this branch. + * @param[in] tapscript tapscript + * @retval true find this branch. + * @retval false not found. + */ + bool IsFindTapScript(const Script& tapscript) const; + + /** + * @brief Get a string format. (cfd original) + * @return text data. + */ + std::string ToString() const; + + /** + * @brief Change tapleaf. + * @param[in] tapscript leaf tapscript. + * @param[in] target_nodes target nodes. + * @return object + */ + TapBranch ChangeTapLeaf( + const Script& tapscript, const std::vector& target_nodes = + std::vector()) const; + + /** + * @brief Convert from string format. (cfd original) + * @param[in] text string format. + * @return object + * @see TapBranch::ToString() + */ + static TapBranch FromString(const std::string& text); + + protected: + bool has_leaf_; //!< exist leaf + uint8_t leaf_version_; //!< leaf version + Script script_; //!< tapscript + ByteData256 root_commitment_; //!< root commitment data + std::vector branch_list_; //!< branch list +}; + +/** + * @brief This class implements a taproot Merklized Alternative Script Trees. + * @see https://bitcoinops.org/en/newsletters/2019/05/14/ + */ +class CFD_CORE_EXPORT TaprootScriptTree : public TapBranch { + public: + /** + * @brief The taproot control node maximum count. + */ + static constexpr size_t kTaprootControlMaxNodeCount = 128; + /** + * @brief The tapleaf version on tapscript. + */ + static constexpr uint8_t kTapScriptLeafVersion = 0xc0; + + /** + * @brief constructor. + */ + TaprootScriptTree(); + /** + * @brief constructor. + * @param[in] script tapscript + */ + explicit TaprootScriptTree(const Script& script); + /** + * @brief constructor. + * @param[in] leaf_version leaf version + * @param[in] script tapscript + */ + explicit TaprootScriptTree(uint8_t leaf_version, const Script& script); + /** + * @brief constructor. convert from tapleaf branch. + * @param[in] leaf_branch leaf branch + */ + explicit TaprootScriptTree(const TapBranch& leaf_branch); + /** + * @brief copy constructor. + * @param[in] tap_tree tree object + */ + TaprootScriptTree(const TaprootScriptTree& tap_tree); + /** + * @brief destructor. + */ + virtual ~TaprootScriptTree() {} + /** + * @brief copy constructor. + * @param[in] object tree object + * @return object + */ + TaprootScriptTree& operator=(const TaprootScriptTree& object) &; + + using TapBranch::AddBranch; + /** + * @brief Add branch. + * @param[in] commitment branch commitment. + */ + virtual void AddBranch(const ByteData256& commitment); + /** + * @brief Add branch. + * @param[in] branch branch. + */ + void AddBranch(const TapBranch& branch); + /** + * @brief Add branch. + * @param[in] tree script tree node. + */ + void AddBranch(const TaprootScriptTree& tree); + + /** + * @brief Get a tapleaf hash. + * @return tapleaf hash. + */ + ByteData256 GetTapLeafHash() const; + /** + * @brief Get tweak. + * @param[in] internal_pubkey internal pubkey + * @return tweak. + */ + ByteData256 GetTapTweak(const SchnorrPubkey& internal_pubkey) const; + + /** + * @brief Get a tweaked pubkey. + * @param[in] internal_pubkey internal pubkey + * @param[out] parity parity flag. + * @return tweaked schnorr pubkey. + */ + SchnorrPubkey GetTweakedPubkey( + const SchnorrPubkey& internal_pubkey, bool* parity = nullptr) const; + /** + * @brief Get a tweaked privkey. + * @param[in] internal_privkey internal privkey + * @param[out] parity parity flag. + * @return tweaked privkey. + */ + Privkey GetTweakedPrivkey( + const Privkey& internal_privkey, bool* parity = nullptr) const; + + /** + * @brief Get a node list. + * @return node list. + */ + virtual std::vector GetNodeList() const; + + /** + * @brief Convert from string format. (cfd original) + * @param[in] text string format. + * @param[in] tapscript leaf tapscript. + * @param[in] target_nodes target nodes. + * @return object + * @see TapBranch::FromString() + */ + static TaprootScriptTree FromString( + const std::string& text, const Script& tapscript, + const std::vector& target_nodes = + std::vector()); + + private: + std::vector nodes_; //!< node list +}; + +/** + * @brief This class contain utility functions to work with taproot. + */ +class CFD_CORE_EXPORT TaprootUtil { + public: + /** + * @brief The annex tag. + */ + static constexpr uint8_t kAnnexTag = 0x50; + + /** + * @brief Check valid leaf version. + * @param[in] leaf_version leaf version + * @retval true valid + * @retval false invalid + */ + static bool IsValidLeafVersion(uint8_t leaf_version); + + /** + * @brief create tapscript control data. + * @param[in] internal_pubkey internal pubkey + * @param[in] merkle_tree merkle tree + * @param[out] witness_program witness program + * @param[out] locking_script taproot locking script + * @return tapscript control data. + */ + static ByteData CreateTapScriptControl( + const SchnorrPubkey& internal_pubkey, + const TaprootScriptTree& merkle_tree, + SchnorrPubkey* witness_program = nullptr, + Script* locking_script = nullptr); + + /** + * @brief Verify taproot commitment. + * @param[in] has_parity parity flag + * @param[in] tapleaf_bit tapleaf bit + * @param[in] target_taproot witness program + * @param[in] internal_pubkey internal pubkey + * @param[in] nodes taptree node list + * @param[in] tapscript tapscript + * @param[out] tapleaf_hash tapleaf hash + * @retval true valid + * @retval false invalid + */ + static bool VerifyTaprootCommitment( + bool has_parity, uint8_t tapleaf_bit, + const SchnorrPubkey& target_taproot, + const SchnorrPubkey& internal_pubkey, + const std::vector& nodes, const Script& tapscript, + ByteData256* tapleaf_hash = nullptr); + + /** + * @brief Parse taproot sign (witness stack) data + * @param[in] witness_stack witness stack + * @param[out] schnorr_signature schnorr signature + * @param[out] has_parity parity flag + * @param[out] tapleaf_bit tapleaf bit + * @param[out] internal_pubkey internal pubkey + * @param[out] nodes taproot node list + * @param[out] tapscript tapscript + * @param[out] stack script stack data + * @param[out] annex annex data + */ + static void ParseTaprootSignData( + const std::vector& witness_stack, + SchnorrSignature* schnorr_signature, bool* has_parity, + uint8_t* tapleaf_bit, SchnorrPubkey* internal_pubkey, + std::vector* nodes, Script* tapscript, + std::vector* stack = nullptr, ByteData* annex = nullptr); +}; + +} // namespace core +} // namespace cfd + +#endif // CFD_CORE_INCLUDE_CFDCORE_CFDCORE_TAPROOT_H_ diff --git a/include/cfdcore/cfdcore_transaction.h b/include/cfdcore/cfdcore_transaction.h index 052cccaa..756773f4 100644 --- a/include/cfdcore/cfdcore_transaction.h +++ b/include/cfdcore/cfdcore_transaction.h @@ -2,7 +2,7 @@ /** * @file cfdcore_transaction.h * - * @brief Transaction関連クラスを定義する。 + * @brief This file that defines Transaction related classes. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_TRANSACTION_H_ @@ -24,6 +24,18 @@ namespace cfd { namespace core { +//! OP_CODESEPARATOR default position +constexpr const uint32_t kDefaultCodeSeparatorPosition = 0xffffffff; + +/** + * @brief Tapscript data struct. + */ +struct TapScriptData { + ByteData256 tap_leaf_hash; //!< tapleaf hash + //! OP_CODESEPARATOR position + uint32_t code_separator_position = kDefaultCodeSeparatorPosition; +}; + //! transaction callback type: add txin constexpr const uint32_t kStateChangeAddTxIn = 0x00000001; //! transaction callback type: update txin @@ -40,28 +52,28 @@ constexpr const uint32_t kStateChangeUpdateTxOut = 0x00000200; constexpr const uint32_t kStateChangeRemoveTxOut = 0x00000400; /** - * @brief TxOut情報を保持するクラス + * @brief Class that holds TxOut information */ class CFD_CORE_EXPORT TxOut : public AbstractTxOut { public: /** - * @brief コンストラクタ + * @brief constructor */ TxOut(); /** - * @brief コンストラクタ + * @brief constructor * @param[in] value amount value. * @param[in] locking_script locking script. */ TxOut(const Amount& value, const Script& locking_script); /** - * @brief コンストラクタ + * @brief constructor * @param[in] value amount value. * @param[in] address out address. */ TxOut(const Amount& value, const Address& address); /** - * @brief デストラクタ + * @brief destructor */ virtual ~TxOut() { // do nothing @@ -69,25 +81,23 @@ class CFD_CORE_EXPORT TxOut : public AbstractTxOut { }; /** - * @brief TxOut情報を参照するためのクラス + * @brief Class for referencing TxOut information. */ class CFD_CORE_EXPORT TxOutReference : public AbstractTxOutReference { public: /** - * @brief コンストラクタ - * @param[in] tx_out 参照するTxOutインスタンス + * @brief constructor + * @param[in] tx_out TxOut instance */ explicit TxOutReference(const TxOut& tx_out); /** - * @brief デフォルトコンストラクタ. - * - * リスト作成用。 + * @brief default constructor. */ TxOutReference() : TxOutReference(TxOut()) { // do nothing } /** - * @brief デストラクタ + * @brief destructor */ virtual ~TxOutReference() { // do nothing @@ -95,13 +105,13 @@ class CFD_CORE_EXPORT TxOutReference : public AbstractTxOutReference { }; /** - * @brief TxIn情報を保持するクラス + * @brief Class that holds TxIn information */ class CFD_CORE_EXPORT TxIn : public AbstractTxIn { public: /** - * @brief 最小のTxInサイズ - * @details 対象サイズ:txid(64), vout(4), sequence(4), scriptLength(1(仮)) + * @brief Minimum TxIn size + * @details txid(32), vout(4), sequence(4), scriptLength(1) */ static constexpr const size_t kMinimumTxInSize = 41; @@ -132,24 +142,24 @@ class CFD_CORE_EXPORT TxIn : public AbstractTxIn { const Script* scriptsig_template = nullptr); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index tx output index(vout) + * @param[in] sequence sequence */ TxIn(const Txid& txid, uint32_t index, uint32_t sequence); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index tx output index(vout) + * @param[in] sequence sequence * @param[in] unlocking_script unlocking script */ TxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script); /** - * @brief デストラクタ + * @brief destructor */ virtual ~TxIn() { // do nothing @@ -157,26 +167,24 @@ class CFD_CORE_EXPORT TxIn : public AbstractTxIn { }; /** - * @brief TxIn情報を参照するためのクラス + * @brief Class for referencing TxIn information */ class CFD_CORE_EXPORT TxInReference : public AbstractTxInReference { public: /** - * @brief コンストラクタ. - * @param[in] tx_in 参照するTxInインスタンス + * @brief constructor. + * @param[in] tx_in TxIn instance to reference */ explicit TxInReference(const TxIn& tx_in); /** - * @brief デフォルトコンストラクタ. - * - * リスト作成用。 + * @brief default constructor. */ TxInReference() : TxInReference(TxIn(Txid(), 0, 0)) { // do nothing } /** - * @brief デストラクタ + * @brief destructor */ virtual ~TxInReference() { // do nothing @@ -184,18 +192,16 @@ class CFD_CORE_EXPORT TxInReference : public AbstractTxInReference { }; /** - * @brief トランザクション情報クラス + * @brief Transaction class */ class CFD_CORE_EXPORT Transaction : public AbstractTransaction { public: /** - * @brief コンストラクタ. - * - * リスト作成用。 + * @brief constructor. */ Transaction(); /** - * @brief コンストラクタ + * @brief constructor * @param[in] version version * @param[in] lock_time lock time */ @@ -206,194 +212,194 @@ class CFD_CORE_EXPORT Transaction : public AbstractTransaction { */ explicit Transaction(const ByteData& byte_data); /** - * @brief コンストラクタ - * @param[in] hex_string txバイトデータのHEX文字列 + * @brief constructor + * @param[in] hex_string HEX string */ explicit Transaction(const std::string& hex_string); /** - * @brief コンストラクタ - * @param[in] transaction トランザクション情報 + * @brief copy constructor. + * @param[in] transaction transaction object. */ - explicit Transaction(const Transaction& transaction); + Transaction(const Transaction& transaction); /** - * @brief デストラクタ + * @brief destructor. */ virtual ~Transaction() { // do nothing } /** - * @brief コピーコンストラクタ. - * @param[in] transaction トランザクション情報 - * @return Transactionオブジェクト + * @brief copy constructor. + * @param[in] transaction transaction object. + * @return transaction object. */ Transaction& operator=(const Transaction& transaction) &; /** - * @brief Transactionの合計バイトサイズを取得する. - * @return 合計バイトサイズ + * @brief Get the total byte size of Transaction. + * @return total byte size */ virtual uint32_t GetTotalSize() const; /** - * @brief Transactionのvsize情報を取得する. + * @brief Get vsize information of Transaction. * @return vsize */ virtual uint32_t GetVsize() const; /** - * @brief TransactionのWeight情報を取得する. + * @brief Get the Weight information of Transaction. * @return weight */ virtual uint32_t GetWeight() const; /** - * @brief TxInを取得する. - * @param[in] index 取得するindex位置 - * @return 指定indexのTxInインスタンス + * @brief Get TxIn. + * @param[in] index txin index. + * @return TxIn object. */ const TxInReference GetTxIn(uint32_t index) const; /** - * @brief TxInのindexを取得する. - * @param[in] txid 取得するTxInのtxid - * @param[in] vout 取得するTxInのvout - * @return 条件に合致するTxInのindex番号 + * @brief Get the index of TxIn. + * @param[in] txid txid + * @param[in] vout vout + * @return TxIn index */ virtual uint32_t GetTxInIndex(const Txid& txid, uint32_t vout) const; /** - * @brief 保持しているTxInの数を取得する. - * @return TxIn数 + * @brief Get the count of TxIn. + * @return TxIn count. */ uint32_t GetTxInCount() const; /** - * @brief TxIn一覧を取得する. - * @return TxInReference一覧 + * @brief Get the TxIn list. + * @return TxInReference list */ const std::vector GetTxInList() const; /** - * @brief TxInを追加する. + * @brief Add TxIn. * @param[in] txid txid * @param[in] index vout * @param[in] sequence sequence - * @param[in] unlocking_script unlocking script (未指定時はEmptyを設定する. default Script::Empty) - * @return 追加したTxInのindex位置 + * @param[in] unlocking_script unlocking script + * @return Index position of added TxIn */ uint32_t AddTxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script = Script::Empty); /** - * @brief TxIn情報を削除する. - * @param[in] index 削除するindex位置 + * @brief Delete the TxIn information. + * @param[in] index index */ void RemoveTxIn(uint32_t index); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking script (Push Op Only) + * @brief Set the unlocking script. + * @param[in] tx_in_index TxIn index + * @param[in] unlocking_script unlocking script (Push Op Only) */ void SetUnlockingScript( uint32_t tx_in_index, const Script& unlocking_script); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking scriptの構成要素リスト + * @brief Set the unlocking script. + * @param[in] tx_in_index TxIn index + * @param[in] unlocking_script Unlocking script component list */ void SetUnlockingScript( uint32_t tx_in_index, const std::vector& unlocking_script); /** - * @brief witness stackの現在の個数を取得する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @return witness stackの個数 + * @brief Get the count of witness stacks. + * @param[in] tx_in_index TxIn index + * @return count of witness stacks. */ uint32_t GetScriptWitnessStackNum(uint32_t tx_in_index) const; /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData160& data); /** - * @brief witness stackに追加する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Add to witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const ByteData256& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する20byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData160& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data Data to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const ByteData256& data); /** - * @brief script witnessを全て削除する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @brief Remove all script witness. + * @param[in] tx_in_index TxIn index */ void RemoveScriptWitnessStackAll(uint32_t tx_in_index); /** - * @brief TxOutを取得する. - * @param[in] index 取得するindex位置 + * @brief Get TxOut. + * @param[in] index txout index * @return TxOutReference */ const TxOutReference GetTxOut(uint32_t index) const; /** - * @brief TxOutのindexを取得する. + * @brief Get the index of TxOut. * @param[in] locking_script locking script - * @return 条件に合致するTxOutのindex番号 + * @return txout index */ virtual uint32_t GetTxOutIndex(const Script& locking_script) const; /** - * @brief TxOutのindexを一括取得する. + * @brief Get the TxOut index all at once. * @param[in] locking_script locking script - * @return 条件に合致するTxOutのindex番号の一覧 + * @return txout index list. */ virtual std::vector GetTxOutIndexList( const Script& locking_script) const; /** - * @brief 保持しているTxOutの数を取得する. - * @return TxOut数 + * @brief Get the count of TxOuts. + * @return count of TxOuts */ uint32_t GetTxOutCount() const; /** - * @brief TxOut一覧を取得する. - * @return TxOutReference一覧 + * @brief Get the TxOut list. + * @return TxOutReference list */ const std::vector GetTxOutList() const; /** - * @brief TxOut情報を追加する. + * @brief Add TxOut information. * @param[in] value amount * @param[in] locking_script locking script - * @return 追加したTxOutのindex位置 + * @return Index position of added TxOut */ uint32_t AddTxOut(const Amount& value, const Script& locking_script); /** @@ -403,105 +409,119 @@ class CFD_CORE_EXPORT Transaction : public AbstractTransaction { */ void SetTxOutValue(uint32_t index, const Amount& value); /** - * @brief TxOut情報を削除する. - * @param[in] index 取得するindex位置 + * @brief Delete the TxOut information. + * @param[in] index txout index */ void RemoveTxOut(uint32_t index); /** - * @brief signatureハッシュを取得する. - * @param[in] txin_index TxInのindex値 - * @param[in] script_data unlocking script もしくは witness_program. + * @brief Get the signature hash. + * @param[in] txin_index TxIn index + * @param[in] script_data unlocking script or witness program. * @param[in] sighash_type SigHashType(@see cfdcore_util.h) - * @param[in] value TxInのAmount値. + * @param[in] value TxIn Amount. * @param[in] version Witness version - * @return signatureハッシュ + * @return signature hash */ ByteData256 GetSignatureHash( uint32_t txin_index, const ByteData& script_data, SigHashType sighash_type, const Amount& value = Amount(), WitnessVersion version = WitnessVersion::kVersionNone) const; /** - * @brief witness情報かどうかを取得する. - * @retval true witness - * @retval false witnessではない + * @brief Get signature hash by schnorr. + * @param[in] txin_index TxIn's index + * @param[in] sighash_type SigHashType(@see cfdcore_util.h) + * @param[in] utxo_list utxo list (for amount & scriptPubkey) + * @param[in] script_data tap script data + * @param[in] annex annex data + * @return signature hash + */ + ByteData256 GetSchnorrSignatureHash( + uint32_t txin_index, SigHashType sighash_type, + const std::vector& utxo_list, + const TapScriptData* script_data = nullptr, + const ByteData& annex = ByteData()) const; + /** + * @brief Whether it holds witness information. + * @retval true witness exist. + * @retval false witness not found. */ virtual bool HasWitness() const; + // internal /** - * @brief libwally処理用フラグを取得する。 - * @return libwally用フラグ + * @brief libwally Get the processing flag. + * @return Flag for libwally */ virtual uint32_t GetWallyFlag() const; protected: - std::vector vin_; ///< TxIn配列 - std::vector vout_; ///< TxOut配列 + std::vector vin_; ///< TxIn array + std::vector vout_; ///< TxOut array /** - * @brief HEX文字列からTransaction情報を設定する. - * @param[in] hex_string TransactionバイトデータのHEX文字列 + * @brief Set Transaction information from HEX string. + * @param[in] hex_string HEX string of Transaction byte data */ void SetFromHex(const std::string& hex_string); private: /** - * @brief TxIn配列のIndex範囲をチェックする. - * @param[in] index TxIn配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @brief check TxIn array range. + * @param[in] index TxIn Index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxInIndex( uint32_t index, int line, const char* caller) const; /** - * @brief TxOut配列のIndex範囲をチェックする. * @brief check TxOut array range. - * @param[in] index TxOut配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @param[in] index TxOut Index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxOutIndex( uint32_t index, int line, const char* caller) const; /** - * @brief witness stackに情報を追加する. - * @param[in] tx_in_index TxIn配列のindex値 - * @param[in] data witness stackに追加するバイトデータ + * @brief Add information to the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] data data to add to the witness stack * @return witness stack */ const ScriptWitness AddScriptWitnessStack( uint32_t tx_in_index, const std::vector& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index TxIn index + * @param[in] witness_index witness stack index + * @param[in] data data to add to the witness stack * @return witness stack */ const ScriptWitness SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const std::vector& data); /** - * @brief Transactionのバイトデータを取得する. - * @param[in] has_witness witnessを含めるかのフラグ - * @return バイトデータ + * @brief Get the byte data of Transaction. + * @param[in] has_witness Flag to include witness + * @return ByteData */ ByteData GetByteData(bool has_witness) const; /** - * @brief TxOut領域のByteDataの整合性チェックと、TxOutへの設定を行う. + * @brief Check the consistency of ByteData in the TxOut area and set to TxOut. * - * tx_pointerがNULLではない場合のみ、TxOutへの設定を行う. - * tx_pointerがNULLの場合は整合性チェックのみ行う. - * @param[in] buffer TxOut領域のByteData - * @param[in] buf_size TxOut領域のByteDataサイズ - * @param[in] txout_num TxOut領域のTxOut情報数 - * @param[in] txout_num_size TxOut情報領域サイズ - * @param[out] tx_pointer Transaction情報バッファ(NULL可) - * @param[out] txout_list TxOut配列(nullptr可) - * @retval true 整合性チェックOK、およびTxOut情報コピーOK - * @retval false 整合性チェックNG、もしくはTxOut情報コピー失敗 + * Set to TxOut only if tx_pointer is not NULL. + * If tx_pointer is NULL, only consistency check is performed. + * @param[in] buffer ByteData in the TxOut area + * @param[in] buf_size ByteData size in the TxOut area + * @param[in] txout_num Number of TxOut information in the TxOut area + * @param[in] txout_num_size TxOut information area size + * @param[out] tx_pointer Transaction information buffer (nullable) + * @param[out] txout_list TxOut array (nullable) + * @retval true Consistency check OK, TxOut information copy OK + * @retval false Consistency check NG or TxOut information copy failure */ static bool CheckTxOutBuffer( const uint8_t* buffer, size_t buf_size, uint64_t txout_num, - size_t txout_num_size, void* tx_pointer = NULL, + size_t txout_num_size, void* tx_pointer = nullptr, std::vector* txout_list = nullptr); }; diff --git a/include/cfdcore/cfdcore_transaction_common.h b/include/cfdcore/cfdcore_transaction_common.h index aaa8ba56..5a51e8f6 100644 --- a/include/cfdcore/cfdcore_transaction_common.h +++ b/include/cfdcore/cfdcore_transaction_common.h @@ -2,7 +2,7 @@ /** * @file cfdcore_transaction_common.h * - * @brief Transaction関連の共通クラスおよび基底クラスを定義する。 + * @brief Define Transaction-related common class and base class. * */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_TRANSACTION_COMMON_H_ @@ -24,69 +24,70 @@ namespace cfd { namespace core { /** - * @brief ハッシュ種別定義 + * @brief Hash type definition */ enum HashType { kP2pkh = 0, //!< P2pkh kP2sh = 1, //!< P2sh kP2wpkh = 2, //!< P2wpkh - kP2wsh = 3 //!< P2wsh + kP2wsh = 3, //!< P2wsh + kTaproot = 6 //!< Taproot }; /** - * @brief witness情報の保持クラス + * @brief witness information retention class */ class CFD_CORE_EXPORT ScriptWitness { public: /** - * @brief コンストラクタ + * @brief constructor. */ ScriptWitness() : witness_stack_() { // do nothing } /** - * @brief デストラクタ + * @brief destructor. */ virtual ~ScriptWitness() { // do nothing } /** - * @brief witness stackを取得する. + * @brief Get the witness stack. * @return witness stack */ const std::vector GetWitness() const; /** - * @brief witness stack数を取得する. - * @return witness stack数 + * @brief Get the number of witness stacks. + * @return number of witness stacks. */ uint32_t GetWitnessNum() const; /** - * @brief witness stackに追加する. - * @param[in] data バイトデータ + * @brief Add to witness stack. + * @param[in] data byte array. */ void AddWitnessStack(const ByteData& data); /** - * @brief witness stackの指定indexを更新する. - * @param[in] index 設定先index値 - * @param[in] data バイトデータ + * @brief Update the specified index of the witness stack. + * @param[in] index index + * @param[in] data byte array. */ void SetWitnessStack(uint32_t index, const ByteData& data); /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check if the data is empty. + * @retval true empty. + * @retval false exist. * @deprecated replace to IsEmpty . */ bool Empty() const; /** - * @brief データが空か取得する. - * @retval true データが空 - * @retval false データが存在 + * @brief Check if the data is empty. + * @retval true empty. + * @retval false exist. */ bool IsEmpty() const; /** - * @brief witness stack情報をserializeする. + * @brief Serialize witness stack information. * @return serialize data */ ByteData Serialize() const; @@ -130,17 +131,17 @@ class CFD_CORE_EXPORT OutPoint { bool IsValid() const; /** - * @brief 等価比較オペレータ - * @param[in] object 比較対象 - * @retval true 等価 - * @retval false 不等価 + * @brief Equals operator. + * @param[in] object compare target. + * @retval true equals + * @retval false not equals */ bool operator==(const OutPoint& object) const; /** - * @brief 不等価比較オペレータ - * @param[in] object 比較対象 - * @retval true 不等価 - * @retval false 等価 + * @brief Not Equals operator. + * @param[in] object compare target. + * @retval true not equals + * @retval false equals */ bool operator!=(const OutPoint& object) const; @@ -150,126 +151,126 @@ class CFD_CORE_EXPORT OutPoint { }; /** - * @brief 不等価比較オペレータ - * @param[in] source 比較元 - * @param[in] dest 比較対象 - * @retval true 不等価 - * @retval false 等価 + * @brief Compare operator. + * @param[in] source source + * @param[in] dest destination + * @retval true match + * @retval false unmatch */ CFD_CORE_EXPORT bool operator<(const OutPoint& source, const OutPoint& dest); /** - * @brief 不等価比較オペレータ - * @param[in] source 比較元 - * @param[in] dest 比較対象 - * @retval true 不等価 - * @retval false 等価 + * @brief Compare operator. + * @param[in] source source + * @param[in] dest destination + * @retval true match + * @retval false unmatch */ CFD_CORE_EXPORT bool operator<=(const OutPoint& source, const OutPoint& dest); /** - * @brief 不等価比較オペレータ - * @param[in] source 比較元 - * @param[in] dest 比較対象 - * @retval true 不等価 - * @retval false 等価 + * @brief Compare operator. + * @param[in] source source + * @param[in] dest destination + * @retval true match + * @retval false unmatch */ CFD_CORE_EXPORT bool operator>(const OutPoint& source, const OutPoint& dest); /** - * @brief 不等価比較オペレータ - * @param[in] source 比較元 - * @param[in] dest 比較対象 - * @retval true 不等価 - * @retval false 等価 + * @brief Compare operator. + * @param[in] source source + * @param[in] dest destination + * @retval true match + * @retval false unmatch */ CFD_CORE_EXPORT bool operator>=(const OutPoint& source, const OutPoint& dest); /** - * @brief TxInの基本情報を保持する基底クラス + * @brief Base class that holds basic information about TxIn. */ class CFD_CORE_EXPORT AbstractTxIn { public: /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index TxOut Index information for txid transactions(vout) + * @param[in] sequence sequence */ AbstractTxIn(const Txid& txid, uint32_t index, uint32_t sequence); /** - * @brief コンストラクタ. + * @brief constructor. * @param[in] txid txid - * @param[in] index txidのトランザクションのTxOutのIndex情報(vout) - * @param[in] sequence sequence情報 + * @param[in] index TxOut Index information for txid transactions(vout) + * @param[in] sequence sequence * @param[in] unlocking_script unlocking script */ AbstractTxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script); /** - * @brief デストラクタ + * @brief destructor */ virtual ~AbstractTxIn() { // do nothing } /** - * @brief txidを取得する. - * @return Txidインスタンス + * @brief Get a txid. + * @return Txid */ Txid GetTxid() const; /** - * @brief voutを取得する. + * @brief Get a vout. * @return vout */ uint32_t GetVout() const; /** - * @brief outpointを取得する. + * @brief Get an outpoint. * @return outpoint */ OutPoint GetOutPoint() const; /** - * @brief unlocking scriptを取得する. + * @brief Get an unlocking script. * @return unlocking script */ Script GetUnlockingScript() const; /** - * @brief unlocking scriptを設定する. + * @brief Set an unlocking script. * @param[in] unlocking_script unlocking script */ void SetUnlockingScript(const Script& unlocking_script); /** - * @brief sequenceを取得する. + * @brief Get a sequence. * @return sequence番号 */ uint32_t GetSequence() const; /** - * @brief script witness情報を取得する. - * @return ScriptWitnessインスタンス + * @brief Get a script witness. + * @return ScriptWitness */ ScriptWitness GetScriptWitness() const; /** - * @brief script witnessの現在のstack数を取得する. - * @return script witnessのstack数 + * @brief Get the current stack number of script witness. + * @return number of script witness. */ uint32_t GetScriptWitnessStackNum() const; /** - * @brief script witnessにバイトデータを追加する. - * @param[in] data witness stack情報 - * @return script witnessオブジェクト + * @brief Add byte data to script witness. + * @param[in] data witness stack + * @return ScriptWitness object */ ScriptWitness AddScriptWitnessStack(const ByteData& data); /** - * @brief script witnessにバイトデータを設定する. - * @param[in] index witness stackのindex値 - * @param[in] data witness stack情報 - * @return ScriptWitnessインスタンス + * @brief Set byte data in script witness. + * @param[in] index witness stack index + * @param[in] data witness stack data + * @return ScriptWitness object */ ScriptWitness SetScriptWitnessStack(uint32_t index, const ByteData& data); /** - * @brief script witnessを全て削除する. + * @brief Remove all script witness. */ void RemoveScriptWitnessStackAll(); /** - * @brief txid/voutによりcoinbaseを判定する. + * @brief Determine coinbase by txid / vout. * @retval true coinbase * @retval false other */ @@ -284,55 +285,55 @@ class CFD_CORE_EXPORT AbstractTxIn { }; /** - * @brief TxInの基本情報を参照するための基底クラス + * @brief Base class for referencing basic information on TxIn. */ class CFD_CORE_EXPORT AbstractTxInReference { public: /** - * @brief コンストラクタ. - * @param[in] tx_in 参照するTxInインスタンス + * @brief constructor. + * @param[in] tx_in TxIn instance to reference */ explicit AbstractTxInReference(const AbstractTxIn& tx_in); /** - * @brief デストラクタ + * @brief destructor. */ virtual ~AbstractTxInReference() { // do nothing } /** - * @brief txidを取得する. - * @return Txidインスタンス + * @brief Get a txid. + * @return Txid object. */ Txid GetTxid() const { return txid_; } /** - * @brief voutを取得する. + * @brief Get a vout. * @return vout */ uint32_t GetVout() const { return vout_; } /** - * @brief outpointを取得する. + * @brief Get an outpoint. * @return outpoint */ OutPoint GetOutPoint() const { return OutPoint(txid_, vout_); } /** - * @brief unlocking scriptを取得する. + * @brief Get an unlocking script. * @return unlocking script */ Script GetUnlockingScript() const { return unlocking_script_; } /** - * @brief sequenceを取得する. - * @return sequence番号 + * @brief Get a sequence. + * @return sequence */ uint32_t GetSequence() const { return sequence_; } /** - * @brief script witness情報を取得する. - * @return ScriptWitnessインスタンス + * @brief Get a script witness. + * @return ScriptWitness */ ScriptWitness GetScriptWitness() const { return script_witness_; } /** - * @brief script witnessの現在のstack数を取得する. - * @return script witnessのstack数 + * @brief Get a stack number of script witness. + * @return stack number of script witness. */ uint32_t GetScriptWitnessStackNum() const { return script_witness_.GetWitnessNum(); @@ -347,38 +348,38 @@ class CFD_CORE_EXPORT AbstractTxInReference { }; /** - * @brief TxOutの基本情報を保持する基底クラス + * @brief Base class that holds basic information about TxOut. */ class CFD_CORE_EXPORT AbstractTxOut { public: /** - * @brief コンストラクタ + * @brief constructor */ AbstractTxOut(); /** - * @brief コンストラクタ + * @brief constructor * @param[in] value amount value. * @param[in] locking_script locking script. */ AbstractTxOut(const Amount& value, const Script& locking_script); /** - * @brief コンストラクタ + * @brief constructor * @param[in] locking_script locking script. */ explicit AbstractTxOut(const Script& locking_script); /** - * @brief デストラクタ + * @brief destructor */ virtual ~AbstractTxOut() { // do nothing } /** - * @brief Amountを取得する. + * @brief Get the amount. * @return amount */ const Amount GetValue() const; /** - * @brief locking script を取得する + * @brief Get the locking script. * @return locking script */ const Script GetLockingScript() const; @@ -389,35 +390,35 @@ class CFD_CORE_EXPORT AbstractTxOut { virtual void SetValue(const Amount& value); protected: - Amount value_; ///< 金額 + Amount value_; ///< amount Script locking_script_; ///< locking script }; /** - * @brief TxOutの基本情報を参照するための基底クラス + * @brief Base class for referencing basic information on TxOut. */ class CFD_CORE_EXPORT AbstractTxOutReference { public: /** - * @brief コンストラクタ - * @param[in] tx_out 参照するTxOutインスタンス + * @brief constructor. + * @param[in] tx_out TxOut instance to reference */ explicit AbstractTxOutReference(const AbstractTxOut& tx_out); /** - * @brief デストラクタ + * @brief destructor */ virtual ~AbstractTxOutReference() { // do nothing } /** - * @brief Amountを取得する. + * @brief Get an amount. * @return amount */ const Amount GetValue() const { return value_; } /** - * @brief locking script を取得する + * @brief Get a locking script. * @return locking script */ const Script GetLockingScript() const { return locking_script_; } @@ -435,133 +436,133 @@ class CFD_CORE_EXPORT AbstractTxOutReference { uint32_t GetSerializeVsize() const; protected: - Amount value_; ///< 金額 + Amount value_; ///< amount Script locking_script_; ///< locking script }; /** - * @brief トランザクション情報の基底クラス + * @brief Base class of transaction information. */ class CFD_CORE_EXPORT AbstractTransaction { public: - /// Transactionの最小サイズ + /// Minimum size of Transaction static constexpr size_t kTransactionMinimumSize = 10; /** - * @brief コンストラクタ + * @brief constructor. */ AbstractTransaction(); /** - * @brief デストラクタ + * @brief destructor. */ virtual ~AbstractTransaction() { AbstractTransaction::FreeWallyAddress(wally_tx_pointer_); } /** - * @brief バージョン情報を取得する. - * @return version番号 + * @brief Get a version information. + * @return version */ int32_t GetVersion() const; /** - * @brief lock timeを取得する. + * @brief Get a lock time. * @return lock time */ uint32_t GetLockTime() const; /** - * @brief TxInのindexを取得する. - * @param[in] txid 取得するTxInのtxid - * @param[in] vout 取得するTxInのvout - * @return 条件に合致するTxInのindex番号 + * @brief Get a TxIn index. + * @param[in] txid txid + * @param[in] vout vout + * @return index */ virtual uint32_t GetTxInIndex(const Txid& txid, uint32_t vout) const = 0; /** - * @brief TxOutのindexを取得する. + * @brief Get a TxOut index. * @param[in] locking_script locking script - * @return 条件に合致するTxOutのindex番号 + * @return index */ virtual uint32_t GetTxOutIndex(const Script& locking_script) const = 0; /** - * @brief Transactionの合計バイトサイズを取得する. - * @return 合計バイトサイズ + * @brief Get the total byte size of Transaction. + * @return Total byte size */ virtual uint32_t GetTotalSize() const; /** - * @brief Transactionのvsize情報を取得する. + * @brief Get vsize information of Transaction. * @return vsize */ virtual uint32_t GetVsize() const; /** - * @brief TransactionのWeight情報を取得する. + * @brief Get the Weight information of Transaction. * @return weight */ virtual uint32_t GetWeight() const; /** - * @brief TransactionのTxOut合計額を取得する. - * @return TxOut合計額 + * @brief Get the total TxOut amount of Transaction. + * @return total TxOut amount */ Amount GetValueOut() const; /** - * @brief witness情報かどうかを取得する. + * @brief Get witness information. * @retval true witness - * @retval false witnessではない + * @retval false not witness */ virtual bool HasWitness() const; /** - * @brief Transactionのハッシュ値を取得する. + * @brief Get the hash value of Transaction. * - * Witness形式の場合、Witness情報はハッシュ計算に含めない. - * @return ハッシュ値 + * In the Witness format, the Witness information is not included in the hash calculation. + * @return Hash value */ ByteData256 GetHash() const; /** - * @brief Witness情報を含めたTransactionのハッシュ値を取得する. - * @return ハッシュ値 + * @brief Get the hash value of Transaction including Witness information. + * @return Hash value */ ByteData256 GetWitnessHash() const; /** - * @brief Transactionのバイトデータを取得する. - * @return バイトデータ + * @brief Get the byte data of Transaction. + * @return byte data */ virtual ByteData GetData() const; /** - * @brief TransactionのバイトデータをHEX文字列変換して取得する. - * @return HEX文字列 + * @brief Get the byte data of Transaction by converting to HEX character string. + * @return hex string. */ std::string GetHex() const; /** - * @brief txidを取得する. + * @brief Get the txid. * - * GetHash()と同値となる. + * Equivalent to GetHash(). * @return txid */ Txid GetTxid() const; /** - * @brief coinbaseかどうか判定する. - * @retval true coinbase transaction - * @retval false 通常のtransaction + * @brief Determine if it is coinbase. + * @retval true coinbase transaction + * @retval false normaltransaction */ bool IsCoinBase() const; /** - * @brief libwally処理用フラグを取得する。 - * @return libwally用フラグ + * @brief libwally Get the processing flag. + * @return Flag for libwally */ virtual uint32_t GetWallyFlag() const = 0; /** - * @brief size情報からvsizeを取得する。 - * @param[in] no_witness_size 非witness領域サイズ - * @param[in] witness_size witness領域サイズ + * @brief Get vsize from size information. + * @param[in] no_witness_size Non-witness area size + * @param[in] witness_size witness area size * @return vsize */ static uint32_t GetVsizeFromSize( uint32_t no_witness_size, uint32_t witness_size); protected: - void* wally_tx_pointer_; ///< libwally tx構造体アドレス + void* wally_tx_pointer_; ///< libwally tx structure address /** * @brief This function is called by the state change. @@ -569,142 +570,141 @@ class CFD_CORE_EXPORT AbstractTransaction { */ virtual void CallbackStateChange(uint32_t type); /** - * @brief TxInを追加する. + * @brief Add TxIn. * @param[in] txid txid * @param[in] index vout * @param[in] sequence sequence - * @param[in] unlocking_script unlocking script (未指定時はEmptyを設定する. default Script::Empty) + * @param[in] unlocking_script unlocking script */ void AddTxIn( const Txid& txid, uint32_t index, uint32_t sequence, const Script& unlocking_script = Script::Empty); /** - * @brief TxIn情報を削除する. - * @param[in] index 削除するindex位置 + * @brief Delete the TxIn information. + * @param[in] index index */ void RemoveTxIn(uint32_t index); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking script (Push Op Only) + * @brief Set the unlocking script. + * @param[in] tx_in_index index + * @param[in] unlocking_script Unlocking script to set to TxIn (Push Op Only) */ void SetUnlockingScript( uint32_t tx_in_index, const Script& unlocking_script); /** - * @brief unlocking scriptを設定する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] unlocking_script TxInに設定するunlocking scriptの構成要素リスト - * @return 生成したUnlockingScript + * @brief Set the unlocking script. + * @param[in] tx_in_index index + * @param[in] unlocking_script List of unlocking script components to set in TxIn + * @return Generated Unlocking Script */ Script SetUnlockingScript( uint32_t tx_in_index, const std::vector& unlocking_script); /** - * @brief script witnessを全て削除する. - * @param[in] tx_in_index 設定するTxInのindex位置 + * @brief Remove all script witness. + * @param[in] tx_in_index index */ void RemoveScriptWitnessStackAll(uint32_t tx_in_index); /** - * @brief TxOut情報を追加する. + * @brief Add TxOut information. * @param[in] value amount * @param[in] locking_script locking script */ void AddTxOut(const Amount& value, const Script& locking_script); /** - * @brief TxOut情報を削除する. - * @param[in] index 取得するindex位置 + * @brief Delete the TxOut information. + * @param[in] index index */ void RemoveTxOut(uint32_t index); /** - * @brief TxIn配列のIndex範囲をチェックする. - * @param[in] index TxIn配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @brief Check the Index range of the TxIn array. + * @param[in] index index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxInIndex( uint32_t index, int line, const char* caller) const = 0; /** - * @brief TxOut配列のIndex範囲をチェックする. * @brief check TxOut array range. - * @param[in] index TxOut配列のIndex値 - * @param[in] line 行数 - * @param[in] caller コール元関数名 + * @param[in] index index + * @param[in] line Number of lines + * @param[in] caller Calling function name */ virtual void CheckTxOutIndex( uint32_t index, int line, const char* caller) const = 0; /** - * @brief witness stackに情報を追加する. - * @param[in] tx_in_index TxIn配列のindex値 - * @param[in] data witness stackに追加するバイトデータ + * @brief Add information to the witness stack. + * @param[in] tx_in_index index + * @param[in] data Byte data to add to the witness stack */ void AddScriptWitnessStack( uint32_t tx_in_index, const std::vector& data); /** - * @brief witness stackの指定index位置を更新する. - * @param[in] tx_in_index 設定するTxInのindex位置 - * @param[in] witness_index witness stackのindex位置 - * @param[in] data witness stackに追加する32byte情報 + * @brief Update the specified index position of the witness stack. + * @param[in] tx_in_index index position of txin + * @param[in] witness_index index position of witness stack + * @param[in] data 32byte information to add to the witness stack */ void SetScriptWitnessStack( uint32_t tx_in_index, uint32_t witness_index, const std::vector& data); /** - * @brief transactionのハッシュ値を取得する. - * @param[in] has_witness witnessを計算に含めるか(wtxid計算を行うかどうか) - * @return ハッシュ値 + * @brief Get the hash value of transaction. + * @param[in] has_witness Whether to include witness in the calculation (whether to perform wtxid calculation) + * @return Hash value */ ByteData256 GetHash(bool has_witness) const; /** - * @brief Transactionのバイトデータを取得する. - * @param[in] has_witness witnessを含めるかのフラグ - * @return バイトデータ + * @brief Get the byte data of Transaction. + * @param[in] has_witness Flag to include witness + * @return byte data */ virtual ByteData GetByteData(bool has_witness) const = 0; /** - * @brief VariableIntデータを取得する. - * @param[in] p_byte_data Byte配列アドレス - * @param[in] data_size Byte配列サイズ - * @param[out] p_result VariableIntデータ - * @param[out] p_size VariableIntデータサイズ - * @retval true 成功 - * @retval false 失敗 + * @brief Get VariableInt data. + * @param[in] p_byte_data Byte array address + * @param[in] data_size Byte array size + * @param[out] p_result VariableInt data + * @param[out] p_size VariableInt data size + * @retval true success + * @retval false fail */ static bool GetVariableInt( const uint8_t* p_byte_data, size_t data_size, uint64_t* p_result, size_t* p_size); /** - * @brief VariableIntデータをコピーする. - * @param[in] v VariableIntデータ - * @param[out] bytes_out コピー先アドレス - * @return コピー先アドレス + * @brief Copy VariableInt data. + * @param[in] v VariableInt data + * @param[out] bytes_out Copy destination address + * @return Copy destination address */ static uint8_t* CopyVariableInt(uint64_t v, uint8_t* bytes_out); /** - * @brief VariableBufferデータをコピーする. - * @param[in] bytes Byte配列アドレス - * @param[in] bytes_len Byte配列サイズ - * @param[out] bytes_out コピー先アドレス - * @return コピー先アドレス + * @brief Copy VariableBuffer data. + * @param[in] bytes Byte array address + * @param[in] bytes_len Byte array size + * @param[out] bytes_out Copy destination address + * @return Copy destination address */ static uint8_t* CopyVariableBuffer( const uint8_t* bytes, size_t bytes_len, uint8_t* bytes_out); /** - * @brief libwallyのヒープアドレスを解放する。 - * @param[in] wally_tx_pointer アドレス + * @brief Free the libwally heap address. + * @param[in] wally_tx_pointer address */ static void FreeWallyAddress(const void* wally_tx_pointer); }; /** - * @brief signature計算を行うクラス. + * @brief A class that performs signature calculations. */ class CFD_CORE_EXPORT SignatureUtil { public: /** - * @brief 楕円曲線暗号を用いて、秘密鍵からsignatureを計算する. - * @param[in] signature_hash signatureハッシュ - * @param[in] private_key 秘密鍵 - * @param[in] has_grind_r EC_FLAG_GRIND_Rフラグ有無 + * @brief Calculate the signature from the private key using elliptic curve cryptography. + * @param[in] signature_hash signature hash + * @param[in] private_key private key + * @param[in] has_grind_r EC_FLAG_GRIND_R flag * @return signature */ static ByteData CalculateEcSignature( diff --git a/include/cfdcore/cfdcore_util.h b/include/cfdcore/cfdcore_util.h index e4e0f671..7e2e0c83 100644 --- a/include/cfdcore/cfdcore_util.h +++ b/include/cfdcore/cfdcore_util.h @@ -2,7 +2,7 @@ /** * @file cfdcore_util.h * - * @brief Utility関連クラス定義 + * @brief The utility related class definition */ #ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_UTIL_H_ #define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_UTIL_H_ @@ -20,17 +20,17 @@ namespace cfd { namespace core { /** - * @brief 20byte長 + * @brief 20byte length */ const uint32_t kByteData160Length = 20; /** - * @brief 32byte長 + * @brief 32byte length */ const uint32_t kByteData256Length = 32; /** - * @brief 64byte長 + * @brief 64byte length */ const uint32_t kByteData512Length = 64; @@ -38,55 +38,73 @@ const uint32_t kByteData512Length = 64; * @brief Sighash flags for transaction signing. */ enum SigHashAlgorithm { - kSigHashAll = 0x01, //!< SIGHASH_ALL - kSigHashNone = 0x02, //!< SIGHASH_NONE - kSigHashSingle = 0x03, //!< SIGHASH_SINGLE + kSigHashDefault = 0, //!< default (= SIGHASH_ALL) + kSigHashAll = 0x01, //!< SIGHASH_ALL + kSigHashNone = 0x02, //!< SIGHASH_NONE + kSigHashSingle = 0x03, //!< SIGHASH_SINGLE + kSigHashUnknown = 0xffff //!< invalid }; /** - * @brief SigHash算出時の種別 + * @brief Type when calculating SigHash */ class CFD_CORE_EXPORT SigHashType { public: /** - * @brief SIGHASH_FORKIDフラグ + * @brief SIGHASH_FORKID flag */ const uint8_t kSigHashForkId = 0x40; + /* + * @brief SIGHASH_RANGEPROOF flag + */ + // const uint8_t kSigHashRangeproof = 0x40; + // for feature /** - * @brief SIGHASH_ANYONECANPAYフラグ + * @brief SIGHASH_ANYONECANPAY flag */ const uint8_t kSigHashAnyOneCanPay = 0x80; /** - * @brief デフォルトコンストラクタ + * @brief Create by SigHash flag. + * @param[in] flag SigHash flag + * @param[in] is_append_anyone_can_pay add SIGHASH_ANYONECANPAY if true. + * @param[in] is_append_fork_id add SIGHASH_FORKID if true. + * @return SigHashType + */ + static SigHashType Create( + uint8_t flag, bool is_append_anyone_can_pay = false, + bool is_append_fork_id = false); + + /** + * @brief default constructor. */ SigHashType(); /** - * @brief コンストラクタ - * @param algorithm Sighashアルゴリズム - * @param is_anyone_can_pay SIGHASH_ANYONECANPAYフラグ有無 - * @param is_fork_id SIGHASH_FORKIDフラグ有無 + * @brief constructor. + * @param algorithm Sighash algorithm + * @param is_anyone_can_pay SIGHASH_ANYONECANPAY flag + * @param is_fork_id SIGHASH_FORKID flag */ explicit SigHashType( SigHashAlgorithm algorithm, bool is_anyone_can_pay = false, bool is_fork_id = false); /** - * @brief コピーコンストラクタ. - * @param[in] sighash_type SigHashType オブジェクト + * @brief copy constructor. + * @param[in] sighash_type SigHashType */ SigHashType(const SigHashType &sighash_type); /** - * @brief コピーコンストラクタ. - * @param[in] sighash_type SigHashType オブジェクト - * @return SigHashType オブジェクト + * @brief copy constructor. + * @param[in] sighash_type SigHashType + * @return SigHashType */ SigHashType &operator=(const SigHashType &sighash_type); /** - * @brief SigHashフラグ取得 - * @return SigHashフラグ + * @brief Get a SigHash flag. + * @return SigHash flag */ uint32_t GetSigHashFlag() const; @@ -108,245 +126,420 @@ class CFD_CORE_EXPORT SigHashType { */ bool IsForkId() const; + /** + * @brief Valid sighash state. + * @retval true valid + * @retval false invalid + */ + bool IsValid() const; + /** * @brief Set parameter from SigHash flag. * @param[in] flag SigHash flag */ void SetFromSigHashFlag(uint8_t flag); + /** + * @brief Set SIGHASH_ANYONECANPAY flag. + * @param[in] is_anyone_can_pay SIGHASH_ANYONECANPAY flag + */ + void SetAnyoneCanPay(bool is_anyone_can_pay); + + /** + * @brief Get string. + * @return SigHashType string. + */ + std::string ToString() const; private: /** - * @brief Sighashアルゴリズム + * @brief Sighash algorithm */ SigHashAlgorithm hash_algorithm_; /** - * @brief SIGHASH_ANYONECANPAYフラグ有無 + * @brief SIGHASH_ANYONECANPAY flag */ bool is_anyone_can_pay_; /** - * @brief SIGHASH_FORKIDフラグ有無 + * @brief SIGHASH_FORKID flag */ bool is_fork_id_; }; /** - * @brief Hash関数を定義したUtilクラス + * @brief Util class that defines the Hash function. */ class CFD_CORE_EXPORT HashUtil { public: + // Ripemd160 -------------------------------------------------------------- + /** + * @brief Hash the string with Ripemd160. + * @param[in] str message text + * @return hashed data + */ + static ByteData160 Ripemd160(const std::string &str); + /** + * @brief Hash the byte array with Ripemd160. + * @param[in] bytes message data + * @return hashed data + */ + static ByteData160 Ripemd160(const std::vector &bytes); + /** + * @brief Hash the byte array with Ripemd160. + * @param[in] data message data + * @return hashed data + */ + static ByteData160 Ripemd160(const ByteData &data); + /** + * @brief Hash the byte array with Ripemd160. + * @param[in] data message data + * @return hashed data + */ + static ByteData160 Ripemd160(const ByteData160 &data); + /** + * @brief Hash the byte array with Ripemd160. + * @param[in] data message data + * @return hashed data + */ + static ByteData160 Ripemd160(const ByteData256 &data); + /** + * @brief Hash the pubkey bytes with Ripemd160. + * @param[in] pubkey Pubkey + * @return pubkey hash + */ + static ByteData160 Ripemd160(const Pubkey &pubkey); + /** + * @brief Hash the script bytes with Ripemd160. + * @param[in] script Script + * @return script hash + */ + static ByteData160 Ripemd160(const Script &script); + // Hash160 -------------------------------------------------------------- /** - * @brief 文字列をHash160でハッシュする. - * @param[in] str 文字列 - * @return hashed ByteData160データ + * @brief Hash the string. + * @param[in] str string + * @return hashed data */ static ByteData160 Hash160(const std::string &str); /** - * @brief byteデータ配列をHash160でハッシュする. - * @param[in] bytes byteデータ配列 - * @return hashed ByteData160データ + * @brief Hash the byte data array. + * @param[in] bytes byte array + * @return hashed data */ static ByteData160 Hash160(const std::vector &bytes); /** - * @brief ByteDataをHash160でハッシュする. - * @param[in] data ByteDataインスタンス - * @return hashed ByteData160データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData160 Hash160(const ByteData &data); /** - * @brief ByteData160をHash160でハッシュする. - * @param[in] data byteデータ配列 - * @return hashed ByteData160データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData160 Hash160(const ByteData160 &data); /** - * @brief ByteData256をHash160でハッシュする. - * @param[in] data byteデータ配列 - * @return hashed ByteData160データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData160 Hash160(const ByteData256 &data); /** - * @brief 公開鍵をHash160でハッシュする. - * @param[in] pubkey Pubkeyインスタンス + * @brief Hash the pubkey. + * @param[in] pubkey Pubkey * @return pubkey hash */ static ByteData160 Hash160(const Pubkey &pubkey); /** - * @brief ScriptをHash160でハッシュする. - * @param[in] script Scriptインスタンス + * @brief Hash the script. + * @param[in] script Script * @return script hash */ static ByteData160 Hash160(const Script &script); // Sha256 -------------------------------------------------------------- /** - * @brief 文字列をSha256でハッシュする. - * @param[in] str 文字列 - * @return hashed ByteData256データ + * @brief Hash the string. + * @param[in] str string + * @return hashed data */ static ByteData256 Sha256(const std::string &str); /** - * @brief byteデータ配列をSha256でハッシュする. - * @param[in] bytes byteデータ配列 - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] bytes byte array + * @return hashed data */ static ByteData256 Sha256(const std::vector &bytes); /** - * @brief ByteDataをSha256でハッシュする. - * @param[in] data ByteDataインスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256(const ByteData &data); /** - * @brief ByteData160をSha256でハッシュする. - * @param[in] data ByteData160インスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256(const ByteData160 &data); /** - * @brief ByteData256をSha256でハッシュする. - * @param[in] data ByteData256インスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256(const ByteData256 &data); /** - * @brief 公開鍵をSha256でハッシュする. - * @param[in] pubkey Pubkeyインスタンス + * @brief Hash the pubkey. + * @param[in] pubkey Pubkey * @return pubkey hash */ static ByteData256 Sha256(const Pubkey &pubkey); /** - * @brief ScriptをSha256でハッシュする. - * @param[in] script Scriptインスタンス + * @brief Hash the script bytes with Ripemd160. + * @param[in] script Script * @return script hash */ static ByteData256 Sha256(const Script &script); // Sha256D -------------------------------------------------------------- /** - * @brief 文字列をSha256Dでハッシュ化する. - * @param[in] str 文字列 - * @return hashed ByteData256データ + * @brief Hash the string. + * @param[in] str string + * @return hashed data */ static ByteData256 Sha256D(const std::string &str); /** - * @brief byteデータ配列をSha256Dでハッシュ化する. - * @param[in] bytes byteデータ配列 - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] bytes byte array + * @return hashed data */ static ByteData256 Sha256D(const std::vector &bytes); /** - * @brief ByteDataをSha256Dでハッシュ化する. - * @param[in] data ByteDataインスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256D(const ByteData &data); /** - * @brief ByteData160をSha256Dでハッシュ化する. - * @param[in] data ByteData160インスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256D(const ByteData160 &data); /** - * @brief ByteData256をSha256Dでハッシュ化する. - * @param[in] data ByteData256インスタンス - * @return hashed ByteData256データ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData256 Sha256D(const ByteData256 &data); /** - * @brief 公開鍵をSha256Dでハッシュ化する. - * @param[in] pubkey Pubkeyインスタンス - * @return hashed ByteData256データ + * @brief Hash the pubkey. + * @param[in] pubkey Pubkey + * @return pubkey hash */ static ByteData256 Sha256D(const Pubkey &pubkey); /** - * @brief ScriptをSha256Dでハッシュ化する. - * @param[in] script Scriptインスタンス - * @return hashed ByteData256データ + * @brief Hash the script bytes with Ripemd160. + * @param[in] script Script + * @return script hash */ static ByteData256 Sha256D(const Script &script); // Sha512 --------------------------------------------------------------- /** - * @brief 文字列をSha512でハッシュ化する. - * @param[in] str 文字列 - * @return hashed ByteDataデータ + * @brief Hash the string. + * @param[in] str string + * @return hashed data */ static ByteData Sha512(const std::string &str); /** - * @brief byteデータ配列をSha512でハッシュ化する. - * @param[in] bytes byteデータ配列 - * @return hashed ByteDataデータ + * @brief Hash the byte data array. + * @param[in] bytes byte array + * @return hashed data */ static ByteData Sha512(const std::vector &bytes); /** - * @brief ByteDataをSha512でハッシュ化する. - * @param[in] data ByteDataインスタンス - * @return hashed ByteDataデータ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData Sha512(const ByteData &data); /** - * @brief ByteData160をSha512でハッシュ化する. - * @param[in] data ByteData160インスタンス - * @return hashed ByteDataデータ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData Sha512(const ByteData160 &data); /** - * @brief ByteData256をSha512でハッシュ化する. - * @param[in] data ByteData256インスタンス - * @return hashed ByteDataデータ + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hashed data */ static ByteData Sha512(const ByteData256 &data); /** - * @brief 公開鍵をSha512でハッシュ化する. - * @param[in] pubkey Pubkeyインスタンス - * @return hashed ByteDataデータ + * @brief Hash the pubkey. + * @param[in] pubkey Pubkey + * @return pubkey hash */ static ByteData Sha512(const Pubkey &pubkey); /** - * @brief ScriptをSha512でハッシュ化する. - * @param[in] script Scriptインスタンス - * @return hashed ByteDataデータ + * @brief Hash the script bytes with Ripemd160. + * @param[in] script Script + * @return script hash */ static ByteData Sha512(const Script &script); + // builder --------------------------------------------------------------- + //! HashType: Ripemd160 + static constexpr uint8_t kRipemd160 = 1; + //! HashType: Hash160 + static constexpr uint8_t kHash160 = 2; + //! HashType: Sha256 + static constexpr uint8_t kSha256 = 3; + //! HashType: Sha256D + static constexpr uint8_t kSha256D = 4; + //! HashType: Sha512 + static constexpr uint8_t kSha512 = 5; + + /** + * @brief constructor. + * @param[in] hash_type hash type. + */ + explicit HashUtil(uint8_t hash_type); + /** + * @brief constructor. + * @param[in] hash_type hash type. + */ + explicit HashUtil(const std::string &hash_type); + /** + * @brief destructor. + */ + virtual ~HashUtil() {} + /** + * @brief copy constructor. + * @param[in] object object + */ + HashUtil(const HashUtil &object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + HashUtil &operator=(const HashUtil &object); + + /** + * @brief Hash the string. + * @param[in] str string + * @return hash util object. + */ + HashUtil &operator<<(const std::string &str); + /** + * @brief Hash the byte data array. + * @param[in] bytes byte array + * @return hash util object. + */ + HashUtil &operator<<(const std::vector &bytes); + /** + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hash util object. + */ + HashUtil &operator<<(const ByteData &data); + /** + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hash util object. + */ + HashUtil &operator<<(const ByteData160 &data); + /** + * @brief Hash the byte data array. + * @param[in] data byte array + * @return hash util object. + */ + HashUtil &operator<<(const ByteData256 &data); + /** + * @brief Hash the pubkey. + * @param[in] pubkey Pubkey + * @return hash util object. + */ + HashUtil &operator<<(const Pubkey &pubkey); + /** + * @brief Hash the script bytes with Ripemd160. + * @param[in] script Script + * @return hash util object. + */ + HashUtil &operator<<(const Script &script); + /** + * @brief Output data. + * @return hashed data + */ + ByteData Output(); + /** + * @brief Output data. + * @return hashed data + */ + ByteData160 Output160(); + /** + * @brief Output data. + * @return hashed data + */ + ByteData256 Output256(); + private: HashUtil(); + + uint8_t hash_type_; //!< hash type + ByteData buffer_; //!< buffer }; /** * @class CryptoUtil - * @brief 暗号化/復号化関数のUtilクラス + * @brief Utility class of encryption / decryption function */ class CFD_CORE_EXPORT CryptoUtil { public: - /// AES Blockサイズ + /// AES Block size static const size_t kAesBlockLength = 16; /** - * @brief 文字列をAES256暗号化する. - * @param[in] key keyとなる32Byteの配列データ - * @param[in] data 暗号化する文字列 - * @return 暗号化したByteData + * @brief AES256 encryption of the string. + * @param[in] key 32-byte array data as a key + * @param[in] data String to encrypt + * @return Encrypted ByteData */ static ByteData EncryptAes256( const std::vector &key, const std::string &data); /** - * @brief ByteDataをAES256復号化する. - * @param[in] key keyとなる32Byteの配列データ - * @param[in] data 暗号化されたByteData - * @return 復号化した文字列 + * @brief Encrypto ByteData with AES256. + * @param[in] key key array with 32Byte. + * @param[in] data target byte data. + * @return encrypted byte data. + */ + static ByteData EncryptAes256(const ByteData &key, const ByteData &data); + /** + * @brief Decrypt ByteData to AES256. + * @param[in] key key array with 32Byte. + * @param[in] data encrypted byte data. + * @return decrypted byte data. */ static std::string DecryptAes256ToString( const std::vector &key, const ByteData &data); /** - * @brief 文字列をAES256CBC暗号化する. - * @param[in] key keyとなる32Byteの配列データ - * @param[in] iv initial vectorとなる16Byteの配列データ - * @param[in] data 暗号化するByteData - * @return 暗号化したByteData + * @brief Decrypto ByteData with AES256. + * @param[in] key key array with 32Byte. + * @param[in] data target encrypted byte data. + * @return decrypted byte data. + */ + static ByteData DecryptAes256(const ByteData &key, const ByteData &data); + /** + * @brief AES256CBC encryption of the string. + * @param[in] key 32-byte array data as a key + * @param[in] iv 16Byte array data that will be the initial vector + * @param[in] data target byte data. + * @return encrypted byte data. */ static ByteData EncryptAes256Cbc( const std::vector &key, const std::vector &iv, @@ -361,11 +554,11 @@ class CFD_CORE_EXPORT CryptoUtil { static ByteData EncryptAes256Cbc( const ByteData &key, const ByteData &iv, const ByteData &data); /** - * @brief ByteDataをAES256CBC復号化する. - * @param[in] key keyとなる32Byteの配列データ - * @param[in] iv initial vectorとなる16Byteの配列データ - * @param[in] data 暗号化されたByteData - * @return 復号化した文字列 + * @brief Decrypt ByteData to AES256CBC. + * @param[in] key key array with 32Byte. + * @param[in] iv initial vector with 16Byte. + * @param[in] data target encrypted byte data. + * @return decrypted byte data. */ static std::string DecryptAes256CbcToString( const std::vector &key, const std::vector &iv, @@ -380,10 +573,10 @@ class CFD_CORE_EXPORT CryptoUtil { static ByteData DecryptAes256Cbc( const ByteData &key, const ByteData &iv, const ByteData &data); /** - * @brief ByteDataのHMAC-SHA256を計算する. - * @param[in] key keyとなるByte配列データ - * @param[in] data ByteDataインスタンス - * @return ByteData256データ + * @brief Calculate HMAC-SHA256 for ByteData. + * @param[in] key Byte array data as a key + * @param[in] data ByteData object + * @return ByteData256 data */ static ByteData256 HmacSha256( const std::vector &key, const ByteData &data); @@ -395,10 +588,10 @@ class CFD_CORE_EXPORT CryptoUtil { */ static ByteData256 HmacSha256(const ByteData &key, const ByteData &data); /** - * @brief ByteDataのHMAC-SHA512を計算する. - * @param[in] key keyとなるByte配列データ - * @param[in] data ByteDataインスタンス - * @return ByteDataデータ + * @brief Calculate HMAC-SHA512 for ByteData. + * @param[in] key key-byte-array + * @param[in] data input-data + * @return ByteData */ static ByteData HmacSha512( const std::vector &key, const ByteData &data); @@ -433,15 +626,15 @@ class CFD_CORE_EXPORT CryptoUtil { static ByteData ConvertSignatureFromDer( const ByteData &der_data, SigHashType *sighash_type); /** - * @brief ByteDataをBase64エンコードする. - * @param[in] data エンコードするByteData - * @return encodeした文字列 + * @brief Base64 encode ByteData. + * @param[in] data ByteData to encode + * @return encoded string */ static std::string EncodeBase64(const ByteData &data); /** - * @brief 文字列をBase64デコードする. - * @param[in] str Base64エンコードされた文字列 - * @return decodeしたByteData + * @brief Base64 decode the string. + * @param[in] str Base64 encoded string + * @return decoded ByteData */ static ByteData DecodeBase64(const std::string &str); /** @@ -451,9 +644,9 @@ class CFD_CORE_EXPORT CryptoUtil { */ static ByteData DecodeBase58(const std::string &str); /** - * @brief 文字列をBase58デコードおよびチェックサム確認する. - * @param[in] str Base58エンコードされた文字列 - * @return decodeしたByteData + * @brief Base58 decode and checksum check the string. + * @param[in] str Base58 encoded string + * @return decoded ByteData */ static ByteData DecodeBase58Check(const std::string &str); /** @@ -470,7 +663,7 @@ class CFD_CORE_EXPORT CryptoUtil { static std::string EncodeBase58Check(const ByteData &data); /** - * @brief merkle rootの簡易計算を行う。 + * @brief Perform a simple calculation of merkle root. * @param[in] hashes hash list * @return merkle root */ @@ -478,7 +671,7 @@ class CFD_CORE_EXPORT CryptoUtil { const std::vector &hashes); /** - * @brief merkle hash計算を行う。 + * @brief Perform merkle hash calculation. * @param[in] left left hash * @param[in] right right hash * @return merkle hash @@ -492,25 +685,25 @@ class CFD_CORE_EXPORT CryptoUtil { /** * @class RandomNumberUtil - * @brief 乱数関連関数のUtilクラス + * @brief Utility class of random number related functions */ class CFD_CORE_EXPORT RandomNumberUtil { public: /** - * 乱数を生成する. - * @param[in] len 乱数の長さ - * @return 乱数配列 + * @brief Generate random numbers. + * @param[in] len Random number length + * @return Random number array */ static std::vector GetRandomBytes(int len); /** - * 乱数で指定範囲のIndexListを生成する. - * @param[in] length リストの長さ + * @brief Generate IndexList of specified range with random numbers. + * @param[in] length List length * @return index list */ static std::vector GetRandomIndexes(uint32_t length); /** - * ランダムなbool値を生成する. - * @param[in,out] random_cache 乱数キャッシュ値 + * @brief Generate a random bool value. + * @param[in,out] random_cache Random number cache value * @return true/false */ static bool GetRandomBool(std::vector *random_cache); @@ -521,35 +714,42 @@ class CFD_CORE_EXPORT RandomNumberUtil { /** * @class StringUtil - * @brief 文字列操作Utilクラス + * @brief String manipulation Util class. */ class CFD_CORE_EXPORT StringUtil { public: /** - * @brief hex文字列からbyteデータ配列への変換をする. - * @param[in] hex_str HEX文字列 - * @return byteデータ配列 + * @brief Check hex string. + * @param[in] hex_str HEX string + * @retval true valid hex string. + * @retval false invalid string. + */ + static bool IsValidHexString(const std::string &hex_str); + /** + * @brief Convert from hex character string to byte data array. + * @param[in] hex_str HEX string + * @return byte data array. */ static std::vector StringToByte(const std::string &hex_str); /** - * @brief byteデータ配列からHEX文字列への変換をする. - * @param[in] bytes byteデータ配列 - * @return HEX文字列 + * @brief Convert from byte data array to HEX character string. + * @param[in] bytes byte data array + * @return HEX string */ static std::string ByteToString(const std::vector &bytes); /** - * @brief 文字列を区切り文字で分割する. - * @param[in] str 分割対象文字列 - * @param[in] delim 区切り文字列 - * @return 区切り文字で区切られた文字列vector + * @brief Divide the string by the delimiter. + * @param[in] str Character string to be divided + * @param[in] delim Delimiter string + * @return String vector separated by delimiter */ static std::vector Split( const std::string &str, const std::string &delim); /** - * @brief 文字列配列を連結する. - * @param[in] str_list 文字列配列 - * @param[in] separate_word 連結文字列 - * @return 連結された文字列 + * @brief Concatenate string arrays. + * @param[in] str_list String array + * @param[in] separate_word Concatenated string + * @return Concatenated string */ static std::string Join( const std::vector &str_list, diff --git a/package.json b/package.json index be7dfb9a..0e73c6f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cfd-core", - "version": "0.2.0", + "version": "0.3.0", "description": "cfd-core", "scripts": { "code_format": "clang-format -i --style=file src/*.cpp src/*.h src/include/cfdcore/*.h include/cfdcore/*.h", diff --git a/src/Makefile.srclist b/src/Makefile.srclist index c77d5dd2..21b0f897 100644 --- a/src/Makefile.srclist +++ b/src/Makefile.srclist @@ -18,8 +18,10 @@ CFDCORE_SOURCES = \ cfdcore_amount.cpp \ cfdcore_coin.cpp \ cfdcore_address.cpp \ + cfdcore_psbt.cpp \ cfdcore_secp256k1.cpp \ cfdcore_schnorrsig.cpp \ + cfdcore_taproot.cpp \ secp256k1_util.cpp \ cfdcore_ecdsa_adaptor.cpp \ ${CFDCORE_ELEMENTS_SOURCES} diff --git a/src/cfdcore_address.cpp b/src/cfdcore_address.cpp index b4a8355c..701d4279 100644 --- a/src/cfdcore_address.cpp +++ b/src/cfdcore_address.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_address.cpp * - * @brief \~japanese Addressを表現するクラス - * \~english Class to show address. + * @brief Class to show address. */ #include "cfdcore/cfdcore_address.h" @@ -14,6 +13,7 @@ #include "cfdcore/cfdcore_logger.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_util.h" #include "cfdcore_wally_util.h" // NOLINT #include "univalue.h" // NOLINT @@ -197,11 +197,43 @@ Address::Address() address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); info(CFD_LOG_SOURCE, "call Address()"); } +Address::Address(const Address& object) + : type_(object.type_), + addr_type_(object.addr_type_), + witness_ver_(object.witness_ver_), + address_(object.address_), + hash_(object.hash_), + pubkey_(object.pubkey_), + schnorr_pubkey_(object.schnorr_pubkey_), + script_tree_(object.script_tree_), + redeem_script_(object.redeem_script_) { + memcpy(checksum_, object.checksum_, sizeof(checksum_)); + format_data_ = object.format_data_; +} + +Address& Address::operator=(const Address& object) { + if (this != &object) { + type_ = object.type_; + addr_type_ = object.addr_type_; + witness_ver_ = object.witness_ver_; + address_ = object.address_; + hash_ = object.hash_; + pubkey_ = object.pubkey_; + schnorr_pubkey_ = object.schnorr_pubkey_; + script_tree_ = object.script_tree_; + redeem_script_ = object.redeem_script_; + memcpy(checksum_, object.checksum_, sizeof(checksum_)); + format_data_ = object.format_data_; + } + return *this; +} + Address::Address(const std::string& address_string) : type_(kMainnet), addr_type_(kP2shAddress), @@ -209,6 +241,7 @@ Address::Address(const std::string& address_string) address_(address_string), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); DecodeAddress(address_string, nullptr); @@ -223,6 +256,7 @@ Address::Address( address_(address_string), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); const std::vector* params = nullptr; @@ -241,6 +275,7 @@ Address::Address( address_(address_string), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); const std::vector params = {network_parameter}; @@ -259,6 +294,7 @@ Address::Address(NetType type, const Pubkey& pubkey, uint8_t prefix) address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); CalculateP2PKH(prefix); @@ -276,6 +312,7 @@ Address::Address( address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_(), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); @@ -295,6 +332,7 @@ Address::Address( address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_(), format_data_(GetTargetFormatData(network_parameters, type)) { memset(checksum_, 0, sizeof(checksum_)); @@ -317,6 +355,7 @@ Address::Address(NetType type, const Script& script, uint8_t prefix) address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script) { memset(checksum_, 0, sizeof(checksum_)); CalculateP2SH(prefix); @@ -334,6 +373,7 @@ Address::Address( address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); @@ -353,6 +393,7 @@ Address::Address( address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script), format_data_(GetTargetFormatData(network_parameters, type)) { memset(checksum_, 0, sizeof(checksum_)); @@ -378,6 +419,7 @@ Address::Address( address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); CalculateP2WPKH(bech32_hrp); @@ -395,6 +437,7 @@ Address::Address( address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_(), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); @@ -414,6 +457,7 @@ Address::Address( address_(""), hash_(), pubkey_(pubkey), + schnorr_pubkey_(), redeem_script_(), format_data_(GetTargetFormatData(network_parameters, type)) { memset(checksum_, 0, sizeof(checksum_)); @@ -439,6 +483,7 @@ Address::Address( address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script) { memset(checksum_, 0, sizeof(checksum_)); CalculateP2WSH(bech32_hrp); @@ -456,6 +501,7 @@ Address::Address( address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); @@ -475,6 +521,7 @@ Address::Address( address_(""), hash_(), pubkey_(), + schnorr_pubkey_(), redeem_script_(script), format_data_(GetTargetFormatData(network_parameters, type)) { memset(checksum_, 0, sizeof(checksum_)); @@ -485,6 +532,141 @@ Address::Address( AddressType::kP2wshAddress, format_data_.GetBech32Hrp()); } +Address::Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey) + : Address(type, witness_ver, pubkey, "") { + // do nothing +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const std::string& bech32_hrp) + : type_((!bech32_hrp.empty()) ? kCustomChain : type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(pubkey), + redeem_script_() { + memset(checksum_, 0, sizeof(checksum_)); + CalculateTaproot(bech32_hrp); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, bech32_hrp); +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const AddressFormatData& network_parameter) + : type_(type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(pubkey), + redeem_script_(), + format_data_(network_parameter) { + memset(checksum_, 0, sizeof(checksum_)); + SetNetType(format_data_); + CalculateTaproot(network_parameter.GetBech32Hrp()); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, + network_parameter.GetBech32Hrp()); +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const SchnorrPubkey& pubkey, + const std::vector& network_parameters) + : type_(type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(pubkey), + redeem_script_(), + format_data_(GetTargetFormatData(network_parameters, type)) { + memset(checksum_, 0, sizeof(checksum_)); + SetNetType(format_data_); + CalculateTaproot(format_data_.GetBech32Hrp()); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, + format_data_.GetBech32Hrp()); +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey) + : Address(type, witness_ver, tree, internal_pubkey, "") { + // do nothing +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, const std::string& bech32_hrp) + : type_((!bech32_hrp.empty()) ? kCustomChain : type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(), + script_tree_(tree), + redeem_script_() { + memset(checksum_, 0, sizeof(checksum_)); + schnorr_pubkey_ = tree.GetTweakedPubkey(internal_pubkey); + CalculateTaproot(bech32_hrp); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, bech32_hrp); +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const AddressFormatData& network_parameter) + : type_(type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(), + script_tree_(tree), + redeem_script_(), + format_data_(network_parameter) { + memset(checksum_, 0, sizeof(checksum_)); + SetNetType(format_data_); + schnorr_pubkey_ = tree.GetTweakedPubkey(internal_pubkey); + CalculateTaproot(network_parameter.GetBech32Hrp()); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, + network_parameter.GetBech32Hrp()); +} + +Address::Address( + NetType type, WitnessVersion witness_ver, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const std::vector& network_parameters) + : type_(type), + addr_type_(AddressType::kTaprootAddress), + witness_ver_(witness_ver), + address_(""), + hash_(), + pubkey_(), + schnorr_pubkey_(), + script_tree_(tree), + redeem_script_(), + format_data_(GetTargetFormatData(network_parameters, type)) { + memset(checksum_, 0, sizeof(checksum_)); + SetNetType(format_data_); + schnorr_pubkey_ = tree.GetTweakedPubkey(internal_pubkey); + CalculateTaproot(format_data_.GetBech32Hrp()); + info( + CFD_LOG_SOURCE, "call Address({},{},{})", type_, addr_type_, + format_data_.GetBech32Hrp()); +} + Address::Address(NetType type, AddressType addr_type, const ByteData160& hash) : type_(type), addr_type_(addr_type), @@ -492,6 +674,7 @@ Address::Address(NetType type, AddressType addr_type, const ByteData160& hash) address_(""), hash_(hash.GetBytes()), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); if (addr_type == kP2pkhAddress) { @@ -515,6 +698,7 @@ Address::Address( address_(""), hash_(hash.GetBytes()), pubkey_(), + schnorr_pubkey_(), redeem_script_(), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); @@ -547,10 +731,11 @@ Address::Address( address_(""), hash_(hash), pubkey_(), + schnorr_pubkey_(), redeem_script_() { memset(checksum_, 0, sizeof(checksum_)); - if (witness_ver_ != WitnessVersion::kVersionNone) { + if (witness_ver_ == WitnessVersion::kVersion0) { if (hash.GetDataSize() == kByteData160Length) { SetAddressType(kP2wpkhAddress); CalculateP2WPKH(ByteData160(hash.GetBytes())); @@ -562,6 +747,15 @@ Address::Address( info(CFD_LOG_SOURCE, "illegal hash data. hash={}", hash.GetHex()); throw CfdException(kCfdIllegalArgumentError, "hash value error."); } + } else if (witness_ver_ != WitnessVersion::kVersionNone) { + if ((witness_ver_ == WitnessVersion::kVersion1) && + (hash.GetDataSize() == SchnorrPubkey::kSchnorrPubkeySize)) { + SetAddressType(kTaprootAddress); + schnorr_pubkey_ = SchnorrPubkey(hash); + } else { + SetAddressType(kWitnessUnknown); + } + CalculateBech32m(hash); } } @@ -574,11 +768,12 @@ Address::Address( address_(""), hash_(hash), pubkey_(), + schnorr_pubkey_(), redeem_script_(), format_data_(network_parameter) { memset(checksum_, 0, sizeof(checksum_)); - if (witness_ver_ != WitnessVersion::kVersionNone) { + if (witness_ver_ == WitnessVersion::kVersion0) { if (hash.GetDataSize() == kByteData160Length) { SetAddressType(kP2wpkhAddress); SetNetType(format_data_); @@ -594,6 +789,16 @@ Address::Address( info(CFD_LOG_SOURCE, "illegal hash data. hash={}", hash.GetHex()); throw CfdException(kCfdIllegalArgumentError, "hash value error."); } + } else if (witness_ver_ != WitnessVersion::kVersionNone) { + if ((witness_ver_ == WitnessVersion::kVersion1) && + (hash.GetDataSize() == SchnorrPubkey::kSchnorrPubkeySize)) { + SetAddressType(kTaprootAddress); + schnorr_pubkey_ = SchnorrPubkey(hash); + } else { + SetAddressType(kWitnessUnknown); + } + SetNetType(format_data_); + CalculateBech32m(hash, network_parameter.GetBech32Hrp()); } } @@ -770,8 +975,45 @@ void Address::CalculateP2WPKH( address_ = WallyUtil::ConvertStringAndFree(output); } +void Address::CalculateTaproot(const std::string& bech32_hrp) { + hash_ = schnorr_pubkey_.GetData(); + CalculateBech32m(hash_, bech32_hrp); +} + +void Address::CalculateBech32m( + const ByteData& hash_data, const std::string& bech32_hrp) { + std::vector pubkey_hash = hash_data.GetBytes(); + pubkey_hash.insert( + pubkey_hash.begin(), static_cast(kByteData256Length)); + pubkey_hash.insert(pubkey_hash.begin(), witness_ver_ + kOp_1 - 1); + + std::string human_code = bech32_hrp; + if (human_code.empty() && (kMainnet <= type_) && (type_ <= kRegtest)) { + human_code = kBitcoinAddressFormatList[type_].GetBech32Hrp(); + format_data_ = kBitcoinAddressFormatList[type_]; + SetNetType(format_data_); + } + char* output = NULL; + int ret = wally_addr_segwit_from_bytes( + pubkey_hash.data(), pubkey_hash.size(), human_code.data(), 0, &output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_addr_segwit_from_bytes error. ret={}.", ret); + info( + CFD_LOG_SOURCE, "input hash={}", + StringUtil::ByteToString(pubkey_hash)); + if (ret == WALLY_EINVAL) { + throw CfdException( + kCfdIllegalArgumentError, "Segwit-address create error."); + } else { + throw CfdException(kCfdInternalError, "Segwit-address create error."); + } + } + + address_ = WallyUtil::ConvertStringAndFree(output); +} + void Address::DecodeAddress( - std::string bs58, + std::string address_string, const std::vector* network_parameters) { static const std::string kBech32Separator = "1"; static const auto StartsWith = [](const std::string& message, @@ -779,6 +1021,7 @@ void Address::DecodeAddress( return (message.find(bech32_hrp + kBech32Separator) == 0); }; + std::string bs58 = address_string; std::string segwit_prefix = ""; int ret = -1; @@ -823,12 +1066,24 @@ void Address::DecodeAddress( } data_part.resize(written); - witness_ver_ = kVersion0; + Script script(ByteData(data_part.data(), static_cast(written))); + if (!script.IsWitnessProgram()) { + throw CfdException(kCfdInternalError, "address decode check error."); + } + witness_ver_ = script.GetWitnessVersion(); - if (written == kScriptHashP2wpkhLength) { - SetAddressType(kP2wpkhAddress); - } else if (written == kScriptHashP2wshLength) { - SetAddressType(kP2wshAddress); + if (witness_ver_ == kVersion1) { + if (written != (SchnorrPubkey::kSchnorrPubkeySize + 2)) { + throw CfdException( + kCfdInternalError, "segwit v1 address decode check error."); + } + SetAddressType(kTaprootAddress); + } else if (witness_ver_ == kVersion0) { + if (written == kScriptHashP2wpkhLength) { + SetAddressType(kP2wpkhAddress); + } else if (written == kScriptHashP2wshLength) { + SetAddressType(kP2wshAddress); + } } // Delete 0byte:WitnessVersion and 1byte:data_part @@ -891,6 +1146,7 @@ void Address::DecodeAddress( // Setting for Hash. hash_ = ByteData(data_part); + if (witness_ver_ == kVersion1) schnorr_pubkey_ = SchnorrPubkey(hash_); SetNetType(format_data_); info( CFD_LOG_SOURCE, "DecodeAddress nettype={},{}", format_data_.GetNetType(), @@ -931,6 +1187,10 @@ AddressFormatData Address::GetTargetFormatData( Script Address::GetLockingScript() const { Script locking_script; switch (addr_type_) { + case AddressType::kTaprootAddress: + locking_script = + ScriptUtil::CreateTaprootLockingScript(ByteData256(hash_)); + break; case AddressType::kP2pkhAddress: { ByteData160 pubkey_hash(hash_.GetBytes()); locking_script = ScriptUtil::CreateP2pkhLockingScript(pubkey_hash); diff --git a/src/cfdcore_amount.cpp b/src/cfdcore_amount.cpp index 002bcb68..25e07dfc 100644 --- a/src/cfdcore_amount.cpp +++ b/src/cfdcore_amount.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_amount.cpp * - * @brief \~japanese Amountを表現するクラス - * \~english Class to show amount. + * @brief Class to show amount. */ #include "cfdcore/cfdcore_amount.h" diff --git a/src/cfdcore_bytedata.cpp b/src/cfdcore_bytedata.cpp index dd3476cd..8bac8c46 100644 --- a/src/cfdcore_bytedata.cpp +++ b/src/cfdcore_bytedata.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_bytedata.cpp * - * @brief \~japanese ByteData関連クラス実装 - * \~english implimentation of ByteData class + * @brief implimentation of ByteData class */ #include "cfdcore/cfdcore_bytedata.h" @@ -20,31 +19,6 @@ namespace core { using logger::warn; -// ----------------------------------------------------------------------------- -// inner file -// ----------------------------------------------------------------------------- -static constexpr uint8_t kViTag16 = 253; //!< VarInt16 -static constexpr uint8_t kViTag32 = 254; //!< VarInt32 -static constexpr uint8_t kViTag64 = 255; //!< VarInt64 -static constexpr uint8_t kViMax8 = 252; //!< VarInt8 - -/** - * @brief serialize from buffer. - * @param[in] data buffer - * @result serialize buffer - */ -static std::vector SerializeFromBuffer( - const std::vector& data) { - std::vector result; - std::vector count_buffer = - ByteData::GetVariableInt(data.size()).GetBytes(); - result.insert(result.end(), count_buffer.begin(), count_buffer.end()); - if (data.size() != 0) { - result.insert(result.end(), data.begin(), data.end()); - } - return result; -} - ////////////////////////////////// /// ByteData ////////////////////////////////// @@ -52,12 +26,17 @@ ByteData::ByteData() : data_(0) { // do nothing } -ByteData::ByteData(const std::vector& vector) : data_(vector) {} +ByteData::ByteData(const std::vector& vector) : data_(vector) { + if (data_.size() > std::numeric_limits::max()) { + warn(CFD_LOG_SOURCE, "It exceeds the handling size."); + throw CfdException(kCfdIllegalStateError, "It exceeds the handling size."); + } +} ByteData::ByteData(const std::string& hex) : data_(StringUtil::StringToByte(hex)) {} -ByteData::ByteData(const uint8_t* buffer, uint32_t size) : data_(0) { +ByteData::ByteData(const uint8_t* buffer, uint32_t size) : data_(size) { if (buffer == nullptr) { if (size == 0) { // create empty buffer @@ -71,6 +50,10 @@ ByteData::ByteData(const uint8_t* buffer, uint32_t size) : data_(0) { } } +ByteData::ByteData(const uint8_t single_byte) : data_(1) { + data_[0] = single_byte; +} + std::string ByteData::GetHex() const { return StringUtil::ByteToString(data_); } @@ -95,36 +78,19 @@ uint8_t ByteData::GetHeadData() const { } ByteData ByteData::Serialize() const { - return ByteData(SerializeFromBuffer(data_)); + Serializer obj(static_cast(data_.size())); + obj.AddVariableBuffer(data_.data(), static_cast(data_.size())); + return obj.Output(); } size_t ByteData::GetSerializeSize() const { - ByteData size_buffer = GetVariableInt(data_.size()); - return size_buffer.GetDataSize() + data_.size(); + return Serializer::GetVariableIntSize(data_.size()) + data_.size(); } ByteData ByteData::GetVariableInt(uint64_t v) { - std::vector size_byte; - if (v <= kViMax8) { - uint8_t v8 = static_cast(v); - size_byte.push_back(v8); - } else if (v <= std::numeric_limits::max()) { - uint16_t v16 = static_cast(v); - size_byte.resize(sizeof(v16) + 1); - size_byte[0] = kViTag16; - memcpy(size_byte.data() + 1, &v16, sizeof(v16)); - } else if (v <= std::numeric_limits::max()) { - uint32_t v32 = static_cast(v); - size_byte.resize(sizeof(v32) + 1); - size_byte[0] = kViTag32; - memcpy(size_byte.data() + 1, &v32, sizeof(v32)); - } else { - size_byte.resize(sizeof(v) + 1); - size_byte[0] = kViTag64; - memcpy(size_byte.data() + 1, &v, sizeof(v)); - } - - return ByteData(size_byte); + Serializer obj(sizeof(v) + 1); + obj.AddVariableInt(v); + return obj.Output(); } bool ByteData::IsLarge(const ByteData& source, const ByteData& destination) { @@ -132,18 +98,29 @@ bool ByteData::IsLarge(const ByteData& source, const ByteData& destination) { } void ByteData::Push(const ByteData& back_insert_data) { - std::vector insert_bytes = back_insert_data.GetBytes(); - data_.insert(data_.end(), insert_bytes.begin(), insert_bytes.end()); + if (back_insert_data.IsEmpty()) return; + const std::vector& insert_bytes = back_insert_data.data_; + data_.reserve(data_.size() + insert_bytes.size() + 8); + std::copy( + insert_bytes.begin(), insert_bytes.end(), std::back_inserter(data_)); } void ByteData::Push(const ByteData160& back_insert_data) { std::vector insert_bytes = back_insert_data.GetBytes(); - data_.insert(data_.end(), insert_bytes.begin(), insert_bytes.end()); + data_.reserve(data_.size() + insert_bytes.size() + 8); + std::copy( + insert_bytes.begin(), insert_bytes.end(), std::back_inserter(data_)); } void ByteData::Push(const ByteData256& back_insert_data) { std::vector insert_bytes = back_insert_data.GetBytes(); - data_.insert(data_.end(), insert_bytes.begin(), insert_bytes.end()); + data_.reserve(data_.size() + insert_bytes.size() + 8); + std::copy( + insert_bytes.begin(), insert_bytes.end(), std::back_inserter(data_)); +} + +bool ByteData::operator==(const ByteData& object) const { + return (data_ == object.data_); } ////////////////////////////////// @@ -203,7 +180,13 @@ ByteData ByteData160::GetData() const { return ByteData(data_); } uint8_t ByteData160::GetHeadData() const { return data_[0]; } ByteData ByteData160::Serialize() const { - return ByteData(SerializeFromBuffer(data_)); + Serializer obj(static_cast(data_.size())); + obj.AddVariableBuffer(data_.data(), static_cast(data_.size())); + return obj.Output(); +} + +bool ByteData160::operator==(const ByteData160& object) const { + return (data_ == object.data_); } ////////////////////////////////// @@ -263,7 +246,333 @@ ByteData ByteData256::GetData() const { return ByteData(data_); } uint8_t ByteData256::GetHeadData() const { return data_[0]; } ByteData ByteData256::Serialize() const { - return ByteData(SerializeFromBuffer(data_)); + Serializer obj(static_cast(data_.size())); + obj.AddVariableBuffer(data_.data(), static_cast(data_.size())); + return obj.Output(); +} + +bool ByteData256::operator==(const ByteData256& object) const { + return (data_ == object.data_); +} + +////////////////////////////////// +/// Serializer +////////////////////////////////// +Serializer::Serializer() : buffer_(8), offset_(0) { + // do nothing +} + +Serializer::Serializer(uint32_t initial_size) + : buffer_(initial_size + 9), offset_(0) { + // do nothing +} + +Serializer::Serializer(const Serializer& object) + : buffer_(object.buffer_), offset_(object.offset_) { + // do nothing +} + +Serializer& Serializer::operator=(const Serializer& object) { + if (this != &object) { + buffer_ = object.buffer_; + offset_ = object.offset_; + } + return *this; +} + +void Serializer::CheckNeedSize(uint32_t need_size) { + size_t size = buffer_.size() - static_cast(offset_); + if (size < need_size) { + size_t cap = buffer_.capacity() - static_cast(offset_); + if (cap < (need_size * 2)) { + buffer_.reserve(buffer_.capacity() + (need_size * 10)); + } + buffer_.resize(buffer_.size() + (need_size * 2)); + } +} + +uint32_t Serializer::GetVariableIntSize(uint64_t value) { + if (value <= kViMax8) + return 1; + else if (value <= std::numeric_limits::max()) + return 3; + else if (value <= std::numeric_limits::max()) + return 5; + else + return 9; +} + +void Serializer::AddVariableInt(uint64_t value) { + // TODO(k-matsuzawa) need endian support. + CheckNeedSize(9); + uint8_t* buf = &buffer_.data()[offset_]; + if (value <= kViMax8) { + *buf = static_cast(value); + ++offset_; + } else if (value <= std::numeric_limits::max()) { + *buf = kViTag16; + ++buf; + uint16_t v16 = static_cast(value); + memcpy(buf, &v16, sizeof(v16)); + offset_ += sizeof(v16) + 1; + } else if (value <= std::numeric_limits::max()) { + *buf = kViTag32; + ++buf; + uint32_t v32 = static_cast(value); + memcpy(buf, &v32, sizeof(v32)); + offset_ += sizeof(v32) + 1; + } else { + *buf = kViTag64; + ++buf; + uint64_t v64 = value; + memcpy(buf, &v64, sizeof(v64)); + offset_ += sizeof(v64) + 1; + } +} + +void Serializer::AddVariableBuffer(const ByteData& buffer) { + auto buf = buffer.GetBytes(); + if (buf.size() > std::numeric_limits::max()) { + warn(CFD_LOG_SOURCE, "It exceeds the handling size."); + throw CfdException(kCfdIllegalStateError, "It exceeds the handling size."); + } + AddVariableBuffer(buf.data(), static_cast(buf.size())); +} + +void Serializer::AddPrefixBuffer(uint64_t prefix, const ByteData& buffer) { + auto buf = buffer.GetBytes(); + if (buf.size() > std::numeric_limits::max()) { + warn(CFD_LOG_SOURCE, "It exceeds the handling size."); + throw CfdException(kCfdIllegalStateError, "It exceeds the handling size."); + } + AddPrefixBuffer(prefix, buf.data(), static_cast(buf.size())); +} + +void Serializer::AddDirectBytes(const ByteData& buffer) { + auto buf = buffer.GetBytes(); + if (buf.size() > std::numeric_limits::max()) { + warn(CFD_LOG_SOURCE, "It exceeds the handling size."); + throw CfdException(kCfdIllegalStateError, "It exceeds the handling size."); + } + AddDirectBytes(buf.data(), static_cast(buf.size())); +} + +void Serializer::AddDirectBytes(const ByteData256& buffer) { + auto buf = buffer.GetBytes(); + AddDirectBytes(buf.data(), static_cast(buf.size())); +} + +void Serializer::AddVariableBuffer( + const uint8_t* buffer, uint32_t buffer_size) { + AddVariableInt(buffer_size); + AddDirectBytes(buffer, buffer_size); +} + +void Serializer::AddPrefixBuffer( + uint64_t prefix, const uint8_t* buffer, uint32_t buffer_size) { + uint32_t size = GetVariableIntSize(prefix) + buffer_size; + AddVariableInt(size); + AddVariableInt(prefix); + AddDirectBytes(buffer, buffer_size); +} + +void Serializer::AddDirectBytes(const uint8_t* buffer, uint32_t buffer_size) { + if ((buffer != nullptr) && (buffer_size != 0)) { + CheckNeedSize(buffer_size); + uint8_t* buf = &buffer_.data()[offset_]; + memcpy(buf, buffer, buffer_size); + offset_ += buffer_size; + } +} + +void Serializer::AddDirectByte(uint8_t byte_data) { + CheckNeedSize(4); + uint8_t* buf = &buffer_.data()[offset_]; + *buf = byte_data; + ++offset_; +} + +void Serializer::AddDirectNumber(uint32_t number) { + CheckNeedSize(sizeof(number)); + uint8_t* buf = &buffer_.data()[offset_]; + // TODO(k-matsuzawa) need endian support. + memcpy(buf, &number, sizeof(number)); + offset_ += sizeof(number); +} + +void Serializer::AddDirectNumber(uint64_t number) { + CheckNeedSize(sizeof(number)); + uint8_t* buf = &buffer_.data()[offset_]; + // TODO(k-matsuzawa) need endian support. + memcpy(buf, &number, sizeof(number)); + offset_ += sizeof(number); +} + +void Serializer::AddDirectNumber(int64_t number) { + CheckNeedSize(sizeof(number)); + uint8_t* buf = &buffer_.data()[offset_]; + // TODO(k-matsuzawa) need endian support. + memcpy(buf, &number, sizeof(number)); + offset_ += sizeof(number); +} + +Serializer& Serializer::operator<<(const ByteData& buffer) { + AddDirectBytes(buffer); + return *this; +} + +Serializer& Serializer::operator<<(const ByteData256& buffer) { + AddDirectBytes(buffer); + return *this; +} + +Serializer& Serializer::operator<<(uint8_t byte_data) { + AddDirectByte(byte_data); + return *this; +} + +Serializer& Serializer::operator<<(uint32_t number) { + AddDirectNumber(number); + return *this; +} + +Serializer& Serializer::operator<<(uint64_t number) { + AddDirectNumber(number); + return *this; +} + +Serializer& Serializer::operator<<(int64_t number) { + AddDirectNumber(number); + return *this; +} + +ByteData Serializer::Output() { return ByteData(buffer_.data(), offset_); } + +Deserializer::Deserializer(const std::vector& buffer) + : buffer_(buffer), offset_(0) { + // do nothing +} + +Deserializer::Deserializer(const ByteData& buffer) + : Deserializer(buffer.GetBytes()) { + // do nothing +} + +Deserializer::Deserializer(const Deserializer& object) + : buffer_(object.buffer_), offset_(object.offset_) { + // do nothing +} + +Deserializer& Deserializer::operator=(const Deserializer& object) { + if (this != &object) { + buffer_ = object.buffer_; + offset_ = object.offset_; + } + return *this; +} + +uint64_t Deserializer::ReadUint64() { + uint64_t result = 0; + CheckReadSize(sizeof(result)); + memcpy(&result, &buffer_.data()[offset_], sizeof(result)); + offset_ += sizeof(result); + return result; +} + +uint32_t Deserializer::ReadUint32() { + uint32_t result = 0; + CheckReadSize(sizeof(result)); + memcpy(&result, &buffer_.data()[offset_], sizeof(result)); + offset_ += sizeof(result); + return result; +} + +uint8_t Deserializer::ReadUint8() { + uint8_t result = 0; + CheckReadSize(sizeof(result)); + memcpy(&result, &buffer_.data()[offset_], sizeof(result)); + offset_ += sizeof(result); + return result; +} + +uint64_t Deserializer::ReadVariableInt() { + CheckReadSize(1); + const uint8_t* buf = buffer_.data() + offset_; + uint64_t value = 0; + if (*buf <= Serializer::kViMax8) { + value = *buf; + offset_ += 1; + } else if (*buf == Serializer::kViTag16) { + CheckReadSize(3); + ++buf; + uint16_t num; + memcpy(&num, buf, sizeof(num)); + value = num; + offset_ += 1 + sizeof(num); + } else if (*buf == Serializer::kViTag32) { + CheckReadSize(5); + ++buf; + uint32_t num; + memcpy(&num, buf, sizeof(num)); + value = num; + offset_ += 1 + sizeof(num); + } else { + CheckReadSize(9); + ++buf; + uint64_t num; + memcpy(&num, buf, sizeof(num)); + value = num; + offset_ += 1 + sizeof(num); + } + return value; +} + +std::vector Deserializer::ReadBuffer(uint32_t size) { + CheckReadSize(size); + std::vector result(size); + memcpy(result.data(), &buffer_.data()[offset_], size); + offset_ += size; + return result; +} + +void Deserializer::ReadArray(uint8_t* output, size_t size) { + if (output != nullptr) { + CheckReadSize(size); + memcpy(output, &buffer_.data()[offset_], size); + offset_ += static_cast(size); + } +} + +std::vector Deserializer::ReadVariableBuffer() { + // TODO(k-matsuzawa) need endian support. + uint64_t data_size = ReadVariableInt(); + if (data_size == 0) { + return std::vector(); + } + CheckReadSize(data_size); + + uint8_t* buf = buffer_.data() + offset_; + std::vector result(data_size); + memcpy(result.data(), buf, data_size); + offset_ += static_cast(data_size); + return result; +} + +ByteData Deserializer::ReadVariableData() { + return ByteData(ReadVariableBuffer()); +} + +uint32_t Deserializer::GetReadSize() { return offset_; } + +void Deserializer::CheckReadSize(uint64_t size) { + if (size > std::numeric_limits::max()) { + warn(CFD_LOG_SOURCE, "It exceeds the handling size."); + throw CfdException(kCfdIllegalStateError, "It exceeds the handling size."); + } + if (buffer_.size() < (offset_ + size)) { + warn(CFD_LOG_SOURCE, "deserialize buffer EOF."); + throw CfdException(kCfdIllegalStateError, "deserialize buffer EOF."); + } } } // namespace core diff --git a/src/cfdcore_coin.cpp b/src/cfdcore_coin.cpp index 2d0f4ee2..ba5e382c 100644 --- a/src/cfdcore_coin.cpp +++ b/src/cfdcore_coin.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_coin.cpp * - * @brief \~japanese Coin(UTXO)関連クラス - * \~english Classes related to Coin(UTXO) + * @brief Classes related to Coin(UTXO) */ #include "cfdcore/cfdcore_coin.h" @@ -41,6 +40,15 @@ Txid::Txid(const ByteData256& data) : data_(ByteData(data.GetBytes())) { // do nothing } +Txid::Txid(const Txid& object) { data_ = object.data_; } + +Txid& Txid::operator=(const Txid& object) { + if (this != &object) { + data_ = object.data_; + } + return *this; +} + const std::string Txid::GetHex() const { const std::vector& data = data_.GetBytes(); std::vector reverse_buffer(data.crbegin(), data.crend()); @@ -81,6 +89,15 @@ BlockHash::BlockHash(const ByteData256& data) // do nothing } +BlockHash::BlockHash(const BlockHash& object) { data_ = object.data_; } + +BlockHash& BlockHash::operator=(const BlockHash& object) { + if (this != &object) { + data_ = object.data_; + } + return *this; +} + const std::string BlockHash::GetHex() const { const std::vector& data = data_.GetBytes(); std::vector reverse_buffer(data.crbegin(), data.crend()); diff --git a/src/cfdcore_descriptor.cpp b/src/cfdcore_descriptor.cpp index 02e55bb7..49665397 100644 --- a/src/cfdcore_descriptor.cpp +++ b/src/cfdcore_descriptor.cpp @@ -237,14 +237,16 @@ DescriptorKeyInfo::DescriptorKeyInfo(const DescriptorKeyInfo& object) { DescriptorKeyInfo& DescriptorKeyInfo::operator=( const DescriptorKeyInfo& object) { - key_type_ = object.key_type_; - pubkey_ = object.pubkey_; - privkey_ = object.privkey_; - extprivkey_ = object.extprivkey_; - extpubkey_ = object.extpubkey_; - parent_info_ = object.parent_info_; - path_ = object.path_; - key_string_ = object.key_string_; + if (this != &object) { + key_type_ = object.key_type_; + pubkey_ = object.pubkey_; + privkey_ = object.privkey_; + extprivkey_ = object.extprivkey_; + extpubkey_ = object.extpubkey_; + parent_info_ = object.parent_info_; + path_ = object.path_; + key_string_ = object.key_string_; + } return *this; } @@ -305,22 +307,41 @@ DescriptorKeyReference::DescriptorKeyReference( extpubkey_(ext_pubkey), argument_((arg) ? *arg : "") {} +DescriptorKeyReference::DescriptorKeyReference( + const KeyData& key, const std::string* arg) + : key_type_(DescriptorKeyType::kDescriptorKeyPublic), + pubkey_(key.GetPubkey()), + key_data_(key), + argument_((arg) ? *arg : "") { + if (key_data_.HasExtPrivkey()) { + extprivkey_ = key_data_.GetExtPrivkey(); + key_type_ = DescriptorKeyType::kDescriptorKeyBip32Priv; + } else if (key_data_.HasExtPubkey()) { + extpubkey_ = key_data_.GetExtPubkey(); + key_type_ = DescriptorKeyType::kDescriptorKeyBip32; + } +} + DescriptorKeyReference::DescriptorKeyReference( const DescriptorKeyReference& object) { key_type_ = object.key_type_; pubkey_ = object.pubkey_; extprivkey_ = object.extprivkey_; extpubkey_ = object.extpubkey_; + key_data_ = object.key_data_; argument_ = object.argument_; } DescriptorKeyReference& DescriptorKeyReference::operator=( const DescriptorKeyReference& object) { - key_type_ = object.key_type_; - pubkey_ = object.pubkey_; - extprivkey_ = object.extprivkey_; - extpubkey_ = object.extpubkey_; - argument_ = object.argument_; + if (this != &object) { + key_type_ = object.key_type_; + pubkey_ = object.pubkey_; + extprivkey_ = object.extprivkey_; + extpubkey_ = object.extpubkey_; + key_data_ = object.key_data_; + argument_ = object.argument_; + } return *this; } @@ -366,6 +387,8 @@ ExtPubkey DescriptorKeyReference::GetExtPubkey() const { "GetExtPubkey unsupported key type."); } +KeyData DescriptorKeyReference::GetKeyData() const { return key_data_; } + DescriptorKeyType DescriptorKeyReference::GetKeyType() const { return key_type_; } @@ -448,15 +471,17 @@ DescriptorScriptReference::DescriptorScriptReference( DescriptorScriptReference& DescriptorScriptReference::operator=( const DescriptorScriptReference& object) { - locking_script_ = object.locking_script_; - script_type_ = object.script_type_; - address_script_ = object.address_script_; - is_script_ = object.is_script_; - redeem_script_ = object.redeem_script_; - child_script_ = object.child_script_; - keys_ = object.keys_; - req_num_ = object.req_num_; - addr_prefixes_ = object.addr_prefixes_; + if (this != &object) { + locking_script_ = object.locking_script_; + script_type_ = object.script_type_; + address_script_ = object.address_script_; + is_script_ = object.is_script_; + redeem_script_ = object.redeem_script_; + child_script_ = object.child_script_; + keys_ = object.keys_; + req_num_ = object.req_num_; + addr_prefixes_ = object.addr_prefixes_; + } return *this; } @@ -466,7 +491,11 @@ Script DescriptorScriptReference::GetLockingScript() const { bool DescriptorScriptReference::HasAddress() const { if (script_type_ == DescriptorScriptType::kDescriptorScriptRaw) { - // TODO(k-matsuzawa) 将来的にはdecoderawtransaction相当には対応させたい + if (locking_script_.IsP2wpkhScript() || locking_script_.IsP2wshScript() || + locking_script_.IsTaprootScript() || locking_script_.IsP2shScript() || + locking_script_.IsP2pkhScript()) { + return true; + } return false; } return true; @@ -477,7 +506,19 @@ Address DescriptorScriptReference::GenerateAddress(NetType net_type) const { bool is_witness = false; switch (script_type_) { case DescriptorScriptType::kDescriptorScriptRaw: - // TODO(k-matsuzawa) 将来的にはdecoderawtransaction相当には対応させたい + if (locking_script_.IsP2wpkhScript() || + locking_script_.IsTaprootScript() || + locking_script_.IsP2wshScript()) { + auto hash = locking_script_.GetElementList()[1].GetBinaryData(); + return Address(net_type, locking_script_.GetWitnessVersion(), hash); + } else if (locking_script_.IsP2shScript()) { + auto hash = locking_script_.GetElementList()[1].GetBinaryData(); + return Address(net_type, AddressType::kP2shAddress, ByteData160(hash)); + } else if (locking_script_.IsP2pkhScript()) { + auto hash = locking_script_.GetElementList()[2].GetBinaryData(); + return Address( + net_type, AddressType::kP2pkhAddress, ByteData160(hash)); + } warn(CFD_LOG_SOURCE, "raw type descriptor is not support."); throw CfdException( CfdError::kCfdIllegalArgumentError, @@ -549,7 +590,17 @@ std::vector
DescriptorScriptReference::GenerateAddresses( AddressType DescriptorScriptReference::GetAddressType() const { switch (script_type_) { case DescriptorScriptType::kDescriptorScriptRaw: - // TODO(k-matsuzawa) 将来的にはdecoderawtransaction相当には対応させたい + if (locking_script_.IsP2wpkhScript()) { + return AddressType::kP2wpkhAddress; + } else if (locking_script_.IsP2wshScript()) { + return AddressType::kP2wshAddress; + } else if (locking_script_.IsTaprootScript()) { + return AddressType::kTaprootAddress; + } else if (locking_script_.IsP2shScript()) { + return AddressType::kP2shAddress; + } else if (locking_script_.IsP2pkhScript()) { + return AddressType::kP2pkhAddress; + } warn( CFD_LOG_SOURCE, "Failed to GenerateAddress. raw type descriptor is not support."); @@ -575,6 +626,9 @@ AddressType DescriptorScriptReference::GetAddressType() const { if (locking_script_.IsP2wshScript()) { return AddressType::kP2wshAddress; } + if (locking_script_.IsTaprootScript()) { + return AddressType::kTaprootAddress; + } if (locking_script_.IsP2pkhScript()) { return AddressType::kP2pkhAddress; } @@ -596,6 +650,9 @@ HashType DescriptorScriptReference::GetHashType() const { if (locking_script_.IsP2wshScript()) { return HashType::kP2wsh; } + if (locking_script_.IsTaprootScript()) { + return HashType::kTaproot; + } if (locking_script_.IsP2pkScript()) { return HashType::kP2pkh; } @@ -684,21 +741,23 @@ DescriptorNode::DescriptorNode(const DescriptorNode& object) { } DescriptorNode& DescriptorNode::operator=(const DescriptorNode& object) { - name_ = object.name_; - value_ = object.value_; - key_info_ = object.key_info_; - is_uncompressed_key_ = object.is_uncompressed_key_; - base_extkey_ = object.base_extkey_; - tweak_sum_ = object.tweak_sum_; - number_ = object.number_; - child_node_ = object.child_node_; - checksum_ = object.checksum_; - depth_ = object.depth_; - need_arg_num_ = object.need_arg_num_; - node_type_ = object.node_type_; - script_type_ = object.script_type_; - key_type_ = object.key_type_; - addr_prefixes_ = object.addr_prefixes_; + if (this != &object) { + name_ = object.name_; + value_ = object.value_; + key_info_ = object.key_info_; + is_uncompressed_key_ = object.is_uncompressed_key_; + base_extkey_ = object.base_extkey_; + tweak_sum_ = object.tweak_sum_; + number_ = object.number_; + child_node_ = object.child_node_; + checksum_ = object.checksum_; + depth_ = object.depth_; + need_arg_num_ = object.need_arg_num_; + node_type_ = object.node_type_; + script_type_ = object.script_type_; + key_type_ = object.key_type_; + addr_prefixes_ = object.addr_prefixes_; + } return *this; } @@ -709,7 +768,7 @@ DescriptorNode DescriptorNode::Parse( node.node_type_ = DescriptorNodeType::kDescriptorTypeScript; node.AnalyzeChild(output_descriptor, 0); node.AnalyzeAll(""); - // Script生成テスト + // Script generate test std::vector list; for (uint32_t index = 0; index < node.GetNeedArgumentNum(); ++index) { list.push_back("0"); @@ -1073,8 +1132,8 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { } if (p_data == nullptr) { if ((parent_name == "wsh") || (parent_name == "sh")) { - size_t max_size = 10000; - if (parent_name == "sh") max_size = 520; + size_t max_size = (parent_name == "sh") ? Script::kMaxRedeemScriptSize + : Script::kMaxScriptSize; std::string miniscript = name_ + "(" + value_ + ")"; std::vector script(max_size); size_t written = 0; @@ -1129,9 +1188,9 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { throw CfdException( CfdError::kCfdIllegalArgumentError, "Failed to multisig node low."); } - if ((child_node_[0].number_ == 0) || (child_node_[0].number_ > 16) || - ((child_node_.size() - 1) < - static_cast(child_node_[0].number_))) { + size_t pubkey_num = child_node_.size() - 1; + if ((child_node_[0].number_ == 0) || + (pubkey_num < static_cast(child_node_[0].number_))) { warn( CFD_LOG_SOURCE, "Failed to multisig require num. num={}", child_node_[0].number_); @@ -1139,7 +1198,9 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { CfdError::kCfdIllegalArgumentError, "Failed to multisig require num."); } - if ((child_node_.size() - 1) > 16) { + size_t max_pubkey_num = + (parent_name == "wsh") ? Script::kMaxMultisigPubkeyNum : 16; + if (pubkey_num > max_pubkey_num) { warn( CFD_LOG_SOURCE, "Failed to multisig pubkey num. num={}", child_node_.size() - 1); @@ -1154,7 +1215,8 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { script_type_ = p_data->type; DescriptorScriptReference ref = GetReference(nullptr); Script script = ref.GetLockingScript(); - if ((script.GetData().GetDataSize() + 3) > 520) { + if ((script.GetData().GetDataSize() + 3) > + Script::kMaxRedeemScriptSize) { warn( CFD_LOG_SOURCE, "Failed to script size over. size={}", script.GetData().GetDataSize()); @@ -1245,14 +1307,16 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { } DescriptorScriptReference DescriptorNode::GetReference( - std::vector* array_argument) const { + std::vector* array_argument, + const DescriptorNode* parent) const { std::vector list; - list = GetReferences(array_argument); + list = GetReferences(array_argument, parent); return list[0]; } std::vector DescriptorNode::GetReferences( - std::vector* array_argument) const { + std::vector* array_argument, + const DescriptorNode* parent) const { if ((depth_ == 0) && (array_argument) && (array_argument->size() > 1)) { std::reverse(array_argument->begin(), array_argument->end()); } @@ -1337,14 +1401,21 @@ std::vector DescriptorNode::GetReferences( // https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki std::sort(pubkeys.begin(), pubkeys.end(), Pubkey::IsLarge); } - locking_script = ScriptUtil::CreateMultisigRedeemScript(reqnum, pubkeys); + bool has_witness = false; + if ((parent != nullptr) && + (parent->GetScriptType() == + DescriptorScriptType::kDescriptorScriptWsh)) { + has_witness = true; + } + locking_script = + ScriptUtil::CreateMultisigRedeemScript(reqnum, pubkeys, has_witness); result.emplace_back( locking_script, script_type_, keys, addr_prefixes_, reqnum); } else if ( (script_type_ == DescriptorScriptType::kDescriptorScriptSh) || (script_type_ == DescriptorScriptType::kDescriptorScriptWsh)) { DescriptorScriptReference ref = - child_node_[0].GetReference(array_argument); + child_node_[0].GetReference(array_argument, this); Script script = ref.GetLockingScript(); if (script_type_ == DescriptorScriptType::kDescriptorScriptWsh) { locking_script = ScriptUtil::CreateP2wshLockingScript(script); @@ -1380,8 +1451,7 @@ std::vector DescriptorNode::GetReferences( locking_script, script_type_, keys, addr_prefixes_); // p2pk - build.AppendData(pubkey); - build.AppendOperator(ScriptOperator::OP_CHECKSIG); + build << pubkey << ScriptOperator::OP_CHECKSIG; locking_script = build.Build(); result.emplace_back( locking_script, script_type_, keys, addr_prefixes_); @@ -1392,8 +1462,7 @@ std::vector DescriptorNode::GetReferences( script_type_ == DescriptorScriptType::kDescriptorScriptWpkh) { locking_script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); } else if (script_type_ == DescriptorScriptType::kDescriptorScriptPk) { - build.AppendData(pubkey); - build.AppendOperator(ScriptOperator::OP_CHECKSIG); + build << pubkey << ScriptOperator::OP_CHECKSIG; locking_script = build.Build(); } result.emplace_back( @@ -1418,15 +1487,23 @@ DescriptorKeyReference DescriptorNode::GetKeyReferences( DescriptorKeyReference result; Pubkey pubkey; std::string using_key = key_info_; + KeyData key_data; if (key_type_ == DescriptorKeyType::kDescriptorKeyPublic) { pubkey = Pubkey(key_info_); result = DescriptorKeyReference(pubkey); + try { + key_data = KeyData(value_); + result = DescriptorKeyReference(key_data); + } catch (const CfdException& except) { + if (value_[0] == '[') throw except; + } } else if ( (key_type_ == DescriptorKeyType::kDescriptorKeyBip32) || (key_type_ == DescriptorKeyType::kDescriptorKeyBip32Priv)) { std::string arg_value; std::string* arg_pointer = nullptr; uint32_t need_arg_num = need_arg_num_; + bool has_base = false; if (need_arg_num == 0) { // 指定キー。強化鍵の場合、xprv/tprvの必要あり。 } else if ((array_argument == nullptr) || array_argument->empty()) { @@ -1440,6 +1517,7 @@ DescriptorKeyReference DescriptorNode::GetKeyReferences( // baseを取得する using_key = base_extkey_; need_arg_num = 0; + has_base = true; } else { // 動的キー生成。強化鍵の場合、xprv/tprvの必要あり。 // array_argumentがnullptrの場合、仮で0を設定する。(生成テスト用) @@ -1476,6 +1554,17 @@ DescriptorKeyReference DescriptorNode::GetKeyReferences( "Failed to generate pubkey from hdkey."); } pubkey = xpub.GetPubkey(); + + try { + if (((need_arg_num == 0) && (!has_base)) || + ((!arg_value.empty()) && + (arg_value.find('/') == std::string::npos))) { + key_data = KeyData(value_, static_cast(xpub.GetChildNum())); + result = DescriptorKeyReference(key_data, arg_pointer); + } + } catch (const CfdException& except) { + if (value_[0] == '[') throw except; + } } if (!pubkey.IsValid()) { @@ -1536,7 +1625,9 @@ Descriptor::Descriptor(const Descriptor& object) { } Descriptor& Descriptor::operator=(const Descriptor& object) { - root_node_ = object.root_node_; + if (this != &object) { + root_node_ = object.root_node_; + } return *this; } @@ -1726,6 +1817,62 @@ std::vector Descriptor::GetReferenceAll( return root_node_.GetReferences(©_list); } +KeyData Descriptor::GetKeyData() const { + if (GetNeedArgumentNum() != 0) { + warn(CFD_LOG_SOURCE, "Failed to empty argument. {}", GetNeedArgumentNum()); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to empty argument. need argument descriptor."); + } + std::vector list; + auto key_list = GetKeyDataAll(&list); + if (key_list.empty()) return KeyData(); + return key_list[0]; +} + +KeyData Descriptor::GetKeyData(const std::string& argument) const { + std::vector list; + for (uint32_t index = 0; index < GetNeedArgumentNum(); ++index) { + list.push_back(argument); + } + return GetKeyData(list); +} + +KeyData Descriptor::GetKeyData( + const std::vector& array_argument) const { + std::vector copy_list = array_argument; + auto key_list = GetKeyDataAll(©_list); + if (key_list.empty()) return KeyData(); + return key_list[0]; +} + +std::vector Descriptor::GetKeyDataAll( + const std::vector* array_argument) const { + std::vector ref_list = + GetReferenceAll(array_argument); + std::vector result; + + for (const auto& ref : ref_list) { + auto script_data = ref; + do { + if (script_data.HasKey()) { + auto key_list = script_data.GetKeyList(); + for (const auto& key : key_list) { + auto key_data = key.GetKeyData(); + if (key_data.IsValid()) { + result.push_back(key_data); + } + } + } + if (!script_data.HasChild()) { + break; + } + script_data = script_data.GetChild(); + } while (true); + } + return result; +} + std::string Descriptor::ToString(bool append_checksum) const { return root_node_.ToString(append_checksum); } diff --git a/src/cfdcore_elements_address.cpp b/src/cfdcore_elements_address.cpp index 95196048..24f23e04 100644 --- a/src/cfdcore_elements_address.cpp +++ b/src/cfdcore_elements_address.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_elements_address.cpp * - * @brief \~japanese Elements対応したAddressクラス定義 - * \~english definition of address class that handles Elements + * @brief definition of address class that handles Elements */ #ifndef CFD_DISABLE_ELEMENTS @@ -31,8 +30,7 @@ using logger::warn; // global / internal // ----------------------------------------------------------------------------- /** - * @brief \~japanese blind addressのキーペア一覧を定義するための構造体. - * \~english Structure to define a list of key pairs for blind address. + * @brief Structure to define a list of key pairs for blind address. */ struct ElementsBlindAddressFormat { std::string prefix_key; //!< prefix key @@ -104,6 +102,23 @@ ElementsConfidentialAddress::ElementsConfidentialAddress( DecodeAddress(confidential_address, prefix_list); } +ElementsConfidentialAddress::ElementsConfidentialAddress( + const ElementsConfidentialAddress& object) { + unblinded_address_ = object.unblinded_address_; + confidential_key_ = object.confidential_key_; + address_ = object.address_; +} + +ElementsConfidentialAddress& ElementsConfidentialAddress::operator=( + const ElementsConfidentialAddress& object) & { + if (this != &object) { + unblinded_address_ = object.unblinded_address_; + confidential_key_ = object.confidential_key_; + address_ = object.address_; + } + return *this; +} + void ElementsConfidentialAddress::DecodeAddress( const std::string& confidential_address, const std::vector& prefix_list) { diff --git a/src/cfdcore_elements_script.cpp b/src/cfdcore_elements_script.cpp index 2aac983c..dc9d4e89 100644 --- a/src/cfdcore_elements_script.cpp +++ b/src/cfdcore_elements_script.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_elements_script.cpp * - * @brief \~japanese Elements対応したScriptクラス定義 - * \~english definition of script class that supports Elements + * @brief definition of script class that supports Elements */ #ifndef CFD_DISABLE_ELEMENTS diff --git a/src/cfdcore_elements_transaction.cpp b/src/cfdcore_elements_transaction.cpp index 486d5784..00a088b8 100644 --- a/src/cfdcore_elements_transaction.cpp +++ b/src/cfdcore_elements_transaction.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_elements_transaction.cpp * - * @brief \~japanese Confidential Transaction関連クラスの実装ファイルです。 - * \~english implementation of Confidential Transaction classes + * @brief implementation of Confidential Transaction classes */ #ifndef CFD_DISABLE_ELEMENTS @@ -11,6 +10,7 @@ #include #include +#include #include #include @@ -289,6 +289,20 @@ ConfidentialNonce::ConfidentialNonce(const Pubkey &pubkey) // do nothing } +ConfidentialNonce::ConfidentialNonce(const ConfidentialNonce &object) { + data_ = object.data_; + version_ = object.version_; +} + +ConfidentialNonce &ConfidentialNonce::operator=( + const ConfidentialNonce &object) { + if (this != &object) { + data_ = object.data_; + version_ = object.version_; + } + return *this; +} + void ConfidentialNonce::CheckVersion(uint8_t version) { if ((version != 0) && (version != 1) && (version != 2) && (version != 3)) { warn(CFD_LOG_SOURCE, "Nonce version Invalid. version={}.", version); @@ -386,6 +400,20 @@ ConfidentialAssetId::ConfidentialAssetId(const ByteData &byte_data) CheckVersion(version_); } +ConfidentialAssetId::ConfidentialAssetId(const ConfidentialAssetId &object) { + data_ = object.data_; + version_ = object.version_; +} + +ConfidentialAssetId &ConfidentialAssetId::operator=( + const ConfidentialAssetId &object) { + if (this != &object) { + data_ = object.data_; + version_ = object.version_; + } + return *this; +} + void ConfidentialAssetId::CheckVersion(uint8_t version) { if ((version != 0) && (version != 1) && (version != 0x0a) && (version != 0x0b)) { @@ -524,6 +552,20 @@ ConfidentialValue::ConfidentialValue(const ByteData &byte_data) CheckVersion(version_); } +ConfidentialValue::ConfidentialValue(const ConfidentialValue &object) { + data_ = object.data_; + version_ = object.version_; +} + +ConfidentialValue &ConfidentialValue::operator=( + const ConfidentialValue &object) { + if (this != &object) { + data_ = object.data_; + version_ = object.version_; + } + return *this; +} + ConfidentialValue::ConfidentialValue(const Amount &amount) : ConfidentialValue(ConvertToConfidentialValue(amount)) { // do nothing @@ -637,6 +679,15 @@ BlindFactor::BlindFactor(const ByteData256 &byte_data) : data_(byte_data) { // do nothing } +BlindFactor::BlindFactor(const BlindFactor &object) { data_ = object.data_; } + +BlindFactor &BlindFactor::operator=(const BlindFactor &object) { + if (this != &object) { + data_ = object.data_; + } + return *this; +} + ByteData256 BlindFactor::GetData() const { return data_; } std::string BlindFactor::GetHex() const { @@ -1120,7 +1171,7 @@ uint32_t ConfidentialTxOutReference::GetSerializeSize( if ((rangeproof_size != nullptr) && (*rangeproof_size != 0)) { work_proof_size = *rangeproof_size; } else if (confidential_value_.HasBlinding()) { - work_proof_size = 4 + range_proof_.GetDataSize(); + work_proof_size = 4 + static_cast(range_proof_.GetDataSize()); } else { int64_t amount = confidential_value_.GetAmount().GetSatoshiValue(); if (amount == 0) amount = kMaxAmount; @@ -1322,10 +1373,39 @@ void ConfidentialTransaction::SetFromHex(const std::string &hex_string) { ConfidentialTransaction &ConfidentialTransaction::operator=( const ConfidentialTransaction &transaction) & { - SetFromHex(transaction.GetHex()); + if (this != &transaction) { + SetFromHex(transaction.GetHex()); + } return *this; } +uint32_t ConfidentialTransaction::GetTotalSize() const { + static constexpr uint32_t kMinimumConfidentialTxSize = 11; + uint32_t length = AbstractTransaction::GetTotalSize(); + if (length < kMinimumConfidentialTxSize) { + length = kMinimumConfidentialTxSize; + } + return length; +} + +uint32_t ConfidentialTransaction::GetVsize() const { + static constexpr uint32_t kMinimumConfidentialTxSize = 11; + uint32_t length = AbstractTransaction::GetVsize(); + if (length < kMinimumConfidentialTxSize) { + length = kMinimumConfidentialTxSize; + } + return length; +} + +uint32_t ConfidentialTransaction::GetWeight() const { + static constexpr uint32_t kMinimumConfidentialTxWeight = 44; + uint32_t weight = AbstractTransaction::GetWeight(); + if (weight < kMinimumConfidentialTxWeight) { + weight = kMinimumConfidentialTxWeight; + } + return weight; +} + const ConfidentialTxInReference ConfidentialTransaction::GetTxIn( uint32_t index) const { CheckTxInIndex(index, __LINE__, __FUNCTION__); @@ -2220,6 +2300,8 @@ void ConfidentialTransaction::BlindTransaction( std::vector vbfs; // serialize std::vector input_abfs; // serialize std::vector empty_factor(kBlindFactorSize); + std::map, int64_t> amount_map; + bool has_amount_check = true; uint32_t blinded_txin_count = 0; size_t blind_target_count = 0; std::vector blind_issuance_indexes; @@ -2227,6 +2309,20 @@ void ConfidentialTransaction::BlindTransaction( int ret; memset(empty_factor.data(), 0, empty_factor.size()); + auto add_map_func = + [](const std::vector &asset_id, const Amount &amount, + bool is_output, + std::map, int64_t> *amount_map) -> void { + if (amount == 0) return; + int64_t value = amount.GetSatoshiValue() * ((is_output) ? -1 : 1); + if (amount_map->find(asset_id) == amount_map->end()) { + amount_map->emplace(asset_id, value); + } else { + auto &map_value = amount_map->at(asset_id); + map_value += value; + } + }; + if (vin_.size() > txin_info_list.size()) { warn( CFD_LOG_SOURCE, "txin_info_list few count. [{},{}].", vin_.size(), @@ -2269,6 +2365,8 @@ void ConfidentialTransaction::BlindTransaction( amount.GetSatoshiValue()); throw CfdException(kCfdIllegalStateError, "satoshi under zero."); } + add_map_func(asset_id, amount, false, &amount_map); + if ((abf != empty_factor) || (vbf != empty_factor)) { ++blinded_txin_count; input_values.push_back(amount.GetSatoshiValue()); @@ -2328,6 +2426,9 @@ void ConfidentialTransaction::BlindTransaction( info( CFD_LOG_SOURCE, "generator_data asset=[{}]", generator_data.GetHex()); + add_map_func( + asset_bytes, vin_[index].GetIssuanceAmount().GetAmount(), false, + &amount_map); } if ((!is_reissue) && (!vin_[index].GetInflationKeys().IsEmpty())) { const std::vector &token_bytes = @@ -2357,6 +2458,9 @@ void ConfidentialTransaction::BlindTransaction( info( CFD_LOG_SOURCE, "generator_data token=[{}]", generator_data.GetHex()); + add_map_func( + token_bytes, vin_[index].GetInflationKeys().GetAmount(), false, + &amount_map); } // Marked for blinding if (asset_blind) { @@ -2508,6 +2612,15 @@ void ConfidentialTransaction::BlindTransaction( input_confidential_keys[index] = txout_confidential_keys[index].Compress(); } + + const auto &temp_value = vout_[index].GetConfidentialValue(); + const auto &temp_asset = vout_[index].GetAsset(); + if (temp_value.HasBlinding() || temp_asset.HasBlinding()) { + has_amount_check = false; + } else { + auto asset_bytes = temp_asset.GetUnblindedData().GetBytes(); + add_map_func(asset_bytes, temp_value.GetAmount(), true, &amount_map); + } } blind_target_count += blind_txout_indexes.size(); if ((blinded_txin_count == 0) && (blind_target_count <= 1)) { @@ -2528,6 +2641,19 @@ void ConfidentialTransaction::BlindTransaction( throw CfdException(kCfdIllegalArgumentError, "txout blind target empty."); } + if (has_amount_check) { + for (auto &item : amount_map) { + if (item.second != 0) { + ConfidentialAssetId temp_asset(ByteData(item.first)); + warn( + CFD_LOG_SOURCE, "unmatch input/output amount. ({},{})", + temp_asset.GetHex(), item.second); + throw CfdException( + kCfdIllegalArgumentError, "unmatch input/output amount."); + } + } + } + std::vector output_abfs(blind_txout_indexes.size()); std::vector output_vbfs(blind_txout_indexes.size() - 1); diff --git a/src/cfdcore_hdwallet.cpp b/src/cfdcore_hdwallet.cpp index 01ff3d41..672fc9e8 100644 --- a/src/cfdcore_hdwallet.cpp +++ b/src/cfdcore_hdwallet.cpp @@ -1,14 +1,15 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfdcore_hdwallet.cpp * - * @brief \~japanese BIP32/BIP39/BIP44関連クラスの実装 - * \~english implementation of BIP32/BIP39/BIP44 classes + * @brief implementation of BIP32/BIP39/BIP44 classes */ #include "cfdcore/cfdcore_hdwallet.h" +#include #include +#include #include #include @@ -24,7 +25,7 @@ namespace core { using logger::warn; // ---------------------------------------------------------------------------- -// ファイル内定義 +// Definitions in the file // ---------------------------------------------------------------------------- /// empty seed string (64byte) static constexpr const char* kEmptySeedStr = @@ -32,7 +33,7 @@ static constexpr const char* kEmptySeedStr = "000000000000000000000000000000000000000000000000000000000"; // NOLINT /** - * @brief Bip32鍵情報の解析を行う。 + * @brief Bip32 Analyze key information. * @param[in] extkey extkey * @param[in] base58 base58 data * @param[in,out] serialize_data serialize data @@ -124,7 +125,7 @@ static void AnalyzeBip32KeyData( } /** - * @brief Base58変換を行う。 + * @brief Perform Base58 conversion. * @param[in] serialize_data serialize data * @param[in] caller_name caller class name * @return base58 string @@ -155,7 +156,7 @@ static std::string ToBase58String( } /** - * @brief 文字列パスから配列を取得する。 + * @brief Get an array from a string path. * @param[in] string_path child number string path * @param[in] caller_name caller class name * @param[in] depth current key depth @@ -201,7 +202,7 @@ static std::vector ToArrayFromString( } } - // strtol関数による変換 + // Conversion by strtol function char* p_str_end = nullptr; uint32_t value; if ((str.size() > 2) && (str[0] == '0') && (str[1] == 'x')) { @@ -282,6 +283,22 @@ ExtPrivkey HDWallet::GeneratePrivkey( return privkey.DerivePrivkey(string_path); } +KeyData HDWallet::GeneratePrivkeyData( + NetType network_type, const std::vector& path) const { + ExtPrivkey privkey(seed_, network_type); + ExtPrivkey key = privkey.DerivePrivkey(path); + auto fingerprint = privkey.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key, path, fingerprint); +} + +KeyData HDWallet::GeneratePrivkeyData( + NetType network_type, const std::string& string_path) const { + ExtPrivkey privkey(seed_, network_type); + ExtPrivkey key = privkey.DerivePrivkey(string_path); + auto fingerprint = privkey.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key, string_path, fingerprint); +} + ExtPubkey HDWallet::GeneratePubkey(NetType network_type) const { ExtPrivkey privkey(seed_, network_type); return privkey.GetExtPubkey(); @@ -305,6 +322,22 @@ ExtPubkey HDWallet::GeneratePubkey( return privkey.DerivePubkey(string_path); } +KeyData HDWallet::GeneratePubkeyData( + NetType network_type, const std::vector& path) const { + ExtPrivkey privkey(seed_, network_type); + ExtPrivkey key = privkey.DerivePrivkey(path); + auto fingerprint = privkey.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key.GetExtPubkey(), path, fingerprint); +} + +KeyData HDWallet::GeneratePubkeyData( + NetType network_type, const std::string& string_path) const { + ExtPrivkey privkey(seed_, network_type); + ExtPrivkey key = privkey.DerivePrivkey(string_path); + auto fingerprint = privkey.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key.GetExtPubkey(), string_path, fingerprint); +} + std::vector HDWallet::GetMnemonicWordlist( const std::string& language) { if (!CheckSupportedLanguages(language)) { @@ -636,6 +669,19 @@ ExtPrivkey ExtPrivkey::DerivePrivkey(const std::string& string_path) const { return DerivePrivkey(path); } +KeyData ExtPrivkey::DerivePrivkeyData( + const std::vector& path) const { + ExtPrivkey key = DerivePrivkey(path); + auto fingerprint = privkey_.GeneratePubkey().GetFingerprint(); + return KeyData(key, path, fingerprint); +} + +KeyData ExtPrivkey::DerivePrivkeyData(const std::string& string_path) const { + ExtPrivkey key = DerivePrivkey(string_path); + auto fingerprint = privkey_.GeneratePubkey().GetFingerprint(); + return KeyData(key, string_path, fingerprint); +} + ExtPubkey ExtPrivkey::GetExtPubkey() const { struct ext_key extkey; @@ -681,6 +727,18 @@ ExtPubkey ExtPrivkey::DerivePubkey(const std::string& string_path) const { return privkey.GetExtPubkey(); } +KeyData ExtPrivkey::DerivePubkeyData(const std::vector& path) const { + ExtPubkey key = DerivePubkey(path); + auto fingerprint = privkey_.GeneratePubkey().GetFingerprint(); + return KeyData(key, path, fingerprint); +} + +KeyData ExtPrivkey::DerivePubkeyData(const std::string& string_path) const { + ExtPubkey key = DerivePubkey(string_path); + auto fingerprint = privkey_.GeneratePubkey().GetFingerprint(); + return KeyData(key, string_path, fingerprint); +} + bool ExtPrivkey::IsValid() const { return privkey_.IsValid(); } ByteData256 ExtPrivkey::GetChainCode() const { return chaincode_; } @@ -967,6 +1025,18 @@ ExtPubkey ExtPubkey::DerivePubkey(const std::string& string_path) const { return DerivePubkey(path); } +KeyData ExtPubkey::DerivePubkeyData(const std::vector& path) const { + ExtPubkey key = DerivePubkey(path); + auto fingerprint = pubkey_.GetFingerprint(); + return KeyData(key, path, fingerprint); +} + +KeyData ExtPubkey::DerivePubkeyData(const std::string& string_path) const { + ExtPubkey key = DerivePubkey(string_path); + auto fingerprint = pubkey_.GetFingerprint(); + return KeyData(key, string_path, fingerprint); +} + ByteData256 ExtPubkey::DerivePubTweak( const std::vector& path) const { ExtPubkey key = DerivePubkey(path); @@ -1012,5 +1082,338 @@ NetType ExtPubkey::GetNetworkType() const { return NetType::kTestnet; } +// ---------------------------------------------------------------------------- +// KeyData +// ---------------------------------------------------------------------------- + +KeyData::KeyData() { + // do nothing +} + +KeyData::KeyData( + const ExtPrivkey& ext_privkey, const std::string& child_path, + const ByteData& finterprint) + : extprivkey_(ext_privkey), fingerprint_(finterprint) { + if (!child_path.empty()) { + path_ = ToArrayFromString(child_path, "KeyData", 0); + } + extpubkey_ = ext_privkey.GetExtPubkey(); + privkey_ = ext_privkey.GetPrivkey(); + pubkey_ = privkey_.GetPubkey(); +} + +KeyData::KeyData( + const ExtPubkey& ext_pubkey, const std::string& child_path, + const ByteData& finterprint) + : extpubkey_(ext_pubkey), fingerprint_(finterprint) { + if (!child_path.empty()) { + path_ = ToArrayFromString(child_path, "KeyData", 0); + } + pubkey_ = ext_pubkey.GetPubkey(); +} + +KeyData::KeyData( + const Privkey& privkey, const std::string& child_path, + const ByteData& finterprint) + : privkey_(privkey), fingerprint_(finterprint) { + if (!child_path.empty()) { + path_ = ToArrayFromString(child_path, "KeyData", 0); + } + pubkey_ = privkey.GetPubkey(); +} + +KeyData::KeyData( + const Pubkey& pubkey, const std::string& child_path, + const ByteData& finterprint) + : pubkey_(pubkey), fingerprint_(finterprint) { + if (!child_path.empty()) { + path_ = ToArrayFromString(child_path, "KeyData", 0); + } +} + +KeyData::KeyData( + const ExtPrivkey& ext_privkey, const std::vector& child_num_list, + const ByteData& finterprint) + : extprivkey_(ext_privkey), + path_(child_num_list), + fingerprint_(finterprint) { + extpubkey_ = ext_privkey.GetExtPubkey(); + privkey_ = ext_privkey.GetPrivkey(); + pubkey_ = privkey_.GetPubkey(); +} + +KeyData::KeyData( + const ExtPubkey& ext_pubkey, const std::vector& child_num_list, + const ByteData& finterprint) + : extpubkey_(ext_pubkey), + path_(child_num_list), + fingerprint_(finterprint) { + pubkey_ = ext_pubkey.GetPubkey(); +} + +KeyData::KeyData( + const Privkey& privkey, const std::vector& child_num_list, + const ByteData& finterprint) + : privkey_(privkey), path_(child_num_list), fingerprint_(finterprint) { + pubkey_ = privkey.GetPubkey(); +} + +KeyData::KeyData( + const Pubkey& pubkey, const std::vector& child_num_list, + const ByteData& finterprint) + : pubkey_(pubkey), path_(child_num_list), fingerprint_(finterprint) { + // do nothing +} + +KeyData::KeyData(const std::string& path_info, int32_t child_num) { + auto key_info = path_info; + if (path_info[0] == '[') { + // key origin information check. cut to ']' + auto pos = path_info.find("]"); + if (pos != std::string::npos) { + key_info = path_info.substr(pos + 1); + auto path = path_info.substr(1, pos - 1); + pos = path.find("/"); + if (pos != std::string::npos) { + if (pos != 0) { + auto fingerprint = path.substr(0, pos); + fingerprint_ = ByteData(fingerprint); + } + auto child_path = path.substr(pos + 1); + path_ = ToArrayFromString(child_path, "KeyData", 0); + } + } + } + // derive key check (xpub,etc)D + std::string hdkey_top; + if (key_info.size() > 4) hdkey_top = key_info.substr(1, 3); + if ((hdkey_top == "pub") || (hdkey_top == "prv")) { + std::string path; + std::string key; + bool has_end_any_hardened = false; + bool exist_hardened = false; + std::vector list = StringUtil::Split(key_info, "/"); + key = list[0]; + if (list.size() > 1) { + size_t index; + for (index = 1; index < list.size(); ++index) { + if (index != 1) path += "/"; + if (list[index] == "*") break; + if ((list[index] == "*'") || (list[index] == "*h") || + (list[index] == "*H")) { + has_end_any_hardened = true; + exist_hardened = true; + break; + } + path += list[index]; + if ((list[index].find("'") != std::string::npos) || + (list[index].find("h") != std::string::npos) || + (list[index].find("H") != std::string::npos)) { + exist_hardened = true; + } else { + auto value = strtoul(list[index].c_str(), nullptr, 0); + if (value >= 0x80000000) exist_hardened = true; + } + } + if ((index + 1) < list.size()) { + warn( + CFD_LOG_SOURCE, + "Failed to extkey path. " + "A '*' can only be specified at the end."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to extkey path. " + "A '*' can only be specified at the end."); + } + if (list.back().find("*") != std::string::npos) { + if (child_num < 0) { + warn( + CFD_LOG_SOURCE, + "Failed to extkey path. " + "A '*' can not support."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to extkey path. " + "A '*' can not support."); + } + path += std::to_string(child_num); + if (has_end_any_hardened) path += "h"; + } + } + std::string base_extkey_; + if (hdkey_top == "prv") { + extprivkey_ = ExtPrivkey(key); + if (!path.empty()) extprivkey_ = extprivkey_.DerivePrivkey(path); + extpubkey_ = extprivkey_.GetExtPubkey(); + privkey_ = extprivkey_.GetPrivkey(); + pubkey_ = privkey_.GetPubkey(); + } else if (exist_hardened) { + warn( + CFD_LOG_SOURCE, "Failed to extPubkey. hardened is extPrivkey only."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to extPubkey. hardened is extPrivkey only."); + } else { + extpubkey_ = ExtPubkey(key); + if (!path.empty()) extpubkey_ = extpubkey_.DerivePubkey(path); + pubkey_ = extpubkey_.GetPubkey(); + } + if (!path.empty()) { + auto path_list = ToArrayFromString(path, "KeyData", 0); + path_.reserve(path_.size() + path_list.size()); + std::copy(path_list.begin(), path_list.end(), std::back_inserter(path_)); + } + } else if (Privkey::HasWif(key_info)) { + privkey_ = Privkey::FromWif(key_info); + pubkey_ = privkey_.GetPubkey(); + } else { + ByteData bytes(key_info); + if (Pubkey::IsValid(bytes)) { + pubkey_ = Pubkey(bytes); + } else { + privkey_ = Privkey(bytes); + pubkey_ = privkey_.GetPubkey(); + } + } +} + +bool KeyData::IsValid() const { return pubkey_.IsValid(); } + +bool KeyData::HasExtPrivkey() const { return extprivkey_.IsValid(); } + +bool KeyData::HasExtPubkey() const { return extpubkey_.IsValid(); } + +bool KeyData::HasPrivkey() const { return privkey_.IsValid(); } + +Pubkey KeyData::GetPubkey() const { return pubkey_; } + +Privkey KeyData::GetPrivkey() const { return privkey_; } + +ExtPrivkey KeyData::GetExtPrivkey() const { return extprivkey_; } + +ExtPubkey KeyData::GetExtPubkey() const { return extpubkey_; } + +ByteData KeyData::GetFingerprint() const { return fingerprint_; } + +std::vector KeyData::GetChildNumArray() const { return path_; } + +KeyData KeyData::DerivePrivkey( + std::vector path, bool has_rebase_path) const { + if (!extprivkey_.IsValid()) { + warn(CFD_LOG_SOURCE, "Failed to invalid extPrivkey."); + throw CfdException( + CfdError::kCfdIllegalStateError, "Failed to invalid extPrivkey."); + } + ExtPrivkey key = extprivkey_.DerivePrivkey(path); + if (has_rebase_path) { + auto fp = extprivkey_.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key, path, fp); + } else { + auto join_path = path_; + join_path.reserve(join_path.size() + path.size()); + std::copy(path.begin(), path.end(), std::back_inserter(join_path)); + return KeyData(key, join_path, fingerprint_); + } +} + +KeyData KeyData::DerivePrivkey(std::string path, bool has_rebase_path) const { + auto arr = ToArrayFromString(path, "KeyData::DerivePrivkey", 0); + return DerivePrivkey(arr, has_rebase_path); +} + +KeyData KeyData::DerivePubkey( + std::vector path, bool has_rebase_path) const { + if (extprivkey_.IsValid()) { + auto key = extprivkey_.DerivePubkey(path); + if (has_rebase_path) { + auto fp = extprivkey_.GetPrivkey().GeneratePubkey().GetFingerprint(); + return KeyData(key, path, fp); + } else { + auto join_path = path_; + join_path.reserve(join_path.size() + path.size()); + std::copy(path.begin(), path.end(), std::back_inserter(join_path)); + return KeyData(key, join_path, fingerprint_); + } + } else if (extpubkey_.IsValid()) { + auto key = extpubkey_.DerivePubkey(path); + if (has_rebase_path) { + auto fp = extpubkey_.GetPubkey().GetFingerprint(); + return KeyData(key, path, fp); + } else { + auto join_path = path_; + join_path.reserve(join_path.size() + path.size()); + std::copy(path.begin(), path.end(), std::back_inserter(join_path)); + return KeyData(key, join_path, fingerprint_); + } + } else { + warn(CFD_LOG_SOURCE, "Failed to invalid extPubkey."); + throw CfdException( + CfdError::kCfdIllegalStateError, "Failed to invalid extPubkey."); + } +} + +KeyData KeyData::DerivePubkey(std::string path, bool has_rebase_path) const { + auto arr = ToArrayFromString(path, "KeyData::DerivePubkey", 0); + return DerivePubkey(arr, has_rebase_path); +} + +std::string KeyData::GetBip32Path( + HardenedType hardened_type, bool has_hex) const { + std::stringstream ss; + bool is_first = true; + for (auto child_num : path_) { + if (!is_first) ss << "/"; + uint32_t num = (hardened_type == HardenedType::kNumber) + ? child_num + : (child_num & 0x7FFFFFFF); + if (has_hex) { + ss << "0x" << std::hex << num; + } else { + ss << num; + } + + if (child_num & 0x80000000) { + switch (hardened_type) { + case kLargeH: + ss << "H"; + break; + case kSmallH: + ss << "h"; + break; + case kNumber: + // do nothing + break; + case kApostrophe: + default: + ss << "'"; + break; + } + } + is_first = false; + } + return ss.str(); +} + +std::string KeyData::ToString( + bool has_pubkey, HardenedType hardened_type, bool has_hex) const { + auto path_str = GetBip32Path(hardened_type, has_hex); + std::stringstream ss; + if ((!path_str.empty()) && (!fingerprint_.IsEmpty())) { + ss << "[" << fingerprint_.GetHex() << "/" << path_str << "]"; + } + if (has_pubkey) { + ss << pubkey_.GetHex(); + } else if (extprivkey_.IsValid()) { + ss << extprivkey_.ToString(); + } else if (extpubkey_.IsValid()) { + ss << extpubkey_.ToString(); + } else if (privkey_.IsValid()) { + ss << privkey_.GetWif(); + } else { + ss << pubkey_.GetHex(); + } + return ss.str(); +} + } // namespace core } // namespace cfd diff --git a/src/cfdcore_key.cpp b/src/cfdcore_key.cpp index 1ea3871d..2334c744 100644 --- a/src/cfdcore_key.cpp +++ b/src/cfdcore_key.cpp @@ -1,9 +1,8 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfdcore_key.cpp * - * @brief \~japanese Pubkey/Privkey関連クラス定義 - * \~english definition for Pubkey/Privkey class + * @brief definition for Pubkey/Privkey class */ #include "cfdcore/cfdcore_key.h" @@ -77,6 +76,24 @@ bool Pubkey::Equals(const Pubkey &pubkey) const { return data_.Equals(pubkey.data_); } +ByteData Pubkey::GetFingerprint(uint32_t get_size) const { + if ((get_size == 0) || (get_size > kByteData160Length)) { + warn(CFD_LOG_SOURCE, "Invalid fingerprint size: {}.", get_size); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid fingerprint size."); + } + + ByteData data; + if (IsCompress()) { + data = data_; + } else { + data = Compress().data_; + } + auto data160 = HashUtil::Hash160(data); + auto bytes = data160.GetBytes(); + return ByteData(bytes.data(), get_size); +} + Pubkey Pubkey::CombinePubkey(const std::vector &pubkeys) { std::vector data_list; for (const auto &pubkey : pubkeys) { @@ -203,7 +220,9 @@ Privkey::Privkey() : data_() { // do nothing } -Privkey::Privkey(const ByteData &byte_data) : data_(byte_data) { +Privkey::Privkey( + const ByteData &byte_data, NetType net_type, bool is_compressed) + : data_(byte_data), is_compressed_(is_compressed), net_type_(net_type) { if (!IsValid(data_.GetBytes())) { warn(CFD_LOG_SOURCE, "Invalid Privkey data. hex={}.", data_.GetHex()); throw CfdException( @@ -211,8 +230,11 @@ Privkey::Privkey(const ByteData &byte_data) : data_(byte_data) { } } -Privkey::Privkey(const ByteData256 &byte_data) - : data_(ByteData(byte_data.GetBytes())) { +Privkey::Privkey( + const ByteData256 &byte_data, NetType net_type, bool is_compressed) + : data_(ByteData(byte_data.GetBytes())), + is_compressed_(is_compressed), + net_type_(net_type) { if (!IsValid(data_.GetBytes())) { warn(CFD_LOG_SOURCE, "Invalid Privkey data. hex={}.", data_.GetHex()); throw CfdException( @@ -220,7 +242,11 @@ Privkey::Privkey(const ByteData256 &byte_data) } } -Privkey::Privkey(const std::string &hex_str) : data_(ByteData(hex_str)) { +Privkey::Privkey( + const std::string &hex_str, NetType net_type, bool is_compressed) + : data_(ByteData(hex_str)), + is_compressed_(is_compressed), + net_type_(net_type) { if (!IsValid(data_.GetBytes())) { warn(CFD_LOG_SOURCE, "Invalid Privkey data. hex={}.", data_.GetHex()); throw CfdException( @@ -252,21 +278,61 @@ std::string Privkey::ConvertWif(NetType net_type, bool is_compressed) const { return wif; } +std::string Privkey::GetWif() const { + return ConvertWif(net_type_, is_compressed_); +} + Privkey Privkey::FromWif( const std::string &wif, NetType net_type, bool is_compressed) { std::vector privkey(kPrivkeySize); - uint32_t prefix = (net_type == kMainnet ? kPrefixMainnet : kPrefixTestnet); - uint32_t flags = - (is_compressed ? WALLY_WIF_FLAG_COMPRESSED - : WALLY_WIF_FLAG_UNCOMPRESSED); + NetType temp_net_type = net_type; + bool is_temp_compressed = is_compressed; + if (net_type == NetType::kCustomChain) { + // auto analyze + size_t written = 0; + size_t uncompressed = 0; + std::vector buf(2 + EC_PRIVATE_KEY_LEN + BASE58_CHECKSUM_LEN); + int ret = wally_base58_to_bytes( + wif.data(), BASE58_FLAG_CHECKSUM, buf.data(), buf.size(), &written); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_base58_to_bytes error. ret={} wif={}.", ret, + wif); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Error decode base58 WIF."); + } + ret = wally_wif_is_uncompressed(wif.data(), &uncompressed); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_wif_is_uncompressed error. ret={} wif={}.", + ret, wif); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Error WIF is uncompressed."); + } - int ret = wally_wif_to_bytes( - wif.data(), prefix, flags, privkey.data(), kPrivkeySize); - if (ret != WALLY_OK) { - warn(CFD_LOG_SOURCE, "wally_wif_to_bytes error. ret={} wif={}.", ret, wif); - throw CfdException( - CfdError::kCfdIllegalArgumentError, "Error WIF to Private key."); + uint32_t prefix = buf[0]; + memcpy(privkey.data(), &buf[1], privkey.size()); + temp_net_type = (prefix == kPrefixMainnet) ? kMainnet : kTestnet; + is_temp_compressed = (uncompressed == 0) ? true : false; + } else { + uint32_t prefix = (net_type == kMainnet ? kPrefixMainnet : kPrefixTestnet); + uint32_t flags = + (is_compressed ? WALLY_WIF_FLAG_COMPRESSED + : WALLY_WIF_FLAG_UNCOMPRESSED); + + int ret = wally_wif_to_bytes( + wif.data(), prefix, flags, privkey.data(), kPrivkeySize); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_wif_to_bytes error. ret={} wif={}.", ret, + wif); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Error WIF to Private key."); + } + temp_net_type = net_type; + is_temp_compressed = is_compressed; } + if (!IsValid(privkey)) { warn( CFD_LOG_SOURCE, "Invalid Privkey data. data={}", @@ -275,7 +341,8 @@ Privkey Privkey::FromWif( CfdError::kCfdIllegalArgumentError, "Invalid Privkey data"); } Privkey key = Privkey(ByteData(privkey)); - key.SetPubkeyCompressed(is_compressed); + key.SetPubkeyCompressed(is_temp_compressed); + key.SetNetType(temp_net_type); return key; } @@ -400,7 +467,6 @@ bool Privkey::IsValid(const std::vector &buffer) { if (buffer.size() > 0) { int ret = wally_ec_private_key_verify(buffer.data(), buffer.size()); return ret == WALLY_OK; - // return buffer.size() == kPrivkeySize; } return false; } @@ -415,6 +481,8 @@ void Privkey::SetPubkeyCompressed(bool is_compressed) { is_compressed_ = is_compressed; } +void Privkey::SetNetType(NetType net_type) { net_type_ = net_type; } + Privkey Privkey::operator+=(const Privkey &right) { Privkey key = CreateTweakAdd(right); *this = key; diff --git a/src/cfdcore_logger.cpp b/src/cfdcore_logger.cpp index e061ac60..2eb5dbc1 100644 --- a/src/cfdcore_logger.cpp +++ b/src/cfdcore_logger.cpp @@ -1,8 +1,7 @@ // Copyright 2019 CryptoGarage /** * @file cfdcore_logger.cpp - * @brief \~japanese ログ機能を実装するファイルです。 - * \~english implementation of logger + * @brief implementation of logger */ #include #include diff --git a/src/cfdcore_manager.cpp b/src/cfdcore_manager.cpp index 5e925a4e..d7c86ee3 100644 --- a/src/cfdcore_manager.cpp +++ b/src/cfdcore_manager.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_manager.cpp * - * @brief \~japanese cfdcore管理クラスの実装ファイルです。 - * \~english implementation for cfdcore manager class + * @brief implementation for cfdcore manager class */ #include "cfdcore_manager.h" // NOLINT diff --git a/src/cfdcore_manager.h b/src/cfdcore_manager.h index 2125528e..341fddc7 100644 --- a/src/cfdcore_manager.h +++ b/src/cfdcore_manager.h @@ -2,8 +2,7 @@ /** * @file cfdcore_manager.h * - * @brief \~japanese cfd-core管理クラス定義ファイル - * \~english Definition of CfdCoreManager class + * @brief Definition of CfdCoreManager class * */ #ifndef CFD_CORE_SRC_CFDCORE_MANAGER_H_ @@ -19,49 +18,33 @@ namespace cfd { namespace core { /** - * @brief \~english cfdcore manaement class - * \~japanese cfdcore管理クラス + * @brief cfdcore manaement class */ class CfdCoreManager { public: /** - * @brief \~english Construct. - * \~japanese コンストラクタ + * @brief Construct. */ CfdCoreManager(); /** - * @brief \~english Destruct. - * \~japanese デストラクタ + * @brief Destructor. */ virtual ~CfdCoreManager(); /** - * \~english * @brief Initialization of cfd core * @param[out] handle_address cfdcore handle value. - * \~japanese - * @brief cfdcoreを初期化する。 - * @param[out] handle_address cfdcoreハンドル値 */ void Initialize(CfdCoreHandle* handle_address); /** - * \~english * @brief Finalize cfdcore * @param[in] handle cfdcire handle value * @param[in] is_finish_process boolean check if process is finished - * \~japanese - * @brief cfdcoreを終了する。 - * @param[in] handle cfdcoreハンドル値。 - * @param[in] is_finish_process プロセス終了時かどうか */ void Finalize(const CfdCoreHandle handle, bool is_finish_process); /** - * \~english * @brief get values of supported LibraryFunction * @return LibraryFunction bitflag. - * \~japanese - * @brief ライブラリがサポートしている機能の値を取得する。 - * @return LibraryFunctionのビットフラグ */ uint64_t GetSupportedFunction(); diff --git a/src/cfdcore_psbt.cpp b/src/cfdcore_psbt.cpp new file mode 100644 index 00000000..066a714b --- /dev/null +++ b/src/cfdcore_psbt.cpp @@ -0,0 +1,3349 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_psbt.cpp + * + * @brief This file is implements Partially Signed Bitcoin Transaction. + */ +#include "cfdcore/cfdcore_psbt.h" + +#include +#include +#include +#include + +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_descriptor.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_hdwallet.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_util.h" +#include "cfdcore_secp256k1.h" // NOLINT +#include "cfdcore_transaction_internal.h" // NOLINT +#include "cfdcore_wally_util.h" // NOLINT + +namespace cfd { +namespace core { + +using logger::info; +using logger::warn; + +// ----------------------------------------------------------------------------- +// File constants +// ----------------------------------------------------------------------------- +static const uint8_t kPsbtSeparator = 0; //!< psbt map separator +//! global xpub key size +static const size_t kPsbtGlobalXpubSize = BIP32_SERIALIZED_LEN + 1; + +// ----------------------------------------------------------------------------- +// Internal +// ----------------------------------------------------------------------------- +/** + * @brief set psbt bip32 key map + * @param[in] key_list bip32 key path list + * @param[in] map_obj map object. + */ +static void SetKeyPathMap( + const std::vector &key_list, struct wally_map *map_obj) { + int ret; + for (auto &key : key_list) { + auto key_vec = key.GetPubkey().GetData().GetBytes(); + std::vector fingerprint(4); + auto fp = key.GetFingerprint(); + auto path = key.GetChildNumArray(); + if (fp.IsEmpty() && path.empty()) { + fingerprint = key.GetPubkey().GetFingerprint().GetBytes(); + } else if (fp.GetDataSize() >= 4) { + fingerprint = fp.GetBytes(); + } + + ret = wally_map_add_keypath_item( + map_obj, key_vec.data(), key_vec.size(), fingerprint.data(), 4, + path.data(), path.size()); + if (ret != WALLY_OK) { + wally_map_free(map_obj); + warn(CFD_LOG_SOURCE, "wally_map_add_keypath_item NG[{}]", ret); + throw CfdException(kCfdMemoryFullError, "psbt add keypath error."); + } + } +} + +/** + * @brief validate psbt utxo data. + * @param[in] txid utxo txid + * @param[in] vout utxo vout + * @param[in] out_script locking script (script pubkey) + * @param[in] redeem_script redeem script + * @param[in] key_list key list + * @param[out] new_redeem_script output redeem script + * @retval true witness + * @retval false not witness + */ +bool ValidatePsbtUtxo( + const Txid &txid, uint32_t vout, const Script &out_script, + const Script &redeem_script, const std::vector &key_list, + Script *new_redeem_script) { + bool has_check_script = false; + bool is_witness = false; + + if (out_script.IsP2pkhScript() || out_script.IsP2wpkhScript()) { + if (!redeem_script.IsEmpty()) { + warn( + CFD_LOG_SOURCE, "pubkey isn't use redeemScript. txid:{},{}", + txid.GetHex(), vout); + throw CfdException( + kCfdIllegalArgumentError, "pubkey isn't use redeemScript."); + } + + is_witness = out_script.IsP2wpkhScript(); + if (key_list.size() > 1) { + warn( + CFD_LOG_SOURCE, "set many key. using key is one.", txid.GetHex(), + vout); + throw CfdException( + kCfdIllegalArgumentError, "set many key. using key is one."); + } else if (key_list.size() == 1) { + auto pubkey = key_list[0].GetPubkey(); + if (is_witness) { + if (!ScriptUtil::CreateP2wpkhLockingScript(pubkey).Equals( + out_script)) { + warn( + CFD_LOG_SOURCE, "unmatch pubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unmatch pubkey."); + } + } else { + if (!ScriptUtil::CreateP2pkhLockingScript(pubkey).Equals(out_script)) { + warn( + CFD_LOG_SOURCE, "unmatch pubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unmatch pubkey."); + } + } + } + } else if (out_script.IsP2shScript()) { + if (redeem_script.IsEmpty() || redeem_script.IsP2wpkhScript()) { + if (redeem_script.IsP2wpkhScript()) { + auto p2sh_wpkh_script = + ScriptUtil::CreateP2shLockingScript(redeem_script); + if (!p2sh_wpkh_script.Equals(out_script)) { + warn( + CFD_LOG_SOURCE, "unmatch scriptPubkey. txid:{},{}", + txid.GetHex(), vout); + throw CfdException( + kCfdIllegalArgumentError, "unmatch scriptPubkey."); + } + is_witness = true; + } + + if (key_list.size() > 1) { + warn( + CFD_LOG_SOURCE, "set many key. using key is one.", txid.GetHex(), + vout); + throw CfdException( + kCfdIllegalArgumentError, "set many key. using key is one."); + } else if (key_list.size() == 1) { + auto pubkey = key_list[0].GetPubkey(); + auto wpkh_script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); + auto sh_script = ScriptUtil::CreateP2shLockingScript(wpkh_script); + if (!sh_script.Equals(out_script)) { + warn( + CFD_LOG_SOURCE, "unmatch pubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unmatch pubkey."); + } + if (new_redeem_script != nullptr) *new_redeem_script = wpkh_script; + is_witness = true; + } + } else { + Address p2sh_addr(NetType::kMainnet, redeem_script); + Address p2wsh_addr( + NetType::kMainnet, WitnessVersion::kVersion0, redeem_script); + auto wsh_script = p2wsh_addr.GetLockingScript(); + auto p2sh_wsh_script = ScriptUtil::CreateP2shLockingScript(wsh_script); + if (p2sh_addr.GetLockingScript().Equals(out_script)) { + has_check_script = true; + } else if (p2sh_wsh_script.Equals(out_script)) { + has_check_script = true; + is_witness = true; + } else { + warn( + CFD_LOG_SOURCE, "unknown scriptPubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unknown scriptPubkey."); + } + } + } else if (out_script.IsP2wshScript()) { + Address addr(NetType::kMainnet, WitnessVersion::kVersion0, redeem_script); + if (!addr.GetLockingScript().Equals(out_script)) { + warn( + CFD_LOG_SOURCE, "unmatch scriptPubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unmatch scriptPubkey."); + } + has_check_script = true; + is_witness = true; + } else { + warn( + CFD_LOG_SOURCE, "unknown scriptPubkey. txid:{},{}", txid.GetHex(), + vout); + throw CfdException(kCfdIllegalArgumentError, "unknown scriptPubkey."); + } + + if (has_check_script) { + uint32_t count = 0; + std::vector pubkeys; + if (redeem_script.IsMultisigScript()) { + pubkeys = ScriptUtil::ExtractPubkeysFromMultisigScript(redeem_script); + } else { + auto items = redeem_script.GetElementList(); + for (auto item : items) { + if (item.IsBinary() && Pubkey::IsValid(item.GetBinaryData())) { + pubkeys.emplace_back(item.GetBinaryData()); + } + } + } + if (!key_list.empty()) { + for (auto key : key_list) { + auto cur_pubkey = key.GetPubkey(); + for (auto pubkey : pubkeys) { + if (pubkey.Equals(cur_pubkey)) { + ++count; + break; + } + } + } + if (count != key_list.size()) { + warn( + CFD_LOG_SOURCE, "unmatch key count. [{}:{}]", count, + key_list.size()); + throw CfdException(kCfdIllegalArgumentError, "psbt key valid error."); + } + } + } + return is_witness; +} + +/** + * @brief set input script and key list. + * @param[in,out] input psbt input + * @param[in] is_witness witness flag + * @param[in] redeem_script redeem script + * @param[in] key_list bip32 key list. + * @param[in] locking_script locking script + */ +void SetPsbtTxInScriptAndKeyList( + struct wally_psbt_input *input, bool is_witness, + const Script &redeem_script, const std::vector &key_list, + const Script &locking_script) { + int ret; + if (!redeem_script.IsEmpty()) { + auto script_val = redeem_script.GetData().GetBytes(); + if (is_witness && (!redeem_script.IsP2wpkhScript())) { + ret = wally_psbt_input_set_witness_script( + input, script_val.data(), script_val.size()); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_psbt_input_set_witness_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness script error."); + } + if (locking_script.IsP2shScript()) { + script_val = ScriptUtil::CreateP2wshLockingScript(redeem_script) + .GetData() + .GetBytes(); + } else { + script_val.clear(); + } + } + if (!script_val.empty()) { + ret = wally_psbt_input_set_redeem_script( + input, script_val.data(), script_val.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_redeem_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add redeem script error."); + } + } + } + + if (!key_list.empty()) { + SetKeyPathMap(key_list, &input->keypaths); + ret = wally_map_sort(&input->keypaths, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt input sort keypaths error."); + } + } +} + +/** + * @brief compare psbt data. + * @param[in,out] src source buffer + * @param[in] src_len source buffer length + * @param[in] dest destination buffer + * @param[in] dest_len destination buffer length + * @param[in] item_name field name + * @param[in] key key name + * @param[in] ignore_duplicate_error ignore duplicate error + * @retval true match + * @retval false unmatch + */ +bool ComparePsbtData( + uint8_t *src, size_t src_len, const uint8_t *dest, size_t dest_len, + const std::string &item_name, const std::string &key, + bool ignore_duplicate_error) { + bool is_compare = false; + if ((src_len == dest_len) && (memcmp(src, dest, src_len) == 0)) { + is_compare = true; + } else if (ignore_duplicate_error) { + // do nothing + } else { + if (key.empty()) { + warn(CFD_LOG_SOURCE, "psbt {} already exist.", item_name); + } else { + warn(CFD_LOG_SOURCE, "psbt {} already exist. key[{}]", item_name, key); + } + throw CfdException( + kCfdIllegalArgumentError, "psbt " + item_name + " duplicated error."); + } + return is_compare; +} + +/** + * @brief match wally tx. + * @param[in] src source + * @param[in] dest destination + * @retval true match + * @retval false unmatch + */ +bool MatchWallyTx(struct wally_tx *src, struct wally_tx *dest) { + std::vector src_txid(WALLY_TXHASH_LEN); + std::vector dest_txid(WALLY_TXHASH_LEN); + int ret = wally_tx_get_txid(src, src_txid.data(), src_txid.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_get_txid NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt get txid error."); + } + ret = wally_tx_get_txid(dest, dest_txid.data(), dest_txid.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_get_txid NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt get txid error."); + } + return (src_txid == dest_txid); +} + +/** + * @brief merge wally map. + * @param[in,out] src source + * @param[in] dst destination + * @param[in] item_name field name + * @param[in] ignore_duplicate_error ignore duplicate error flag. + */ +void MergeWallyMap( + struct wally_map *src, const struct wally_map *dst, + const std::string &item_name, bool ignore_duplicate_error) { + bool is_find; + int ret; + std::vector regist_indexes; + for (size_t dst_idx = 0; dst_idx < dst->num_items; ++dst_idx) { + auto dst_item = &dst->items[dst_idx]; + is_find = false; + for (size_t src_idx = 0; src_idx < src->num_items; ++src_idx) { + auto src_item = &src->items[src_idx]; + if ((src_item->key_len == dst_item->key_len) && + (memcmp(src_item->key, dst_item->key, src_item->key_len) == 0)) { + is_find = true; + ByteData key(src_item->key, static_cast(src_item->key_len)); + ComparePsbtData( + src_item->value, src_item->value_len, dst_item->value, + dst_item->value_len, item_name, key.GetHex(), + ignore_duplicate_error); + break; + } + } + if (!is_find) regist_indexes.push_back(dst_idx); + } + if (!regist_indexes.empty()) { + for (auto dst_idx : regist_indexes) { + auto dst_item = &dst->items[dst_idx]; + ret = wally_map_add( + src, dst_item->key, dst_item->key_len, dst_item->value, + dst_item->value_len); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdMemoryFullError, "psbt add " + item_name + " error."); + } + } + + ret = wally_map_sort(src, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt sort map error."); + } + } +} + +/** + * @brief alloc wally buffer. + * @param[in] source source + * @param[in] length source length + * @return alloc buffer address + */ +uint8_t *AllocWallyBuffer(const uint8_t *source, size_t length) { + wally_malloc_t malloc_func = nullptr; + + int ret; + if (malloc_func == nullptr) { + struct wally_operations ops; + memset(&ops, 0, sizeof(ops)); + ops.struct_size = sizeof(ops); + ret = wally_get_operations(&ops); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_get_operations NG[{}]", ret); + throw CfdException(kCfdInternalError, "OperationFunctions get error."); + } + malloc_func = ops.malloc_fn; + } + void *addr = malloc_func(length); + if (addr == nullptr) { + warn(CFD_LOG_SOURCE, "wally malloc NG."); + throw CfdException(kCfdMemoryFullError, "malloc error."); + } + memcpy(addr, source, length); + return static_cast(addr); +} + +/** + * @brief free wally buffer + * @param[in] source buffer + */ +void FreeWallyBuffer(void *source) { + wally_free_t free_func = nullptr; + + int ret; + if (free_func == nullptr) { + struct wally_operations ops; + memset(&ops, 0, sizeof(ops)); + ops.struct_size = sizeof(ops); + ret = wally_get_operations(&ops); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_get_operations NG[{}]", ret); + throw CfdException(kCfdInternalError, "OperationFunctions get error."); + } + free_func = ops.free_fn; + } + free_func(source); +} + +/** + * @brief merge input item. + * @param[in,out] psbt source psbt input. + * @param[in] psbt_dest destination psbt input. + * @param[in] ignore_duplicate_error ignore duplicate error flag + * @param[in] item_name field name + */ +void MergePsbtInputItem( + struct wally_psbt_input *psbt, const struct wally_psbt_input *psbt_dest, + bool ignore_duplicate_error, const std::string &item_name) { + int ret; + if (psbt_dest->utxo != nullptr) { + if (psbt->utxo == nullptr) { + ret = wally_psbt_input_set_utxo(psbt, psbt_dest->utxo); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_utxo NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt set utxo error."); + } + } else if (MatchWallyTx(psbt->utxo, psbt_dest->utxo)) { + // match + } else if (ignore_duplicate_error) { + // do nothing + } else { + warn(CFD_LOG_SOURCE, "psbt txin utxo already exist."); + throw CfdException( + kCfdIllegalArgumentError, "psbt txin utxo duplicated error."); + } + } + if (psbt_dest->witness_utxo != nullptr) { + if (psbt->witness_utxo == nullptr) { + ret = wally_psbt_input_set_witness_utxo(psbt, psbt_dest->witness_utxo); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set witness utxo error."); + } + } else if ( + (psbt->witness_utxo->satoshi == psbt_dest->witness_utxo->satoshi) && + ComparePsbtData( + psbt->witness_utxo->script, psbt->witness_utxo->script_len, + psbt_dest->witness_utxo->script, + psbt_dest->witness_utxo->script_len, item_name, "scriptPubkey", + ignore_duplicate_error)) { + // match + } else if (ignore_duplicate_error) { + // do nothing + } else { + warn(CFD_LOG_SOURCE, "psbt txin witness utxo already exist."); + throw CfdException( + kCfdIllegalArgumentError, + "psbt txin witness utxo duplicated error."); + } + } + if (psbt_dest->sighash > 0) { + if (psbt->sighash == 0) { + psbt->sighash = psbt_dest->sighash; + } else if (psbt->sighash == psbt_dest->sighash) { + // match + } else if (ignore_duplicate_error) { + // do nothing + } else { + std::string field_name = "txin sighashtype"; + warn(CFD_LOG_SOURCE, "psbt {} already exist.", field_name); + throw CfdException( + kCfdIllegalArgumentError, + "psbt " + field_name + " duplicated error."); + } + } + if (psbt_dest->redeem_script_len > 0) { + if (psbt->redeem_script_len == 0) { + psbt->redeem_script = AllocWallyBuffer( + psbt_dest->redeem_script, psbt_dest->redeem_script_len); + psbt->redeem_script_len = psbt_dest->redeem_script_len; + } else { + ComparePsbtData( + psbt->redeem_script, psbt->redeem_script_len, + psbt_dest->redeem_script, psbt_dest->redeem_script_len, + "txin redeem script", "", ignore_duplicate_error); + } + } + if (psbt_dest->witness_script_len > 0) { + if (psbt->witness_script_len == 0) { + psbt->witness_script = AllocWallyBuffer( + psbt_dest->witness_script, psbt_dest->witness_script_len); + psbt->witness_script_len = psbt_dest->witness_script_len; + } else { + ComparePsbtData( + psbt->witness_script, psbt->witness_script_len, + psbt_dest->witness_script, psbt_dest->witness_script_len, + "txin witness script", "", ignore_duplicate_error); + } + } + MergeWallyMap( + &psbt->keypaths, &psbt_dest->keypaths, "txin keypaths", + ignore_duplicate_error); + MergeWallyMap( + &psbt->signatures, &psbt_dest->signatures, "txin signatures", + ignore_duplicate_error); + MergeWallyMap( + &psbt->unknowns, &psbt_dest->unknowns, "txin unknowns", + ignore_duplicate_error); +} + +/** + * @brief merge output item. + * @param[in,out] psbt source psbt output. + * @param[in] psbt_dest destination psbt output. + * @param[in] ignore_duplicate_error ignore duplicate error flag + */ +void MergePsbtOutputItem( + struct wally_psbt_output *psbt, const struct wally_psbt_output *psbt_dest, + bool ignore_duplicate_error) { + if (psbt_dest->redeem_script_len > 0) { + if (psbt->redeem_script_len == 0) { + psbt->redeem_script = AllocWallyBuffer( + psbt_dest->redeem_script, psbt_dest->redeem_script_len); + psbt->redeem_script_len = psbt_dest->redeem_script_len; + } else { + ComparePsbtData( + psbt->redeem_script, psbt->redeem_script_len, + psbt_dest->redeem_script, psbt_dest->redeem_script_len, + "txout redeem script", "", ignore_duplicate_error); + } + } + if (psbt_dest->witness_script_len > 0) { + if (psbt->witness_script_len == 0) { + psbt->witness_script = AllocWallyBuffer( + psbt_dest->witness_script, psbt_dest->witness_script_len); + psbt->witness_script_len = psbt_dest->witness_script_len; + } else { + ComparePsbtData( + psbt->witness_script, psbt->witness_script_len, + psbt_dest->witness_script, psbt_dest->witness_script_len, + "txout witness script", "", ignore_duplicate_error); + } + } + MergeWallyMap( + &psbt->keypaths, &psbt_dest->keypaths, "txout keypaths", + ignore_duplicate_error); + MergeWallyMap( + &psbt->unknowns, &psbt_dest->unknowns, "txout unknowns", + ignore_duplicate_error); +} + +/** + * @brief merge input list. + * @param[in,out] psbt source psbt. + * @param[in] psbt_dest destination psbt. + * @param[in] ignore_duplicate_error ignore duplicate error flag + */ +void MergePsbtInputs( + struct wally_psbt *psbt, const struct wally_psbt *psbt_dest, + bool ignore_duplicate_error) { + bool is_find; + int ret; + std::vector append_indexes; + for (size_t dst_idx = 0; dst_idx < psbt_dest->num_inputs; ++dst_idx) { + auto dest_txin = &psbt_dest->tx->inputs[dst_idx]; + is_find = false; + for (size_t src_idx = 0; src_idx < psbt->num_inputs; ++src_idx) { + auto src_txin = &psbt->tx->inputs[src_idx]; + if ((src_txin->index == dest_txin->index) && + (memcmp( + src_txin->txhash, dest_txin->txhash, + sizeof(src_txin->txhash)) == 0)) { + is_find = true; + Txid txid(ByteData256(ByteData( + src_txin->txhash, + static_cast(sizeof(src_txin->txhash))))); + std::string item_key = + txid.GetHex() + "," + std::to_string(src_txin->index); + if (src_txin->sequence == dest_txin->sequence) { + // do nothing + } else if (ignore_duplicate_error) { + // do nothing + } else { + warn(CFD_LOG_SOURCE, "psbt sequence duplicate. [{}]", item_key); + throw CfdException( + kCfdIllegalArgumentError, "psbt sequence duplicate error."); + } + MergePsbtInputItem( + &psbt->inputs[src_idx], &psbt_dest->inputs[dst_idx], + ignore_duplicate_error, item_key); + break; + } + } + if (!is_find) append_indexes.push_back(dst_idx); + } + + uint32_t index; + for (auto dst_idx : append_indexes) { + index = static_cast(psbt->num_inputs); + ret = wally_psbt_add_input_at( + psbt, index, WALLY_PSBT_FLAG_NON_FINAL, + &psbt_dest->tx->inputs[dst_idx]); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_add_input_at NG[{}]", ret); + throw CfdException( + kCfdMemoryFullError, "psbt add global unkonwns error."); + } + auto dest_txin = &psbt_dest->tx->inputs[dst_idx]; + Txid txid(ByteData256(ByteData( + dest_txin->txhash, static_cast(sizeof(dest_txin->txhash))))); + std::string item_key = + txid.GetHex() + "," + std::to_string(dest_txin->index); + MergePsbtInputItem( + &psbt->inputs[index], &psbt_dest->inputs[dst_idx], + ignore_duplicate_error, item_key); + } +} + +/** + * @brief merge output list. + * @param[in,out] psbt source psbt. + * @param[in] psbt_dest destination psbt. + * @param[in] ignore_duplicate_error ignore duplicate error flag + */ +void MergePsbtOutputs( + struct wally_psbt *psbt, const struct wally_psbt *psbt_dest, + bool ignore_duplicate_error) { + bool is_find; + int ret; + std::vector append_indexes; + size_t start_idx = 0; + for (size_t dst_idx = 0; dst_idx < psbt_dest->num_outputs; ++dst_idx) { + auto dest_txout = &psbt_dest->tx->outputs[dst_idx]; + is_find = false; + for (size_t src_idx = start_idx; src_idx < psbt->num_outputs; ++src_idx) { + auto src_txout = &psbt->tx->outputs[src_idx]; + if ((src_txout->satoshi == dest_txout->satoshi) && + (src_txout->script_len == dest_txout->script_len) && + (memcmp( + src_txout->script, dest_txout->script, + sizeof(src_txout->script_len)) == 0)) { + is_find = true; + start_idx = src_idx + 1; + MergePsbtOutputItem( + &psbt->outputs[src_idx], &psbt_dest->outputs[dst_idx], + ignore_duplicate_error); + break; + } + } + if (!is_find) append_indexes.push_back(dst_idx); + } + + uint32_t index; + for (auto dst_idx : append_indexes) { + index = static_cast(psbt->num_outputs); + ret = wally_psbt_add_output_at( + psbt, index, 0, &psbt_dest->tx->outputs[dst_idx]); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_add_output_at NG[{}]", ret); + throw CfdException( + kCfdMemoryFullError, "psbt add global unkonwns error."); + } + MergePsbtOutputItem( + &psbt->outputs[index], &psbt_dest->outputs[dst_idx], + ignore_duplicate_error); + } +} + +/** + * @brief merge psbt. + * @param[in] src source psbt + * @param[in] dest destination psbt + * @param[in] ignore_duplicate_error ignore duplicate error + * @return merged psbt + */ +struct wally_psbt *MergePsbt( + const void *src, const void *dest, bool ignore_duplicate_error) { + const struct wally_psbt *psbt_src = + static_cast(src); + const struct wally_psbt *psbt_dest = + static_cast(dest); + + if ((psbt_src->tx == nullptr) || + (psbt_src->num_inputs != psbt_src->tx->num_inputs) || + (psbt_src->num_outputs != psbt_src->tx->num_outputs)) { + warn(CFD_LOG_SOURCE, "psbt src format error."); + throw CfdException(kCfdIllegalArgumentError, "psbt src format error."); + } + if ((psbt_dest->tx == nullptr) || + (psbt_dest->num_inputs != psbt_dest->tx->num_inputs) || + (psbt_dest->num_outputs != psbt_dest->tx->num_outputs)) { + warn(CFD_LOG_SOURCE, "psbt dest format error."); + throw CfdException(kCfdIllegalArgumentError, "psbt dest format error."); + } + + struct wally_psbt *psbt = nullptr; + int ret = wally_psbt_clone_alloc(psbt_src, 0, &psbt); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_clone_alloc NG[{}]", ret); + throw CfdException(kCfdMemoryFullError, "psbt clone error."); + } + + try { + if (memcmp(psbt->magic, psbt_dest->magic, sizeof(psbt->magic)) != 0) { + warn(CFD_LOG_SOURCE, "psbt unmatch magic."); + throw CfdException( + kCfdIllegalArgumentError, "psbt unmatch magic error."); + } + if (psbt->version != psbt_dest->version) { + warn( + CFD_LOG_SOURCE, "psbt unmatch version: [{},{}]", psbt->version, + psbt_dest->version); + throw CfdException( + kCfdIllegalArgumentError, "psbt unmatch version error."); + } + MergeWallyMap( + &psbt->unknowns, &psbt_dest->unknowns, "global unknowns", + ignore_duplicate_error); + + MergePsbtInputs(psbt, psbt_dest, ignore_duplicate_error); + MergePsbtOutputs(psbt, psbt_dest, ignore_duplicate_error); + } catch (const CfdException &except) { + wally_psbt_free(psbt); + throw except; + } + return psbt; +} + +/** + * @brief write psbt output. + * @param[in,out] builder serialize object + * @param[in] output psbt output + */ +static void WritePsbtOutput( + Serializer *builder, const struct wally_psbt_output *output) { + if (output->redeem_script_len != 0) { + builder->AddDirectByte(1); + builder->AddVariableInt(Psbt::kPsbtOutputRedeemScript); + builder->AddVariableBuffer( + output->redeem_script, + static_cast(output->redeem_script_len)); + } + if (output->witness_script_len != 0) { + builder->AddDirectByte(1); + builder->AddVariableInt(Psbt::kPsbtOutputWitnessScript); + builder->AddVariableBuffer( + output->witness_script, + static_cast(output->witness_script_len)); + } + for (size_t i = 0; i < output->keypaths.num_items; ++i) { + auto *item = &output->keypaths.items[i]; + builder->AddPrefixBuffer( + Psbt::kPsbtOutputBip32Derivation, item->key, + static_cast(item->key_len)); + builder->AddVariableBuffer( + item->value, static_cast(item->value_len)); + } + for (size_t i = 0; i < output->unknowns.num_items; ++i) { + auto *item = &output->unknowns.items[i]; + builder->AddVariableBuffer( + item->key, static_cast(item->key_len)); + builder->AddVariableBuffer( + item->value, static_cast(item->value_len)); + } + builder->AddDirectByte(kPsbtSeparator); +} + +/** + * @brief create psbt output only object. + * @param[in] psbt psbt object + * @return psbt binary data. + */ +ByteData CreatePsbtOutputOnlyData(const struct wally_psbt *psbt) { + Serializer builder; + builder.AddDirectBytes(psbt->magic, sizeof(psbt->magic)); + + builder.AddDirectByte(1); + builder.AddVariableInt(Psbt::kPsbtGlobalUnsignedTx); + auto tx = ConvertBitcoinTxFromWally(psbt->tx, false).GetBytes(); + builder.AddVariableBuffer(tx.data(), static_cast(tx.size())); + + if (psbt->version > 0) { + builder.AddDirectByte(1); + builder.AddVariableInt(Psbt::kPsbtGlobalVersion); + std::vector data(sizeof(psbt->version)); + memcpy(data.data(), &psbt->version, data.size()); + // TODO(k-matsuzawa) need endian support. + builder.AddVariableBuffer(data.data(), sizeof(psbt->version)); + } + + for (size_t i = 0; i < psbt->unknowns.num_items; ++i) { + auto *item = &psbt->unknowns.items[i]; + builder.AddVariableBuffer(item->key, static_cast(item->key_len)); + builder.AddVariableBuffer( + item->value, static_cast(item->value_len)); + } + builder.AddDirectByte(kPsbtSeparator); + + // input is unsupport. + + for (size_t i = 0; i < psbt->num_outputs; ++i) { + WritePsbtOutput(&builder, &psbt->outputs[i]); + } + return builder.Output(); +} + +/** + * @brief find psbt map data. + * @param[in] map_object map + * @param[in] key key data + * @param[in] field_name field name + * @param[out] index index + */ +static void FindPsbtMap( + const struct wally_map *map_object, const std::vector &key, + const std::string &field_name, size_t *index = nullptr) { + size_t exist = 0; + int ret = wally_map_find(map_object, key.data(), key.size(), &exist); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_find NG[{}]", ret); + throw CfdException( + kCfdInternalError, "psbt find " + field_name + " error."); + } + if ((index == nullptr) && (exist != 0)) { + warn(CFD_LOG_SOURCE, "{} duplicates.", field_name); + throw CfdException( + kCfdIllegalArgumentError, "psbt " + field_name + " duplicates error."); + } else if (index != nullptr) { + if (exist == 0) { + warn(CFD_LOG_SOURCE, "{} not found.", field_name); + throw CfdException( + kCfdIllegalArgumentError, + "psbt " + field_name + " not found error."); + } + *index = exist - 1; + } +} + +/** + * @brief set psbt global data. + * @param[in] key key + * @param[in] value value + * @param[in,out] psbt psbt object + * @return key type + */ +static uint8_t SetPsbtGlobal( + const std::vector &key, const std::vector &value, + struct wally_psbt *psbt) { + if (psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + int ret; + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtGlobalUnsignedTx) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + warn(CFD_LOG_SOURCE, "setting global tx is not supported."); + throw CfdException( + kCfdIllegalArgumentError, + "psbt setting global tx is not supported error."); + } else if (key[0] == Psbt::kPsbtGlobalVersion) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + warn(CFD_LOG_SOURCE, "setting global version is not supported."); + throw CfdException( + kCfdIllegalArgumentError, + "psbt setting global version is not supported error."); + } else { + FindPsbtMap(&psbt->unknowns, key, "global unknowns"); + ret = wally_map_add( + &psbt->unknowns, key.data(), key.size(), value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add global unknowns error."); + } + } + return key[0]; +} + +/** + * @brief Get psbt global data. + * @param[in] key_data key + * @param[in] psbt psbt object + * @param[out] is_find psbt key find + * @return value + */ +static ByteData GetPsbtGlobal( + const ByteData &key_data, struct wally_psbt *psbt, bool *is_find) { + if (psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + if (is_find != nullptr) *is_find = false; + const auto key = key_data.GetBytes(); + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtGlobalUnsignedTx) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (is_find != nullptr) *is_find = true; + Transaction tx(ConvertBitcoinTxFromWally(psbt->tx, false)); + return tx.GetData(); + } else if (key[0] == Psbt::kPsbtGlobalVersion) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (is_find != nullptr) *is_find = true; + // TODO(k-matsuzawa) need endian support. + Serializer builder; + builder.AddDirectNumber(psbt->version); + return builder.Output(); + } else { + size_t index = 0; + try { + FindPsbtMap(&psbt->unknowns, key, "global unknowns", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + psbt->unknowns.items[index].value, + static_cast(psbt->unknowns.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } + return ByteData(); +} + +/** + * @brief set psbt input data. + * @param[in] key key + * @param[in] value value + * @param[in,out] input psbt input + * @return key type + */ +static uint8_t SetPsbtInput( + const std::vector &key, const std::vector &value, + struct wally_psbt_input *input) { + int ret; + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtInputNonWitnessUtxo) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + Transaction tx(value); + struct wally_tx *wally_tx_obj = nullptr; + ret = wally_tx_from_hex(tx.GetHex().c_str(), 0, &wally_tx_obj); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_from_hex NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt tx from hex error."); + } else if ( + (wally_tx_obj->num_inputs == 0) || (wally_tx_obj->num_outputs == 0)) { + wally_tx_free(wally_tx_obj); + warn(CFD_LOG_SOURCE, "invalind utxo transaction format."); + throw CfdException(kCfdIllegalArgumentError, "psbt invalid tx error."); + } + + ret = wally_psbt_input_set_utxo(input, wally_tx_obj); + wally_tx_free(wally_tx_obj); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input utxo error."); + } + } else if (key[0] == Psbt::kPsbtInputWitnessUtxo) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + // TODO(k-matsuzawa) need endian support. + Deserializer parser(value); + uint64_t amount = parser.ReadUint64(); + auto script = parser.ReadVariableBuffer(); + struct wally_tx_output txout; + memset(&txout, 0, sizeof(txout)); + txout.satoshi = static_cast(amount); + txout.script = script.data(); + txout.script_len = script.size(); + ret = wally_psbt_input_set_witness_utxo(input, &txout); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set output witnessUtxo error."); + } + } else if (key[0] == Psbt::kPsbtInputPartialSig) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + FindPsbtMap(&input->signatures, pk_bytes, "input signatures"); + + ret = wally_map_add( + &input->signatures, pk_bytes.data(), pk_bytes.size(), value.data(), + value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input signatures error."); + } + } else if (key[0] == Psbt::kPsbtInputSighashType) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (value.size() < 4) { + warn(CFD_LOG_SOURCE, "psbt invalid value format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid value format error."); + } + // TODO(k-matsuzawa) need endian support. + uint32_t sighash = 0; + memcpy(&sighash, value.data(), sizeof(sighash)); + ret = wally_psbt_input_set_sighash(input, sighash); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_sighash NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input sighash error."); + } + } else if (key[0] == Psbt::kPsbtInputRedeemScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + ret = + wally_psbt_input_set_redeem_script(input, value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_redeem_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input redeemScript error."); + } + } else if (key[0] == Psbt::kPsbtInputWitnessScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + ret = + wally_psbt_input_set_witness_script(input, value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input witnessScript error."); + } + } else if (key[0] == Psbt::kPsbtInputBip32Derivation) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + FindPsbtMap(&input->keypaths, pk_bytes, "input bip32 pubkey"); + + if (value.size() < 4) { + warn(CFD_LOG_SOURCE, "psbt invalid value format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid value format error."); + } + size_t path_len = value.size() - 4; + std::vector path(path_len / 4); + if (path_len != 0) { + // TODO(k-matsuzawa) need endian support. + memcpy(path.data(), &value.data()[4], path_len); + } + ret = wally_map_add_keypath_item( + &input->keypaths, pk_bytes.data(), pk_bytes.size(), value.data(), 4, + path.data(), path.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add_keypath_item NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input pubkey error."); + } + } else if (key[0] == Psbt::kPsbtInputFinalScriptsig) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + ret = wally_psbt_input_set_final_scriptsig( + input, value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_final_scriptsig NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input final scriptsig error."); + } + } else if (key[0] == Psbt::kPsbtInputFinalScriptWitness) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + Deserializer parser(value); + uint64_t num = parser.ReadVariableInt(); + std::vector> stack_list(num); + for (uint64_t idx = 0; idx < num; ++idx) { + stack_list[idx] = parser.ReadVariableBuffer(); + } + + struct wally_tx_witness_stack *stack = nullptr; + ret = wally_tx_witness_stack_init_alloc(num, &stack); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_witness_stack_init_alloc NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt alloc witness stack error."); + } + for (const auto &stack_data : stack_list) { + ret = wally_tx_witness_stack_add( + stack, stack_data.data(), stack_data.size()); + if (ret != WALLY_OK) { + wally_tx_witness_stack_free(stack); + warn(CFD_LOG_SOURCE, "wally_tx_witness_stack_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness stack error."); + } + } + ret = wally_psbt_input_set_final_witness(input, stack); + wally_tx_witness_stack_free(stack); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_final_witness NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, + "psbt set input final witnessStack error."); + } + } else { + FindPsbtMap(&input->unknowns, key, "input unknowns"); + ret = wally_map_add( + &input->unknowns, key.data(), key.size(), value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add input unknowns error."); + } + } + return key[0]; +} + +/** + * @brief Get psbt input data. + * @param[in] key_data key + * @param[in] input psbt input + * @param[out] is_find psbt key find + * @return value + */ +static ByteData GetPsbtInput( + const ByteData &key_data, const struct wally_psbt_input *input, + bool *is_find) { + const auto key = key_data.GetBytes(); + if (is_find != nullptr) *is_find = false; + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtInputNonWitnessUtxo) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->utxo != nullptr) { + if (is_find != nullptr) *is_find = true; + Transaction tx(ConvertBitcoinTxFromWally(input->utxo, false)); + return tx.GetData(); + } else if (is_find == nullptr) { + warn(CFD_LOG_SOURCE, "psbt target {} not found.", key_data.GetHex()); + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputWitnessUtxo) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + // TODO(k-matsuzawa) need endian support. + if (input->witness_utxo != nullptr) { + if (is_find != nullptr) *is_find = true; + Serializer builder; + builder.AddDirectNumber(input->witness_utxo->satoshi); + builder.AddVariableBuffer(ByteData( + input->witness_utxo->script, + static_cast(input->witness_utxo->script_len))); + return builder.Output(); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputPartialSig) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + size_t index = 0; + try { + FindPsbtMap(&input->signatures, pk_bytes, "input signatures", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + input->signatures.items[index].value, + static_cast(input->signatures.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } else if (key[0] == Psbt::kPsbtInputSighashType) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->sighash != 0) { + if (is_find != nullptr) *is_find = true; + // TODO(k-matsuzawa) need endian support. + Serializer builder; + builder.AddDirectNumber(input->sighash); + return builder.Output(); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputRedeemScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->redeem_script_len != 0) { + if (is_find != nullptr) *is_find = true; + return ByteData( + input->redeem_script, + static_cast(input->redeem_script_len)); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputWitnessScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->witness_script_len != 0) { + if (is_find != nullptr) *is_find = true; + return ByteData( + input->witness_script, + static_cast(input->witness_script_len)); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputBip32Derivation) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + size_t index = 0; + try { + FindPsbtMap(&input->keypaths, pk_bytes, "input bip32 pubkey", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + input->keypaths.items[index].value, + static_cast(input->keypaths.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } else if (key[0] == Psbt::kPsbtInputFinalScriptsig) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->final_scriptsig_len != 0) { + if (is_find != nullptr) *is_find = true; + return ByteData( + input->final_scriptsig, + static_cast(input->final_scriptsig_len)); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtInputFinalScriptWitness) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (input->final_witness != nullptr) { + if (is_find != nullptr) *is_find = true; + Serializer builder; + size_t num = input->final_witness->num_items; + builder.AddVariableInt(num); + for (uint64_t idx = 0; idx < num; ++idx) { + builder.AddVariableBuffer(ByteData( + input->final_witness->items[idx].witness, + static_cast( + input->final_witness->items[idx].witness_len))); + } + return builder.Output(); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else { + size_t index = 0; + try { + FindPsbtMap(&input->unknowns, key, "input unknowns", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + input->unknowns.items[index].value, + static_cast(input->unknowns.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } + return ByteData(); +} + +/** + * @brief set psbt output data. + * @param[in] key key + * @param[in] value value + * @param[in,out] output psbt output + * @return key type + */ +static uint8_t SetPsbtOutput( + const std::vector &key, const std::vector &value, + struct wally_psbt_output *output) { + int ret; + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtOutputRedeemScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (output->redeem_script != nullptr) { + warn(CFD_LOG_SOURCE, "output redeemScript duplicates."); + throw CfdException( + kCfdIllegalArgumentError, + "psbt output redeemScript duplicates error."); + } + ret = wally_psbt_output_set_redeem_script( + output, value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_output_set_redeem_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set output redeemScript error."); + } + } else if (key[0] == Psbt::kPsbtOutputWitnessScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (output->witness_script != nullptr) { + warn(CFD_LOG_SOURCE, "output witnessScript duplicates."); + throw CfdException( + kCfdIllegalArgumentError, + "psbt output witnessScript duplicates error."); + } + ret = wally_psbt_output_set_witness_script( + output, value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_output_set_witness_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set output witnessScript error."); + } + } else if (key[0] == Psbt::kPsbtOutputBip32Derivation) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + FindPsbtMap(&output->keypaths, pk_bytes, "output bip32 pubkey"); + + if (value.size() < 4) { + warn(CFD_LOG_SOURCE, "psbt invalid value format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid value format error."); + } + size_t path_len = value.size() - 4; + std::vector path(path_len / 4); + if (path_len != 0) { + // TODO(k-matsuzawa) need endian support. + memcpy(path.data(), &value.data()[4], path_len); + } + ret = wally_map_add_keypath_item( + &output->keypaths, pk_bytes.data(), pk_bytes.size(), value.data(), 4, + path.data(), path.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add_keypath_item NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set output pubkey error."); + } + } else { + FindPsbtMap(&output->unknowns, key, "output unknowns"); + ret = wally_map_add( + &output->unknowns, key.data(), key.size(), value.data(), value.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add output unknowns error."); + } + } + return key[0]; +} + +/** + * @brief Get psbt output data. + * @param[in] key_data key + * @param[in] output psbt output + * @param[out] is_find psbt key find + * @return value + */ +static ByteData GetPsbtOutput( + const ByteData &key_data, struct wally_psbt_output *output, + bool *is_find) { + if (is_find != nullptr) *is_find = false; + const auto key = key_data.GetBytes(); + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtOutputRedeemScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (output->redeem_script_len != 0) { + if (is_find != nullptr) *is_find = true; + return ByteData( + output->redeem_script, + static_cast(output->redeem_script_len)); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtOutputWitnessScript) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (output->witness_script_len != 0) { + if (is_find != nullptr) *is_find = true; + return ByteData( + output->witness_script, + static_cast(output->witness_script_len)); + } else if (is_find == nullptr) { + throw CfdException( + kCfdIllegalArgumentError, + "psbt target key " + key_data.GetHex() + " not found error."); + } + } else if (key[0] == Psbt::kPsbtOutputBip32Derivation) { + std::vector pubkey(key.size() - 1); + if (pubkey.size() != 0) { + memcpy(pubkey.data(), &key.data()[1], pubkey.size()); + } + Pubkey pk(pubkey); + auto pk_bytes = pk.GetData().GetBytes(); + size_t index = 0; + try { + FindPsbtMap(&output->keypaths, pk_bytes, "output bip32 pubkey", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + output->keypaths.items[index].value, + static_cast(output->keypaths.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } else { + size_t index = 0; + try { + FindPsbtMap(&output->unknowns, key, "output unknowns", &index); + if (is_find != nullptr) *is_find = true; + return ByteData( + output->unknowns.items[index].value, + static_cast(output->unknowns.items[index].value_len)); + } catch (const CfdException &except) { + if ((is_find == nullptr) || + (except.GetErrorCode() != kCfdIllegalArgumentError)) { + throw except; + } + } + } + return ByteData(); +} + +/** + * @brief parse psbt output data. + * @param[in] parser deserialize object + * @param[in,out] output psbt output + */ +static void ParsePsbtOutput( + Deserializer *parser, struct wally_psbt_output *output) { + int ret; + std::vector key; + do { + key = parser->ReadVariableBuffer(); + if (!key.empty()) { + std::vector buf = parser->ReadVariableBuffer(); + SetPsbtOutput(key, buf, output); + } + } while (!key.empty()); + + ret = wally_map_sort(&output->keypaths, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt output sort keypaths error."); + } + + ret = wally_map_sort(&output->unknowns, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt output sort unknowns error."); + } +} + +/** + * @brief parse psbt data. + * @param[in] data psbt binary data + * @return psbt object + */ +struct wally_psbt *ParsePsbtData(const ByteData &data) { + static const uint8_t kPsbtMagic[] = {'p', 's', 'b', 't', 0xff}; + + struct wally_psbt *psbt = nullptr; + std::vector bytes = data.GetBytes(); + int ret = wally_psbt_from_bytes(bytes.data(), bytes.size(), &psbt); + if (ret == WALLY_OK) { + if ((psbt->num_inputs != 0) || (psbt->num_outputs != 0)) { + return psbt; + } + std::vector tmp_buf(bytes.size()); + size_t tmp_size = 0; + ret = wally_psbt_to_bytes( + psbt, 0, tmp_buf.data(), tmp_buf.size(), &tmp_size); + if ((ret == WALLY_OK) && (tmp_size == bytes.size())) { + // It was able to convert the data correctly. + return psbt; + } + wally_psbt_free(psbt); + psbt = nullptr; + } else if (ret != WALLY_EINVAL) { + warn(CFD_LOG_SOURCE, "wally_psbt_from_bytes NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt from bytes error."); + } + + Deserializer parser(data); + uint8_t magic[sizeof(kPsbtMagic)]; + memset(magic, 0, sizeof(magic)); + if (bytes.size() > 5) parser.ReadArray(magic, sizeof(magic)); + if (memcmp(magic, kPsbtMagic, sizeof(magic)) != 0) { + warn(CFD_LOG_SOURCE, "psbt unmatch magic."); + throw CfdException(kCfdInternalError, "psbt unmatch magic error."); + } + ret = wally_psbt_init_alloc(0, 0, 0, 0, &psbt); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_init_alloc NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt alloc error."); + } + + try { + memcpy(psbt->magic, magic, sizeof(psbt->magic)); + + std::vector key; + do { + key = parser.ReadVariableBuffer(); + if (!key.empty()) { + std::vector buf = parser.ReadVariableBuffer(); + bool has_key_1byte = (key.size() == 1); + if (key[0] == Psbt::kPsbtGlobalUnsignedTx) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (psbt->tx != nullptr) { + warn(CFD_LOG_SOURCE, "global tx duplicates."); + throw CfdException( + kCfdIllegalArgumentError, "psbt global tx duplicates error."); + } + + Transaction transaction(buf); + if (transaction.GetTxInCount() != 0) { + // failed to psbt format check on libwally-core. + warn(CFD_LOG_SOURCE, "psbt format error."); + throw CfdException(kCfdIllegalArgumentError, "psbt format error."); + } + auto txouts = transaction.GetTxOutList(); + struct wally_tx tx; + memset(&tx, 0, sizeof(tx)); + tx.version = transaction.GetVersion(); + tx.locktime = transaction.GetLockTime(); + ret = wally_psbt_set_global_tx(psbt, &tx); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_set_global_tx NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx error."); + } + for (uint32_t index = 0; index < txouts.size(); ++index) { + const auto &txout = txouts[index]; + auto script_val = txout.GetLockingScript().GetData().GetBytes(); + struct wally_tx_output output; + memset(&output, 0, sizeof(output)); + output.satoshi = + static_cast(txout.GetValue().GetSatoshiValue()); + output.script = script_val.data(); + output.script_len = script_val.size(); + ret = wally_psbt_add_output_at(psbt, index, 0, &output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_add_output_at NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set txout error."); + } + } + } else if (key[0] == Psbt::kPsbtGlobalVersion) { + if (!has_key_1byte) { + warn(CFD_LOG_SOURCE, "psbt invalid key format."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid key format error."); + } + if (psbt->version > 0) { + warn(CFD_LOG_SOURCE, "psbt version duplicates."); + throw CfdException( + kCfdIllegalArgumentError, "psbt version duplicates error."); + } + if (buf.size() != sizeof(psbt->version)) { + warn(CFD_LOG_SOURCE, "psbt invlid version size."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invlid version size error."); + } + memcpy(&psbt->version, buf.data(), sizeof(psbt->version)); + if (psbt->version > Psbt::GetDefaultVersion()) { + warn( + CFD_LOG_SOURCE, "psbt unsupported version[{}]", psbt->version); + throw CfdException( + kCfdIllegalArgumentError, "psbt unsupported version error."); + } + } else { + ret = wally_map_add( + &psbt->unknowns, key.data(), key.size(), buf.data(), buf.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add unknowns error."); + } + } + } + } while (!key.empty()); + + if (psbt->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt global tx not found."); + throw CfdException( + kCfdIllegalArgumentError, "psbt global tx not found error."); + } + + ret = wally_map_sort(&psbt->unknowns, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt sort unknowns error."); + } + + if (psbt->tx->num_inputs != 0) { + warn(CFD_LOG_SOURCE, "psbt exist input. please use libwally-core."); + throw CfdException(kCfdIllegalArgumentError, "psbt exist input."); + } + + for (size_t i = 0; i < psbt->tx->num_outputs; ++i) { + ParsePsbtOutput(&parser, &psbt->outputs[i]); + } + + uint32_t offset = parser.GetReadSize(); + if (bytes.size() != offset) { + warn(CFD_LOG_SOURCE, "psbt analyze error."); + throw CfdException(kCfdIllegalArgumentError, "psbt analyze error."); + } + return psbt; + } catch (const CfdError &except) { + wally_psbt_free(psbt); + throw except; + } catch (const std::exception &except) { + wally_psbt_free(psbt); + warn(CFD_LOG_SOURCE, "unknown exception."); + throw CfdException(kCfdUnknownError, std::string(except.what())); + } catch (...) { + wally_psbt_free(psbt); + warn(CFD_LOG_SOURCE, "unknown error."); + throw CfdException(); + } +} + +// ----------------------------------------------------------------------------- +// Psbt +// ----------------------------------------------------------------------------- +Psbt::Psbt() : Psbt(Psbt::GetDefaultVersion(), 2, static_cast(0)) { + // do nothing +} + +Psbt::Psbt(uint32_t version, uint32_t lock_time) + : Psbt(Psbt::GetDefaultVersion(), version, lock_time) { + // constructor +} + +Psbt::Psbt(uint32_t psbt_version, uint32_t version, uint32_t lock_time) { + struct wally_psbt *psbt_pointer = nullptr; + int ret = wally_psbt_init_alloc(psbt_version, 0, 0, 0, &psbt_pointer); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_init_alloc NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt data generate error."); + } + + struct wally_tx tx; + memset(&tx, 0, sizeof(tx)); + tx.version = version; + tx.locktime = lock_time; + ret = wally_psbt_set_global_tx(psbt_pointer, &tx); + if (ret != WALLY_OK) { + wally_psbt_free(psbt_pointer); // free + warn(CFD_LOG_SOURCE, "wally_psbt_set_global_tx NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx error."); + } + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = RebuildTransaction(wally_psbt_pointer_); +} + +Psbt::Psbt(const std::string &base64) + : Psbt(CryptoUtil::DecodeBase64(base64)) { + // do nothing +} + +Psbt::Psbt(const ByteData &byte_data) { + struct wally_psbt *psbt_pointer = ParsePsbtData(byte_data); + size_t is_elements = 0; + int ret = wally_psbt_is_elements(psbt_pointer, &is_elements); + if (ret != WALLY_OK) { + wally_psbt_free(psbt_pointer); + warn(CFD_LOG_SOURCE, "wally_psbt_is_elements NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt elements check error."); + } + if (is_elements != 0) { + wally_psbt_free(psbt_pointer); + warn(CFD_LOG_SOURCE, "psbt elements format."); + throw CfdException(kCfdInternalError, "psbt bitcoin tx format error."); + } + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = RebuildTransaction(wally_psbt_pointer_); +} + +Psbt::Psbt(const Transaction &transaction) + : Psbt(Psbt::GetDefaultVersion(), transaction) { + // constructor +} + +Psbt::Psbt(uint32_t psbt_version, const Transaction &transaction) { + std::string tx_hex = transaction.GetHex(); + auto txin_list = transaction.GetTxInList(); + auto txout_list = transaction.GetTxOutList(); + struct wally_tx *tx = nullptr; + int ret = wally_tx_from_hex(tx_hex.data(), 0, &tx); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_from_hex NG[{}]", ret); + if (txin_list.empty() || txout_list.empty()) { + // fall-through + } else { + throw CfdException(kCfdInternalError, "psbt tx from hex error."); + } + } else if ( + (tx->num_inputs != txin_list.size()) || + (tx->num_outputs != txout_list.size())) { + // free and direct creating. + wally_tx_free(tx); + tx = nullptr; + } + + struct wally_psbt *psbt_pointer = nullptr; + ret = wally_psbt_init_alloc( + psbt_version, txin_list.size(), txout_list.size(), 0, &psbt_pointer); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_init_alloc NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt data generate error."); + } + + if (tx == nullptr) { + ret = wally_tx_init_alloc( + transaction.GetVersion(), transaction.GetLockTime(), txin_list.size(), + txout_list.size(), &tx); + if (ret != WALLY_OK) { + wally_psbt_free(psbt_pointer); // free + warn(CFD_LOG_SOURCE, "wally_psbt_set_global_tx NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx error."); + } + + for (auto txin : txin_list) { + auto txid_val = txin.GetTxid().GetData().GetBytes(); + ret = wally_tx_add_raw_input( + tx, txid_val.data(), txid_val.size(), txin.GetVout(), + txin.GetSequence(), nullptr, 0, nullptr, 0); + if (ret != WALLY_OK) { + wally_tx_free(tx); + wally_psbt_free(psbt_pointer); // free + warn(CFD_LOG_SOURCE, "wally_tx_add_raw_input NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx input error."); + } + } + for (auto txout : txout_list) { + auto script_val = txout.GetLockingScript().GetData().GetBytes(); + ret = wally_tx_add_raw_output( + tx, static_cast(txout.GetValue().GetSatoshiValue()), + script_val.data(), script_val.size(), 0); + if (ret != WALLY_OK) { + wally_tx_free(tx); + wally_psbt_free(psbt_pointer); // free + warn(CFD_LOG_SOURCE, "wally_tx_add_raw_output NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx output error."); + } + } + } + + ret = wally_psbt_set_global_tx(psbt_pointer, tx); + wally_tx_free(tx); + if (ret != WALLY_OK) { + wally_psbt_free(psbt_pointer); // free + warn(CFD_LOG_SOURCE, "wally_psbt_set_global_tx NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt set tx error."); + } + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = RebuildTransaction(wally_psbt_pointer_); +} + +Psbt::Psbt(const Psbt &psbt) : Psbt(psbt.GetData()) { + // copy constructor +} + +Psbt &Psbt::operator=(const Psbt &psbt) & { + if (this != &psbt) { + struct wally_psbt *psbt_pointer = nullptr; + struct wally_psbt *psbt_src_pointer = nullptr; + psbt_src_pointer = + static_cast(psbt.wally_psbt_pointer_); + int ret = wally_psbt_clone_alloc(psbt_src_pointer, 0, &psbt_pointer); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_clone_alloc NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt clone error."); + } + FreeWallyPsbtAddress(wally_psbt_pointer_); // free + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = RebuildTransaction(wally_psbt_pointer_); + } + return *this; +} + +void Psbt::FreeWallyPsbtAddress(const void *wally_psbt_pointer) { + if (wally_psbt_pointer != nullptr) { + struct wally_psbt *psbt_pointer = nullptr; + // ignore const + memcpy(&psbt_pointer, &wally_psbt_pointer, sizeof(void *)); + wally_psbt_free(psbt_pointer); + } +} + +Transaction Psbt::RebuildTransaction(const void *wally_psbt_pointer) { + Transaction tx; + if (wally_psbt_pointer != nullptr) { + const struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer); + if (psbt_pointer->tx != nullptr) { + tx = Transaction(ConvertBitcoinTxFromWally(psbt_pointer->tx, false)); + } + } + return tx; +} + +uint32_t Psbt::GetDefaultVersion() { return WALLY_PSBT_HIGHEST_VERSION; } + +ByteData Psbt::CreateRecordKey(uint8_t type) { return ByteData(type); } + +ByteData Psbt::CreateFixRecordKey( + uint8_t type, const ByteData &fixed_size_key) { + return ByteData(type).Concat(fixed_size_key); +} + +ByteData Psbt::CreateRecordKey(uint8_t type, const ByteData &key_bytes) { + return ByteData(type).Concat(key_bytes.Serialize()); +} + +ByteData Psbt::CreateRecordKey(uint8_t type, const std::string &key) { + return CreateRecordKey( + type, ByteData( + reinterpret_cast(key.data()), + static_cast(strlen(key.c_str())))); +} + +ByteData Psbt::CreateRecordKey( + uint8_t type, const ByteData &prefix, uint8_t sub_type) { + return ByteData(type).Concat(prefix.Serialize(), ByteData(sub_type)); +} + +ByteData Psbt::CreateRecordKey( + uint8_t type, const std::string &prefix, uint8_t sub_type) { + return CreateRecordKey( + type, + ByteData( + reinterpret_cast(prefix.data()), + static_cast(strlen(prefix.c_str()))), + sub_type); +} + +ByteData Psbt::CreateRecordKey( + uint8_t type, const ByteData &prefix, uint8_t sub_type, + const ByteData &sub_key_bytes) { + return ByteData(type).Concat( + prefix.Serialize(), ByteData(sub_type), sub_key_bytes.Serialize()); +} + +ByteData Psbt::CreateRecordKey( + uint8_t type, const std::string &prefix, uint8_t sub_type, + const std::string &sub_key) { + return CreateRecordKey( + type, + ByteData( + reinterpret_cast(prefix.data()), + static_cast(strlen(prefix.c_str()))), + sub_type, + ByteData( + reinterpret_cast(sub_key.data()), + static_cast(strlen(sub_key.c_str())))); +} + +ByteData Psbt::CreatePubkeyRecordKey(uint8_t type, const Pubkey &pubkey) { + return ByteData(type).Concat(pubkey.GetData()); +} + +std::string Psbt::GetBase64() const { + return CryptoUtil::EncodeBase64(GetData()); +} + +ByteData Psbt::GetData() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + size_t size = 0; + + if ((psbt_pointer != nullptr) && (psbt_pointer->num_inputs == 0)) { + return CreatePsbtOutputOnlyData(psbt_pointer); + } + + std::vector bytes(GetDataSize()); + int ret = + wally_psbt_to_bytes(psbt_pointer, 0, bytes.data(), bytes.size(), &size); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_to_bytes NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt to bytes error."); + } + return ByteData(bytes.data(), static_cast(size)); +} + +uint32_t Psbt::GetDataSize() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + size_t size = 0; + + if ((psbt_pointer != nullptr) && (psbt_pointer->num_inputs == 0)) { + auto data = CreatePsbtOutputOnlyData(psbt_pointer); + return static_cast(data.GetDataSize()); + } + + int ret = wally_psbt_get_length(psbt_pointer, 0, &size); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_get_length NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt get length error."); + } + return static_cast(size); +} + +bool Psbt::IsFinalized() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + size_t data = 0; + int ret = wally_psbt_is_finalized(psbt_pointer, &data); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_is_finalized NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt check finalized error."); + } + return (data == 1); +} + +bool Psbt::IsFinalizedInput(uint32_t index) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + size_t data = 0; + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + + if ((psbt_pointer->inputs == nullptr) || + (psbt_pointer->num_inputs <= index)) { + warn(CFD_LOG_SOURCE, "psbt input out-of-range."); + throw CfdException(kCfdOutOfRangeError, "psbt input out-of-range."); + } + + int ret = wally_psbt_input_is_finalized(&psbt_pointer->inputs[index], &data); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_is_finalized NG[{}]", ret); + throw CfdException( + kCfdIllegalStateError, "psbt input check finalized error."); + } + return (data == 1); +} + +void Psbt::Finalize() { + if (!IsFinalized()) { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + int ret = wally_psbt_finalize(psbt_pointer); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_finalize NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt finalize error."); + } + } +} + +ByteData Psbt::Extract() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + struct wally_tx *tx = nullptr; + int ret = wally_psbt_extract(psbt_pointer, &tx); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_extract NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt extract error."); + } + try { + auto tx_bytes = ConvertBitcoinTxFromWally(tx, false); + wally_tx_free(tx); + return tx_bytes; + } catch (const CfdException &except) { + wally_tx_free(tx); + throw except; + } +} + +Transaction Psbt::ExtractTransaction() const { return Transaction(Extract()); } + +Transaction Psbt::GetTransaction() const { return base_tx_; } + +void Psbt::Combine(const Psbt &transaction) { + std::vector bytes = transaction.GetData().GetBytes(); + struct wally_psbt *src_pointer = nullptr; + int ret = wally_psbt_from_bytes(bytes.data(), bytes.size(), &src_pointer); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_from_bytes NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt from bytes error."); + } + + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + ret = wally_psbt_combine(psbt_pointer, src_pointer); + wally_psbt_free(src_pointer); // free + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_combine NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt combine error."); + } + base_tx_ = RebuildTransaction(wally_psbt_pointer_); +} + +void Psbt::Sign(const Privkey &privkey, bool has_grind_r) { + std::vector key = privkey.GetData().GetBytes(); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + int ret = wally_psbt_sign( + psbt_pointer, key.data(), key.size(), + (has_grind_r) ? EC_FLAG_GRIND_R : 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_sign NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt sign error."); + } +} + +void Psbt::Join(const Psbt &transaction, bool ignore_duplicate_error) { + struct wally_psbt *psbt_pointer = MergePsbt( + wally_psbt_pointer_, transaction.wally_psbt_pointer_, + ignore_duplicate_error); + FreeWallyPsbtAddress(wally_psbt_pointer_); // free + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = RebuildTransaction(wally_psbt_pointer_); +} + +uint32_t Psbt::GetTxInCount() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } else if (psbt_pointer->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt base tx is null"); + throw CfdException(kCfdIllegalStateError, "psbt base tx is null."); + } + return static_cast(psbt_pointer->tx->num_inputs); +} + +uint32_t Psbt::GetTxOutCount() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } else if (psbt_pointer->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt base tx is null"); + throw CfdException(kCfdIllegalStateError, "psbt base tx is null."); + } + return static_cast(psbt_pointer->tx->num_outputs); +} + +uint32_t Psbt::AddTxIn(const TxIn &txin) { + return AddTxIn(txin.GetTxid(), txin.GetVout(), txin.GetSequence()); +} + +uint32_t Psbt::AddTxIn(const TxInReference &txin) { + return AddTxIn(txin.GetTxid(), txin.GetVout(), txin.GetSequence()); +} + +uint32_t Psbt::AddTxIn(const Txid &txid, uint32_t vout, uint32_t sequence) { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint32_t index = static_cast(psbt_pointer->num_inputs); + struct wally_tx_input *input = nullptr; + std::vector txhash = txid.GetData().GetBytes(); + + int ret = wally_tx_input_init_alloc( + txhash.data(), txhash.size(), vout, sequence, nullptr, 0, nullptr, + &input); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_input_init_alloc NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt alloc input error."); + } + + ret = wally_psbt_add_input_at( + psbt_pointer, index, WALLY_PSBT_FLAG_NON_FINAL, input); + wally_tx_input_free(input); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_add_input_at NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt add input error."); + } + base_tx_ = RebuildTransaction(wally_psbt_pointer_); + return index; +} + +void Psbt::SetTxInUtxo( + uint32_t index, const Transaction &tx, const KeyData &key) { + SetTxInUtxo(index, tx, Script(), key); +} + +void Psbt::SetTxInUtxo( + uint32_t index, const Transaction &tx, const Script &redeem_script, + const KeyData &key) { + std::vector list; + if (key.IsValid()) list.push_back(key); + SetTxInUtxo(index, tx, redeem_script, list); +} + +void Psbt::SetTxInUtxo( + uint32_t index, const Transaction &tx, const Script &redeem_script, + const std::vector &key_list) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint8_t *txhash = psbt_pointer->tx->inputs[index].txhash; + uint32_t vout = psbt_pointer->tx->inputs[index].index; + auto txid = tx.GetTxid(); + auto tx_txid = txid.GetData().GetBytes(); + if ((memcmp(txhash, tx_txid.data(), tx_txid.size()) != 0) || + (vout >= tx.GetTxOutCount())) { + warn(CFD_LOG_SOURCE, "unmatch outpoint."); + throw CfdException(kCfdIllegalArgumentError, "unmatch outpoint."); + } + + auto txout = tx.GetTxOut(vout); + Script new_redeem_script = redeem_script; + bool is_witness = ValidatePsbtUtxo( + txid, vout, txout.GetLockingScript(), redeem_script, key_list, + &new_redeem_script); + + struct wally_tx *wally_tx_obj = nullptr; + int ret = wally_tx_from_hex(tx.GetHex().c_str(), 0, &wally_tx_obj); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_from_hex NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt tx from hex error."); + } else if ( + (wally_tx_obj->num_inputs == 0) || (wally_tx_obj->num_outputs == 0)) { + wally_tx_free(wally_tx_obj); + warn(CFD_LOG_SOURCE, "invalind utxo transaction format."); + throw CfdException(kCfdIllegalArgumentError, "psbt invalid tx error."); + } + + ret = wally_psbt_input_set_utxo(&psbt_pointer->inputs[index], wally_tx_obj); + if (ret != WALLY_OK) { + wally_tx_free(wally_tx_obj); + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_utxo NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt add utxo error."); + } + if (is_witness) { + ret = wally_psbt_input_set_witness_utxo( + &psbt_pointer->inputs[index], &wally_tx_obj->outputs[vout]); + if (ret != WALLY_OK) { + wally_tx_free(wally_tx_obj); + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness utxo error."); + } + } + wally_tx_free(wally_tx_obj); + + SetPsbtTxInScriptAndKeyList( + &psbt_pointer->inputs[index], is_witness, new_redeem_script, key_list, + txout.GetLockingScript()); +} + +void Psbt::SetTxInUtxo( + uint32_t index, const TxOutReference &txout, const KeyData &key) { + SetTxInUtxo(index, txout, Script(), key); +} + +void Psbt::SetTxInUtxo( + uint32_t index, const TxOutReference &txout, const Script &redeem_script, + const KeyData &key) { + std::vector list; + if (key.IsValid()) list.push_back(key); + SetTxInUtxo(index, txout, redeem_script, list); +} + +void Psbt::SetTxInUtxo( + uint32_t index, const TxOutReference &txout, const Script &redeem_script, + const std::vector &key_list) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint8_t *txhash = psbt_pointer->tx->inputs[index].txhash; + uint32_t vout = psbt_pointer->tx->inputs[index].index; + Txid txid(ByteData256( + ByteData(txhash, sizeof(psbt_pointer->tx->inputs[index].txhash)))); + + auto script = txout.GetLockingScript(); + Script new_redeem_script = redeem_script; + bool is_witness = ValidatePsbtUtxo( + txid, vout, script, redeem_script, key_list, &new_redeem_script); + if (!is_witness) { + warn(CFD_LOG_SOURCE, "non witness output is not supported."); + throw CfdException(kCfdIllegalArgumentError, "psbt utxo type error."); + } + + struct wally_tx_output *output = nullptr; + auto script_val = script.GetData().GetBytes(); + int ret = wally_tx_output_init_alloc( + static_cast(txout.GetValue().GetSatoshiValue()), + script_val.data(), script_val.size(), &output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_output_init_alloc NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt alloc output error."); + } + + ret = + wally_psbt_input_set_witness_utxo(&psbt_pointer->inputs[index], output); + wally_tx_output_free(output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness utxo error."); + } + + SetPsbtTxInScriptAndKeyList( + &psbt_pointer->inputs[index], is_witness, new_redeem_script, key_list, + script); +} + +void Psbt::SetTxInWitnessUtxoDirect( + uint32_t index, const TxOutReference &txout) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + struct wally_tx_output *output = nullptr; + auto script = txout.GetLockingScript(); + auto script_val = script.GetData().GetBytes(); + int ret = wally_tx_output_init_alloc( + static_cast(txout.GetValue().GetSatoshiValue()), + script_val.data(), script_val.size(), &output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_output_init_alloc NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt alloc output error."); + } + + ret = + wally_psbt_input_set_witness_utxo(&psbt_pointer->inputs[index], output); + wally_tx_output_free(output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_witness_utxo NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness utxo error."); + } +} + +void Psbt::SetTxInBip32KeyDirect(uint32_t index, const KeyData &key_data) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + std::vector key_list = {key_data}; + SetKeyPathMap(key_list, &psbt_pointer->inputs[index].keypaths); + int ret = wally_map_sort(&psbt_pointer->inputs[index].keypaths, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt input sort keypaths error."); + } +} + +void Psbt::SetTxInSignature( + uint32_t index, const KeyData &key, const ByteData &signature) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + auto pubkey = key.GetPubkey().GetData().GetBytes(); + auto sig = signature.GetBytes(); + + int ret = wally_psbt_input_add_signature( + &psbt_pointer->inputs[index], pubkey.data(), pubkey.size(), sig.data(), + sig.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_add_signature NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt add input sig error."); + } +} + +void Psbt::SetTxInSighashType( + uint32_t index, const SigHashType &sighash_type) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint32_t sighash = sighash_type.GetSigHashFlag(); + + int ret = + wally_psbt_input_set_sighash(&psbt_pointer->inputs[index], sighash); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_sighash NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set input sighash error."); + } +} + +void Psbt::SetTxInFinalScript( + uint32_t index, const std::vector &unlocking_script) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (unlocking_script.empty()) { + warn(CFD_LOG_SOURCE, "unlocking script is empty."); + throw CfdException( + kCfdIllegalArgumentError, "psbt unlocking script is empty."); + } + bool is_witness = false; + auto redeem_script = GetTxInRedeemScript(index, true); + + auto utxo = GetTxInUtxo(index, true, &is_witness); + bool is_wsh = false; + int ret; + if (is_witness) { + auto last_stack = unlocking_script.back(); + if (redeem_script.GetData().Equals(last_stack)) { + is_wsh = true; + } else if (Pubkey::IsValid(last_stack)) { + // p2wpkh + } else { + warn(CFD_LOG_SOURCE, "invalid unlocking_script."); + throw CfdException( + kCfdIllegalArgumentError, "psbt invalid unlocking_script error."); + } + + struct wally_tx_witness_stack *stacks = nullptr; + ret = wally_tx_witness_stack_init_alloc(unlocking_script.size(), &stacks); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_witness_stack_init_alloc NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt init witness stack error."); + } + for (auto script : unlocking_script) { + auto script_val = script.GetBytes(); + ret = wally_tx_witness_stack_add( + stacks, script_val.data(), script_val.size()); + if (ret != WALLY_OK) { + wally_tx_witness_stack_free(stacks); + warn(CFD_LOG_SOURCE, "wally_tx_witness_stack_add NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add witness stack error."); + } + } + + ret = wally_psbt_input_set_final_witness( + &psbt_pointer->inputs[index], stacks); + wally_tx_witness_stack_free(stacks); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_final_witness NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set witness script error."); + } + } else { + Script script_sig; + if (unlocking_script.size() == 1) { + script_sig = Script(unlocking_script[0]); + } else { + ScriptBuilder build; + for (auto script : unlocking_script) { + auto script_val = script.GetBytes(); + if (script_val.size() == 1) { + build.AppendOperator(static_cast(script_val[0])); + } else { + build.AppendData(script); + } + } + script_sig = build.Build(); + } + auto sig_val = script_sig.GetData().GetBytes(); + ret = wally_psbt_input_set_final_scriptsig( + &psbt_pointer->inputs[index], sig_val.data(), sig_val.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_final_scriptsig NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set scriptsig error."); + } + } + + if (is_witness && utxo.GetLockingScript().IsP2shScript()) { + Script locking_script; + if (is_wsh) { + locking_script = ScriptUtil::CreateP2wshLockingScript(redeem_script); + } else if (redeem_script.IsEmpty()) { + auto key = GetTxInKeyData(index, true); + locking_script = ScriptUtil::CreateP2wpkhLockingScript(key.GetPubkey()); + } else { + locking_script = redeem_script; // p2wpkh locking script + } + ScriptBuilder builder; + builder.AppendData(locking_script.GetData()); + auto sig_val = builder.Build().GetData().GetBytes(); + ret = wally_psbt_input_set_final_scriptsig( + &psbt_pointer->inputs[index], sig_val.data(), sig_val.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_input_set_final_scriptsig NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt set scriptsig error."); + } + } +} + +void Psbt::SetTxInRecord( + uint32_t index, const ByteData &key, const ByteData &value) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (key.IsEmpty()) { + warn(CFD_LOG_SOURCE, "psbt empty key error."); + throw CfdException(kCfdIllegalArgumentError, "psbt empty key error."); + } + + auto key_vec = key.GetBytes(); + auto val_vec = value.GetBytes(); + uint8_t type = SetPsbtInput(key_vec, val_vec, &psbt_pointer->inputs[index]); + + struct wally_map *map_ptr = nullptr; + if (type <= Psbt::kPsbtInputFinalScriptWitness) { + if (type == Psbt::kPsbtInputPartialSig) { + map_ptr = &psbt_pointer->inputs[index].signatures; + } else if (type == Psbt::kPsbtInputBip32Derivation) { + map_ptr = &psbt_pointer->inputs[index].keypaths; + } + } else { + map_ptr = &psbt_pointer->inputs[index].unknowns; + } + if (map_ptr != nullptr) { + int ret = wally_map_sort(map_ptr, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt input sort unknowns error."); + } + } +} + +Transaction Psbt::GetTxInUtxoFull( + uint32_t index, bool ignore_error, bool *is_witness) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (psbt_pointer->inputs[index].utxo != nullptr) { + if (is_witness != nullptr) { + *is_witness = (psbt_pointer->inputs[index].witness_utxo != nullptr); + } + return Transaction( + ConvertBitcoinTxFromWally(psbt_pointer->inputs[index].utxo, false)); + } else if (ignore_error) { + return Transaction(); + } else { + warn(CFD_LOG_SOURCE, "utxo full data not found."); + throw CfdException( + kCfdIllegalStateError, "psbt utxo full data not found error."); + } +} + +TxOut Psbt::GetTxInUtxo( + uint32_t index, bool ignore_error, bool *is_witness) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (psbt_pointer->inputs[index].witness_utxo != nullptr) { + if (is_witness != nullptr) *is_witness = true; + return TxOut( + Amount(static_cast( + psbt_pointer->inputs[index].witness_utxo->satoshi)), + Script(ByteData( + psbt_pointer->inputs[index].witness_utxo->script, + static_cast( + psbt_pointer->inputs[index].witness_utxo->script_len)))); + } else if (psbt_pointer->inputs[index].utxo != nullptr) { + if (is_witness != nullptr) { + *is_witness = (psbt_pointer->inputs[index].witness_utxo != nullptr); + } + uint32_t vout = psbt_pointer->tx->inputs[index].index; + return TxOut( + Amount(static_cast( + psbt_pointer->inputs[index].utxo->outputs[vout].satoshi)), + Script(ByteData( + psbt_pointer->inputs[index].utxo->outputs[vout].script, + static_cast( + psbt_pointer->inputs[index].utxo->outputs[vout].script_len)))); + } else if (ignore_error) { + return TxOut(); + } else { + warn(CFD_LOG_SOURCE, "utxo not found."); + throw CfdException(kCfdIllegalStateError, "psbt utxo not found error."); + } +} + +Script Psbt::GetTxInRedeemScript( + uint32_t index, bool ignore_error, bool *is_witness) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (psbt_pointer->inputs[index].witness_script != nullptr) { + if (is_witness != nullptr) *is_witness = true; + return Script(ByteData( + psbt_pointer->inputs[index].witness_script, + static_cast( + psbt_pointer->inputs[index].witness_script_len))); + } else if (psbt_pointer->inputs[index].redeem_script != nullptr) { + if (is_witness != nullptr) *is_witness = false; + return Script(ByteData( + psbt_pointer->inputs[index].redeem_script, + static_cast(psbt_pointer->inputs[index].redeem_script_len))); + } else if (ignore_error) { + return Script(); + } else { + warn(CFD_LOG_SOURCE, "script not found."); + throw CfdException(kCfdIllegalStateError, "psbt script not found error."); + } +} + +Script Psbt::GetTxInRedeemScriptDirect( + uint32_t index, bool ignore_error, bool is_witness) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (is_witness && (psbt_pointer->inputs[index].witness_script != nullptr)) { + return Script(ByteData( + psbt_pointer->inputs[index].witness_script, + static_cast( + psbt_pointer->inputs[index].witness_script_len))); + } else if ( + (!is_witness) && + (psbt_pointer->inputs[index].redeem_script != nullptr)) { + return Script(ByteData( + psbt_pointer->inputs[index].redeem_script, + static_cast(psbt_pointer->inputs[index].redeem_script_len))); + } else if (ignore_error) { + return Script(); + } else { + warn(CFD_LOG_SOURCE, "script not found."); + throw CfdException(kCfdIllegalStateError, "psbt script not found error."); + } +} + +std::vector Psbt::GetTxInKeyDataList(uint32_t index) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + size_t key_max = psbt_pointer->inputs[index].keypaths.num_items; + std::vector arr; + arr.reserve(key_max); + struct wally_map_item *item; + for (size_t key_index = 0; key_index < key_max; ++key_index) { + item = &psbt_pointer->inputs[index].keypaths.items[key_index]; + ByteData key(item->key, static_cast(item->key_len)); + Pubkey pubkey(key); + ByteData fingerprint; + std::vector path; + if (((item->value_len % 4) == 0) && (item->value_len > 0)) { + fingerprint = ByteData(item->value, 4); + + // TODO(k-matsuzawa) Need endian support. + size_t arr_max = item->value_len / 4; + uint32_t *val_arr = reinterpret_cast(item->value); + for (size_t arr_index = 1; arr_index < arr_max; ++arr_index) { + path.push_back(val_arr[arr_index]); + } + } + arr.emplace_back(KeyData(pubkey, path, fingerprint)); + } + return arr; +} + +KeyData Psbt::GetTxInKeyData(uint32_t index, bool ignore_error) const { + std::vector keys = GetTxInKeyDataList(index); + if (!keys.empty()) { + return keys[0]; + } else if (ignore_error) { + return KeyData(); + } else { + warn(CFD_LOG_SOURCE, "key not found."); + throw CfdException(kCfdIllegalStateError, "psbt key not found error."); + } +} + +std::vector Psbt::GetTxInSignaturePubkeyList(uint32_t index) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + size_t key_max = psbt_pointer->inputs[index].signatures.num_items; + std::vector arr; + arr.reserve(key_max); + struct wally_map_item *item; + for (size_t key_index = 0; key_index < key_max; ++key_index) { + item = &psbt_pointer->inputs[index].signatures.items[key_index]; + ByteData key(item->key, static_cast(item->key_len)); + Pubkey pubkey(key); + arr.emplace_back(pubkey); + } + return arr; +} + +ByteData Psbt::GetTxInSignature(uint32_t index, const Pubkey &pubkey) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + auto key_vec = pubkey.GetData().GetBytes(); + size_t exist = 0; + int ret = wally_map_find( + &psbt_pointer->inputs[index].signatures, key_vec.data(), key_vec.size(), + &exist); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_find NG[{}]", ret); + throw CfdException(kCfdMemoryFullError, "psbt find signature key error."); + } + if (exist == 0) { + warn(CFD_LOG_SOURCE, "target key not found."); + throw CfdException( + kCfdIllegalStateError, "psbt signature target key not found."); + } + uint32_t map_index = static_cast(exist) - 1; + return ByteData( + psbt_pointer->inputs[index].signatures.items[map_index].value, + static_cast( + psbt_pointer->inputs[index].signatures.items[map_index].value_len)); +} + +bool Psbt::IsFindTxInSignature(uint32_t index, const Pubkey &pubkey) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + auto key_vec = pubkey.GetData().GetBytes(); + size_t exist = 0; + int ret = wally_map_find( + &psbt_pointer->inputs[index].signatures, key_vec.data(), key_vec.size(), + &exist); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_find NG[{}]", ret); + throw CfdException(kCfdMemoryFullError, "psbt find signature key error."); + } + return (exist == 0) ? false : true; +} + +SigHashType Psbt::GetTxInSighashType(uint32_t index) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (psbt_pointer->inputs[index].sighash != 0) { + SigHashType sighash_type; + sighash_type.SetFromSigHashFlag( + static_cast(psbt_pointer->inputs[index].sighash)); + return sighash_type; + } else { + warn(CFD_LOG_SOURCE, "sighash not found."); + throw CfdException(kCfdIllegalStateError, "psbt sighash not found error."); + } +} + +bool Psbt::IsFindTxInSighashType(uint32_t index) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + return psbt_pointer->inputs[index].sighash != 0; +} + +std::vector Psbt::GetTxInFinalScript( + uint32_t index, bool is_witness_stack) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + std::vector result; + + if (is_witness_stack) { + auto stacks = psbt_pointer->inputs[index].final_witness; + if (stacks != nullptr) { + for (size_t stack_idx = 0; stack_idx < stacks->num_items; ++stack_idx) { + result.emplace_back(ByteData( + stacks->items[stack_idx].witness, + static_cast(stacks->items[stack_idx].witness_len))); + } + } + } else { + result.emplace_back(ByteData( + psbt_pointer->inputs[index].final_scriptsig, + static_cast( + psbt_pointer->inputs[index].final_scriptsig_len))); + } + return result; +} + +ByteData Psbt::GetTxInRecord(uint32_t index, const ByteData &key) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + return GetPsbtInput(key, &psbt_pointer->inputs[index], nullptr); +} + +bool Psbt::IsFindTxInRecord(uint32_t index, const ByteData &key) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + bool is_find = false; + GetPsbtInput(key, &psbt_pointer->inputs[index], &is_find); + return is_find; +} + +std::vector Psbt::GetTxInRecordKeyList(uint32_t index) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + std::vector result; + auto input = &psbt_pointer->inputs[index]; + for (size_t idx = 0; idx < input->unknowns.num_items; ++idx) { + auto item = &input->unknowns.items[idx]; + result.emplace_back( + ByteData(item->key, static_cast(item->key_len))); + } + return result; +} + +void Psbt::ClearTxInSignData(uint32_t index) { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + struct wally_psbt_input *input = &psbt_pointer->inputs[index]; + + if (input->redeem_script != nullptr) { + memset(input->redeem_script, 0, input->redeem_script_len); + FreeWallyBuffer(input->redeem_script); + input->redeem_script_len = 0; + input->redeem_script = nullptr; + } + if (input->witness_script != nullptr) { + memset(input->witness_script, 0, input->witness_script_len); + FreeWallyBuffer(input->witness_script); + input->witness_script_len = 0; + input->witness_script = nullptr; + } + for (size_t idx = 0; idx < input->keypaths.num_items; ++idx) { + auto keypath = &input->keypaths.items[idx]; + memset(keypath->key, 0, keypath->key_len); + memset(keypath->value, 0, keypath->value_len); + FreeWallyBuffer(keypath->key); + FreeWallyBuffer(keypath->value); + memset(keypath, 0, sizeof(*keypath)); + } + input->keypaths.num_items = 0; + for (size_t idx = 0; idx < input->signatures.num_items; ++idx) { + auto sig = &input->signatures.items[idx]; + memset(sig->key, 0, sig->key_len); + memset(sig->value, 0, sig->value_len); + FreeWallyBuffer(sig->key); + FreeWallyBuffer(sig->value); + memset(sig, 0, sizeof(*sig)); + } + input->signatures.num_items = 0; + input->sighash = 0; +} + +uint32_t Psbt::AddTxOut(const TxOut &txout) { + return AddTxOut(txout.GetLockingScript(), txout.GetValue()); +} + +uint32_t Psbt::AddTxOut(const TxOutReference &txout) { + return AddTxOut(txout.GetLockingScript(), txout.GetValue()); +} + +uint32_t Psbt::AddTxOut(const Script &locking_script, const Amount &amount) { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint32_t index = static_cast(psbt_pointer->num_outputs); + auto script = locking_script.GetData().GetBytes(); + struct wally_tx_output *output = nullptr; + + int ret = wally_tx_output_init_alloc( + static_cast(amount.GetSatoshiValue()), script.data(), + script.size(), &output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_output_init_alloc NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt alloc output error."); + } + + ret = wally_psbt_add_output_at(psbt_pointer, index, 0, output); + wally_tx_output_free(output); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_psbt_add_output_at NG[{}]", ret); + throw CfdException(kCfdIllegalArgumentError, "psbt add output error."); + } + base_tx_ = RebuildTransaction(wally_psbt_pointer_); + return index; +} + +void Psbt::SetTxOutData(uint32_t index, const KeyData &key) { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + std::vector arr = GetTxOutKeyDataList(index); + Pubkey pubkey = key.GetPubkey(); + for (auto &item : arr) { + if (pubkey.Equals(item.GetPubkey())) return; + } + + struct wally_tx_output *txout = &psbt_pointer->tx->outputs[index]; + Script locking_script( + ByteData(txout->script, static_cast(txout->script_len))); + Script redeem_script; + Script script; + + if (locking_script.IsP2pkhScript()) { + script = ScriptUtil::CreateP2pkhLockingScript(pubkey); + } else if (locking_script.IsP2wpkhScript()) { + script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); + } else if (locking_script.IsP2shScript()) { + auto wpkh_script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); + script = ScriptUtil::CreateP2shLockingScript(wpkh_script); + redeem_script = wpkh_script; + } + if (!locking_script.Equals(script)) { + warn(CFD_LOG_SOURCE, "unmatch pubkey."); + throw CfdException(kCfdIllegalArgumentError, "psbt unmatch pubkey error."); + } + + if (!GetTxOutScript(index, true).IsEmpty()) redeem_script = Script(); + SetTxOutData(index, redeem_script, key); +} + +void Psbt::SetTxOutData( + uint32_t index, const Script &redeem_script, const KeyData &key) { + SetTxOutData(index, redeem_script, std::vector{key}); +} + +void Psbt::SetTxOutData( + uint32_t index, const Script &redeem_script, + const std::vector &key_list) { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + struct wally_tx_output *txout = &psbt_pointer->tx->outputs[index]; + Script script( + ByteData(txout->script, static_cast(txout->script_len))); + ByteData256 empty_bytes; + Txid txid(empty_bytes); + Script new_redeem_script = redeem_script; + bool is_witness = ValidatePsbtUtxo( + txid, index, script, redeem_script, key_list, &new_redeem_script); + + int ret; + if (!new_redeem_script.IsEmpty()) { + auto script_val = new_redeem_script.GetData().GetBytes(); + if (is_witness && (!new_redeem_script.IsP2wpkhScript())) { + ret = wally_psbt_output_set_witness_script( + &psbt_pointer->outputs[index], script_val.data(), script_val.size()); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_psbt_output_set_witness_script NG[{}]", + ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add output witness script error."); + } + if (script.IsP2shScript()) { + script_val = ScriptUtil::CreateP2wshLockingScript(new_redeem_script) + .GetData() + .GetBytes(); + } else { + script_val.clear(); + } + } + if (!script_val.empty()) { + ret = wally_psbt_output_set_redeem_script( + &psbt_pointer->outputs[index], script_val.data(), script_val.size()); + if (ret != WALLY_OK) { + warn( + CFD_LOG_SOURCE, "wally_psbt_output_set_redeem_script NG[{}]", ret); + throw CfdException( + kCfdIllegalArgumentError, "psbt add output redeem script error."); + } + } + } + + if (!key_list.empty()) { + SetKeyPathMap(key_list, &psbt_pointer->outputs[index].keypaths); + ret = wally_map_sort(&psbt_pointer->outputs[index].keypaths, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException( + kCfdInternalError, "psbt output sort keypaths error."); + } + } +} + +void Psbt::SetTxOutRecord( + uint32_t index, const ByteData &key, const ByteData &value) { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (key.IsEmpty()) { + warn(CFD_LOG_SOURCE, "psbt empty key error."); + throw CfdException(kCfdIllegalArgumentError, "psbt empty key error."); + } + + auto key_vec = key.GetBytes(); + auto val_vec = value.GetBytes(); + uint8_t type = + SetPsbtOutput(key_vec, val_vec, &psbt_pointer->outputs[index]); + + struct wally_map *map_ptr = nullptr; + switch (type) { + case kPsbtOutputRedeemScript: + // fall-through + case kPsbtOutputWitnessScript: + break; + case kPsbtOutputBip32Derivation: + map_ptr = &psbt_pointer->outputs[index].keypaths; + break; + default: + map_ptr = &psbt_pointer->outputs[index].unknowns; + break; + } + if (map_ptr != nullptr) { + int ret = wally_map_sort(map_ptr, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException( + kCfdInternalError, "psbt output sort unknowns error."); + } + } +} + +Script Psbt::GetTxOutScript( + uint32_t index, bool ignore_error, bool *is_witness) const { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + if (psbt_pointer->outputs[index].witness_script != nullptr) { + if (is_witness != nullptr) *is_witness = true; + return Script(ByteData( + psbt_pointer->outputs[index].witness_script, + static_cast( + psbt_pointer->outputs[index].witness_script_len))); + } else if (psbt_pointer->outputs[index].redeem_script != nullptr) { + if (is_witness != nullptr) *is_witness = false; + return Script(ByteData( + psbt_pointer->outputs[index].redeem_script, + static_cast( + psbt_pointer->outputs[index].redeem_script_len))); + } else if (ignore_error) { + return Script(); + } else { + warn(CFD_LOG_SOURCE, "script not found."); + throw CfdException(kCfdIllegalStateError, "psbt script not found error."); + } +} + +KeyData Psbt::GetTxOutKeyData(uint32_t index, bool ignore_error) const { + auto arr = GetTxOutKeyDataList(index); + if (arr.size() > 0) { + return arr[0]; + } else if (ignore_error) { + return KeyData(); + } else { + warn(CFD_LOG_SOURCE, "key not found."); + throw CfdException(kCfdIllegalStateError, "psbt key not found error."); + } +} + +std::vector Psbt::GetTxOutKeyDataList(uint32_t index) const { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + size_t key_max = psbt_pointer->outputs[index].keypaths.num_items; + std::vector arr; + arr.reserve(key_max); + struct wally_map_item *item; + for (size_t key_index = 0; key_index < key_max; ++key_index) { + item = &psbt_pointer->outputs[index].keypaths.items[key_index]; + ByteData key(item->key, static_cast(item->key_len)); + Pubkey pubkey(key); + ByteData fingerprint; + std::vector path; + if (((item->value_len % 4) == 0) && (item->value_len > 0)) { + fingerprint = ByteData(item->value, 4); + + // TODO(k-matsuzawa) Need endian support. + size_t arr_max = item->value_len / 4; + uint32_t *val_arr = reinterpret_cast(item->value); + for (size_t arr_index = 1; arr_index < arr_max; ++arr_index) { + path.push_back(val_arr[arr_index]); + } + } + arr.emplace_back(KeyData(pubkey, path, fingerprint)); + } + return arr; +} + +ByteData Psbt::GetTxOutRecord(uint32_t index, const ByteData &key) const { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + return GetPsbtOutput(key, &psbt_pointer->outputs[index], nullptr); +} + +bool Psbt::IsFindTxOutRecord(uint32_t index, const ByteData &key) const { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + bool is_find = false; + GetPsbtOutput(key, &psbt_pointer->outputs[index], &is_find); + return is_find; +} + +std::vector Psbt::GetTxOutRecordKeyList(uint32_t index) const { + CheckTxOutIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + std::vector result; + auto output = &psbt_pointer->outputs[index]; + for (size_t idx = 0; idx < output->unknowns.num_items; ++idx) { + auto item = &output->unknowns.items[idx]; + result.emplace_back( + ByteData(item->key, static_cast(item->key_len))); + } + return result; +} + +uint32_t Psbt::GetPsbtVersion() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + return psbt_pointer->version; +} + +void Psbt::SetGlobalXpubkey(const KeyData &key) { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + + if (!key.HasExtPubkey()) { + warn(CFD_LOG_SOURCE, "psbt global xpub can set only ExtPubkey."); + throw CfdException( + kCfdIllegalArgumentError, "psbt global xpub can set only ExtPubkey."); + } + uint8_t xpub_key = Psbt::kPsbtGlobalXpub; + ByteData key_top(&xpub_key, 1); + ByteData key_data = key_top.Concat(key.GetExtPubkey().GetData()); + + auto fingerprint = key.GetFingerprint().GetBytes(); + auto num_list = key.GetChildNumArray(); + if (fingerprint.size() < 4) { + warn(CFD_LOG_SOURCE, "psbt fingerprint size low 4 byte."); + throw CfdException( + kCfdIllegalArgumentError, "psbt fingerprint size low 4 byte."); + } + // if (num_list.empty()) { + // warn(CFD_LOG_SOURCE, "psbt empty bip32 path."); + // throw CfdException(kCfdIllegalArgumentError, "psbt empty bip32 path."); + // } + Serializer builder(4 + (static_cast(num_list.size()) * 4)); + builder.AddDirectBytes(fingerprint.data(), 4); + for (const auto child_num : num_list) { + builder.AddDirectNumber(child_num); + } + SetGlobalRecord(key_data, builder.Output()); +} + +KeyData Psbt::GetGlobalXpubkeyBip32(const ExtPubkey &key) const { + uint8_t xpub_key = Psbt::kPsbtGlobalXpub; + ByteData key_top(&xpub_key, 1); + ByteData key_data = key_top.Concat(key.GetData()); + auto data = GetGlobalRecord(key_data); + + ByteData fingerprint; + std::vector path; + if (((data.GetDataSize() % 4) == 0) && (data.GetDataSize() > 0)) { + auto data_arr = data.GetBytes(); + fingerprint = ByteData(data_arr.data(), 4); + + // TODO(k-matsuzawa) Need endian support. + size_t arr_max = data_arr.size() / 4; + uint32_t *val_arr = reinterpret_cast(data_arr.data()); + for (size_t arr_index = 1; arr_index < arr_max; ++arr_index) { + path.push_back(val_arr[arr_index]); + } + } + return KeyData(key, path, fingerprint); +} + +bool Psbt::IsFindGlobalXpubkey(const ExtPubkey &key) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + bool is_find = false; + uint8_t xpub_key = Psbt::kPsbtGlobalXpub; + ByteData key_top(&xpub_key, 1); + ByteData key_data = key_top.Concat(key.GetData()); + GetPsbtGlobal(key_data, psbt_pointer, &is_find); + return is_find; +} + +std::vector Psbt::GetGlobalXpubkeyDataList() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } + + size_t key_max = psbt_pointer->unknowns.num_items; + std::vector arr; + arr.reserve(key_max); + struct wally_map_item *item; + for (size_t key_index = 0; key_index < key_max; ++key_index) { + item = &psbt_pointer->unknowns.items[key_index]; + if (item->key_len != kPsbtGlobalXpubSize) continue; + if (item->key[0] != Psbt::kPsbtGlobalXpub) continue; + ByteData key(&item->key[1], static_cast(item->key_len) - 1); + ExtPubkey ext_pubkey(key); + + ByteData fingerprint; + std::vector path; + if (((item->value_len % 4) == 0) && (item->value_len > 0)) { + fingerprint = ByteData(item->value, 4); + + // TODO(k-matsuzawa) Need endian support. + size_t arr_max = item->value_len / 4; + uint32_t *val_arr = reinterpret_cast(item->value); + for (size_t arr_index = 1; arr_index < arr_max; ++arr_index) { + path.push_back(val_arr[arr_index]); + } + } + arr.emplace_back(KeyData(ext_pubkey, path, fingerprint)); + } + return arr; +} + +void Psbt::SetGlobalRecord(const ByteData &key, const ByteData &value) { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (key.IsEmpty()) { + warn(CFD_LOG_SOURCE, "psbt empty key error."); + throw CfdException(kCfdIllegalArgumentError, "psbt empty key error."); + } + auto key_vec = key.GetBytes(); + auto val_vec = value.GetBytes(); + + SetPsbtGlobal(key_vec, val_vec, psbt_pointer); + + int ret = wally_map_sort(&psbt_pointer->unknowns, 0); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_map_sort NG[{}]", ret); + throw CfdException(kCfdInternalError, "psbt sort unknowns error."); + } +} + +ByteData Psbt::GetGlobalRecord(const ByteData &key) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + return GetPsbtGlobal(key, psbt_pointer, nullptr); +} + +bool Psbt::IsFindGlobalRecord(const ByteData &key) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + bool is_find = false; + GetPsbtGlobal(key, psbt_pointer, &is_find); + return is_find; +} + +std::vector Psbt::GetGlobalRecordKeyList() const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + std::vector result; + for (size_t idx = 0; idx < psbt_pointer->unknowns.num_items; ++idx) { + auto item = &psbt_pointer->unknowns.items[idx]; + result.emplace_back( + ByteData(item->key, static_cast(item->key_len))); + } + return result; +} + +void Psbt::CheckTxInIndex(uint32_t index, int line, const char *caller) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } else if (psbt_pointer->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt base tx is null"); + throw CfdException(kCfdIllegalStateError, "psbt base tx is null."); + } else if (psbt_pointer->num_inputs <= index) { + spdlog::source_loc location = {CFD_LOG_FILE, line, caller}; + warn(location, "psbt vin[{}] out_of_range.", index); + throw CfdException(kCfdOutOfRangeError, "psbt vin out_of_range error."); + } else if (psbt_pointer->tx->num_inputs <= index) { + spdlog::source_loc location = {CFD_LOG_FILE, line, caller}; + warn(location, "tx vin[{}] out_of_range.", index); + throw CfdException(kCfdOutOfRangeError, "tx vin out_of_range error."); + } +} + +void Psbt::CheckTxOutIndex( + uint32_t index, int line, const char *caller) const { + struct wally_psbt *psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + if (psbt_pointer == nullptr) { + warn(CFD_LOG_SOURCE, "psbt pointer is null"); + throw CfdException(kCfdIllegalStateError, "psbt pointer is null."); + } else if (psbt_pointer->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt base tx is null"); + throw CfdException(kCfdIllegalStateError, "psbt base tx is null."); + } else if (psbt_pointer->num_outputs <= index) { + spdlog::source_loc location = {CFD_LOG_FILE, line, caller}; + warn(location, "psbt vout[{}] out_of_range.", index); + throw CfdException(kCfdOutOfRangeError, "psbt vout out_of_range error."); + } else if (psbt_pointer->tx->num_outputs <= index) { + spdlog::source_loc location = {CFD_LOG_FILE, line, caller}; + warn(location, "tx vout[{}] out_of_range.", index); + throw CfdException(kCfdOutOfRangeError, "tx vout out_of_range error."); + } +} + +} // namespace core +} // namespace cfd diff --git a/src/cfdcore_schnorrsig.cpp b/src/cfdcore_schnorrsig.cpp index fda861a2..33ce96f7 100644 --- a/src/cfdcore_schnorrsig.cpp +++ b/src/cfdcore_schnorrsig.cpp @@ -7,6 +7,7 @@ #include #include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_util.h" #include "secp256k1.h" // NOLINT #include "secp256k1_schnorrsig.h" // NOLINT #include "secp256k1_util.h" // NOLINT @@ -19,14 +20,27 @@ using cfd::core::ByteData; using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; +using cfd::core::HashUtil; // ---------------------------------------------------------------------------- // SchnorrSignature // ---------------------------------------------------------------------------- -SchnorrSignature::SchnorrSignature() : data_() {} - -SchnorrSignature::SchnorrSignature(const ByteData &data) : data_(data) { - if ((data_.GetDataSize()) != SchnorrSignature::kSchnorrSignatureSize) { +SchnorrSignature::SchnorrSignature() + : data_(), sighash_type_(SigHashAlgorithm::kSigHashDefault) {} + +SchnorrSignature::SchnorrSignature(const ByteData &data) + : data_(data), sighash_type_(SigHashAlgorithm::kSigHashDefault) { + if (data_.GetDataSize() == SchnorrSignature::kSchnorrSignatureSize + 1) { + auto bytes = data.GetBytes(); + uint8_t sighash_type = bytes[SchnorrSignature::kSchnorrSignatureSize]; + if ((sighash_type == 0) || (!IsValidSigHashType(sighash_type))) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid Schnorr signature hash type."); + } + sighash_type_.SetFromSigHashFlag(sighash_type); + data_ = ByteData(bytes.data(), SchnorrSignature::kSchnorrSignatureSize); + } else if (data_.GetDataSize() != SchnorrSignature::kSchnorrSignatureSize) { throw CfdException( CfdError::kCfdIllegalArgumentError, "Invalid Schnorr signature data."); } @@ -35,9 +49,35 @@ SchnorrSignature::SchnorrSignature(const ByteData &data) : data_(data) { SchnorrSignature::SchnorrSignature(const std::string &data) : SchnorrSignature(ByteData(data)) {} -ByteData SchnorrSignature::GetData() const { return data_; } +SchnorrSignature::SchnorrSignature(const SchnorrSignature &object) { + data_ = object.data_; + sighash_type_ = object.sighash_type_; +} + +SchnorrSignature &SchnorrSignature::operator=(const SchnorrSignature &object) { + if (this != &object) { + data_ = object.data_; + sighash_type_ = object.sighash_type_; + } + return *this; +} + +ByteData SchnorrSignature::GetData(bool append_sighash_type) const { + if ((!append_sighash_type) || (sighash_type_.GetSigHashFlag() == 0) || + (data_.GetDataSize() != SchnorrSignature::kSchnorrSignatureSize)) { + return data_; + } + + uint8_t sighash_type = static_cast(sighash_type_.GetSigHashFlag()); + return data_.Concat(ByteData(sighash_type)); +} + +std::string SchnorrSignature::GetHex(bool append_sighash_type) const { + if (sighash_type_.GetSigHashFlag() == 0) return data_.GetHex(); + return GetData(append_sighash_type).GetHex(); +} -std::string SchnorrSignature::GetHex() const { return data_.GetHex(); } +SigHashType SchnorrSignature::GetSigHashType() const { return sighash_type_; } SchnorrPubkey SchnorrSignature::GetNonce() const { auto bytes = data_.GetBytes(); @@ -52,6 +92,26 @@ Privkey SchnorrSignature::GetPrivkey() const { return Privkey(ByteData(std::vector(start, end))); } +void SchnorrSignature::SetSigHashType(const SigHashType &sighash_type) { + if (!IsValidSigHashType( + static_cast(sighash_type.GetSigHashFlag()))) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid sighash type for schnorr signature."); + } + sighash_type_ = sighash_type; +} + +bool SchnorrSignature::IsValidSigHashType(uint8_t sighash_type_value) { + bool is_anyone_can_pay = (sighash_type_value & 0x80) ? true : false; + if ((is_anyone_can_pay && + ((sighash_type_value <= 0x80) || (sighash_type_value > 0x83))) || + ((!is_anyone_can_pay) && (sighash_type_value > 0x03))) { + return false; + } + return true; +} + // ---------------------------------------------------------------------------- // SchnorrPubkey // ---------------------------------------------------------------------------- @@ -150,6 +210,10 @@ SchnorrPubkey SchnorrPubkey::CreateTweakAddFromPrivkey( ByteData SchnorrPubkey::GetData() const { return data_.GetData(); } +ByteData256 SchnorrPubkey::GetByteData256() const { + return ByteData256(data_); +} + std::string SchnorrPubkey::GetHex() const { return data_.GetHex(); } bool SchnorrPubkey::Equals(const SchnorrPubkey &pubkey) const { @@ -181,7 +245,7 @@ bool SchnorrPubkey::Verify( Pubkey SchnorrPubkey::CreatePubkey(bool parity) const { uint8_t head = (parity) ? 3 : 2; - ByteData data = ByteData(&head, 1).Concat(data_); + ByteData data = ByteData(head).Concat(data_); return Pubkey(data); } @@ -272,7 +336,7 @@ SchnorrSignature SignCommon( ret = secp256k1_schnorrsig_sign( ctx, raw_sig.data(), msg.GetBytes().data(), &keypair, nfn, - ndata.GetBytes().data()); + (ndata.IsEmpty()) ? nullptr : ndata.GetBytes().data()); if (ret != 1) { throw CfdException( @@ -282,6 +346,10 @@ SchnorrSignature SignCommon( return SchnorrSignature(raw_sig); } +SchnorrSignature SchnorrUtil::Sign(const ByteData256 &msg, const Privkey &sk) { + return SignCommon(msg, sk, nullptr, ByteData()); +} + SchnorrSignature SchnorrUtil::Sign( const ByteData256 &msg, const Privkey &sk, const ByteData256 &aux_rand) { return SignCommon(msg, sk, nullptr, aux_rand.GetData()); @@ -312,6 +380,50 @@ Pubkey SchnorrUtil::ComputeSigPoint( return ConvertSecpPubkey(secp_sigpoint); } +Pubkey SchnorrUtil::ComputeSigPointBatch( + const std::vector &msgs, + const std::vector &nonces, const SchnorrPubkey &pubkey) { + if (msgs.size() != nonces.size() || msgs.empty()) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Expected same number of messages or nonces, and at least one " + "message."); + } + + Pubkey rs; + + if (msgs.size() == 1) { + rs = Pubkey(ByteData("02").Concat(nonces[0].GetData())); + } else { + std::vector pub_nonces; + for (const auto &nonce : nonces) { + pub_nonces.push_back(Pubkey(ByteData("02").Concat(nonce.GetData()))); + } + rs = Pubkey::CombinePubkey(pub_nonces); + } + + auto bip340_challenge = ByteData( + "7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c7bb52d7" + "a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c"); + Privkey res; + for (size_t i = 0; i < msgs.size(); i++) { + auto m_tagged_hash = + HashUtil::Sha256(bip340_challenge.Concat(nonces[i].GetData()) + .Concat(pubkey.GetData()) + .Concat(msgs[i])); + if (i == 0) { + res = Privkey(m_tagged_hash); + } else { + res = res.CreateTweakAdd(m_tagged_hash); + } + } + + auto xe = Pubkey(ByteData("02").Concat(pubkey.GetData())) + .CreateTweakMul(ByteData256(res.GetData())); + + return Pubkey::CombinePubkey({rs, xe}); +} + bool SchnorrUtil::Verify( const SchnorrSignature &signature, const ByteData256 &msg, const SchnorrPubkey &pubkey) { diff --git a/src/cfdcore_script.cpp b/src/cfdcore_script.cpp index ee1f2e64..a656e675 100644 --- a/src/cfdcore_script.cpp +++ b/src/cfdcore_script.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_script.cpp * - * @brief \~japanese Script関連クラス実装 - * \~english implementation of Script related class + * @brief implementation of Script related class */ #include "cfdcore/cfdcore_script.h" @@ -173,6 +172,8 @@ const ScriptOperator ScriptOperator::OP_NOP7(kOpNop7, "OP_NOP7"); const ScriptOperator ScriptOperator::OP_NOP8(kOpNop8, "OP_NOP8"); const ScriptOperator ScriptOperator::OP_NOP9(kOpNop9, "OP_NOP9"); const ScriptOperator ScriptOperator::OP_NOP10(kOpNop10, "OP_NOP10"); +const ScriptOperator ScriptOperator::OP_CHECKSIGADD( + kOpCheckSigAdd, "OP_CHECKSIGADD"); const ScriptOperator ScriptOperator::OP_INVALIDOPCODE( kOpInvalidOpCode, "OP_INVALIDOPCODE"); #ifndef CFD_DISABLE_ELEMENTS @@ -189,6 +190,180 @@ const ScriptOperator ScriptOperator::OP_PUBKEYHASH( kOpPubkeyHash, "OP_PUBKEYHASH"); const ScriptOperator ScriptOperator::OP_PUBKEY(kOpPubkey, "OP_PUBKEY"); #endif // CFD_DISABLE_ELEMENTS +const ScriptOperator ScriptOperator::OP_SUCCESS80( + kOpSuccess80, "OP_SUCCESS80"); +const ScriptOperator ScriptOperator::OP_SUCCESS98( + kOpSuccess98, "OP_SUCCESS98"); +const ScriptOperator ScriptOperator::OP_SUCCESS126( + kOpSuccess126, "OP_SUCCESS126"); +const ScriptOperator ScriptOperator::OP_SUCCESS127( + kOpSuccess127, "OP_SUCCESS127"); +const ScriptOperator ScriptOperator::OP_SUCCESS128( + kOpSuccess128, "OP_SUCCESS128"); +const ScriptOperator ScriptOperator::OP_SUCCESS129( + kOpSuccess129, "OP_SUCCESS129"); +const ScriptOperator ScriptOperator::OP_SUCCESS131( + kOpSuccess131, "OP_SUCCESS131"); +const ScriptOperator ScriptOperator::OP_SUCCESS132( + kOpSuccess132, "OP_SUCCESS132"); +const ScriptOperator ScriptOperator::OP_SUCCESS133( + kOpSuccess133, "OP_SUCCESS133"); +const ScriptOperator ScriptOperator::OP_SUCCESS134( + kOpSuccess134, "OP_SUCCESS134"); +const ScriptOperator ScriptOperator::OP_SUCCESS137( + kOpSuccess137, "OP_SUCCESS137"); +const ScriptOperator ScriptOperator::OP_SUCCESS138( + kOpSuccess138, "OP_SUCCESS138"); +const ScriptOperator ScriptOperator::OP_SUCCESS141( + kOpSuccess141, "OP_SUCCESS141"); +const ScriptOperator ScriptOperator::OP_SUCCESS142( + kOpSuccess142, "OP_SUCCESS142"); +const ScriptOperator ScriptOperator::OP_SUCCESS149( + kOpSuccess149, "OP_SUCCESS149"); +const ScriptOperator ScriptOperator::OP_SUCCESS150( + kOpSuccess150, "OP_SUCCESS150"); +const ScriptOperator ScriptOperator::OP_SUCCESS151( + kOpSuccess151, "OP_SUCCESS151"); +const ScriptOperator ScriptOperator::OP_SUCCESS152( + kOpSuccess152, "OP_SUCCESS152"); +const ScriptOperator ScriptOperator::OP_SUCCESS153( + kOpSuccess153, "OP_SUCCESS153"); +const ScriptOperator ScriptOperator::OP_SUCCESS187( + kOpSuccess187, "OP_SUCCESS187"); +const ScriptOperator ScriptOperator::OP_SUCCESS188( + kOpSuccess188, "OP_SUCCESS188"); +const ScriptOperator ScriptOperator::OP_SUCCESS189( + kOpSuccess189, "OP_SUCCESS189"); +const ScriptOperator ScriptOperator::OP_SUCCESS190( + kOpSuccess190, "OP_SUCCESS190"); +const ScriptOperator ScriptOperator::OP_SUCCESS191( + kOpSuccess191, "OP_SUCCESS191"); +const ScriptOperator ScriptOperator::OP_SUCCESS192( + kOpSuccess192, "OP_SUCCESS192"); +const ScriptOperator ScriptOperator::OP_SUCCESS193( + kOpSuccess193, "OP_SUCCESS193"); +const ScriptOperator ScriptOperator::OP_SUCCESS194( + kOpSuccess194, "OP_SUCCESS194"); +const ScriptOperator ScriptOperator::OP_SUCCESS195( + kOpSuccess195, "OP_SUCCESS195"); +const ScriptOperator ScriptOperator::OP_SUCCESS196( + kOpSuccess196, "OP_SUCCESS196"); +const ScriptOperator ScriptOperator::OP_SUCCESS197( + kOpSuccess197, "OP_SUCCESS197"); +const ScriptOperator ScriptOperator::OP_SUCCESS198( + kOpSuccess198, "OP_SUCCESS198"); +const ScriptOperator ScriptOperator::OP_SUCCESS199( + kOpSuccess199, "OP_SUCCESS199"); +const ScriptOperator ScriptOperator::OP_SUCCESS200( + kOpSuccess200, "OP_SUCCESS200"); +const ScriptOperator ScriptOperator::OP_SUCCESS201( + kOpSuccess201, "OP_SUCCESS201"); +const ScriptOperator ScriptOperator::OP_SUCCESS202( + kOpSuccess202, "OP_SUCCESS202"); +const ScriptOperator ScriptOperator::OP_SUCCESS203( + kOpSuccess203, "OP_SUCCESS203"); +const ScriptOperator ScriptOperator::OP_SUCCESS204( + kOpSuccess204, "OP_SUCCESS204"); +const ScriptOperator ScriptOperator::OP_SUCCESS205( + kOpSuccess205, "OP_SUCCESS205"); +const ScriptOperator ScriptOperator::OP_SUCCESS206( + kOpSuccess206, "OP_SUCCESS206"); +const ScriptOperator ScriptOperator::OP_SUCCESS207( + kOpSuccess207, "OP_SUCCESS207"); +const ScriptOperator ScriptOperator::OP_SUCCESS208( + kOpSuccess208, "OP_SUCCESS208"); +const ScriptOperator ScriptOperator::OP_SUCCESS209( + kOpSuccess209, "OP_SUCCESS209"); +const ScriptOperator ScriptOperator::OP_SUCCESS210( + kOpSuccess210, "OP_SUCCESS210"); +const ScriptOperator ScriptOperator::OP_SUCCESS211( + kOpSuccess211, "OP_SUCCESS211"); +const ScriptOperator ScriptOperator::OP_SUCCESS212( + kOpSuccess212, "OP_SUCCESS212"); +const ScriptOperator ScriptOperator::OP_SUCCESS213( + kOpSuccess213, "OP_SUCCESS213"); +const ScriptOperator ScriptOperator::OP_SUCCESS214( + kOpSuccess214, "OP_SUCCESS214"); +const ScriptOperator ScriptOperator::OP_SUCCESS215( + kOpSuccess215, "OP_SUCCESS215"); +const ScriptOperator ScriptOperator::OP_SUCCESS216( + kOpSuccess216, "OP_SUCCESS216"); +const ScriptOperator ScriptOperator::OP_SUCCESS217( + kOpSuccess217, "OP_SUCCESS217"); +const ScriptOperator ScriptOperator::OP_SUCCESS218( + kOpSuccess218, "OP_SUCCESS218"); +const ScriptOperator ScriptOperator::OP_SUCCESS219( + kOpSuccess219, "OP_SUCCESS219"); +const ScriptOperator ScriptOperator::OP_SUCCESS220( + kOpSuccess220, "OP_SUCCESS220"); +const ScriptOperator ScriptOperator::OP_SUCCESS221( + kOpSuccess221, "OP_SUCCESS221"); +const ScriptOperator ScriptOperator::OP_SUCCESS222( + kOpSuccess222, "OP_SUCCESS222"); +const ScriptOperator ScriptOperator::OP_SUCCESS223( + kOpSuccess223, "OP_SUCCESS223"); +const ScriptOperator ScriptOperator::OP_SUCCESS224( + kOpSuccess224, "OP_SUCCESS224"); +const ScriptOperator ScriptOperator::OP_SUCCESS225( + kOpSuccess225, "OP_SUCCESS225"); +const ScriptOperator ScriptOperator::OP_SUCCESS226( + kOpSuccess226, "OP_SUCCESS226"); +const ScriptOperator ScriptOperator::OP_SUCCESS227( + kOpSuccess227, "OP_SUCCESS227"); +const ScriptOperator ScriptOperator::OP_SUCCESS228( + kOpSuccess228, "OP_SUCCESS228"); +const ScriptOperator ScriptOperator::OP_SUCCESS229( + kOpSuccess229, "OP_SUCCESS229"); +const ScriptOperator ScriptOperator::OP_SUCCESS230( + kOpSuccess230, "OP_SUCCESS230"); +const ScriptOperator ScriptOperator::OP_SUCCESS231( + kOpSuccess231, "OP_SUCCESS231"); +const ScriptOperator ScriptOperator::OP_SUCCESS232( + kOpSuccess232, "OP_SUCCESS232"); +const ScriptOperator ScriptOperator::OP_SUCCESS233( + kOpSuccess233, "OP_SUCCESS233"); +const ScriptOperator ScriptOperator::OP_SUCCESS234( + kOpSuccess234, "OP_SUCCESS234"); +const ScriptOperator ScriptOperator::OP_SUCCESS235( + kOpSuccess235, "OP_SUCCESS235"); +const ScriptOperator ScriptOperator::OP_SUCCESS236( + kOpSuccess236, "OP_SUCCESS236"); +const ScriptOperator ScriptOperator::OP_SUCCESS237( + kOpSuccess237, "OP_SUCCESS237"); +const ScriptOperator ScriptOperator::OP_SUCCESS238( + kOpSuccess238, "OP_SUCCESS238"); +const ScriptOperator ScriptOperator::OP_SUCCESS239( + kOpSuccess239, "OP_SUCCESS239"); +const ScriptOperator ScriptOperator::OP_SUCCESS240( + kOpSuccess240, "OP_SUCCESS240"); +const ScriptOperator ScriptOperator::OP_SUCCESS241( + kOpSuccess241, "OP_SUCCESS241"); +const ScriptOperator ScriptOperator::OP_SUCCESS242( + kOpSuccess242, "OP_SUCCESS242"); +const ScriptOperator ScriptOperator::OP_SUCCESS243( + kOpSuccess243, "OP_SUCCESS243"); +const ScriptOperator ScriptOperator::OP_SUCCESS244( + kOpSuccess244, "OP_SUCCESS244"); +const ScriptOperator ScriptOperator::OP_SUCCESS245( + kOpSuccess245, "OP_SUCCESS245"); +const ScriptOperator ScriptOperator::OP_SUCCESS246( + kOpSuccess246, "OP_SUCCESS246"); +const ScriptOperator ScriptOperator::OP_SUCCESS247( + kOpSuccess247, "OP_SUCCESS247"); +const ScriptOperator ScriptOperator::OP_SUCCESS248( + kOpSuccess248, "OP_SUCCESS248"); +const ScriptOperator ScriptOperator::OP_SUCCESS249( + kOpSuccess249, "OP_SUCCESS249"); +const ScriptOperator ScriptOperator::OP_SUCCESS250( + kOpSuccess250, "OP_SUCCESS250"); +const ScriptOperator ScriptOperator::OP_SUCCESS251( + kOpSuccess251, "OP_SUCCESS251"); +const ScriptOperator ScriptOperator::OP_SUCCESS252( + kOpSuccess252, "OP_SUCCESS252"); +const ScriptOperator ScriptOperator::OP_SUCCESS253( + kOpSuccess253, "OP_SUCCESS253"); +const ScriptOperator ScriptOperator::OP_SUCCESS254( + kOpSuccess254, "OP_SUCCESS254"); ScriptOperator::ScriptOperator(ScriptType data_type) : ScriptOperator(data_type, "") { @@ -295,14 +470,29 @@ ScriptOperator ScriptOperator::Get(const std::string& message) { return ite->second; } +bool ScriptOperator::IsOpSuccess(ScriptType op_code) { + if ((op_code == kOpSuccess80) || (op_code == kOpSuccess98) || + ((op_code >= kOpSuccess126) && (op_code <= kOpSuccess129)) || + ((op_code >= kOpSuccess131) && (op_code <= kOpSuccess134)) || + ((op_code >= kOpSuccess137) && (op_code <= kOpSuccess138)) || + ((op_code >= kOpSuccess141) && (op_code <= kOpSuccess142)) || + ((op_code >= kOpSuccess149) && (op_code <= kOpSuccess153)) || + ((op_code >= kOpSuccess187) && (op_code <= kOpSuccess254))) { + return true; + } + return false; +} + ScriptOperator::ScriptOperator(const ScriptOperator& object) : data_type_(object.data_type_), text_data_(object.text_data_) { // do nothing } ScriptOperator& ScriptOperator::operator=(const ScriptOperator& object) { - data_type_ = object.data_type_; - text_data_ = object.text_data_; + if (this != &object) { + data_type_ = object.data_type_; + text_data_ = object.text_data_; + } return *this; } @@ -410,11 +600,21 @@ ScriptElement::ScriptElement(int64_t value) } } +ScriptElement::ScriptElement(int64_t value, bool is_binary) + : ScriptElement(value) { + if (is_binary && type_ == kElementOpCode) { + type_ = kElementNumber; + binary_data_ = ByteData(SerializeScriptNum(value_)); + } +} + ScriptElement& ScriptElement::operator=(const ScriptElement& element) { - type_ = element.type_; - op_code_ = element.op_code_; - binary_data_ = element.binary_data_; - value_ = element.value_; + if (this != &element) { + type_ = element.type_; + op_code_ = element.op_code_; + binary_data_ = element.binary_data_; + value_ = element.value_; + } return *this; } @@ -583,6 +783,19 @@ Script::Script(const ByteData& bytedata) SetStackData(bytedata); } +Script::Script(const Script& object) { + script_data_ = object.script_data_; + script_stack_ = object.script_stack_; +} + +Script& Script::operator=(const Script& object) & { + if (this != &object) { + script_data_ = object.script_data_; + script_stack_ = object.script_stack_; + } + return *this; +} + void Script::SetStackData(const ByteData& bytedata) { std::vector buffer = bytedata.GetBytes(); static const std::set kUseScriptNum1{ @@ -610,6 +823,7 @@ void Script::SetStackData(const ByteData& bytedata) { kOpMin, kOpMax, kOpGreaterThanOrEqual}; + static const std::set kUseScriptNum3{kOpWithIn, kOpCheckSigAdd}; // create stack bool is_collect_buffer = false; @@ -668,7 +882,7 @@ void Script::SetStackData(const ByteData& bytedata) { } else { // if ((bytedata == OP_0) || ((byteadata >= OP_PUSHDATA1) // && (byteadata <= OP_NOP10))) - // TODO(k-matsuzawa): script拡張を考慮しOP値の厳格なチェックには行わない。 + // TODO(k-matsuzawa): Considering script extension, do not perform strict check of OP value. // NOLINT // Setting for ScriptOperator ScriptType type = (ScriptType)view_data; @@ -693,7 +907,7 @@ void Script::SetStackData(const ByteData& bytedata) { if (script_stack_.size() > 2) { convert_count = 2; } - } else if (type == kOpWithIn) { // 3個 + } else if (kUseScriptNum3.count(type) > 0) { if (script_stack_.size() > 3) { convert_count = 3; } @@ -738,7 +952,7 @@ void Script::SetStackData(const ByteData& bytedata) { if (collect_buffer_size <= kMaxScriptNumSize) { ScriptElement script_element = - ScriptElement(ConvertToNumber(collect_buffer)); + ScriptElement(ConvertToNumber(collect_buffer), true); script_stack_.push_back(script_element); } else { ByteData byte_array = ByteData(collect_buffer); @@ -790,6 +1004,10 @@ const std::string Script::GetHex() const { return script_data_.GetHex(); } bool Script::IsEmpty() const { return script_data_.GetBytes().empty(); } +bool Script::Equals(const Script& object) const { + return script_data_.Equals(object.script_data_); +} + std::vector Script::GetElementList() const { return script_stack_; } @@ -861,6 +1079,19 @@ bool Script::IsMultisigScript() const { ScriptOperator::OP_CHECKMULTISIG) { return false; } + int64_t req_num = script_stack_[0].GetNumber(); + int64_t num = script_stack_[(script_stack_.size() - 2)].GetNumber(); + if (req_num <= 16 && !script_stack_[0].IsOpCode()) { + return false; + } + if (num <= 16 && !script_stack_[(script_stack_.size() - 2)].IsOpCode()) { + return false; + } + + if (req_num > num || req_num == 0 || + num != static_cast(script_stack_.size() - 3)) { + return false; + } for (size_t i = 1; i < (script_stack_.size() - 2); ++i) { if (!script_stack_[i].IsBinary() || @@ -868,21 +1099,43 @@ bool Script::IsMultisigScript() const { return false; } } + return true; +} - if (script_stack_[0].GetNumber() > - script_stack_[(script_stack_.size() - 2)].GetNumber()) { +bool Script::IsWitnessProgram() const { + if ((script_data_.GetDataSize() < kMinWitnessProgramLength) || + (kMaxWitnessProgramLength < script_data_.GetDataSize()) || + (script_stack_.size() != 2) || (!script_stack_[0].IsOpCode()) || + (!script_stack_[1].IsBinary())) { + return false; + } + auto op_code = script_stack_[0].GetOpCode().GetDataType(); + if ((op_code != ScriptType::kOp_0) && + ((op_code < ScriptType::kOp_1) || (op_code > ScriptType::kOp_16))) { return false; } + auto hash_size = script_stack_[1].GetBinaryData().GetDataSize(); + if (op_code == ScriptType::kOp_0) { + if ((hash_size != 0x14) && (hash_size != 0x20)) return false; + } else if (op_code == ScriptType::kOp_1) { + if (hash_size != 0x20) return false; + } return true; } -bool Script::IsWitnessProgram() const { - return ( - (kMinWitnessProgramLength <= script_data_.GetDataSize() || - script_data_.GetDataSize() <= kMaxWitnessProgramLength) && - script_stack_[0].GetOpCode() == ScriptOperator::OP_0 && - script_stack_[1].IsBinary()); +WitnessVersion Script::GetWitnessVersion() const { + if (IsWitnessProgram()) { + auto val = script_stack_[0].GetOpCode().GetDataType(); + if (kOp_0 == val) { + return WitnessVersion::kVersion0; + } else if ((kOp_1 <= val) && (val <= kOp_16)) { + auto num = val - kOp_1; + auto version = WitnessVersion::kVersion1 + num; + return static_cast(version); + } + } + return WitnessVersion::kVersionNone; } bool Script::IsP2wpkhScript() const { @@ -903,6 +1156,15 @@ bool Script::IsP2wshScript() const { script_stack_[1].GetBinaryData().GetDataSize() == kByteData256Length); } +bool Script::IsTaprootScript() const { + return ( + script_data_.GetDataSize() == kScriptHashTaprootLength && + script_stack_.size() == 2 && + script_stack_[0].GetOpCode() == ScriptOperator::OP_1 && + script_stack_[1].IsBinary() && + script_stack_[1].GetBinaryData().GetDataSize() == kByteData256Length); +} + bool Script::IsPegoutScript() const { if ((script_data_.GetDataSize() < 2) || (script_stack_[0].GetOpCode() != ScriptOperator::OP_RETURN)) { @@ -1036,6 +1298,47 @@ ScriptBuilder& ScriptBuilder::AppendElement(const ScriptElement& element) { return *this; } +ScriptBuilder& ScriptBuilder::operator<<(const std::string& message) { + return AppendString(message); +} + +ScriptBuilder& ScriptBuilder::operator<<(ScriptType type) { + return AppendOperator(type); +} + +ScriptBuilder& ScriptBuilder::operator<<( + const ScriptOperator& operate_object) { + return AppendOperator(operate_object); +} + +ScriptBuilder& ScriptBuilder::operator<<(const ByteData& data) { + return AppendData(data); +} + +ScriptBuilder& ScriptBuilder::operator<<(const ByteData160& data) { + return AppendData(data); +} + +ScriptBuilder& ScriptBuilder::operator<<(const ByteData256& data) { + return AppendData(data); +} + +ScriptBuilder& ScriptBuilder::operator<<(const Pubkey& pubkey) { + return AppendData(pubkey); +} + +ScriptBuilder& ScriptBuilder::operator<<(const Script& script) { + return AppendData(script); +} + +ScriptBuilder& ScriptBuilder::operator<<(const int64_t& data) { + return AppendData(data); +} + +ScriptBuilder& ScriptBuilder::operator<<(const ScriptElement& element) { + return AppendElement(element); +} + Script ScriptBuilder::Build() { ByteData data(script_byte_array_); if (data.GetDataSize() > Script::kMaxScriptSize) { @@ -1137,6 +1440,13 @@ Script ScriptUtil::CreateP2wshLockingScript(const Script& redeem_script) { return CreateP2wshLockingScript(script_hash); } +Script ScriptUtil::CreateTaprootLockingScript(const ByteData256& data) { + ScriptBuilder builder; + builder.AppendOperator(ScriptOperator::OP_1); + builder.AppendData(data); + return builder.Build(); +} + bool ScriptUtil::IsValidRedeemScript(const Script& redeem_script) { size_t script_buf_size = redeem_script.GetData().GetDataSize(); if (script_buf_size > Script::kMaxRedeemScriptSize) { @@ -1150,7 +1460,8 @@ bool ScriptUtil::IsValidRedeemScript(const Script& redeem_script) { // OP_n ... OP_ OP_CHECKMULTISIG Script ScriptUtil::CreateMultisigRedeemScript( - uint32_t require_signature_num, const std::vector& pubkeys) { + uint32_t require_signature_num, const std::vector& pubkeys, + bool has_witness) { if (require_signature_num == 0) { warn(CFD_LOG_SOURCE, "Invalid require_sig_num. require_sig_num = 0"); throw CfdException( @@ -1172,7 +1483,8 @@ Script ScriptUtil::CreateMultisigRedeemScript( CfdError::kCfdIllegalArgumentError, "CreateMultisigScript require_num is over."); } - if (pubkeys.size() > 15) { + size_t max_num = (has_witness) ? Script::kMaxMultisigPubkeyNum : 15; + if (pubkeys.size() > max_num) { warn(CFD_LOG_SOURCE, "pubkey array size is over."); throw CfdException( CfdError::kCfdIllegalArgumentError, @@ -1184,15 +1496,12 @@ Script ScriptUtil::CreateMultisigRedeemScript( // create script ScriptBuilder builder; - builder.AppendOperator(op_require_num.GetOpCode()); - for (const Pubkey& pubkey : pubkeys) { - builder.AppendData(pubkey); - } - builder.AppendOperator(op_pubkey_num.GetOpCode()); - builder.AppendOperator(ScriptOperator::OP_CHECKMULTISIG); + builder << op_require_num; + for (const Pubkey& pubkey : pubkeys) builder << pubkey; + builder << op_pubkey_num << ScriptOperator::OP_CHECKMULTISIG; Script redeem_script = builder.Build(); - if (!IsValidRedeemScript(redeem_script)) { + if ((!has_witness) && (!IsValidRedeemScript(redeem_script))) { warn(CFD_LOG_SOURCE, "Multisig script size is over."); throw CfdException( CfdError::kCfdIllegalArgumentError, diff --git a/src/cfdcore_secp256k1.cpp b/src/cfdcore_secp256k1.cpp index 8d6b82ed..ce34e50f 100644 --- a/src/cfdcore_secp256k1.cpp +++ b/src/cfdcore_secp256k1.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_secp256k1.cpp * - * @brief \~english definition for secp256k1 related classes - * \~japanese secp256k1関連クラス定義 + * @brief definition for secp256k1 related classes */ #include "cfdcore_secp256k1.h" // NOLINT diff --git a/src/cfdcore_secp256k1.h b/src/cfdcore_secp256k1.h index e9487bb4..0d7e3cc9 100644 --- a/src/cfdcore_secp256k1.h +++ b/src/cfdcore_secp256k1.h @@ -16,8 +16,7 @@ namespace cfd { namespace core { /** - * @brief \~japanese secp256k1クラス. - * \~english secp256k1 class + * @brief secp256k1 class */ class Secp256k1 { public: @@ -28,24 +27,15 @@ class Secp256k1 { static uint32_t GetSurjectionproofInputLimit(); /** - * \~english * @brief Construct * @param[in] context Secp256k1 Context - * \~japanese - * @brief コンストラクタ - * @param[in] context Secp256k1コンテキスト */ explicit Secp256k1(void* context); /** - * \~english * @brief function for join Pubkey * @param[in] pubkey_list input list for Pubkey to join * @return data of combined Pubkey - * \~japanese - * @brief Pubkey合成処理 - * @param[in] pubkey_list 合成するPubkeyリスト - * @return 合成したPubkeyデータ */ ByteData CombinePubkeySecp256k1Ec(const std::vector& pubkey_list); @@ -57,88 +47,53 @@ class Secp256k1 { ByteData CompressPubkeySecp256k1Ec(const ByteData& uncompressed_pubkey); /** - * \~english * @brief Tweak a private key by adding tweak. * @param[in] privkey private key.(must 32-byte) * @param[in] tweak tweak value to be added.(32-byte) * @return ByteData instance shows private key - * \~japanese - * @brief 加算によって、PrivateKeyを調整する。 - * @param[in] privkey 秘密鍵.(32-byte固定) - * @param[in] tweak 調整値.(32-byte) - * @return private key が格納された ByteDataインスタンス. */ ByteData AddTweakPrivkeySecp256k1Ec( const ByteData& privkey, const ByteData& tweak); /** - * \~english * @brief Tweak a private key by multiplying it by a tweak. * @param[in] privkey private key.(must 32-byte) * @param[in] tweak tweak value to be multiplied.(32-byte) * @return ByteData instance shows private key - * \~japanese - * @brief 乗算によって、PrivateKeyを調整する。 - * @param[in] tweak 秘密鍵.(32-byte固定) - * @param[in] tweak 調整値.(32-byte) - * @return private key が格納された ByteDataインスタンス. */ ByteData MulTweakPrivkeySecp256k1Ec( const ByteData& privkey, const ByteData& tweak); /** - * \~english * @brief function for adjusting Pubkey * @param[in] pubkey Pubkey * @param[in] tweak tweak value to be added.(32-byte) * @param[in] is_tweak_check boolean check for pubkey adjustment * @return data of adjusted Pubkey data - * \~japanese - * @brief 加算によって、PubKeyを調整する。 - * @param[in] pubkey Pubkey - * @param[in] tweak 調整値.(32-byte) - * @param[in] is_tweak_check pubkey調整チェック実施有無 - * @return 調整後のPubkeyデータ */ ByteData AddTweakPubkeySecp256k1Ec( const ByteData& pubkey, const ByteData& tweak, bool is_tweak_check); /** - * \~english * @brief Tweak a public key by multiplying it by a tweak value. * @param[in] pubkey Pubkey * @param[in] tweak tweak value to be multiplied.(32-byte) * @return data of adjusted Pubkey data - * \~japanese - * @brief 乗算によって、 PublicKey を調整する。 - * @param[in] pubkey Pubkey - * @param[in] tweak 調整値.(32-byte) - * @return 調整後のPubkeyデータ */ ByteData MulTweakPubkeySecp256k1Ec( const ByteData& pubkey, const ByteData& tweak); /** - * \~english * @brief function for negate Privkey * @param[in] privkey Privkey * @return data of negated Privkey - * \~japanese - * @brief Privkey negate処理 - * @param[in] privkey Privkey - * @return 加工後のPrivkeyデータ */ ByteData NegatePrivkeySecp256k1Ec(const ByteData& privkey); /** - * \~english * @brief function for negate Pubkey * @param[in] pubkey Pubkey * @return data of negated Pubkey - * \~japanese - * @brief Pubkey negate処理 - * @param[in] pubkey Pubkey - * @return 加工後のPubkeyデータ */ ByteData NegatePubkeySecp256k1Ec(const ByteData& pubkey); @@ -156,8 +111,7 @@ class Secp256k1 { uint64_t* min_value, uint64_t* max_value); /** - * @brief \~japanese Whitelist 証明情報生成処理 - * \~english Whitelist generation process for certificate info + * @brief Whitelist generation process for certificate info * \~ * @param[in] offline_pubkey offline pubkey * @param[in] online_privkey online private key @@ -174,8 +128,7 @@ class Secp256k1 { private: /** - * @brief \~japanese Secp256k1コンテキスト - * \~english Secp256k1 Context + * @brief Secp256k1 Context */ void* secp256k1_context_; }; diff --git a/src/cfdcore_taproot.cpp b/src/cfdcore_taproot.cpp new file mode 100644 index 00000000..36829cee --- /dev/null +++ b/src/cfdcore_taproot.cpp @@ -0,0 +1,685 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_taproot.cpp + * + * @brief This file implements for taproot utility class. + */ + +#include "cfdcore/cfdcore_taproot.h" + +#include +#include +#include + +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_transaction_common.h" +#include "cfdcore/cfdcore_util.h" +#include "cfdcore_wally_util.h" // NOLINT + +namespace cfd { +namespace core { + +using logger::warn; + +// ---------------------------------------------------------------------------- +// TapBranch +// ---------------------------------------------------------------------------- +TapBranch::TapBranch() : has_leaf_(false), leaf_version_(0) {} + +TapBranch::TapBranch(const ByteData256& commitment) + : has_leaf_(false), leaf_version_(0) { + root_commitment_ = commitment; +} + +TapBranch::TapBranch(const TapBranch& tap_tree) { + has_leaf_ = tap_tree.has_leaf_; + leaf_version_ = tap_tree.leaf_version_; + script_ = tap_tree.script_; + root_commitment_ = tap_tree.root_commitment_; + branch_list_ = tap_tree.branch_list_; +} + +void TapBranch::AddBranch(const SchnorrPubkey& pubkey) { + AddBranch(pubkey.GetByteData256()); +} + +void TapBranch::AddBranch(const ByteData256& commitment) { + branch_list_.emplace_back(commitment); + if (branch_list_.size() > TaprootScriptTree::kTaprootControlMaxNodeCount) { + throw CfdException( + CfdError::kCfdIllegalStateError, "tapbranch maximum over."); + } +} + +void TapBranch::AddBranch(const TapBranch& branch) { + branch_list_.emplace_back(branch); + if (branch_list_.size() > TaprootScriptTree::kTaprootControlMaxNodeCount) { + throw CfdException( + CfdError::kCfdIllegalStateError, "tapbranch maximum over."); + } +} + +TapBranch& TapBranch::operator=(const TapBranch& object) { + if (this != &object) { + has_leaf_ = object.has_leaf_; + leaf_version_ = object.leaf_version_; + script_ = object.script_; + root_commitment_ = object.root_commitment_; + branch_list_ = object.branch_list_; + } + return *this; +} + +ByteData256 TapBranch::GetBaseHash() const { + if (!has_leaf_) return root_commitment_; + + static auto kTaggedHash = HashUtil::Sha256("TapLeaf"); + return (HashUtil(HashUtil::kSha256) + << kTaggedHash << kTaggedHash << ByteData(leaf_version_) + << script_.GetData().Serialize()) + .Output256(); +} + +ByteData256 TapBranch::GetCurrentBranchHash() const { + return GetBranchHash(static_cast(branch_list_.size())); +} + +ByteData256 TapBranch::GetBranchHash(uint8_t depth) const { + ByteData256 hash = GetBaseHash(); + if (branch_list_.empty()) return hash; + + static auto kTaggedHash = HashUtil::Sha256("TapBranch"); + ByteData tapbranch_base = kTaggedHash.Concat(kTaggedHash); + auto nodes = GetNodeList(); + uint8_t index = 0; + for (const auto& node : nodes) { + if (index > depth) break; + auto hasher = HashUtil(HashUtil::kSha256) << tapbranch_base; + const auto& node_bytes = node.GetBytes(); + const auto& hash_bytes = hash.GetBytes(); + if (std::lexicographical_compare( + hash_bytes.begin(), hash_bytes.end(), node_bytes.begin(), + node_bytes.end())) { + hash = (hasher << hash << node).Output256(); + } else { + hash = (hasher << node << hash).Output256(); + } + ++index; + } + return hash; +} + +bool TapBranch::HasTapLeaf() const { return has_leaf_; } + +uint8_t TapBranch::GetLeafVersion() const { return leaf_version_; } + +Script TapBranch::GetScript() const { return script_; } + +std::vector TapBranch::GetBranchList() const { + return branch_list_; +} + +std::vector TapBranch::GetNodeList() const { + std::vector list; + for (const auto& branch : branch_list_) { + list.emplace_back(branch.GetCurrentBranchHash()); + } + return list; +} + +bool TapBranch::IsFindTapScript(const Script& tapscript) const { + if (has_leaf_ && script_.Equals(tapscript)) return true; + + for (const auto& branch : branch_list_) { + if (branch.IsFindTapScript(tapscript)) return true; + } + return false; +} + +std::string TapBranch::ToString() const { + std::string buf; + if (has_leaf_) { + std::string ver_str; + if (leaf_version_ != TaprootScriptTree::kTapScriptLeafVersion) { + ver_str = "," + ByteData(leaf_version_).GetHex(); + } + buf = "tl(" + script_.GetHex() + ver_str + ")"; + } else { + buf = root_commitment_.GetHex(); + } + if (branch_list_.empty()) return buf; + + ByteData256 hash = GetBaseHash(); + static auto kTaggedHash = HashUtil::Sha256("TapBranch"); + ByteData tapbranch_base = kTaggedHash.Concat(kTaggedHash); + auto nodes = GetNodeList(); + for (const auto& branch : branch_list_) { + auto hasher = HashUtil(HashUtil::kSha256) << tapbranch_base; + const auto node = branch.GetCurrentBranchHash(); + const auto& node_bytes = node.GetBytes(); + const auto& hash_bytes = hash.GetBytes(); + if (std::lexicographical_compare( + hash_bytes.begin(), hash_bytes.end(), node_bytes.begin(), + node_bytes.end())) { + hash = (hasher << hash << node).Output256(); + buf = "{" + buf + "," + branch.ToString() + "}"; + } else { + hash = (hasher << node << hash).Output256(); + buf = "{" + branch.ToString() + "," + buf + "}"; + } + } + return buf; +} + +TapBranch TapBranch::ChangeTapLeaf( + const Script& tapscript, + const std::vector& target_nodes) const { + if (!IsFindTapScript(tapscript)) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "This tapscript not exist in this tree."); + } + auto nodes = GetNodeList(); + if (has_leaf_ && script_.Equals(tapscript)) { + if (target_nodes.empty() || (target_nodes == nodes)) return *this; + } + + auto reverse_nodes = target_nodes; + if (!reverse_nodes.empty()) { + std::reverse(reverse_nodes.begin(), reverse_nodes.end()); + } + std::string reverse_nodes_str; + for (const auto& node : reverse_nodes) reverse_nodes_str += node.GetHex(); + + std::vector target_branch_indexes; + std::vector checked_nodes_size_list; + + std::vector new_branches; + for (size_t index = 0; index < branch_list_.size(); ++index) { + const auto& branch = branch_list_[index]; + if (branch.IsFindTapScript(tapscript)) { + std::vector check_nodes; + if (reverse_nodes.empty()) { + target_branch_indexes.emplace_back(index); + checked_nodes_size_list.emplace_back(0); + } else { + auto branch_nodes = branch.GetNodeList(); + for (size_t idx = nodes.size() - 1; idx > index; --idx) { + check_nodes.emplace_back(nodes[idx]); + } + if (index == 0) { + check_nodes.emplace_back(GetBaseHash()); + } else { + check_nodes.emplace_back( + GetBranchHash(static_cast(index - 1))); + } + + std::string check_nodes_str; + for (const auto& node : check_nodes) { + auto hex = node.GetHex(); + check_nodes_str += hex; + } + + bool has_match = true; + for (size_t idx = 0; idx < check_nodes.size(); ++idx) { + if (!reverse_nodes[idx].Equals(check_nodes[idx])) { + has_match = false; + break; + } + } + if (has_match) { + target_branch_indexes.emplace_back(index); + checked_nodes_size_list.emplace_back(check_nodes.size()); + } + } + } + } + + for (size_t index = 0; index < target_branch_indexes.size(); ++index) { + const auto& target_index = target_branch_indexes[index]; + const auto& checked_size = checked_nodes_size_list[index]; + std::vector check_nodes; + if (!target_nodes.empty()) { + size_t copy_len = target_nodes.size() - checked_size; + for (size_t idx = 0; idx < copy_len; ++idx) { + check_nodes.emplace_back(target_nodes[idx]); + } + } + + try { + auto new_branch = + branch_list_[target_index].ChangeTapLeaf(tapscript, check_nodes); + // ignore invalid target. + if (new_branch.GetBaseHash().IsEmpty()) continue; + + auto based_branch = *this; + std::vector copy_branches; + for (size_t idx = 0; idx < target_index; ++idx) { + copy_branches.emplace_back(branch_list_[idx]); + } + based_branch.branch_list_ = copy_branches; + + new_branch.AddBranch(based_branch); + for (size_t idx = target_index + 1; idx < branch_list_.size(); ++idx) { + new_branch.AddBranch(branch_list_[idx]); + } + new_branches.emplace_back(new_branch); + } catch (const CfdException&) { + // target not found + } + } + if (new_branches.empty()) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "The specified tapscript does not exist under this branch."); + } + return new_branches[0]; // response is top data. +} + +TapBranch TapBranch::FromString(const std::string& text) { + static auto check_tapleaf_func = [](const std::string& text, + TapBranch* branch) -> bool { + if (text.size() < 6) return false; + std::string head = text.substr(0, 3); + if ((head == "tl(") && (*(text.end() - 1) == ')')) { + size_t leaf_ver_offset = text.find(','); + if (leaf_ver_offset == std::string::npos) { + *branch = TaprootScriptTree(Script(text.substr(3, text.length() - 4))); + } else { + auto script_str = text.substr(3, leaf_ver_offset - 3); + auto leaf_ver_str = text.substr(leaf_ver_offset + 1, 2); + char* err = nullptr; + auto leaf_version = strtol(leaf_ver_str.c_str(), &err, 16); + if (((err != nullptr) && (*err != '\0')) || (leaf_version < 0) || + (leaf_version > std::numeric_limits::max())) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid leaf version."); + } + *branch = TaprootScriptTree( + static_cast(leaf_version), Script(script_str)); + } + return true; + } + return false; + }; + + static auto analyze_func = [](const std::string& target) -> TapBranch { + TapBranch result; + if (*target.begin() == '{') { + result = TapBranch::FromString(target); // analyze branch + } else if (!check_tapleaf_func(target, &result)) { + result = TapBranch(ByteData256(target)); + } + return result; + }; + + static auto collect_items_func = + [](const std::string& text) -> std::vector { + std::vector result; + uint8_t depth = 0; + size_t start_block_index = 0; + size_t end_block_index = 0; + size_t split_index = 0; + for (size_t idx = 0; idx < text.size(); ++idx) { + const char& str = text[idx]; + if (str == '{') { + if (depth == 0) start_block_index = idx + 1; + ++depth; + if (depth == std::numeric_limits::max()) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid tree format."); + } + } else if (str == '}') { + if (depth == 0) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid tree format."); + } + --depth; + if (depth == 0) { + if (split_index == 0) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid tree format. empty split block."); + } + end_block_index = idx; + size_t offset = (split_index == 0) ? start_block_index : split_index; + if (end_block_index <= offset) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid tree item."); + } + result.emplace_back(text.substr(offset, idx - offset)); + } + } else if (str == ',') { + if (depth == 1) { + size_t offset = (split_index == 0) ? start_block_index : split_index; + if ((offset + 3) < text.size()) { + auto head = text.substr(offset, 3); + char prev_str = 0; + if (idx > 0) prev_str = text[idx - 1]; + // ignore leaf ver + if ((head == "tl(") && (prev_str != ')')) continue; + } + + if (split_index != 0) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid tree splitformat."); + } + result.emplace_back(text.substr(offset, idx - offset)); + split_index = idx + 1; + } + } + } + + if (result.empty()) { + // do nothing + } else if ((result.size() != 2) || ((end_block_index + 1) < text.size())) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Invalid tree format."); + } + return result; + }; + + if (text.find(' ') != std::string::npos) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Contains invalid charactor."); + } + + TapBranch result; + auto text_list = collect_items_func(text); + if (text_list.empty()) { + result = analyze_func(text); + } else { + auto branch1 = analyze_func(text_list.at(0)); + auto branch2 = analyze_func(text_list.at(1)); + if ((!branch1.has_leaf_) && (branch2.has_leaf_)) { + branch2.AddBranch(branch1); + result = branch2; + } else { + branch1.AddBranch(branch2); + result = branch1; + } + } + + return result; +} + +// ---------------------------------------------------------------------------- +// TaprootScriptTree +// ---------------------------------------------------------------------------- +TaprootScriptTree::TaprootScriptTree() : TapBranch() { + has_leaf_ = true; + leaf_version_ = kTapScriptLeafVersion; +} + +TaprootScriptTree::TaprootScriptTree(const Script& script) + : TaprootScriptTree(kTapScriptLeafVersion, script) {} + +TaprootScriptTree::TaprootScriptTree( + uint8_t leaf_version, const Script& script) + : TapBranch() { + has_leaf_ = true; + leaf_version_ = leaf_version; + script_ = script; + if (!TaprootUtil::IsValidLeafVersion(leaf_version)) { + warn(CFD_LOG_SOURCE, "Unsupported leaf version. [{}]", leaf_version); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Unsupported leaf version."); + } +} + +TaprootScriptTree::TaprootScriptTree(const TapBranch& leaf_branch) + : TapBranch(leaf_branch) { + if (!leaf_branch.HasTapLeaf()) { + warn(CFD_LOG_SOURCE, "object is not tapleaf."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "object is not tapleaf."); + } + if (!TaprootUtil::IsValidLeafVersion(leaf_branch.GetLeafVersion())) { + warn( + CFD_LOG_SOURCE, "Unsupported leaf version. [{}]", + leaf_branch.GetLeafVersion()); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Unsupported leaf version."); + } + has_leaf_ = true; + leaf_version_ = leaf_branch.GetLeafVersion(); + script_ = leaf_branch.GetScript(); + branch_list_ = leaf_branch.GetBranchList(); + nodes_ = leaf_branch.GetNodeList(); +} + +TaprootScriptTree::TaprootScriptTree(const TaprootScriptTree& tap_tree) + : TapBranch(tap_tree) { + if (!TaprootUtil::IsValidLeafVersion(tap_tree.leaf_version_)) { + warn( + CFD_LOG_SOURCE, "Unsupported leaf version. [{}]", + tap_tree.leaf_version_); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Unsupported leaf version."); + } + has_leaf_ = tap_tree.has_leaf_; + leaf_version_ = tap_tree.leaf_version_; + script_ = tap_tree.script_; + root_commitment_ = tap_tree.root_commitment_; + branch_list_ = tap_tree.branch_list_; + nodes_ = tap_tree.nodes_; +} + +TaprootScriptTree& TaprootScriptTree::operator=( + const TaprootScriptTree& object) & { + if (this != &object) { + has_leaf_ = object.has_leaf_; + leaf_version_ = object.leaf_version_; + script_ = object.script_; + root_commitment_ = object.root_commitment_; + branch_list_ = object.branch_list_; + nodes_ = object.nodes_; + } + return *this; +} + +void TaprootScriptTree::AddBranch(const ByteData256& commitment) { + TapBranch::AddBranch(commitment); + nodes_.emplace_back(commitment); +} + +void TaprootScriptTree::AddBranch(const TapBranch& branch) { + TapBranch::AddBranch(branch); + nodes_.emplace_back(branch.GetCurrentBranchHash()); +} + +void TaprootScriptTree::AddBranch(const TaprootScriptTree& tree) { + TapBranch::AddBranch(tree); + nodes_.emplace_back(tree.GetCurrentBranchHash()); +} + +ByteData256 TaprootScriptTree::GetTapLeafHash() const { return GetBaseHash(); } + +ByteData256 TaprootScriptTree::GetTapTweak( + const SchnorrPubkey& internal_pubkey) const { + ByteData256 hash = GetCurrentBranchHash(); + static auto kTaggedHash = HashUtil::Sha256("TapTweak"); + auto hasher = HashUtil(HashUtil::kSha256) + << kTaggedHash << kTaggedHash << internal_pubkey.GetData() + << hash; + return hasher.Output256(); +} + +SchnorrPubkey TaprootScriptTree::GetTweakedPubkey( + const SchnorrPubkey& internal_pubkey, bool* parity) const { + ByteData256 hash = GetTapTweak(internal_pubkey); + return internal_pubkey.CreateTweakAdd(hash, parity); +} + +Privkey TaprootScriptTree::GetTweakedPrivkey( + const Privkey& internal_privkey, bool* parity) const { + bool is_parity = false; + auto internal_pubkey = + SchnorrPubkey::FromPrivkey(internal_privkey, &is_parity); + Privkey privkey = internal_privkey; + if (is_parity) privkey = internal_privkey.CreateNegate(); + + ByteData256 hash = GetTapTweak(internal_pubkey); + internal_pubkey.CreateTweakAdd(hash, &is_parity); + if (parity != nullptr) *parity = is_parity; + return privkey.CreateTweakAdd(hash); +} + +std::vector TaprootScriptTree::GetNodeList() const { + return nodes_; +} + +TaprootScriptTree TaprootScriptTree::FromString( + const std::string& text, const Script& tapscript, + const std::vector& target_nodes) { + auto branch = TapBranch::FromString(text); + auto check_nodes = target_nodes; + if (!check_nodes.empty()) { + TaprootScriptTree target_leaf(tapscript); + if (check_nodes.back().Equals(target_leaf.GetTapLeafHash())) { + check_nodes.erase(check_nodes.end() - 1); + } + } + branch = branch.ChangeTapLeaf(tapscript, check_nodes); + return TaprootScriptTree(branch); +} + +// ---------------------------------------------------------------------------- +// TaprootUtil +// ---------------------------------------------------------------------------- +bool TaprootUtil::IsValidLeafVersion(uint8_t leaf_version) { + // BIP-0341 + static const uint32_t kValidLeafVersions[] = {0x66, 0x7e, 0x80, 0x84, 0x96, + 0x98, 0xba, 0xbc, 0xbe}; + for (auto valid_ver : kValidLeafVersions) { + if (leaf_version == valid_ver) return true; + } + + if ((leaf_version % 2) != 0) return false; // Odd + if ((leaf_version >= 0xc0) && (leaf_version <= 0xfe)) return true; + return false; +} + +ByteData TaprootUtil::CreateTapScriptControl( + const SchnorrPubkey& internal_pubkey, const TaprootScriptTree& merkle_tree, + SchnorrPubkey* witness_program, Script* locking_script) { + bool parity = false; + auto pubkey_data = + merkle_tree.GetTweakedPubkey(internal_pubkey, &parity).GetByteData256(); + uint8_t top = merkle_tree.GetLeafVersion(); + if (parity) top |= 0x01; + Serializer builder; + builder.AddDirectByte(top); + builder.AddDirectBytes(internal_pubkey.GetData()); + for (const auto& node : merkle_tree.GetNodeList()) { + builder.AddDirectBytes(node); + } + if (witness_program != nullptr) { + *witness_program = SchnorrPubkey(pubkey_data); + } + if (locking_script != nullptr) { + *locking_script = ScriptUtil::CreateTaprootLockingScript(pubkey_data); + } + return builder.Output(); +} + +bool TaprootUtil::VerifyTaprootCommitment( + bool has_parity, uint8_t tapleaf_bit, + const SchnorrPubkey& target_taproot, // witness program + const SchnorrPubkey& internal_pubkey, + const std::vector& nodes, const Script& tapscript, + ByteData256* tapleaf_hash) { + if (nodes.size() > TaprootScriptTree::kTaprootControlMaxNodeCount) { + warn(CFD_LOG_SOURCE, "control node maximum over. [{}]", nodes.size()); + return false; + } + + // Compute the tapleaf hash. + TaprootScriptTree tree(tapleaf_bit, tapscript); + if (tapleaf_hash != nullptr) *tapleaf_hash = tree.GetTapLeafHash(); + + // Compute the Merkle root from the leaf and the provided path. + for (const auto& node : nodes) { + tree.AddBranch(node); + } + // Compute the tweak from the Merkle root and the inner pubkey. + auto hash = tree.GetTapTweak(internal_pubkey); + // Verify that the output pubkey matches the tweaked inner pubkey, after correcting for parity. // NOLINT + return target_taproot.IsTweaked(internal_pubkey, hash, has_parity); +} + +void TaprootUtil::ParseTaprootSignData( + const std::vector& witness_stack, + SchnorrSignature* schnorr_signature, bool* has_parity, + uint8_t* tapleaf_bit, SchnorrPubkey* internal_pubkey, + std::vector* nodes, Script* tapscript, + std::vector* stack, ByteData* annex) { + static constexpr size_t kControlMinimumSize = + SchnorrPubkey::kSchnorrPubkeySize + 1; + + size_t size = witness_stack.size(); + if ((size >= 2) && (!witness_stack.back().IsEmpty()) && + (witness_stack.back().GetHeadData() == TaprootUtil::kAnnexTag)) { + if (annex != nullptr) *annex = witness_stack.back(); + --size; + } + + if (size == 0) { + warn(CFD_LOG_SOURCE, "witness_stack is empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "witness_stack is empty."); + } else if (size == 1) { + if (schnorr_signature != nullptr) { + *schnorr_signature = SchnorrSignature(witness_stack.at(0)); + } + } else { + Script script(witness_stack.at(size - 2)); + ByteData data = witness_stack.at(size - 1); + if ((data.GetDataSize() < kControlMinimumSize) || + (((data.GetDataSize() - 1) % kByteData256Length) != 0)) { + warn(CFD_LOG_SOURCE, "wrong taproot control size."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "wrong taproot control size."); + } + size_t max_node = + (data.GetDataSize() - kControlMinimumSize) / kByteData256Length; + if (max_node > TaprootScriptTree::kTaprootControlMaxNodeCount) { + warn( + CFD_LOG_SOURCE, "taproot control node maximum over. [{}]", max_node); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "taproot control node maximum over."); + } + + Deserializer parser(data); + uint8_t top = parser.ReadUint8(); + if (has_parity != nullptr) *has_parity = (top & 0x01); + if (tapleaf_bit != nullptr) *tapleaf_bit = top & 0xfe; + + ByteData256 pubkey_bytes(parser.ReadBuffer(kByteData256Length)); + if (internal_pubkey != nullptr) { + *internal_pubkey = SchnorrPubkey(pubkey_bytes); + } + if (nodes != nullptr) { + for (size_t index = 0; index < max_node; ++index) { + ByteData256 node(parser.ReadBuffer(kByteData256Length)); + nodes->emplace_back(node); + } + } + + if (tapscript != nullptr) *tapscript = script; + if ((stack != nullptr) && (size > 2)) { + for (size_t index = 0; index < size - 2; ++index) { + stack->emplace_back(witness_stack.at(index)); + } + } + } +} + +} // namespace core +} // namespace cfd diff --git a/src/cfdcore_transaction.cpp b/src/cfdcore_transaction.cpp index 1e7b4a3f..5a048ab1 100644 --- a/src/cfdcore_transaction.cpp +++ b/src/cfdcore_transaction.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_transaction.cpp * - * @brief \~japanese Transaction関連クラスの実装ファイルです。 - * \~english implementation of Transaction related class + * @brief implementation of Transaction related class */ #include "cfdcore/cfdcore_transaction.h" @@ -14,8 +13,11 @@ #include "cfdcore/cfdcore_bytedata.h" #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_util.h" -#include "cfdcore_wally_util.h" // NOLINT +#include "cfdcore_transaction_internal.h" // NOLINT +#include "cfdcore_wally_util.h" // NOLINT namespace cfd { namespace core { @@ -74,6 +76,7 @@ uint32_t TxIn::EstimateTxInSize( uint32_t *no_witness_area_size, const Script *scriptsig_template) { bool is_pubkey = false; bool is_witness = true; + bool is_taproot = false; bool use_unlocking_script = true; uint32_t size = kMinimumTxInSize; uint32_t witness_size = 0; @@ -99,6 +102,10 @@ uint32_t TxIn::EstimateTxInSize( case AddressType::kP2shP2wpkhAddress: is_pubkey = true; break; + case AddressType::kTaprootAddress: + is_taproot = true; + use_unlocking_script = false; + break; default: if (redeem_script.IsEmpty()) { warn(CFD_LOG_SOURCE, "unknown address type, and empty redeem script."); @@ -116,6 +123,9 @@ uint32_t TxIn::EstimateTxInSize( (scriptsig_template != nullptr) && (!scriptsig_template->IsEmpty())) { script_size = static_cast( scriptsig_template->GetData().GetSerializeSize()); + } else if (is_taproot) { + // signature(64byte) + sighash type(1byte) + serialized(1byte) + script_size = SchnorrSignature::kSchnorrSignatureSize + 2; } else { // Forehead is a big size script_size = (EC_SIGNATURE_DER_MAX_LEN - 2) * 2; @@ -142,19 +152,16 @@ uint32_t TxIn::EstimateTxInSize( } if (is_witness) { - if (is_pubkey) { - witness_size = script_size; - if (use_unlocking_script) { - size += 22; // wpkh locking script length - } + witness_size = script_size + 1; // witness stack count area(1byte) + if (!use_unlocking_script) { + // do nothing + } else if (is_pubkey) { + size += 23; // wpkh locking script length + serialize size(1byte) } else { - witness_size = script_size; - if (use_unlocking_script) { - size += 34; // wsh locking script length - } + size += 35; // wsh locking script length + serialize size(1byte) } } else { - size += script_size; + size += script_size + 1; // serialize size(1byte) } if (witness_area_size != nullptr) { *witness_area_size = static_cast(witness_size); @@ -447,7 +454,9 @@ void Transaction::SetFromHex(const std::string &hex_string) { } Transaction &Transaction::operator=(const Transaction &transaction) & { - SetFromHex(transaction.GetHex()); + if (this != &transaction) { + SetFromHex(transaction.GetHex()); + } return *this; } @@ -740,6 +749,11 @@ ByteData256 Transaction::GetSignatureHash( throw CfdException( kCfdIllegalArgumentError, "Failed to GetSignatureHash. empty script."); } + if (version >= WitnessVersion::kVersion1) { + warn(CFD_LOG_SOURCE, "unsupport witness version on ECDSA."); + throw CfdException( + kCfdIllegalArgumentError, "unsupport witness version on ECDSA."); + } std::vector buffer(SHA256_LEN); const std::vector &bytes = script_data.GetBytes(); struct wally_tx *tx_pointer = NULL; @@ -785,150 +799,133 @@ ByteData256 Transaction::GetSignatureHash( return ByteData256(buffer); } -bool Transaction::HasWitness() const { - for (const TxIn &txin : vin_) { - if (!txin.GetScriptWitness().GetWitness().empty()) { - return true; - } +ByteData256 Transaction::GetSchnorrSignatureHash( + uint32_t txin_index, SigHashType sighash_type, + const std::vector &utxo_list, const TapScriptData *script_data, + const ByteData &annex) const { + CheckTxInIndex(txin_index, __LINE__, __FUNCTION__); + if (this->vin_.size() > utxo_list.size()) { + warn(CFD_LOG_SOURCE, "not enough utxo list."); + throw CfdException(kCfdIllegalArgumentError, "not enough utxo list."); + } + if ((!annex.IsEmpty()) && (annex.GetHeadData() != TaprootUtil::kAnnexTag)) { + warn(CFD_LOG_SOURCE, "invalid annex tag."); + throw CfdException(kCfdIllegalArgumentError, "invalid annex tag"); } - return false; -} -ByteData Transaction::GetByteData(bool has_witness) const { - struct wally_tx *tx_pointer = - static_cast(wally_tx_pointer_); - size_t size = 0; - uint32_t flag = 0; - if (has_witness) { - flag = WALLY_TX_FLAG_USE_WITNESS; + const Script locking_script = utxo_list[txin_index].GetLockingScript(); + if (!locking_script.IsWitnessProgram()) { + warn(CFD_LOG_SOURCE, "target vin is not segwit."); + throw CfdException(kCfdIllegalArgumentError, "target vin is not segwit."); + } else if (locking_script.GetWitnessVersion() != WitnessVersion::kVersion1) { + warn(CFD_LOG_SOURCE, "target vin is not segwit v1."); + throw CfdException( + kCfdIllegalArgumentError, "target vin is not segwit v1."); } - int ret = wally_tx_get_length(tx_pointer, flag, &size); - if (ret != WALLY_OK) { - warn(CFD_LOG_SOURCE, "wally_tx_get_length NG[{}].", ret); - throw CfdException(kCfdIllegalStateError, "tx length calc error."); + uint8_t sighash_type_value = + static_cast(sighash_type.GetSigHashFlag()); + bool is_anyone_can_pay = sighash_type.IsAnyoneCanPay(); + if (!SchnorrSignature::IsValidSigHashType(sighash_type_value)) { + warn(CFD_LOG_SOURCE, "Invalid sighash type on segwit v1."); + throw CfdException( + kCfdIllegalArgumentError, "Invalid sighash type on segwit v1."); + } else if (sighash_type_value == 0) { + sighash_type_value = 0x01; // SIGHASH_ALL } - if (size < kTransactionMinimumHexSize) { - ret = WALLY_EINVAL; + bool has_sighash_all = ((sighash_type_value & 0x0f) == 1) ? true : false; + + uint8_t ext_flag = 0; // 0 - 127 + uint8_t has_tap_script = 0; + uint8_t key_version = 0; + if ((script_data != nullptr) && (!script_data->tap_leaf_hash.IsEmpty())) { + has_tap_script = 1; } - std::vector buffer(size); - if (ret != WALLY_EINVAL) { - ret = wally_tx_to_bytes( - tx_pointer, flag, buffer.data(), buffer.size(), &size); + ext_flag |= has_tap_script; + + Serializer builder; + auto top = HashUtil::Sha256("TapSighash"); + builder.AddDirectBytes(top); + builder.AddDirectBytes(top); // double data + builder.AddDirectByte(0); // EPOCH + builder.AddDirectByte(static_cast(sighash_type.GetSigHashFlag())); + builder.AddDirectNumber(static_cast(GetVersion())); + builder.AddDirectNumber(GetLockTime()); + if (!is_anyone_can_pay) { + Serializer prevouts_buf; + Serializer amounts_buf; + Serializer scripts_buf; + Serializer sequences_buf; + for (size_t index = 0; index < vin_.size(); ++index) { + prevouts_buf.AddDirectBytes(vin_[index].GetTxid().GetData()); + prevouts_buf.AddDirectNumber(vin_[index].GetVout()); + amounts_buf.AddDirectNumber( + utxo_list[index].GetValue().GetSatoshiValue()); + scripts_buf.AddVariableBuffer( + utxo_list[index].GetLockingScript().GetData()); + sequences_buf.AddDirectNumber(vin_[index].GetSequence()); + } + builder.AddDirectBytes(HashUtil::Sha256(prevouts_buf.Output())); + builder.AddDirectBytes(HashUtil::Sha256(amounts_buf.Output())); + builder.AddDirectBytes(HashUtil::Sha256(scripts_buf.Output())); + builder.AddDirectBytes(HashUtil::Sha256(sequences_buf.Output())); + } + if (has_sighash_all) { + Serializer outputs_buf; + for (const auto &txout : vout_) { + outputs_buf.AddDirectNumber(txout.GetValue().GetSatoshiValue()); + outputs_buf.AddVariableBuffer(txout.GetLockingScript().GetData()); + } + builder.AddDirectBytes(HashUtil::Sha256(outputs_buf.Output())); } - if (ret == WALLY_EINVAL) { - /* TODO: About conversion to the object. - * In libwally, txin / txout does not allow empty data. - * Therefore, if txin / txout is empty, object to byte is an error. - * Therefore, it performs its own processing under certain circumstances. - */ - if ((tx_pointer->num_inputs == 0) || (tx_pointer->num_outputs == 0)) { - info(CFD_LOG_SOURCE, "wally_tx_get_length size[{}]", size); - // Necessary size calculation because wally_tx_get_length may be - // an invalid value (reserved more) - size_t need_size = sizeof(struct wally_tx); - need_size += tx_pointer->num_inputs * sizeof(struct wally_tx_input); - need_size += tx_pointer->num_outputs * sizeof(struct wally_tx_output); - for (uint32_t i = 0; i < tx_pointer->num_inputs; ++i) { - const struct wally_tx_input *input = tx_pointer->inputs + i; - need_size += input->script_len + 10; - } - for (uint32_t i = 0; i < tx_pointer->num_outputs; ++i) { - const struct wally_tx_output *output = tx_pointer->outputs + i; - need_size += output->script_len + 10; - } - if (flag) { - for (uint32_t i = 0; i < tx_pointer->num_inputs; ++i) { - const struct wally_tx_input *input = tx_pointer->inputs + i; - size_t num_items = input->witness ? input->witness->num_items : 0; - for (uint32_t j = 0; j < num_items; ++j) { - const struct wally_tx_witness_item *stack; - stack = input->witness->items + j; - need_size += stack->witness_len + 10; - } - need_size += 10; - } - } - if (need_size > buffer.size()) { - buffer.resize(need_size); - info(CFD_LOG_SOURCE, "buffer.resize[{}]", need_size); - } - uint8_t *address_pointer = buffer.data(); - memcpy( - address_pointer, &tx_pointer->version, sizeof(tx_pointer->version)); - address_pointer += sizeof(tx_pointer->version); - if (flag && (tx_pointer->num_inputs != 0)) { // witness - *address_pointer = 0; // marker is 0 - ++address_pointer; - *address_pointer = 1; // flag is 1(witness) - ++address_pointer; - } + uint8_t spend_type = (ext_flag << 1) + (annex.IsEmpty() ? 0 : 1); + builder.AddDirectByte(spend_type); + if (is_anyone_can_pay) { + builder.AddDirectBytes(vin_[txin_index].GetTxid().GetData()); + builder.AddDirectNumber(vin_[txin_index].GetVout()); + builder.AddDirectNumber( + utxo_list[txin_index].GetValue().GetSatoshiValue()); + builder.AddVariableBuffer( + utxo_list[txin_index].GetLockingScript().GetData()); + builder.AddDirectNumber(vin_[txin_index].GetSequence()); + } else { + builder.AddDirectNumber(txin_index); + } - // txin - address_pointer = - CopyVariableInt(tx_pointer->num_inputs, address_pointer); - for (uint32_t i = 0; i < tx_pointer->num_inputs; ++i) { - const struct wally_tx_input *input = tx_pointer->inputs + i; - memcpy(address_pointer, input->txhash, sizeof(input->txhash)); - address_pointer += sizeof(input->txhash); - memcpy(address_pointer, &input->index, sizeof(input->index)); - address_pointer += sizeof(input->index); - address_pointer = CopyVariableBuffer( - input->script, input->script_len, address_pointer); - memcpy(address_pointer, &input->sequence, sizeof(input->sequence)); - address_pointer += sizeof(input->sequence); - } + if (!annex.IsEmpty()) builder.AddDirectBytes(HashUtil::Sha256(annex)); - // txout - address_pointer = - CopyVariableInt(tx_pointer->num_outputs, address_pointer); - for (uint32_t i = 0; i < tx_pointer->num_outputs; ++i) { - const struct wally_tx_output *output = tx_pointer->outputs + i; - memcpy(address_pointer, &output->satoshi, sizeof(output->satoshi)); - address_pointer += sizeof(output->satoshi); - address_pointer = CopyVariableBuffer( - output->script, output->script_len, address_pointer); - } + if (sighash_type.GetSigHashAlgorithm() == SigHashAlgorithm::kSigHashSingle) { + CheckTxOutIndex(txin_index, __LINE__, __FUNCTION__); + Serializer outputs_buf; + outputs_buf.AddDirectNumber( + vout_[txin_index].GetValue().GetSatoshiValue()); + outputs_buf.AddVariableBuffer( + vout_[txin_index].GetLockingScript().GetData()); + builder.AddDirectBytes(HashUtil::Sha256(outputs_buf.Output())); + } - // witness - if (flag) { - for (uint32_t i = 0; i < tx_pointer->num_inputs; ++i) { - const struct wally_tx_input *input = tx_pointer->inputs + i; - size_t num_items = input->witness ? input->witness->num_items : 0; - address_pointer = CopyVariableInt(num_items, address_pointer); - for (uint32_t j = 0; j < num_items; ++j) { - const struct wally_tx_witness_item *stack; - stack = input->witness->items + j; - address_pointer = CopyVariableBuffer( - stack->witness, stack->witness_len, address_pointer); - } - } - } + if (has_tap_script == 1) { + builder.AddDirectBytes(script_data->tap_leaf_hash.GetData()); + builder.AddDirectByte(key_version); + builder.AddDirectNumber(script_data->code_separator_position); + } + return HashUtil::Sha256(builder.Output()); +} - // locktime - memcpy( - address_pointer, &tx_pointer->locktime, - sizeof(tx_pointer->locktime)); - address_pointer += sizeof(tx_pointer->locktime); - - unsigned char *start_address = buffer.data(); - size = address_pointer - start_address; - if (buffer.size() > size) { - buffer.resize(size); - info(CFD_LOG_SOURCE, "set buffer size[{}]", size); - } - } else { - // Exception - warn(CFD_LOG_SOURCE, "wally_tx_to_bytes NG[{}].", ret); - throw CfdException(kCfdIllegalStateError, "tx hex convert error."); +bool Transaction::HasWitness() const { + for (const TxIn &txin : vin_) { + if (!txin.GetScriptWitness().GetWitness().empty()) { + return true; } - } else if (ret != WALLY_OK) { - warn(CFD_LOG_SOURCE, "wally_tx_to_bytes NG[{}].", ret); - throw CfdException(kCfdIllegalStateError, "tx hex convert error."); } + return false; +} - return ByteData(buffer); +ByteData Transaction::GetByteData(bool has_witness) const { + struct wally_tx *tx_pointer = + static_cast(wally_tx_pointer_); + return ConvertBitcoinTxFromWally(tx_pointer, !has_witness); } uint32_t Transaction::GetWallyFlag() const { @@ -953,5 +950,137 @@ void Transaction::CheckTxOutIndex( } } +// ----------------------------------------------------------------------------- +// Internal API +// ----------------------------------------------------------------------------- +ByteData ConvertBitcoinTxFromWally( + const struct wally_tx *tx, bool force_exclude_witness) { + int ret; + size_t witness_count = 0; + ret = wally_tx_get_witness_count(tx, &witness_count); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_get_witness_count NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt witness count get error."); + } + + uint32_t flags = (witness_count != 0) ? WALLY_TX_FLAG_USE_WITNESS : 0; + if (force_exclude_witness) flags = 0; + + size_t size = 0; + ret = wally_tx_get_length(tx, flags, &size); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_tx_get_length NG[{}]", ret); + throw CfdException(kCfdIllegalStateError, "psbt tx size get error."); + } + + try { + std::vector buf(size); + size_t tx_size = 0; + + if (size < kTransactionMinimumHexSize) { + ret = WALLY_EINVAL; + } else { + ret = wally_tx_to_bytes(tx, flags, buf.data(), buf.size(), &tx_size); + } + + if (ret == WALLY_OK) { + return ByteData(buf.data(), static_cast(tx_size)); + } else if (ret == WALLY_EINVAL) { + /* TODO: About conversion to the object. + * In libwally, txin / txout does not allow empty data. + * Therefore, if txin / txout is empty, object to byte is an error. + * Therefore, it performs its own processing under certain circumstances. + */ + if ((tx->num_inputs == 0) || (tx->num_outputs == 0)) { + info(CFD_LOG_SOURCE, "wally_tx_get_length size[{}]", size); + // Necessary size calculation because wally_tx_get_length may be + // an invalid value (reserved more) + size_t need_size = sizeof(struct wally_tx); + need_size += tx->num_inputs * sizeof(struct wally_tx_input); + need_size += tx->num_outputs * sizeof(struct wally_tx_output); + for (uint32_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *input = tx->inputs + i; + need_size += input->script_len + 10; + } + for (uint32_t i = 0; i < tx->num_outputs; ++i) { + const struct wally_tx_output *output = tx->outputs + i; + need_size += output->script_len + 10; + } + if (flags != 0) { + for (uint32_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *input = tx->inputs + i; + size_t num_items = input->witness ? input->witness->num_items : 0; + for (uint32_t j = 0; j < num_items; ++j) { + const struct wally_tx_witness_item *stack; + stack = input->witness->items + j; + need_size += stack->witness_len + 10; + } + need_size += 10; + } + } + + Serializer builder(static_cast(need_size)); + builder.AddDirectNumber(tx->version); + if ((flags != 0) && (tx->num_inputs != 0)) { // witness + builder.AddDirectByte(0); // marker is 0 + builder.AddDirectByte(1); // flag is 1(witness) + } + + builder.AddVariableInt(tx->num_inputs); + for (uint32_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *input = tx->inputs + i; + builder.AddDirectBytes(input->txhash, sizeof(input->txhash)); + builder.AddDirectNumber(input->index); + builder.AddVariableBuffer( + input->script, static_cast(input->script_len)); + builder.AddDirectNumber(input->sequence); + } + + builder.AddVariableInt(tx->num_outputs); + for (uint32_t i = 0; i < tx->num_outputs; ++i) { + const struct wally_tx_output *output = tx->outputs + i; + builder.AddDirectNumber(output->satoshi); + builder.AddVariableBuffer( + output->script, static_cast(output->script_len)); + } + + if (flags != 0) { // witness + for (uint32_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *input = tx->inputs + i; + uint32_t num_items = + input->witness + ? static_cast(input->witness->num_items) + : 0; + builder.AddVariableInt(num_items); + for (uint32_t j = 0; j < num_items; ++j) { + const struct wally_tx_witness_item *stack; + stack = input->witness->items + j; + builder.AddVariableBuffer( + stack->witness, static_cast(stack->witness_len)); + } + } + } + + builder.AddDirectNumber(tx->locktime); + return builder.Output(); + } else { + warn(CFD_LOG_SOURCE, "wally_tx_to_bytes NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "tx hex convert error."); + } + } else { + warn(CFD_LOG_SOURCE, "wally_tx_to_bytes NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "psbt tx hex convert error."); + } + } catch (const CfdError &except) { + throw except; + } catch (const std::exception &except) { + warn(CFD_LOG_SOURCE, "unknown exception."); + throw CfdException(kCfdUnknownError, std::string(except.what())); + } catch (...) { + warn(CFD_LOG_SOURCE, "unknown error."); + throw CfdException(); + } +} + } // namespace core } // namespace cfd diff --git a/src/cfdcore_transaction_common.cpp b/src/cfdcore_transaction_common.cpp index b6611f95..3bb69c21 100644 --- a/src/cfdcore_transaction_common.cpp +++ b/src/cfdcore_transaction_common.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_transaction_common.cpp * - * @brief \~japanese Transaction関連基底クラスの実装ファイルです。 - * \~english implementation of Transaction related common classes + * @brief implementation of Transaction related common classes */ #include "cfdcore/cfdcore_transaction_common.h" diff --git a/src/cfdcore_transaction_internal.h b/src/cfdcore_transaction_internal.h new file mode 100644 index 00000000..6a2fad3a --- /dev/null +++ b/src/cfdcore_transaction_internal.h @@ -0,0 +1,31 @@ +// Copyright 2020 CryptoGarage +/** + * @file cfdcore_transaction_internal.h + * + * @brief transaction internal header. + * + */ +#ifndef CFD_CORE_SRC_CFDCORE_TRANSACTION_INTERNAL_H_ +#define CFD_CORE_SRC_CFDCORE_TRANSACTION_INTERNAL_H_ +#ifdef __cplusplus + +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore_wally_util.h" // NOLINT + +namespace cfd { +namespace core { + +/** + * @brief convert bitcoin transaction from wally tx. + * @param[in] tx wally tx + * @param[in] force_exclude_witness exclude witness force flag. + * @return transaction byte data. + */ +extern ByteData ConvertBitcoinTxFromWally( + const struct wally_tx *tx, bool force_exclude_witness); + +} // namespace core +} // namespace cfd + +#endif // __cplusplus +#endif // CFD_CORE_SRC_CFDCORE_TRANSACTION_INTERNAL_H_ diff --git a/src/cfdcore_util.cpp b/src/cfdcore_util.cpp index d005809d..ee912873 100644 --- a/src/cfdcore_util.cpp +++ b/src/cfdcore_util.cpp @@ -2,8 +2,7 @@ /** * @file cfdcore_util.cpp * - * @brief \~japanese Utility関連クラス定義 - * \~english definition related to Utility classes + * @brief definition related to Utility classes */ #include "cfdcore/cfdcore_util.h" @@ -25,6 +24,26 @@ namespace core { using logger::info; using logger::warn; +////////////////////////////////// +/// Inner File Definition +////////////////////////////////// +/** + * @brief support hash format data. + */ +struct SupportHashFormat { + std::string name; //!< hash name + uint8_t type; //!< hash type +}; + +/** + * @brief support hash format list. + */ +static const SupportHashFormat kFormatList[] = { + {"ripemd160", HashUtil::kRipemd160}, {"hash160", HashUtil::kHash160}, + {"sha256", HashUtil::kSha256}, {"sha256d", HashUtil::kSha256D}, + {"sha512", HashUtil::kSha512}, {"", 0}, +}; + ////////////////////////////////// /// SigHashType ////////////////////////////////// @@ -50,12 +69,23 @@ SigHashType::SigHashType(const SigHashType &sighash_type) { } SigHashType &SigHashType::operator=(const SigHashType &sighash_type) { - hash_algorithm_ = sighash_type.hash_algorithm_; - is_anyone_can_pay_ = sighash_type.is_anyone_can_pay_; - is_fork_id_ = sighash_type.is_fork_id_; + if (this != &sighash_type) { + hash_algorithm_ = sighash_type.hash_algorithm_; + is_anyone_can_pay_ = sighash_type.is_anyone_can_pay_; + is_fork_id_ = sighash_type.is_fork_id_; + } return *this; } +SigHashType SigHashType::Create( + uint8_t flag, bool is_append_anyone_can_pay, bool is_append_fork_id) { + SigHashType obj; + obj.SetFromSigHashFlag(flag); + if (is_append_anyone_can_pay) obj.is_anyone_can_pay_ = true; + if (is_append_fork_id) obj.is_fork_id_ = true; + return obj; +} + uint32_t SigHashType::GetSigHashFlag() const { uint32_t flag = hash_algorithm_; if (is_anyone_can_pay_) { @@ -92,9 +122,81 @@ void SigHashType::SetFromSigHashFlag(uint8_t flag) { is_fork_id_ = is_fork_id; } +void SigHashType::SetAnyoneCanPay(bool is_anyone_can_pay) { + is_anyone_can_pay_ = is_anyone_can_pay; +} + +std::string SigHashType::ToString() const { + std::string result; + if (hash_algorithm_ == kSigHashAll) { + result = "ALL"; + } else if (hash_algorithm_ == kSigHashNone) { + result = "NONE"; + } else if (hash_algorithm_ == kSigHashSingle) { + result = "SINGLE"; + } else { + return ""; + } + if (is_anyone_can_pay_) result += "|ANYONECANPAY"; + return result; +} + +bool SigHashType::IsValid() const { + if (hash_algorithm_ <= kSigHashSingle) return true; + return false; +} + ////////////////////////////////// /// HashUtil ////////////////////////////////// +// Ripemd160 -------------------------------------------------------------- +ByteData160 HashUtil::Ripemd160(const std::string &str) { + std::vector output(RIPEMD160_LEN); + int ret = wally_ripemd160( + reinterpret_cast(str.data()), str.size(), output.data(), + output.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_hash160 NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "hash160 calc error."); + } + + ByteData160 byte160(output); + return byte160; +} + +ByteData160 HashUtil::Ripemd160(const std::vector &bytes) { + std::vector output(RIPEMD160_LEN); + int ret = wally_ripemd160( + bytes.data(), bytes.size(), output.data(), output.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_hash160 NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "hash160 calc error."); + } + + ByteData160 byte160(output); + return byte160; +} + +ByteData160 HashUtil::Ripemd160(const ByteData &data) { + return Ripemd160(data.GetBytes()); +} + +ByteData160 HashUtil::Ripemd160(const ByteData160 &data) { + return Ripemd160(data.GetBytes()); +} + +ByteData160 HashUtil::Ripemd160(const ByteData256 &data) { + return Ripemd160(data.GetBytes()); +} + +ByteData160 HashUtil::Ripemd160(const Pubkey &pubkey) { + return Ripemd160(pubkey.GetData().GetBytes()); +} + +ByteData160 HashUtil::Ripemd160(const Script &script) { + return Ripemd160(script.GetData().GetBytes()); +} + // Hash160 ----------------------------------------------------------------- ByteData160 HashUtil::Hash160(const std::string &str) { std::vector output(HASH160_LEN); @@ -297,6 +399,135 @@ ByteData HashUtil::Sha512(const Script &script) { return Sha512(script.GetData()); } +HashUtil::HashUtil(uint8_t hash_type) : hash_type_(hash_type) { + for (const auto &item : kFormatList) { + if (item.type == 0) break; + if (hash_type == item.type) return; + } + throw CfdException(kCfdInternalError, "unknown hash type."); +} + +HashUtil::HashUtil(const std::string &hash_type) { + if (hash_type.length() > 20) + throw CfdException(kCfdIllegalArgumentError, "unsupported hash type."); + + std::string name = hash_type; + for (size_t index = 0; index < name.length(); ++index) { + if ((name[index] >= 'A') && (name[index] <= 'Z')) { + name[index] += 'a' - 'A'; + } + } + for (const auto &item : kFormatList) { + if (item.type == 0) break; + if (name == item.name) { + hash_type_ = item.type; + return; + } + } + throw CfdException(kCfdIllegalArgumentError, "unsupported hash type."); +} + +HashUtil::HashUtil(const HashUtil &object) { + hash_type_ = object.hash_type_; + buffer_ = object.buffer_; +} + +HashUtil &HashUtil::operator=(const HashUtil &object) { + if (this != &object) { + hash_type_ = object.hash_type_; + buffer_ = object.buffer_; + } + return *this; +} + +HashUtil &HashUtil::operator<<(const std::string &str) { + buffer_.Push(ByteData( + reinterpret_cast(str.data()), + static_cast(str.size()))); + return *this; +} + +HashUtil &HashUtil::operator<<(const std::vector &bytes) { + if (!bytes.empty()) buffer_.Push(ByteData(bytes)); + return *this; +} + +HashUtil &HashUtil::operator<<(const ByteData &data) { + if (!data.IsEmpty()) buffer_.Push(data); + return *this; +} + +HashUtil &HashUtil::operator<<(const ByteData160 &data) { + buffer_.Push(data); + return *this; +} + +HashUtil &HashUtil::operator<<(const ByteData256 &data) { + buffer_.Push(data); + return *this; +} + +HashUtil &HashUtil::operator<<(const Pubkey &pubkey) { + buffer_.Push(pubkey.GetData()); + return *this; +} + +HashUtil &HashUtil::operator<<(const Script &script) { + buffer_.Push(script.GetData()); + return *this; +} + +ByteData HashUtil::Output() { + switch (hash_type_) { + case kRipemd160: + return Ripemd160(buffer_.GetBytes()).GetData(); + case kHash160: + return Hash160(buffer_.GetBytes()).GetData(); + case kSha256: + return Sha256(buffer_.GetBytes()).GetData(); + case kSha256D: + return Sha256D(buffer_.GetBytes()).GetData(); + case kSha512: + return Sha512(buffer_.GetBytes()); + default: + throw CfdException(kCfdInternalError, "unknown hash type."); + } +} + +ByteData160 HashUtil::Output160() { + switch (hash_type_) { + case kRipemd160: + return Ripemd160(buffer_.GetBytes()); + case kHash160: + return Hash160(buffer_.GetBytes()); + case kSha256: + // fall-through + case kSha256D: + // fall-through + case kSha512: + // fall-through + default: + throw CfdException(kCfdInternalError, "unknown hash type."); + } +} + +ByteData256 HashUtil::Output256() { + switch (hash_type_) { + case kSha256: + return Sha256(buffer_); + case kSha256D: + return Sha256D(buffer_); + case kRipemd160: + // fall-through + case kHash160: + // fall-through + case kSha512: + // fall-through + default: + throw CfdException(kCfdInternalError, "unknown hash type."); + } +} + ////////////////////////////////// /// CrytoUtil ////////////////////////////////// @@ -331,6 +562,44 @@ ByteData CryptoUtil::EncryptAes256( return ByteData(output); } +ByteData CryptoUtil::EncryptAes256(const ByteData &key, const ByteData &data) { + if (key.GetDataSize() != AES_KEY_LEN_256) { + warn(CFD_LOG_SOURCE, "wally_aes key size NG."); + throw CfdException( + kCfdIllegalStateError, "EncryptAes256Cbc key size error."); + } + + if (data.IsEmpty()) { + warn(CFD_LOG_SOURCE, "wally_aes data is Empty."); + throw CfdException( + kCfdIllegalStateError, "EncryptAes256Cbc data isEmpty."); + } + + size_t data_size = data.GetDataSize(); + if (data.GetDataSize() % kAesBlockLength != 0) { + data_size = ((data.GetDataSize() / kAesBlockLength) + 1) * kAesBlockLength; + } + std::vector output(data_size); + std::vector key_data = key.GetBytes(); + std::vector value_data = data.GetBytes(); + std::vector input(data_size); + // To fill the end with 0 + memcpy( + input.data(), reinterpret_cast(value_data.data()), + value_data.size()); + + // Encrypt data using AES (ECB mode, no padding). + int ret = wally_aes( + key_data.data(), key_data.size(), input.data(), input.size(), + AES_FLAG_ENCRYPT, output.data(), output.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_aes NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "EncryptAes256 error."); + } + + return ByteData(output); +} + std::string CryptoUtil::DecryptAes256ToString( const std::vector &key, const ByteData &data) { if (key.size() != AES_KEY_LEN_256) { @@ -340,7 +609,7 @@ std::string CryptoUtil::DecryptAes256ToString( std::vector output(data.GetDataSize()); - // Encrypt data using AES (ECB mode, no padding). + // Decrypt data using AES (ECB mode, no padding). int ret = wally_aes( key.data(), key.size(), data.GetBytes().data(), data.GetDataSize(), AES_FLAG_DECRYPT, output.data(), output.size()); @@ -354,6 +623,29 @@ std::string CryptoUtil::DecryptAes256ToString( return ret_str; } +ByteData CryptoUtil::DecryptAes256(const ByteData &key, const ByteData &data) { + if (key.GetDataSize() != AES_KEY_LEN_256) { + warn(CFD_LOG_SOURCE, "wally_aes key size NG."); + throw CfdException( + kCfdIllegalStateError, "DecryptAes256Cbc key size error."); + } + + std::vector output(data.GetDataSize()); + std::vector key_data = key.GetBytes(); + std::vector value_data = data.GetBytes(); + + // Decrypt data using AES (ECB mode, no padding). + int ret = wally_aes( + key_data.data(), key_data.size(), value_data.data(), value_data.size(), + AES_FLAG_DECRYPT, output.data(), output.size()); + if (ret != WALLY_OK) { + warn(CFD_LOG_SOURCE, "wally_aes NG[{}].", ret); + throw CfdException(kCfdIllegalStateError, "DecryptAes256 error."); + } + + return ByteData(output); +} + ByteData CryptoUtil::EncryptAes256Cbc( const std::vector &key, const std::vector &iv, const std::string &data) { @@ -913,6 +1205,16 @@ bool RandomNumberUtil::GetRandomBool(std::vector *random_cache) { ////////////////////////////////// /// StringUtil ////////////////////////////////// +bool StringUtil::IsValidHexString(const std::string &hex_str) { + if (hex_str.empty()) return true; + + std::vector buffer(hex_str.size() + 1); + size_t buf_size = 0; + int ret = wally_hex_to_bytes( + hex_str.data(), buffer.data(), buffer.size(), &buf_size); + return (ret == WALLY_OK); +} + std::vector StringUtil::StringToByte(const std::string &hex_str) { if (hex_str.empty()) { info(CFD_LOG_SOURCE, "hex_str empty. return empty buffer."); diff --git a/src/cfdcore_wally_util.h b/src/cfdcore_wally_util.h index f2e089e1..f3fe56ce 100644 --- a/src/cfdcore_wally_util.h +++ b/src/cfdcore_wally_util.h @@ -22,6 +22,7 @@ #include "wally_core.h" // NOLINT #include "wally_crypto.h" // NOLINT #include "wally_descriptor.h" // NOLINT +#include "wally_psbt.h" // NOLINT #include "wally_script.h" // NOLINT #include "wally_transaction.h" // NOLINT @@ -205,7 +206,7 @@ class WallyUtil { * @param[in] mnemonic mnemonic vector to check valid * @param[in] language language to verify * @retval true mnemonic checksum is valid - * @retval true mnemonic checksum is invalid + * @retval false mnemonic checksum is invalid */ static bool CheckValidMnemonic( const std::vector& mnemonic, const std::string& language); diff --git a/src/include/cfdcore/cfdcore_logger.h b/src/include/cfdcore/cfdcore_logger.h index 017b7d88..cc2ff5b7 100644 --- a/src/include/cfdcore/cfdcore_logger.h +++ b/src/include/cfdcore/cfdcore_logger.h @@ -1,8 +1,7 @@ // Copyright 2019 CryptoGarage /** * @file cfdcore_logger.h - * @brief \~japanese ログ機能を定義するファイルです。 - * \~english definition file for logger + * @brief definition file for logger */ #ifndef CFD_CORE_SRC_INCLUDE_CFDCORE_CFDCORE_LOGGER_H_ #define CFD_CORE_SRC_INCLUDE_CFDCORE_CFDCORE_LOGGER_H_ diff --git a/test/Makefile.srclist b/test/Makefile.srclist index c32ba723..a54d1d21 100644 --- a/test/Makefile.srclist +++ b/test/Makefile.srclist @@ -15,6 +15,8 @@ TEST_CFDCORE_SOURCES= \ test_bytedata.cpp \ test_bytedata160.cpp \ test_bytedata256.cpp \ + test_serializer.cpp \ + test_deserializer.cpp \ test_hashutil.cpp \ test_hdwallet.cpp \ test_cryptoutil.cpp \ @@ -28,6 +30,8 @@ TEST_CFDCORE_SOURCES= \ test_privkey.cpp \ test_extpubkey.cpp \ test_extprivkey.cpp \ + test_keydata.cpp \ + test_psbt.cpp \ test_descriptor.cpp \ test_address.cpp \ test_outpoint.cpp \ @@ -45,6 +49,8 @@ TEST_CFDCORE_SOURCES= \ test_sighashtype.cpp \ test_schnorrsig.cpp \ test_ecdsa_adaptor.cpp \ + test_taproot_merkletree.cpp \ + test_taproot_util.cpp \ ${TEST_CFDCORE_ELEMENTS_SOURCES} TEST_CFDCORE_STATIC_SOURCES= \ diff --git a/test/test_address.cpp b/test/test_address.cpp index 94167529..3757c289 100644 --- a/test/test_address.cpp +++ b/test/test_address.cpp @@ -6,6 +6,8 @@ #include "cfdcore/cfdcore_util.h" #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_elements_address.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_taproot.h" using cfd::core::Address; using cfd::core::NetType; @@ -19,6 +21,8 @@ using cfd::core::HashUtil; using cfd::core::Script; using cfd::core::ScriptBuilder; using cfd::core::ScriptOperator; +using cfd::core::SchnorrPubkey; +using cfd::core::TaprootScriptTree; using cfd::core::CfdException; using cfd::core::AddressFormatData; using cfd::core::GetBitcoinAddressFormatList; @@ -172,6 +176,81 @@ TEST(Address, P2wshAddressTest) { EXPECT_EQ(NetType::kRegtest, address.GetNetType()); } +TEST(Address, TaprootAddressTest) { + try { + const SchnorrPubkey pubkey = SchnorrPubkey( + "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"); + Address address; + address = Address(NetType::kMainnet, + WitnessVersion::kVersion1, pubkey); + EXPECT_STREQ("bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kMainnet, address.GetNetType()); + EXPECT_EQ(AddressType::kTaprootAddress, address.GetAddressType()); + EXPECT_EQ(WitnessVersion::kVersion1, address.GetWitnessVersion()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetHash().GetHex().c_str()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetSchnorrPubkey().GetHex().c_str()); + EXPECT_STREQ("", address.GetScript().GetHex().c_str()); + EXPECT_STREQ("51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetLockingScript().GetHex().c_str()); + + EXPECT_NO_THROW((address = Address(NetType::kTestnet, + WitnessVersion::kVersion1, pubkey))); + EXPECT_STREQ("tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kTestnet, address.GetNetType()); + + EXPECT_NO_THROW((address = Address(NetType::kRegtest, + WitnessVersion::kVersion1, pubkey))); + EXPECT_STREQ("bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kRegtest, address.GetNetType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } +} + +TEST(Address, TaprootScriptAddressTest) { + try { + const SchnorrPubkey pubkey( + "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"); + ScriptBuilder build; + build.AppendOperator(ScriptOperator::OP_TRUE); + TaprootScriptTree tree(build.Build()); + Address address; + address = Address(NetType::kMainnet, + WitnessVersion::kVersion1, tree, pubkey); + EXPECT_STREQ("bc1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vsftyvah", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kMainnet, address.GetNetType()); + EXPECT_EQ(AddressType::kTaprootAddress, address.GetAddressType()); + EXPECT_EQ(WitnessVersion::kVersion1, address.GetWitnessVersion()); + EXPECT_STREQ("88de1a59b38939f58fb4f8c5ffc3d56390d43e9e91c7b1d67f91e070f3108799", + address.GetHash().GetHex().c_str()); + EXPECT_EQ("tl(51)", + address.GetScriptTree().ToString()); + EXPECT_STREQ("", address.GetScript().GetHex().c_str()); + EXPECT_STREQ("512088de1a59b38939f58fb4f8c5ffc3d56390d43e9e91c7b1d67f91e070f3108799", + address.GetLockingScript().GetHex().c_str()); + + EXPECT_NO_THROW((address = Address(NetType::kTestnet, + WitnessVersion::kVersion1, tree, pubkey))); + EXPECT_STREQ("tb1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vs7rjr8c", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kTestnet, address.GetNetType()); + + EXPECT_NO_THROW((address = Address(NetType::kRegtest, + WitnessVersion::kVersion1, tree, pubkey))); + EXPECT_STREQ("bcrt1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vsn6c9jz", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kRegtest, address.GetNetType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } +} + TEST(Address, NoSegwitAddressFromHashTest) { const Pubkey pubkey = Pubkey( "027592aab5d43618dda13fba71e3993cd7517a712d3da49664c06ee1bd3d1f70af"); @@ -267,6 +346,39 @@ TEST(Address, SegwitAddressFromHashTest) { CfdException); } +TEST(Address, TaprootAddressFromHashTest) { + try { + const ByteData pubkey = ByteData( + "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"); + Address address; + address = Address(NetType::kMainnet, WitnessVersion::kVersion1, pubkey); + EXPECT_STREQ("bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kMainnet, address.GetNetType()); + EXPECT_EQ(AddressType::kTaprootAddress, address.GetAddressType()); + EXPECT_EQ(WitnessVersion::kVersion1, address.GetWitnessVersion()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetHash().GetHex().c_str()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetSchnorrPubkey().GetHex().c_str()); + EXPECT_STREQ("", address.GetScript().GetHex().c_str()); + EXPECT_STREQ("51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetLockingScript().GetHex().c_str()); + + address = Address(NetType::kTestnet, WitnessVersion::kVersion1, pubkey); + EXPECT_STREQ("tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kTestnet, address.GetNetType()); + + address = Address(NetType::kRegtest, WitnessVersion::kVersion1, pubkey); + EXPECT_STREQ("bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kRegtest, address.GetNetType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } +} + TEST(Address, NoSegwitAddressFromStringTest) { Address address; // P2PKH @@ -334,6 +446,17 @@ TEST(Address, SegwitAddressFromStringTest) { EXPECT_EQ(AddressType::kP2wpkhAddress, address.GetAddressType()); EXPECT_EQ(WitnessVersion::kVersion0, address.GetWitnessVersion()); + EXPECT_NO_THROW((address = Address("bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq"))); + EXPECT_STREQ("bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", + address.GetAddress().c_str()); + EXPECT_EQ(NetType::kRegtest, address.GetNetType()); + EXPECT_EQ(AddressType::kTaprootAddress, address.GetAddressType()); + EXPECT_EQ(WitnessVersion::kVersion1, address.GetWitnessVersion()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetHash().GetHex().c_str()); + EXPECT_STREQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + address.GetSchnorrPubkey().GetHex().c_str()); + // Illegal data EXPECT_THROW((address = Address("bcrt1qjfw5q2ygp0gvn450h3lu0hlwjanfsc5u4fujx6")), CfdException); @@ -708,10 +831,19 @@ static const std::vector test_vectors = { { "XTfKFxkeC83awc3HnPFbZxgMRdBAjDpDbc", ElementsNetType::kElementsRegtest, - ElementsAddressType::kP2shP2wshAddress, + ElementsAddressType::kP2shAddress, Pubkey(), Script("522102723d9fb5ad0c7f7d70c897731bcf6a58a4dee8113d7d848bff9f6f7bc01ff36621023bf567600a7972e22ac50eef693f05935cbcf48fb7bb550d7ab7e050f98567e352ae"), }, + // p2sh-segwit multisig + { + "XGpSNPYXP2h5FnDXiv5fGKdp4u2HjuexMu", + ElementsNetType::kElementsRegtest, + ElementsAddressType::kP2shP2wshAddress, + Pubkey(), + // Script("522102723d9fb5ad0c7f7d70c897731bcf6a58a4dee8113d7d848bff9f6f7bc01ff36621023bf567600a7972e22ac50eef693f05935cbcf48fb7bb550d7ab7e050f98567e352ae"), + Script("0020f41c58db6607eb43a43554cd45787df1d9ee89a2f001bff8ae9ce427d2d8cad4"), + }, // regtest bech32 address // bech32 pubkey { diff --git a/test/test_cryptoutil.cpp b/test/test_cryptoutil.cpp index c4d4be4b..7f20c3c1 100644 --- a/test/test_cryptoutil.cpp +++ b/test/test_cryptoutil.cpp @@ -38,6 +38,16 @@ TEST(CryptoUtil, EncryptAes256String19) { "752fe203af4a4d427997e5d2c8b246530e0546b66d2982a49e333e77295dccea"); } +TEST(CryptoUtil, EncryptAes256ByteData) { + ByteData key( + "616975656F616975656F616975656F616975656F616975656F616975656F6169"); + ByteData data("74657374207465737420746573742074657374"); + ByteData byte_data = CryptoUtil::EncryptAes256(key, data); + EXPECT_STREQ( + byte_data.GetHex().c_str(), + "752fe203af4a4d427997e5d2c8b246530e0546b66d2982a49e333e77295dccea"); +} + TEST(CryptoUtil, EncryptAes256KeyEmpty) { try { std::vector key; @@ -94,6 +104,17 @@ TEST(CryptoUtil, DecryptAes256ToString2) { EXPECT_STREQ(result.c_str(), "test test test test"); } +TEST(CryptoUtil, DecryptAes256ByteData) { + ByteData key( + "616975656F616975656F616975656F616975656F616975656F616975656F6169"); + ByteData data( + "752fe203af4a4d427997e5d2c8b246530e0546b66d2982a49e333e77295dccea"); + ByteData result = CryptoUtil::DecryptAes256(key, data); + + EXPECT_STREQ(result.GetHex().c_str(), + "7465737420746573742074657374207465737400000000000000000000000000"); +} + TEST(CryptoUtil, DecryptAes256ToStringKeyEmpty) { try { std::vector key; diff --git a/test/test_descriptor.cpp b/test/test_descriptor.cpp index 8e4aa509..a6086298 100644 --- a/test/test_descriptor.cpp +++ b/test/test_descriptor.cpp @@ -231,6 +231,38 @@ TEST(Descriptor, Parse_sh_multi) { } } +TEST(Descriptor, Parse_sh_multi_maximum) { + std::string descriptor = "sh(multi(15,02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0,0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c,024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82,03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38,020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1,0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147,0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c,020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9,0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37,0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e,03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b,02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd,0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d,0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2,025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db))"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + std::vector script_list; + + EXPECT_NO_THROW(desc = Descriptor::Parse(descriptor)); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(script_list = desc.GetReferenceAll()); + EXPECT_STREQ(desc_str.c_str(), descriptor.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), + "OP_HASH160 3b94abddb86c04958381b48c615a7766bcb3e98f OP_EQUAL"); + EXPECT_EQ(script_list.size(), 1); + if (script_list.size() == 1) { + EXPECT_STREQ(script_list[0].GetRedeemScript().ToString().c_str(), + "15 02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0 0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c 024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82 03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38 020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1 0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147 0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c 020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9 0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37 0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e 03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b 02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd 0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d 0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2 025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db 15 OP_CHECKMULTISIG"); + EXPECT_TRUE(script_list[0].GetChild().HasReqNum()); + EXPECT_EQ(script_list[0].GetChild().GetReqNum(), 15); + } + + std::string descriptor_err = "sh(multi(15,02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0,0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c,024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82,03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38,020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1,0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147,0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c,020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9,0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37,0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e,03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b,02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd,0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d,0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2,025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db,030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55))"; + try { + Descriptor::Parse(descriptor_err); + EXPECT_TRUE(false); + } catch (const CfdException& except) { + EXPECT_STREQ("CreateMultisigScript pubkeys array size is over.", + except.what()); + } +} + TEST(Descriptor, Parse_sortedmulti) { std::string descriptor = "sh(sortedmulti(2,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))"; Descriptor desc; @@ -277,6 +309,43 @@ TEST(Descriptor, Parse_wsh_multi) { } } +TEST(Descriptor, Parse_wsh_multi_maximum) { + std::string descriptor = "wsh(multi(15,02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0,0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c,024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82,03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38,020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1,0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147,0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c,020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9,0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37,0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e,03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b,02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd,0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d,0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2,025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db,030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55,0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e,02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8,03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950,024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561))"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + std::vector script_list; + + try { + desc = Descriptor::Parse(descriptor); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + EXPECT_NO_THROW(desc = Descriptor::Parse(descriptor)); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(script_list = desc.GetReferenceAll()); + EXPECT_STREQ(desc_str.c_str(), descriptor.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), + "0 5213817e55f6309979372ce754d259e0658ca56e68ce0bcee2b281c7af92fc5f"); + EXPECT_EQ(script_list.size(), 1); + if (script_list.size() == 1) { + EXPECT_STREQ(script_list[0].GetRedeemScript().ToString().c_str(), + "15 02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0 0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c 024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82 03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38 020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1 0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147 0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c 020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9 0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37 0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e 03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b 02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd 0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d 0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2 025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db 030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55 0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e 02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8 03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950 024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561 20 OP_CHECKMULTISIG"); + EXPECT_TRUE(script_list[0].GetChild().HasReqNum()); + EXPECT_EQ(script_list[0].GetChild().GetReqNum(), 15); + } + + std::string descriptor_err = "wsh(multi(15,02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0,0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c,024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82,03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38,020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1,0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147,0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c,020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9,0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37,0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e,03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b,02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd,0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d,0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2,025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db,030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55,0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e,02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8,03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950,024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561,03f3aba2366b71f8473dd8dd4186005a9e3c6f9a32f76fc45493fd2a78b78c0d8d))"; + try { + Descriptor::Parse(descriptor_err); + EXPECT_TRUE(false); + } catch (const CfdException& except) { + EXPECT_STREQ("Failed to multisig pubkey num.", + except.what()); + } +} + TEST(Descriptor, Parse_sh_wsh_multi) { std::string descriptor = "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))"; Descriptor desc; @@ -328,6 +397,37 @@ TEST(Descriptor, Parse_raw) { EXPECT_NO_THROW(desc_str = desc.ToString()); EXPECT_STREQ(desc_str.c_str(), descriptor.c_str()); EXPECT_STREQ(locking_script.ToString().c_str(), "OP_RETURN 54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"); + + std::vector script_list; + std::vector arg_list1; + EXPECT_NO_THROW(script_list = desc.GetReferenceAll(&arg_list1)); + EXPECT_EQ(script_list.size(), 1); + if (script_list.size() == 1) { + EXPECT_FALSE(script_list[0].HasAddress()); + } +} + +TEST(Descriptor, Parse_raw_wsh) { + std::string descriptor = "raw(0020ef8110fa7ddefb3e2d02b2c1b1480389b4bc93f606281570cfc20dba18066aee)#2xu4jtw0"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + + EXPECT_NO_THROW(desc = Descriptor::Parse(descriptor)); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString()); + EXPECT_STREQ(desc_str.c_str(), descriptor.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), "0 ef8110fa7ddefb3e2d02b2c1b1480389b4bc93f606281570cfc20dba18066aee"); + + std::vector script_list; + std::vector arg_list1; + EXPECT_NO_THROW(script_list = desc.GetReferenceAll(&arg_list1)); + EXPECT_EQ(script_list.size(), 1); + if (script_list.size() == 1) { + EXPECT_TRUE(script_list[0].HasAddress()); + EXPECT_STREQ(script_list[0].GenerateAddress(NetType::kMainnet).GetAddress().c_str(), + "bc1qa7q3p7nammanutgzktqmzjqr3x6teylkqc5p2ux0cgxm5xqxdthq02yr5g"); + } } TEST(Descriptor, Parse_pk_extkey) { @@ -384,6 +484,14 @@ TEST(Descriptor, Parse_pkh_extkey_derive) { "OP_DUP OP_HASH160 2a05c214617c9b0434c92d0583200a85ef61818f OP_EQUALVERIFY OP_CHECKSIG"); EXPECT_STREQ(gen_script.ToString().c_str(), "OP_DUP OP_HASH160 c463e6dedb2b780434e60fcee3f2d0a0fbcbbc90 OP_EQUALVERIFY OP_CHECKSIG"); + try { + auto key = desc.GetKeyData("0"); + EXPECT_TRUE(key.IsValid()); + EXPECT_STREQ(key.ToString().c_str(), + "[d34db33f/44'/0'/0'/1/0]03095e95d8c50ae3f3fea93fa8e983f710489f60ff681a658c06eba64622c824b1"); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + } } TEST(Descriptor, Parse_wsh_extkey_derive) { @@ -821,6 +929,16 @@ TEST(Descriptor, CheckChecksum) { EXPECT_NO_THROW(desc = Descriptor::Parse(base_descriptor)); EXPECT_NO_THROW(desc_str = desc.ToString()); EXPECT_STREQ(desc_str.c_str(), success_descriptor.c_str()); + try { + auto key_list = desc.GetKeyDataAll(); + EXPECT_FALSE(key_list.empty()); + if (!key_list.empty()) { + EXPECT_STREQ(key_list[0].ToString().c_str(), + "[ef57314e/0'/0'/4']03d3f817091de0bbe51e19b53303b12e463f664894d49cb5bf5bb19c88fbc54d8d"); + } + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + } } TEST(Descriptor, CreateDescriptor_wpkh) { @@ -835,6 +953,14 @@ TEST(Descriptor, CreateDescriptor_wpkh) { EXPECT_NO_THROW(desc = Descriptor::CreateDescriptor(DescriptorScriptType::kDescriptorScriptWpkh, key_info)); EXPECT_NO_THROW(desc_str = desc.ToString()); EXPECT_STREQ(desc_str.c_str(), ext_descriptor.c_str()); + try { + auto key = desc.GetKeyData(); + EXPECT_TRUE(key.IsValid()); + EXPECT_STREQ(key.ToString().c_str(), + "[1422fcb3/0'/0'/68']02bedf98a38247c1718fdff7e07561b4dc15f10323ebb0accab581778e72c2e995"); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + } } TEST(Descriptor, CreateDescriptor_sh_wsh_sortedmulti) { diff --git a/test/test_deserializer.cpp b/test/test_deserializer.cpp new file mode 100644 index 00000000..654c2909 --- /dev/null +++ b/test/test_deserializer.cpp @@ -0,0 +1,32 @@ +#include "gtest/gtest.h" +#include + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_exception.h" + +using cfd::core::ByteData; +using cfd::core::ByteData160; +using cfd::core::ByteData256; +using cfd::core::Deserializer; + +TEST(Deserializer, Normal) { + std::vector empty_data; + Deserializer empty_parser(empty_data); + + Deserializer parser(ByteData("010203040506070808090a0b0c0d0e0f")); + EXPECT_EQ(1, parser.ReadUint8()); + std::vector exp_buf = {2, 3}; + auto buf = parser.ReadBuffer(2); + + EXPECT_EQ(exp_buf.size(), buf.size()); + if (exp_buf.size() == buf.size()) { + for (size_t idx=0; idx blind_list; + BlindParameter param; + param.asset = ConfidentialAssetId(inputasset); + param.abf = BlindFactor(inputassetblinder); + param.vbf = BlindFactor(inputblinder); + param.value = ConfidentialValue(Amount::CreateByCoinAmount(inputamount)); + blind_list.push_back(param); + std::vector pubkeys; + pubkeys.push_back(pubkey1); + pubkeys.push_back(pubkey2); + pubkeys.push_back(Pubkey()); + + try { + tx.BlindTxOut(blind_list, pubkeys); + } catch (const CfdException& except) { + EXPECT_STREQ("unmatch input/output amount.", except.what()); + return; + } + EXPECT_STREQ("", "Error Test Fail."); +} + TEST(ConfidentialTransaction, SignatureHashTest) { ConfidentialTransaction tx( "020000000001319bff5f4311e6255ecf4dd472650a6ef85fde7d11cd10d3e6ba5974174aeb560100000000ffffffff0201f38611eb688e6fcd06f25e2faf52b9f98364dc14c379ab085f1b57d56b4b1a6f0100000bd2cc1584c002deb65cc52301e1622f482a2f588b9800d2b8386ffabf74d6b2d73d17503a2f921976a9146a98a3f2935718df72518c00768ec67c589e0b2888ac01f38611eb688e6fcd06f25e2faf52b9f98364dc14c379ab085f1b57d56b4b1a6f0100000000004c4b40000000000000"); diff --git a/test/test_elements_confidentialtxin.cpp b/test/test_elements_confidentialtxin.cpp index 74d01805..2ef28552 100644 --- a/test/test_elements_confidentialtxin.cpp +++ b/test/test_elements_confidentialtxin.cpp @@ -243,26 +243,26 @@ TEST(ConfidentialTxIn, EstimateTxInSize) { static const std::string multisig_script = "522102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b8253ae"; static const std::string scriptsig_template = "00473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb014752210205ffcdde75f262d66ada3dd877c7471f8f8ee9ee24d917c3e18d01cee458bafe2102be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec552ae"; static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 149, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shAddress, 204, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wpkhAddress, 174, 111, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wshAddress, 220, 145, Script("51"), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wpkhAddress, 152, 111, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 207, 166, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 300, 259, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 190, 149, exp_script, 0, Script(), false, false, false, scriptsig_template, 0, 0}, + {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shP2wpkhAddress, 176, 112, Script(), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shP2wshAddress, 222, 146, Script("51"), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 153, 112, Script(), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2wshAddress, 208, 167, exp_script, 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2wshAddress, 301, 260, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2wshAddress, 191, 150, exp_script, 0, Script(), false, false, false, scriptsig_template, 0, 0}, // pegin - {AddressType::kP2wpkhAddress, 610, 569, Script(), 226, Script("51"), false, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 611, 570, Script(), 226, Script("51"), false, false, false, "", 0, 0}, // issue - {AddressType::kP2wpkhAddress, 234, 111, Script(), 0, Script(), true, false, false, "", 0, 0}, - {AddressType::kP2wpkhAddress, /*6064*/ 8498, /*5893*/ 8327, Script(), 0, Script(), true, true, false, + {AddressType::kP2wpkhAddress, 235, 112, Script(), 0, Script(), true, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 8499, 8328, Script(), 0, Script(), true, true, false, "", 0, 36}, - {AddressType::kP2wpkhAddress, 8626, 8455, Script(), 0, Script(), true, true, false, + {AddressType::kP2wpkhAddress, 8627, 8456, Script(), 0, Script(), true, true, false, "", 0, 52}, // reissue - {AddressType::kP2wpkhAddress, /*3173*/ 4390, /*3002*/ 4219, Script(), 0, Script(), true, true, true, + {AddressType::kP2wpkhAddress, 4391, 4220, Script(), 0, Script(), true, true, true, "", 0, 36}, - {AddressType::kP2wpkhAddress, 4454, 4283, Script(), 0, Script(), true, true, true, + {AddressType::kP2wpkhAddress, 4455, 4284, Script(), 0, Script(), true, true, true, "", 0, 52}, }; @@ -294,10 +294,10 @@ TEST(ConfidentialTxIn, EstimateTxInSize) { TEST(ConfidentialTxIn, EstimateTxInVsize) { static const std::string multisig_script = "522102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b8253ae"; static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 149, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shAddress, 204, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wpkhAddress, 91, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wshAddress, 112, 0, Script("51"), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shP2wpkhAddress, 92, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2shP2wshAddress, 113, 0, Script("51"), 0, Script(), false, false, false, "", 0, 0}, {AddressType::kP2wpkhAddress, 69, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, {AddressType::kP2wshAddress, 83, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, {AddressType::kP2wshAddress, 106, 0, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0}, @@ -431,25 +431,25 @@ TEST(ConfidentialTxInReference, EstimateTxInSize) { } const std::vector test_vector = { - {txin, AddressType::kP2pkhAddress, 149, 0, Script(), Script(), false, "", 0, 0}, - {txin, AddressType::kP2shAddress, 204, 0, exp_script, Script(), false, "", 0, 0}, - {txin, AddressType::kP2shP2wpkhAddress, 174, 111, Script(), Script(), false, "", 0, 0}, - {txin, AddressType::kP2shP2wshAddress, 220, 145, Script("51"), Script(), false, "", 0, 0}, - {txin, AddressType::kP2wpkhAddress, 152, 111, Script(), Script(), false, "", 0, 0}, - {txin, AddressType::kP2wshAddress, 207, 166, exp_script,Script(), false, "", 0, 0}, - {txin, AddressType::kP2wshAddress, 300, 259, Script(multisig_script), Script(), false, + {txin, AddressType::kP2pkhAddress, 150, 0, Script(), Script(), false, "", 0, 0}, + {txin, AddressType::kP2shAddress, 205, 0, exp_script, Script(), false, "", 0, 0}, + {txin, AddressType::kP2shP2wpkhAddress, 176, 112, Script(), Script(), false, "", 0, 0}, + {txin, AddressType::kP2shP2wshAddress, 222, 146, Script("51"), Script(), false, "", 0, 0}, + {txin, AddressType::kP2wpkhAddress, 153, 112, Script(), Script(), false, "", 0, 0}, + {txin, AddressType::kP2wshAddress, 208, 167, exp_script,Script(), false, "", 0, 0}, + {txin, AddressType::kP2wshAddress, 301, 260, Script(multisig_script), Script(), false, "", 0, 0}, - {txin, AddressType::kP2wshAddress, 190, 149, exp_script, Script(), false, + {txin, AddressType::kP2wshAddress, 191, 150, exp_script, Script(), false, scriptsig_template, 0, 0}, // pegin - {pegin_txin, AddressType::kP2wpkhAddress, 608, 567, Script(), Script("51"), false, "", 0, 0}, + {pegin_txin, AddressType::kP2wpkhAddress, 609, 568, Script(), Script("51"), false, "", 0, 0}, // issue - {issue_txin, AddressType::kP2wpkhAddress, 4142, 3971, Script(), Script(), true, "", 0, 0}, - {issue_txin, AddressType::kP2wpkhAddress, 6064, 5893, Script(), Script(), true, "", 0, 36}, - {issue_txin, AddressType::kP2wpkhAddress, 8626, 8455, Script(), Script(), true, "", 0, 52}, + {issue_txin, AddressType::kP2wpkhAddress, 4143, 3972, Script(), Script(), true, "", 0, 0}, + {issue_txin, AddressType::kP2wpkhAddress, 6065, 5894, Script(), Script(), true, "", 0, 36}, + {issue_txin, AddressType::kP2wpkhAddress, 8627, 8456, Script(), Script(), true, "", 0, 52}, // reissue - {reissue_txin, AddressType::kP2wpkhAddress, 3173, 3002, Script(), Script(), true, "", 0, 36}, - {reissue_txin, AddressType::kP2wpkhAddress, 4454, 4283, Script(),Script(), true, "", 0, 52}, + {reissue_txin, AddressType::kP2wpkhAddress, 3174, 3003, Script(), Script(), true, "", 0, 36}, + {reissue_txin, AddressType::kP2wpkhAddress, 4455, 4284, Script(),Script(), true, "", 0, 52}, }; for (const auto& test_data : test_vector) { @@ -510,10 +510,10 @@ TEST(ConfidentialTxInReference, EstimateTxInVsize) { } const std::vector test_vector = { - {txin, AddressType::kP2pkhAddress, 149, 0, Script(), Script(), false, "", 0, 0}, - {txin, AddressType::kP2shAddress, 204, 0, exp_script, Script(), false, "", 0, 0}, - {txin, AddressType::kP2shP2wpkhAddress, 91, 0, Script(), Script(), false, "", 0, 0}, - {txin, AddressType::kP2shP2wshAddress, 112, 0, Script("51"), Script(), false, "", 0, 0}, + {txin, AddressType::kP2pkhAddress, 150, 0, Script(), Script(), false, "", 0, 0}, + {txin, AddressType::kP2shAddress, 205, 0, exp_script, Script(), false, "", 0, 0}, + {txin, AddressType::kP2shP2wpkhAddress, 92, 0, Script(), Script(), false, "", 0, 0}, + {txin, AddressType::kP2shP2wshAddress, 113, 0, Script("51"), Script(), false, "", 0, 0}, {txin, AddressType::kP2wpkhAddress, 69, 0, Script(), Script(), false, "", 0, 0}, {txin, AddressType::kP2wshAddress, 83, 0, exp_script, Script(), false, "", 0, 0}, {txin, AddressType::kP2wshAddress, 106, 0, Script(multisig_script), Script(), false, "", 0, 0}, diff --git a/test/test_extprivkey.cpp b/test/test_extprivkey.cpp index d9278cc9..8417d64b 100644 --- a/test/test_extprivkey.cpp +++ b/test/test_extprivkey.cpp @@ -12,6 +12,7 @@ using cfd::core::ByteData; using cfd::core::ByteData256; using cfd::core::ExtPubkey; using cfd::core::ExtPrivkey; +using cfd::core::KeyData; using cfd::core::NetType; using cfd::core::Privkey; @@ -189,6 +190,15 @@ TEST(ExtPrivkey, DerivePrivkeyTest) { EXPECT_EQ(child2.GetChildNum(), child.GetChildNum()); EXPECT_THROW((child2 = extkey.DerivePrivkey("m/0/44")), CfdException); + + // KeyData + KeyData data1 = extkey.DerivePrivkeyData("0/44"); + EXPECT_STREQ("[ae05dbb7/0/44]xprvA5P4YtgFjzqM4QpXJZ8Zr7Wkhng7ugTybA3KWMAqDfAamqu5nqJ3zKRhB29cxuqCc8hPagZcN5BsuoXx4Xn7iYHnQvEdyMwZRFgoJXs8CDN", data1.ToString(false).c_str()); + EXPECT_STREQ("xprvA5P4YtgFjzqM4QpXJZ8Zr7Wkhng7ugTybA3KWMAqDfAamqu5nqJ3zKRhB29cxuqCc8hPagZcN5BsuoXx4Xn7iYHnQvEdyMwZRFgoJXs8CDN", data1.GetExtPrivkey().ToString().c_str()); + + KeyData data2 = extkey.DerivePrivkeyData(path); + EXPECT_STREQ("[ae05dbb7/0/44]xprvA5P4YtgFjzqM4QpXJZ8Zr7Wkhng7ugTybA3KWMAqDfAamqu5nqJ3zKRhB29cxuqCc8hPagZcN5BsuoXx4Xn7iYHnQvEdyMwZRFgoJXs8CDN", data2.ToString(false).c_str()); + EXPECT_STREQ("xprvA5P4YtgFjzqM4QpXJZ8Zr7Wkhng7ugTybA3KWMAqDfAamqu5nqJ3zKRhB29cxuqCc8hPagZcN5BsuoXx4Xn7iYHnQvEdyMwZRFgoJXs8CDN", data2.GetExtPrivkey().ToString().c_str()); } TEST(ExtPrivkey, GetExtPubkeyTest) { @@ -227,5 +237,14 @@ TEST(ExtPrivkey, DerivePubkeyTest) { child2 = extkey.DerivePubkey("0/44h"); EXPECT_STREQ(child2.ToString().c_str(), child.ToString().c_str()); EXPECT_EQ(child2.GetVersion(), child.GetVersion()); + + // KeyData + KeyData data1 = extkey.DerivePubkeyData("0/44h"); + EXPECT_STREQ("[ae05dbb7/0/44']xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", data1.ToString(false).c_str()); + EXPECT_STREQ("xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", data1.GetExtPubkey().ToString().c_str()); + + KeyData data2 = extkey.DerivePubkeyData(path); + EXPECT_STREQ("[ae05dbb7/0/44']xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", data2.ToString(false).c_str()); + EXPECT_STREQ("xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", data2.GetExtPubkey().ToString().c_str()); } diff --git a/test/test_extpubkey.cpp b/test/test_extpubkey.cpp index b1216463..91713c9b 100644 --- a/test/test_extpubkey.cpp +++ b/test/test_extpubkey.cpp @@ -10,6 +10,7 @@ using cfd::core::CfdException; using cfd::core::ByteData; using cfd::core::ByteData256; using cfd::core::ExtPubkey; +using cfd::core::KeyData; using cfd::core::Privkey; using cfd::core::Pubkey; using cfd::core::NetType; @@ -164,6 +165,15 @@ TEST(ExtPubkey, DerivePubkeyTest) { EXPECT_NO_THROW((child2 = extkey.DerivePubkey("/1/1"))); // start slash EXPECT_THROW((child2 = extkey.DerivePubkey("1/2//3")), CfdException); // empty number + + // KeyData + KeyData data1 = extkey.DerivePubkeyData("0/44"); + EXPECT_STREQ("[b7665978/0/44]tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", data1.ToString(false).c_str()); + EXPECT_STREQ("tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", data1.GetExtPubkey().ToString().c_str()); + + KeyData data2 = extkey.DerivePubkeyData(path); + EXPECT_STREQ("[b7665978/0/44]tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", data2.ToString(false).c_str()); + EXPECT_STREQ("tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", data2.GetExtPubkey().ToString().c_str()); } TEST(ExtPubkey, DerivePubTweakTest) { diff --git a/test/test_hashutil.cpp b/test/test_hashutil.cpp index f7cb8d7b..2b8ca428 100644 --- a/test/test_hashutil.cpp +++ b/test/test_hashutil.cpp @@ -7,12 +7,6 @@ #include "cfdcore/cfdcore_script.h" #include "cfdcore/cfdcore_exception.h" -// https://qiita.com/yohm/items/477bac065f4b772127c7 - -// The main function are using gtest's main(). - -// TEST(test_suite_name, test_name) - using cfd::core::ByteData; using cfd::core::ByteData160; using cfd::core::ByteData256; @@ -24,6 +18,66 @@ using cfd::core::Script; // https://bc-2.jp/tools/txeditor2.html // https://hogehoge.tk/tool/ +// Ripemd160 ----------------------------------------------------------------- +TEST(HashUtil, Ripemd160String) { + ByteData160 byte_data = HashUtil::Ripemd160("The quick brown fox jumps over the lazy dog"); + EXPECT_STREQ(byte_data.GetHex().c_str(), + "37f332f68db77bd9d7edd4969571ad671cf9dd3b"); + + byte_data = HashUtil::Ripemd160("The quick brown fox jumps over the lazy cog"); + EXPECT_STREQ(byte_data.GetHex().c_str(), + "132072df690933835eb8b6ad0b77e7b6f14acad7"); + + byte_data = HashUtil::Ripemd160(""); + EXPECT_STREQ(byte_data.GetHex().c_str(), + "9c1185a5c5e9fc54612808977ee8f548b2258d31"); +} + +TEST(HashUtil, Ripemd160) { + ByteData160 byte_data; + byte_data = HashUtil::Ripemd160(ByteData("0123456789abcdef")); + EXPECT_STREQ(byte_data.GetHex().c_str(), "cea1b21f1a739fba68d1d4290437d2c5609be1d3"); + + byte_data = HashUtil::Ripemd160(ByteData160("0123456789abcdef0123456789abcdef01234567")); + EXPECT_STREQ(byte_data.GetHex().c_str(), "49ec9207a365f6f330d529ca2a79e23a7ea2b526"); + + byte_data = HashUtil::Ripemd160(ByteData256("1234567890123456789012345678901234567890123456789012345678901234")); + EXPECT_STREQ(byte_data.GetHex().c_str(), "a5b1c86f10c81c3c543304e9891815d8de036296"); + + byte_data = HashUtil::Ripemd160(Pubkey("032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db")); + EXPECT_STREQ(byte_data.GetHex().c_str(), "1c8eae98d10ae2eb0ce0a99d446f0156c6f596ca"); + + byte_data = HashUtil::Ripemd160(Script("21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac")); + EXPECT_STREQ(byte_data.GetHex().c_str(), "6be854f95bade5490a020c3841c50d08339a5c89"); +} + +TEST(HashUtil, Ripemd160ByOperator) { + ByteData byte_data = (HashUtil(HashUtil::kRipemd160) + << "The quick brown fox jumps over the lazy dog").Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), + "37f332f68db77bd9d7edd4969571ad671cf9dd3b"); + + byte_data = (HashUtil(HashUtil::kRipemd160) + << ByteData("0123456789abcdef")).Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), "cea1b21f1a739fba68d1d4290437d2c5609be1d3"); + + byte_data = (HashUtil(HashUtil::kRipemd160) + << ByteData160("0123456789abcdef0123456789abcdef01234567")).Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), "49ec9207a365f6f330d529ca2a79e23a7ea2b526"); + + byte_data = (HashUtil(HashUtil::kRipemd160) + << ByteData256("1234567890123456789012345678901234567890123456789012345678901234")).Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), "a5b1c86f10c81c3c543304e9891815d8de036296"); + + byte_data = (HashUtil(HashUtil::kRipemd160) + << Pubkey("032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db")).Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), "1c8eae98d10ae2eb0ce0a99d446f0156c6f596ca"); + + byte_data = (HashUtil(HashUtil::kRipemd160) + << Script("21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac")).Output(); + EXPECT_STREQ(byte_data.GetHex().c_str(), "6be854f95bade5490a020c3841c50d08339a5c89"); +} + // Hash160 ----------------------------------------------------------------- TEST(HashUtil, Hash160String) { ByteData160 byte_data = HashUtil::Hash160("test Hash160 OK"); @@ -88,6 +142,11 @@ TEST(HashUtil, Sha256String) { EXPECT_STREQ( byte_data.GetHex().c_str(), "98478d92e5005d232ad06c805eccf5381f47f6f51ee7803e5206dc04e2639a62"); + + ByteData byte_data2 = (HashUtil("Sha256") << "test Sha256 OK").Output(); + EXPECT_STREQ( + byte_data2.GetHex().c_str(), + "98478d92e5005d232ad06c805eccf5381f47f6f51ee7803e5206dc04e2639a62"); } TEST(HashUtil, Sha256Bytes) { @@ -124,6 +183,11 @@ TEST(HashUtil, Sha256ByteData256) { EXPECT_STREQ( byte_data.GetHex().c_str(), "ca1194a558362b5fa6e7887da7b41ec6faeb01c9477a0afd46dfc0692be33482"); + + ByteData byte_data2 = (HashUtil("Sha256") << target).Output(); + EXPECT_STREQ( + byte_data2.GetHex().c_str(), + "ca1194a558362b5fa6e7887da7b41ec6faeb01c9477a0afd46dfc0692be33482"); } TEST(HashUtil, Sha256BytePubkey) { diff --git a/test/test_hdwallet.cpp b/test/test_hdwallet.cpp index 097fa3d2..997efd6a 100644 --- a/test/test_hdwallet.cpp +++ b/test/test_hdwallet.cpp @@ -16,6 +16,7 @@ using cfd::core::HDWallet; using cfd::core::ByteData256; using cfd::core::ExtPubkey; using cfd::core::ExtPrivkey; +using cfd::core::KeyData; using cfd::core::NetType; TEST(HDWallet, GetMnemonicWordlistTest) { @@ -323,6 +324,16 @@ TEST(HDWallet, GeneratePrivkeyTest) { ExtPrivkey privkeyh; EXPECT_NO_THROW((privkeyh = wallet.GeneratePrivkey(NetType::kMainnet, "m/0h/44h"))); EXPECT_STREQ(privkeyh.ToString().c_str(), "xprv9xcgxExFiq8qWLdxFHXpEZF8VH7Qr9YDZb8c7vMsqygWk2YGTBgSnDtm1LESskfAJqGMWkWWGagNCSbHdVgA8EFxSbfAQTKSD1z4iJ8qHtq"); + + KeyData keypath1; + EXPECT_NO_THROW((keypath1 = wallet.GeneratePrivkeyData(NetType::kMainnet, "m/0h/44h"))); + EXPECT_STREQ(keypath1.ToString().c_str(), "[b4e3f5ed/0'/44']035d3d3ee3ce7044686e0eb4697d92478658ac9f854c3c2bccd7a5a8aa74d3fc7a"); + EXPECT_STREQ(keypath1.ToString(false).c_str(), "[b4e3f5ed/0'/44']xprv9xcgxExFiq8qWLdxFHXpEZF8VH7Qr9YDZb8c7vMsqygWk2YGTBgSnDtm1LESskfAJqGMWkWWGagNCSbHdVgA8EFxSbfAQTKSD1z4iJ8qHtq"); + + std::vector path2 = {0x80000000, 0x80000000 + 44}; + KeyData keypath2; + EXPECT_NO_THROW((keypath2 = wallet.GeneratePrivkeyData(NetType::kMainnet, path2))); + EXPECT_STREQ(keypath2.ToString(false).c_str(), "[b4e3f5ed/0'/44']xprv9xcgxExFiq8qWLdxFHXpEZF8VH7Qr9YDZb8c7vMsqygWk2YGTBgSnDtm1LESskfAJqGMWkWWGagNCSbHdVgA8EFxSbfAQTKSD1z4iJ8qHtq"); } TEST(HDWallet, GeneratePubkeyTest) { @@ -353,4 +364,14 @@ TEST(HDWallet, GeneratePubkeyTest) { ExtPubkey pubkeyh; EXPECT_NO_THROW((pubkeyh = wallet.GeneratePubkey(NetType::kMainnet, "m/0H/44H"))); EXPECT_STREQ(pubkeyh.ToString().c_str(), "xpub6Bc3MkV9ZCh8ipiRMK4pbhBs3JwuFcG4vp4CvJmVQKDVcpsQzizhL2DErc5DHMQuKwBxTg1jLP6PCqriLmLsJzjB2kD9TE9hvqxQ4yLKtcV"); + + KeyData keypath1; + EXPECT_NO_THROW((keypath1 = wallet.GeneratePubkeyData(NetType::kMainnet, "m/0H/44H"))); + EXPECT_STREQ(keypath1.ToString().c_str(), "[b4e3f5ed/0'/44']035d3d3ee3ce7044686e0eb4697d92478658ac9f854c3c2bccd7a5a8aa74d3fc7a"); + EXPECT_STREQ(keypath1.ToString(false).c_str(), "[b4e3f5ed/0'/44']xpub6Bc3MkV9ZCh8ipiRMK4pbhBs3JwuFcG4vp4CvJmVQKDVcpsQzizhL2DErc5DHMQuKwBxTg1jLP6PCqriLmLsJzjB2kD9TE9hvqxQ4yLKtcV"); + + std::vector path2 = {0x80000000, 0x80000000 + 44}; + KeyData keypath2; + EXPECT_NO_THROW((keypath2 = wallet.GeneratePubkeyData(NetType::kMainnet, path2))); + EXPECT_STREQ(keypath2.ToString(false).c_str(), "[b4e3f5ed/0'/44']xpub6Bc3MkV9ZCh8ipiRMK4pbhBs3JwuFcG4vp4CvJmVQKDVcpsQzizhL2DErc5DHMQuKwBxTg1jLP6PCqriLmLsJzjB2kD9TE9hvqxQ4yLKtcV"); } diff --git a/test/test_keydata.cpp b/test/test_keydata.cpp new file mode 100644 index 00000000..042a4604 --- /dev/null +++ b/test/test_keydata.cpp @@ -0,0 +1,789 @@ +#include "gtest/gtest.h" +#include +#include + +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_hdwallet.h" + +// The main function are using gtest's main(). + +using cfd::core::ByteData; +using cfd::core::CfdException; +using cfd::core::HardenedType; +using cfd::core::KeyData; +using cfd::core::ByteData256; +using cfd::core::ExtPubkey; +using cfd::core::ExtPrivkey; +using cfd::core::NetType; +using cfd::core::Pubkey; +using cfd::core::Privkey; + +TEST(KeyData, Constructor) { + KeyData empty_obj; + EXPECT_FALSE(empty_obj.IsValid()); +} + +TEST(KeyData, Pubkey1) { + Pubkey pubkey1("021362bdf255b304dcd29bfdb6b5c63c68ef7df60e2b1fc156716efe077b794647"); + ByteData fingerprint1("12345678"); + std::vector array1{0, 1, 2}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("0/1/2", obj.GetBip32Path().c_str()); + EXPECT_STREQ("12345678", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "021362bdf255b304dcd29bfdb6b5c63c68ef7df60e2b1fc156716efe077b794647", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "KxqjPLtQqydD8d6eUrpJ7Q1266k8Mw8f5eoyEztY3Kc5z4f2RQTG", + obj.GetPrivkey().GetWif().c_str()); + EXPECT_STREQ( + "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "cQNmd1D8MqzijUuXHb2yS5oRSm2F3TSTTMvcHC3V7CiKxArpg1bg", + obj.GetPrivkey().GetWif().c_str()); + EXPECT_STREQ( + "02e3cf2c4dca39b502a6f8ba37e5d63a9757492c2155bf99418d9532728cd23d93", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd3", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_TRUE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xprv9zt1onyw8BdEf7SQ6wUVH3bQQdGD9iy9QzXveQQRhX7i5iUN7jZgLbqFEe491LfjozztYa6bJAGZ65GmDCNcbjMdjZcgmdisPJwVjcfcDhV", + obj.GetExtPrivkey().ToString().c_str()); + EXPECT_STREQ( + "038746b92b722894e533dbbda3fb7fa673da00f4b309bf98a2cf586c27100004b0", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_TRUE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xprv9zt1onyw8BdEf7SQ6wUVH3bQQdGD9iy9QzXveQQRhX7i5iUN7jZgLbqFEe491LfjozztYa6bJAGZ65GmDCNcbjMdjZcgmdisPJwVjcfcDhV", + obj.GetExtPrivkey().ToString().c_str()); + EXPECT_STREQ( + "038746b92b722894e533dbbda3fb7fa673da00f4b309bf98a2cf586c27100004b0", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003, 0x80000000, 1}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_TRUE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'/0'/1", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xprvA4VvhNxX2aGK493zrwSDXjMPmt3tSyU3V76RkSSkexsoshMJvD4FfYdZJLTRrYaK2rg16qPEmg4KcwDnJ6VNwynQArQorw9R9fe1XZqTgKf", + obj.GetExtPrivkey().ToString().c_str()); + EXPECT_STREQ( + "02a6f2b5dc540788a972bf7e2e5f6275e3b78375cc8739ebc0bc509f06bb0a38c4", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "tpubDF7yNiHQHdfns9Mc3XM7PYcS2dqrPqcit3FLkebvHxS4atZxifANou2KTvpQQQP82ANDCkPc5MPQZ28pjYGgmDXGy1iyzaiX6MTBv8i4cua", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd3", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003, 1, 2}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'/1/2", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "tpubDKETJ63aebYdrKgfJg1fAQXgiNX9WGC4YyAGU3o5F9xhqx3Q2Y2Qnn9d3LPG5wfajojW4PGmdcFJCGJaCL8mjcTAS2aD7uBS34zL5diACGD", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "038e04e1ba2657af7032efd287da4feaf47ac06bd18380595ae96bd626e8c2ad89", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "KxqjPLtQqydD8d6eUrpJ7Q1266k8Mw8f5eoyEztY3Kc5z4f2RQTG", + obj.GetPrivkey().GetWif().c_str()); + EXPECT_STREQ( + "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "KxqjPLtQqydD8d6eUrpJ7Q1266k8Mw8f5eoyEztY3Kc5z4f2RQTG", + obj.GetPrivkey().GetWif().c_str()); + EXPECT_STREQ( + "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27", + obj.GetPrivkey().GetHex().c_str()); + EXPECT_STREQ( + "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0, 1, 2}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_FALSE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("0/1/2", obj.GetBip32Path().c_str()); + EXPECT_STREQ("12345678", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "021362bdf255b304dcd29bfdb6b5c63c68ef7df60e2b1fc156716efe077b794647", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003, 0x80000000, 1}; + std::vector array2{0x80000000, 1}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_TRUE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'/0'/1", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xprvA4VvhNxX2aGK493zrwSDXjMPmt3tSyU3V76RkSSkexsoshMJvD4FfYdZJLTRrYaK2rg16qPEmg4KcwDnJ6VNwynQArQorw9R9fe1XZqTgKf", + obj.GetExtPrivkey().ToString().c_str()); + EXPECT_STREQ( + "02a6f2b5dc540788a972bf7e2e5f6275e3b78375cc8739ebc0bc509f06bb0a38c4", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_TRUE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_TRUE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("0'/1", obj.GetBip32Path().c_str()); + EXPECT_STREQ("ae05dbb7", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xprvA4VvhNxX2aGK493zrwSDXjMPmt3tSyU3V76RkSSkexsoshMJvD4FfYdZJLTRrYaK2rg16qPEmg4KcwDnJ6VNwynQArQorw9R9fe1XZqTgKf", + obj.GetExtPrivkey().ToString().c_str()); + EXPECT_STREQ( + "02a6f2b5dc540788a972bf7e2e5f6275e3b78375cc8739ebc0bc509f06bb0a38c4", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003, 0x80000000, 1}; + std::vector array2{0x80000000, 1}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'/0'/1", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xpub6HVH6tVQrwpcGd8TxxyDtsJ8KutNrSBtrL22YprNDJQnkVgTTkNWDLx39bC6VALjHR73fZR8tuETUUNJqW9gbAoDjDoSTdVZp5kVKjG2pmx", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "02a6f2b5dc540788a972bf7e2e5f6275e3b78375cc8739ebc0bc509f06bb0a38c4", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("0'/1", obj.GetBip32Path().c_str()); + EXPECT_STREQ("ae05dbb7", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "xpub6HVH6tVQrwpcGd8TxxyDtsJ8KutNrSBtrL22YprNDJQnkVgTTkNWDLx39bC6VALjHR73fZR8tuETUUNJqW9gbAoDjDoSTdVZp5kVKjG2pmx", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "02a6f2b5dc540788a972bf7e2e5f6275e3b78375cc8739ebc0bc509f06bb0a38c4", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx array1{0x80000001, 2, 0x80000003, 1, 2}; + std::vector array2{1, 2}; + + auto check_func = [](const KeyData& obj, const std::vector& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1'/2/3'/1/2", obj.GetBip32Path().c_str()); + EXPECT_STREQ("3456789a", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "tpubDKETJ63aebYdrKgfJg1fAQXgiNX9WGC4YyAGU3o5F9xhqx3Q2Y2Qnn9d3LPG5wfajojW4PGmdcFJCGJaCL8mjcTAS2aD7uBS34zL5diACGD", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "038e04e1ba2657af7032efd287da4feaf47ac06bd18380595ae96bd626e8c2ad89", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx& arr_obj) { + auto obj_arr = obj.GetChildNumArray(); + EXPECT_TRUE(obj.IsValid()); + EXPECT_FALSE(obj.HasPrivkey()); + EXPECT_TRUE(obj.HasExtPubkey()); + EXPECT_FALSE(obj.HasExtPrivkey()); + EXPECT_EQ(arr_obj.size(), obj_arr.size()); + EXPECT_STREQ("1/2", obj.GetBip32Path().c_str()); + EXPECT_STREQ("40c902dd", obj.GetFingerprint().GetHex().c_str()); + EXPECT_STREQ( + "tpubDKETJ63aebYdrKgfJg1fAQXgiNX9WGC4YyAGU3o5F9xhqx3Q2Y2Qnn9d3LPG5wfajojW4PGmdcFJCGJaCL8mjcTAS2aD7uBS34zL5diACGD", + obj.GetExtPubkey().ToString().c_str()); + EXPECT_STREQ( + "038e04e1ba2657af7032efd287da4feaf47ac06bd18380595ae96bd626e8c2ad89", + obj.GetPubkey().GetHex().c_str()); + for (size_t idx=0; idx + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_hdwallet.h" +#include "cfdcore/cfdcore_psbt.h" +#include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_transaction.h" + +using cfd::core::CfdException; +using cfd::core::Address; +using cfd::core::Amount; +using cfd::core::ExtPubkey; +using cfd::core::Script; +using cfd::core::ScriptBuilder; +using cfd::core::ScriptOperator; +using cfd::core::ScriptHash; +using cfd::core::ScriptElement; +using cfd::core::ByteData; +using cfd::core::HDWallet; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::NetType; +using cfd::core::Psbt; +using cfd::core::Txid; +using cfd::core::TxIn; +using cfd::core::TxInReference; +using cfd::core::Transaction; +using cfd::core::TxOut; +using cfd::core::TxOutReference; +using cfd::core::CryptoUtil; +using cfd::core::SigHashType; +using cfd::core::WitnessVersion; +using cfd::core::ScriptUtil; +using cfd::core::KeyData; + + +TEST(Psbt, EmptyObject) { + try { + Psbt empty_obj; + EXPECT_EQ(2, empty_obj.GetTransaction().GetVersion()); + EXPECT_EQ(0, empty_obj.GetTransaction().GetLockTime()); + EXPECT_EQ(19, empty_obj.GetDataSize()); + EXPECT_STREQ("70736274ff01000a0200000000000000000000", + empty_obj.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAAoCAAAAAAAAAAAAAA==", empty_obj.GetBase64().c_str()); + EXPECT_EQ(0, empty_obj.GetPsbtVersion()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + Psbt obj(1, 100); + EXPECT_EQ(1, obj.GetTransaction().GetVersion()); + EXPECT_EQ(100, obj.GetTransaction().GetLockTime()); + EXPECT_EQ(19, obj.GetDataSize()); + EXPECT_STREQ("70736274ff01000a0100000000006400000000", + obj.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAAoBAAAAAABkAAAAAA==", obj.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + Psbt from_b64("cHNidP8BAAoBAAAAAABkAAAAAA=="); + EXPECT_EQ(1, from_b64.GetTransaction().GetVersion()); + EXPECT_EQ(100, from_b64.GetTransaction().GetLockTime()); + EXPECT_EQ(19, from_b64.GetDataSize()); + EXPECT_STREQ("70736274ff01000a0100000000006400000000", + from_b64.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAAoBAAAAAABkAAAAAA==", from_b64.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } +} + +struct CfdPsbtTestData { + std::string hex; + std::string base64; + std::string error_message; +}; + +// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki +static const std::vector g_cfd_psbt_testdata = { + { + "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300", + "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA==", + "psbt unmatch magic error." + }, + { + "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==", + "psbt format error." + }, + { + "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", + "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA=", + "psbt format error." + }, + { + "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", + "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==", + "psbt global tx not found error." + }, + { + "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000", + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA", + "psbt format error." + }, + { + "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA", + "psbt invalid key format error." + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA", + "psbt format error." + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==", + "psbt format error." + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA", + "psbt format error." + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA", + "psbt format error." + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==", + "psbt format error." + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "psbt format error." + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "psbt format error." + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "psbt format error." + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "psbt format error." + }, + { + "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", + "psbt format error." + }, + { + "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", + "psbt format error." + }, + { + "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d06d57f8a8751ae00", + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnQbVf4qHUa4A", + "psbt format error." + }, + { + "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000", + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA", + "" + }, + { + "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA", + "" + }, + { + "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000", + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==", + "" + }, + { + "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000", + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", + "" + }, + { + "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=", + "" + }, + { + "70736274ff01005202000000019dfc6628c26c5899fe1bd3dc338665bfd55d7ada10f6220973df2d386dec12760100000000ffffffff01f03dcd1d000000001600147b3a00bfdc14d27795c2b74901d09da6ef133579000000004f01043587cf02da3fd0088000000097048b1ad0445b1ec8275517727c87b4e4ebc18a203ffa0f94c01566bd38e9000351b743887ee1d40dc32a6043724f2d6459b3b5a4d73daec8fbae0472f3bc43e20cd90c6a4fae000080000000804f01043587cf02da3fd00880000001b90452427139cd78c2cff2444be353cd58605e3e513285e528b407fae3f6173503d30a5e97c8adbc557dac2ad9a7e39c1722ebac69e668b6f2667cc1d671c83cab0cd90c6a4fae000080010000800001012b0065cd1d000000002200202c5486126c4978079a814e13715d65f36459e4d6ccaded266d0508645bafa6320105475221029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c88712103372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b52ae2206029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c887110d90c6a4fae0000800000008000000000220603372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b10d90c6a4fae0000800100008000000000002202039eff1f547a1d5f92dfa2ba7af6ac971a4bd03ba4a734b03156a256b8ad3a1ef910ede45cc500000080000000800100008000", + "cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA", + "" + }, + { + "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=", + "" + }, + { + "70736274ff01009d0100000002710ea76ab45c5cb6438e607e59cc037626981805ae9e0dfd9089012abb0be5350100000000ffffffff190994d6a8b3c8c82ccbcfb2fba4106aa06639b872a8d447465c0d42588d6d670000000000ffffffff0200e1f505000000001976a914b6bc2c0ee5655a843d79afedd0ccc3f7dd64340988ac605af405000000001600141188ef8e4ce0449eaac8fb141cbf5a1176e6a088000000004f010488b21e039e530cac800000003dbc8a5c9769f031b17e77fea1518603221a18fd18f2b9a54c6c8c1ac75cbc3502f230584b155d1c7f1cd45120a653c48d650b431b67c5b2c13f27d7142037c1691027569c503100008000000080000000800001011f00e1f5050000000016001433b982f91b28f160c920b4ab95e58ce50dda3a4a220203309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c47304402202d704ced830c56a909344bd742b6852dccd103e963bae92d38e75254d2bb424502202d86c437195df46c0ceda084f2a291c3da2d64070f76bf9b90b195e7ef28f77201220603309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c1827569c5031000080000000800000008000000000010000000001011f00e1f50500000000160014388fb944307eb77ef45197d0b0b245e079f011de220202c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b11047304402204cb1fb5f869c942e0e26100576125439179ae88dca8a9dc3ba08f7953988faa60220521f49ca791c27d70e273c9b14616985909361e25be274ea200d7e08827e514d01220602c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b1101827569c5031000080000000800000008000000000000000000000220202d20ca502ee289686d21815bd43a80637b0698e1fbcdbe4caed445f6c1a0a90ef1827569c50310000800000008000000080000000000400000000", + "cHNidP8BAJ0BAAAAAnEOp2q0XFy2Q45gflnMA3YmmBgFrp4N/ZCJASq7C+U1AQAAAAD/////GQmU1qizyMgsy8+y+6QQaqBmObhyqNRHRlwNQliNbWcAAAAAAP////8CAOH1BQAAAAAZdqkUtrwsDuVlWoQ9ea/t0MzD991kNAmIrGBa9AUAAAAAFgAUEYjvjkzgRJ6qyPsUHL9aEXbmoIgAAAAATwEEiLIeA55TDKyAAAAAPbyKXJdp8DGxfnf+oVGGAyIaGP0Y8rmlTGyMGsdcvDUC8jBYSxVdHH8c1FEgplPEjWULQxtnxbLBPyfXFCA3wWkQJ1acUDEAAIAAAACAAAAAgAABAR8A4fUFAAAAABYAFDO5gvkbKPFgySC0q5XljOUN2jpKIgIDMJaA8zx9446mpHzU7NZvH1pJdHxv+4gI7QkDkkPjrVxHMEQCIC1wTO2DDFapCTRL10K2hS3M0QPpY7rpLTjnUlTSu0JFAiAthsQ3GV30bAztoITyopHD2i1kBw92v5uQsZXn7yj3cgEiBgMwloDzPH3jjqakfNTs1m8fWkl0fG/7iAjtCQOSQ+OtXBgnVpxQMQAAgAAAAIAAAACAAAAAAAEAAAAAAQEfAOH1BQAAAAAWABQ4j7lEMH63fvRRl9CwskXgefAR3iICAsd3Fh9z0LfHK57nveZQKT0T8JW8dlatH1Jdpf0uELEQRzBEAiBMsftfhpyULg4mEAV2ElQ5F5rojcqKncO6CPeVOYj6pgIgUh9JynkcJ9cOJzybFGFphZCTYeJb4nTqIA1+CIJ+UU0BIgYCx3cWH3PQt8crnue95lApPRPwlbx2Vq0fUl2l/S4QsRAYJ1acUDEAAIAAAACAAAAAgAAAAAAAAAAAAAAiAgLSDKUC7iiWhtIYFb1DqAY3sGmOH7zb5MrtRF9sGgqQ7xgnVpxQMQAAgAAAAIAAAACAAAAAAAQAAAAA", + "" + }, + { + "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac0000000000010122d3dff505000000001976a914d48ed3110b94014cb114bd32d6f4d066dc74256b88ac0001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000", + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752af2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq8iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028900010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQABBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ad2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "" + }, + { + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "" + }, + { + "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f00", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwA=", + "" + }, + { + "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000a0f0102030405060708100f0102030405060708090a0b0c0d0e0f000a0f0102030405060708100f0102030405060708090a0b0c0d0e0f000a0f0102030405060708100f0102030405060708090a0b0c0d0e0f00", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA=", + "" + }, + { + "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a0100000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0a0f0102030405060708100f0102030405060708090a0b0c0d0e0f000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0a0f0102030405060708100f0102030405060708090a0b0c0d0e0f000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0a0f0102030405060708100f0102030405060708090a0b0c0d0e0f00", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8KDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PCg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA=", + "" + } +}; + +TEST(Psbt, ParsePsbt) { + for (const auto& data : g_cfd_psbt_testdata) { + { + SCOPED_TRACE("hex[" + data.hex + "]"); + try { + Psbt from_b64(data.base64); + if (data.error_message.empty()) { + EXPECT_EQ(data.hex, from_b64.GetData().GetHex()); + EXPECT_EQ(data.base64, from_b64.GetBase64()); + } else { + EXPECT_STREQ("The current test should generate an error.", data.hex.c_str()); + } + } catch (const CfdException& except) { + EXPECT_STREQ(data.error_message.c_str(), except.what()); + } + } + } +} + +static const std::string g_psbt_utxo_witness = + "02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000"; + // [0]: +/* + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + EXPECT_STREQ("tprv8ghSmFAFHkyY1BsSsnwxKsPMgh9zSAFd8vLTniA2d6sK25xpWpGd4qQ4EpGzYwfvDwR77r7bzmdGbmasLwMjceUWThXGU9vsYQSsxgzKY7T", wallet1.GeneratePrivkey(NetType::kTestnet, "44h/0h/0h").ToString().c_str()); + std::string out_path = "44h/0h/0h/0/1"; + std::string fee_path = "44h/0h/0h/1/1"; + std::string fee0_path = "44h/0h/0h/1/0"; + auto data3 = wallet1.GeneratePrivkeyData(NetType::kTestnet, out_path); + auto data4 = wallet1.GeneratePrivkeyData(NetType::kTestnet, fee_path); + auto data0 = wallet1.GeneratePrivkeyData(NetType::kTestnet, fee0_path); + Address addr3 = Address(NetType::kTestnet, WitnessVersion::kVersion0, data3.GetPubkey()); + Address addr4 = Address(NetType::kTestnet, WitnessVersion::kVersion0, data4.GetPubkey()); + Address addr4sh = Address(NetType::kTestnet, addr4.GetLockingScript()); + Address addr0 = Address(NetType::kTestnet, WitnessVersion::kVersion0, data0.GetPubkey()); + Address addr0pkh = Address(NetType::kTestnet, data0.GetPubkey()); + Address addr0sh = Address(NetType::kTestnet, addr0.GetLockingScript()); + Transaction tx; + tx.AddTxIn(Txid("8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1"), 0, 0xffffffff); + tx.AddTxOut(Amount(4899996680), addr3.GetLockingScript()); + tx.AddTxOut(Amount(100000000), addr4sh.GetLockingScript()); + Amount amt(5000000000); + auto sighash = tx.GetSignatureHash(0, addr0pkh.GetLockingScript().GetData(), SigHashType(), amt, WitnessVersion::kVersion0); + auto sk = data0.GetPrivkey(); + auto sig = sk.CalculateEcSignature(sighash); + auto der = CryptoUtil::ConvertSignatureToDer(sig, SigHashType()); + tx.AddScriptWitnessStack(0, der); + tx.AddScriptWitnessStack(0, data0.GetPubkey().GetData()); + tx.SetUnlockingScript(0, std::vector{addr0.GetLockingScript().GetData()}); + EXPECT_STREQ("", tx.GetHex().c_str()); + */ + +static const std::string g_psbt_utxo_legacy = + "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000"; + // [0]: 03044f2b3045e62efa936c5169586798a2f241890e19a2d6bc4d7a535429501d87 +/* + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + EXPECT_STREQ("tprv8fbPrDdF7Cdde4fLncessNymvfuvREAzhoMmfs3XqCuLcNVB9didfgXgb5V1NrxkqF7ZKibuyib4n6bujk1L5NfgVYnZZCwuyDdT21JAquv", wallet2.GeneratePrivkey(NetType::kTestnet, "44h/0h/0h").ToString().c_str()); + std::string out_path = "44h/0h/0h/0/1"; + std::string fee_path = "44h/0h/0h/1/1"; + auto data3 = wallet2.GeneratePrivkeyData(NetType::kTestnet, out_path); + auto data4 = wallet2.GeneratePrivkeyData(NetType::kTestnet, fee_path); + Address addr3 = Address(NetType::kTestnet, data3.GetPubkey()); + Address addr4 = Address(NetType::kTestnet, data4.GetPubkey()); + Transaction tx; + tx.AddTxIn(Txid("405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6"), 0, 0xffffffff); + tx.AddTxOut(Amount(499995000), addr3.GetLockingScript()); + auto sighash = tx.GetSignatureHash(0, addr4.GetLockingScript().GetData(), SigHashType()); + auto sk = data4.GetPrivkey(); + auto sig = sk.CalculateEcSignature(sighash); + auto der = CryptoUtil::ConvertSignatureToDer(sig, SigHashType()); + tx.SetUnlockingScript(0, std::vector{der, data4.GetPubkey().GetData()}); + EXPECT_STREQ("", tx.GetHex().c_str()); + */ + +static const std::string g_psbt_utxo_sh_wsh_multi = "02000000000102f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0100000017160014c1768dd714526732f54de6c8581a78e385d0900affffffffc6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e4001000000171600141305fd9c86bb2cd2d671928388eccd2b1140aa5affffffff010858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f2317587024730440220032350deae8e8fc60a45800508b09f475ba6043b40ce87f8020a6cc532e401ab02205979d8ad394e19ef9881bb987f77dab465f1112a746ca17a509215c2dbfea93201210206d743464ae738be5ba6d07dd434bed3f24af32f9fad805ffb9a241ec56ea24802473044022051f8776ba287c7424b2d783ebde964e25bde5fec4728735b8b455c806daae16602207988cabc3475109e264a7d378873d0fcee78959c46facdafb099af4ca6eace94012103e241f36e5e17a599c465ec82f3520e6d7a0506cc862de06ff4ba26b255a2f62c00000000"; +/* + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string in_path = "44h/0h/0h/0/10"; + std::string out_path = "44h/0h/0h/0/11"; + auto data11 = wallet1.GeneratePrivkeyData(NetType::kTestnet, in_path); + auto data12 = wallet2.GeneratePrivkeyData(NetType::kTestnet, in_path); + auto data21 = wallet1.GeneratePrivkeyData(NetType::kTestnet, out_path); + auto data22 = wallet2.GeneratePrivkeyData(NetType::kTestnet, out_path); + Address addr11 = Address(NetType::kTestnet, WitnessVersion::kVersion0, data11.GetPubkey()); + Address addr12 = Address(NetType::kTestnet, WitnessVersion::kVersion0, data12.GetPubkey()); + Address addr11pkh = Address(NetType::kTestnet, data11.GetPubkey()); + Address addr12pkh = Address(NetType::kTestnet, data12.GetPubkey()); + auto multisig = ScriptUtil::CreateMultisigRedeemScript( + 2, std::vector{data21.GetPubkey(), data22.GetPubkey()}); + Address addr2 = Address(NetType::kTestnet, WitnessVersion::kVersion0, multisig); + Address addr2sh = Address(NetType::kTestnet, addr2.GetLockingScript()); + Transaction tx; + tx.AddTxIn(Txid("8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1"), 1, 0xffffffff); + tx.AddTxIn(Txid("405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6"), 1, 0xffffffff); + tx.AddTxOut(Amount(499996680), addr2sh.GetLockingScript()); + Amount amt(250000000); + auto sighash1 = tx.GetSignatureHash(0, addr11pkh.GetLockingScript().GetData(), SigHashType(), amt, WitnessVersion::kVersion0); + auto sig1 = data11.GetPrivkey().CalculateEcSignature(sighash1); + auto der1 = CryptoUtil::ConvertSignatureToDer(sig1, SigHashType()); + tx.AddScriptWitnessStack(0, der1); + tx.AddScriptWitnessStack(0, data11.GetPubkey().GetData()); + tx.SetUnlockingScript(0, std::vector{addr11.GetLockingScript().GetData()}); + auto sighash2 = tx.GetSignatureHash(1, addr12pkh.GetLockingScript().GetData(), SigHashType(), amt, WitnessVersion::kVersion0); + auto sig2 = data12.GetPrivkey().CalculateEcSignature(sighash2); + auto der2 = CryptoUtil::ConvertSignatureToDer(sig2, SigHashType()); + tx.AddScriptWitnessStack(1, der2); + tx.AddScriptWitnessStack(1, data12.GetPubkey().GetData()); + tx.SetUnlockingScript(1, std::vector{addr12.GetLockingScript().GetData()}); + EXPECT_STREQ("", tx.GetHex().c_str()); + */ + +static const std::string g_psbt_seed1 = "8bc106907003ea0b55f3ed4ce2fcf9a198d8c43f07e6ade8aacc5c20c33db12e"; +// 44'/0'/0': tprv8ghSmFAFHkyY1BsSsnwxKsPMgh9zSAFd8vLTniA2d6sK25xpWpGd4qQ4EpGzYwfvDwR77r7bzmdGbmasLwMjceUWThXGU9vsYQSsxgzKY7T +static const std::string g_psbt_seed2 = "d3e3539eafb6af1f0ae374ecffd33bed394f5eb2e39f8957be63c258ac32ca97"; +// 44'/0'/0': tprv8fbPrDdF7Cdde4fLncessNymvfuvREAzhoMmfs3XqCuLcNVB9didfgXgb5V1NrxkqF7ZKibuyib4n6bujk1L5NfgVYnZZCwuyDdT21JAquv + +TEST(Psbt, SetTxInOnly) { + Psbt psbt; + EXPECT_EQ(0, psbt.GetTxInCount()); + TxIn txin_w(Txid("c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26"), 1, 0xffffffff); + TxIn txin_l(Txid("c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d"), 0, 0xffffffff); + TxInReference txin_r(txin_l); + psbt.AddTxIn(txin_w); + psbt.AddTxIn(txin_r); + EXPECT_EQ(2, psbt.GetTxInCount()); + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA==", psbt.GetBase64().c_str()); +} + +TEST(Psbt, SetInputOnly) { + Psbt psbt("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + try { + psbt.SetTxInUtxo(0, Transaction(g_psbt_utxo_witness), key1); + psbt.SetTxInSighashType(0, SigHashType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + psbt.SetTxInUtxo(1, Transaction(g_psbt_utxo_legacy), key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_TRUE(psbt.IsFindTxInSighashType(0)); + EXPECT_FALSE(psbt.IsFindTxInSighashType(1)); + EXPECT_EQ(SigHashType().GetSigHashFlag(), psbt.GetTxInSighashType(0).GetSigHashFlag()); + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, SetInputOnly_EmptyKeyData) { + Psbt psbt("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + + auto ekey1 = wallet1.GeneratePubkey(NetType::kTestnet, path1); + auto ekey2 = wallet2.GeneratePubkey(NetType::kTestnet, path2); + KeyData key1(ekey1.GetPubkey(), "", ByteData()); + KeyData key2(ekey2.GetPubkey(), "", ByteData()); + try { + psbt.SetTxInUtxo(0, Transaction(g_psbt_utxo_witness), key1); + psbt.SetTxInSighashType(0, SigHashType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + psbt.SetTxInUtxo(1, Transaction(g_psbt_utxo_legacy), key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_TRUE(psbt.IsFindTxInSighashType(0)); + EXPECT_FALSE(psbt.IsFindTxInSighashType(1)); + EXPECT_EQ(SigHashType().GetSigHashFlag(), psbt.GetTxInSighashType(0).GetSigHashFlag()); + auto gkey1 = psbt.GetTxInKeyData(0, true); + EXPECT_EQ("02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", gkey1.ToString()); + auto key_list = psbt.GetTxInKeyDataList(0); + EXPECT_EQ(1, key_list.size()); + if (key_list.size() == 1) { + EXPECT_EQ("02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + key_list[0].ToString()); + } + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe04962c4e08000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7048d20443a00", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4ElixOCAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHBI0gRDoA", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, SetInputOnlySimpleWitness) { + Psbt psbt("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + try { + auto tx = psbt.GetTransaction(); + psbt.SetTxInUtxo(0, Transaction(g_psbt_utxo_witness).GetTxOut(tx.GetTxIn(0).GetVout()), key1); + psbt.SetTxInSighashType(0, SigHashType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + psbt.SetTxInUtxo(1, Transaction(g_psbt_utxo_legacy), key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_TRUE(psbt.IsFindTxInSighashType(0)); + EXPECT_FALSE(psbt.IsFindTxInSighashType(1)); + EXPECT_EQ(SigHashType().GetSigHashFlag(), psbt.GetTxInSighashType(0).GetSigHashFlag()); + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff00000000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, SetInputDirectWitness) { + Psbt psbt("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + try { + auto tx = psbt.GetTransaction(); + psbt.SetTxInWitnessUtxoDirect(0, Transaction(g_psbt_utxo_witness).GetTxOut(tx.GetTxIn(0).GetVout())); + psbt.SetTxInBip32KeyDirect(0, key1); + auto p2wpkh_locking_script = ScriptUtil::CreateP2wpkhLockingScript(key1.GetPubkey()); + psbt.SetTxInRecord(0, + Psbt::CreateRecordKey(Psbt::kPsbtInputRedeemScript), + p2wpkh_locking_script.GetData()); + psbt.SetTxInSighashType(0, SigHashType()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + psbt.SetTxInRecord(1, + Psbt::CreateRecordKey(Psbt::kPsbtInputNonWitnessUtxo), + Transaction(g_psbt_utxo_legacy).GetData()); + psbt.SetTxInBip32KeyDirect(1, key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_TRUE(psbt.IsFindTxInSighashType(0)); + EXPECT_FALSE(psbt.IsFindTxInSighashType(1)); + EXPECT_EQ(SigHashType().GetSigHashFlag(), psbt.GetTxInSighashType(0).GetSigHashFlag()); + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff00000000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, SetTxOutOnly) { + Psbt psbt; + EXPECT_EQ(0, psbt.GetTxOutCount()); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/0/2"; + std::string path2 = "44h/0h/0h/0/2"; + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + auto addr1 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key1.GetPubkey()); + auto addr2 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key2.GetPubkey()); + + Amount amount(100000000); + TxOut txout_1(amount, addr1); + TxOut txout_2(amount, addr2); + TxOutReference txout_2r(txout_2); + psbt.AddTxOut(txout_1); + psbt.AddTxOut(txout_2r); + EXPECT_EQ(2, psbt.GetTxOutCount()); + + EXPECT_STREQ("70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, SetOutputOnly) { + Psbt psbt("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/0/2"; + std::string path2 = "44h/0h/0h/0/2"; + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + auto addr1 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key1.GetPubkey()); + auto addr2 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key2.GetPubkey()); + + Amount amount(100000000); + try { + psbt.SetTxOutData(0, key1); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + psbt.SetTxOutData(1, key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_STREQ("70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a5550000000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + auto b64 = CryptoUtil::EncodeBase64(psbt.GetData()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt.GetBase64().c_str()); + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt2.GetBase64().c_str()); + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", b64.c_str()); +} + +TEST(Psbt, FromTransaction) { + Transaction tx("0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000"); + Psbt psbt(tx); + + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000000000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, JoinTxOnly) { + Psbt psbt; + Psbt psbt_txin_only("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + Psbt psbt_txout_only("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA="); + + psbt = psbt_txin_only; + psbt.Join(psbt_txout_only, true); + + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000000000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, Join) { + Psbt psbt_input_only("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA=="); + Psbt psbt_output_only("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="); + Psbt psbt; + + psbt = psbt_input_only; + psbt.Join(psbt_output_only); + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, JoinWithInput) { + Psbt psbt_input_only("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA=="); + Psbt psbt_output_only("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="); + Psbt psbt; + + psbt = psbt_output_only; + psbt.Join(psbt_input_only); + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, JoinDuplicateInput) { + Psbt psbt_input_only("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA=="); + Psbt psbt("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="); + + try { + psbt.Join(psbt_input_only, true); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + Psbt psbt2(psbt.GetData()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt2.GetBase64().c_str()); +} + +TEST(Psbt, UsecaseTest1) { + static const std::string seed1 = "8bc106907003ea0b55f3ed4ce2fcf9a198d8c43f07e6ade8aacc5c20c33db12e"; + static const std::string seed2 = "d3e3539eafb6af1f0ae374ecffd33bed394f5eb2e39f8957be63c258ac32ca97"; + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + auto key_in1 = wallet1.GeneratePrivkeyData(NetType::kTestnet, path1); + auto key_in2 = wallet2.GeneratePrivkeyData(NetType::kTestnet, path2); + + // Creator,Updater + Psbt psbt("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="); + + // Signer + Psbt psbt1; + Psbt psbt2; + try { + psbt1 = psbt; + psbt1.Sign(key_in1.GetPrivkey()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + try { + psbt2 = psbt; + psbt2.Sign(key_in2.GetPrivkey()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + // Combiner + try { + psbt.Combine(psbt1); + psbt.Combine(psbt2); + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787220202565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe4730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220202d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACICAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHRzBEAiBm7JVulng81hIsnAdzKjrpkxtpq9r/HzYsKO1fqWco0wIgahbUrkb+1I09BJJzdhO9m1H+PiRTtERuRHxlvyyWfhABIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAACICA0c7/Ix3DBsiCi56rkut9sDX6vKQKNWynTQ4ASuyie+BGCpwR2AsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgNkdK/yYzw1GGVTn7UrYrnW+55OI1dmKOHwoKeZNFjgbBida22GLAAAgAAAAIAAAACAAAAAAAIAAAAA", psbt.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + // Input Finalizer + try { + EXPECT_FALSE(psbt.IsFinalized()); + EXPECT_FALSE(psbt.IsFinalizedInput(0)); +#if 0 + auto sig1 = psbt.GetTxInSignature(0, key_in1.GetPubkey()); + auto sig2 = psbt.GetTxInSignature(1, key_in2.GetPubkey()); + psbt.SetTxInFinalScript( + 0, std::vector{sig1, key_in1.GetPubkey().GetData()}); + EXPECT_FALSE(psbt.IsFinalizedInput(1)); + psbt.SetTxInFinalScript( + 1, std::vector{sig2, key_in2.GetPubkey().GetData()}); + psbt.ClearTxInSignData(0); + psbt.ClearTxInSignData(1); +#else + psbt.Finalize(); +#endif + EXPECT_TRUE(psbt.IsFinalizedInput(0)); + EXPECT_TRUE(psbt.IsFinalizedInput(1)); + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010717160014962c4e08f336d3afbc3415c9d359ae104047052001086b024730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb866012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac0000000001076a473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c700220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + // Transaction Extractor + try { + EXPECT_TRUE(psbt.IsFinalized()); + auto tx = psbt.ExtractTransaction(); + EXPECT_STREQ("02000000000102267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000017160014962c4e08f336d3afbc3415c9d359ae1040470520ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc0000000006a473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555024730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb866012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0000000000", tx.GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } +} + +TEST(Psbt, UsecaseMultisig) { + Psbt psbt; + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/0/12"; + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path1); + auto out_multisig = ScriptUtil::CreateMultisigRedeemScript( + 2, std::vector{key1.GetPubkey(), key2.GetPubkey()}); + auto addr = Address(NetType::kTestnet, WitnessVersion::kVersion0, out_multisig); + + std::string in_path = "44h/0h/0h/0/11"; + auto key_in1 = wallet1.GeneratePrivkeyData(NetType::kTestnet, in_path); + auto key_in2 = wallet2.GeneratePrivkeyData(NetType::kTestnet, in_path); + auto multisig = ScriptUtil::CreateMultisigRedeemScript( + 2, std::vector{key_in1.GetPubkey(), key_in2.GetPubkey()}); + EXPECT_STREQ("522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae", multisig.GetHex().c_str()); + + Transaction utxo_tx(g_psbt_utxo_sh_wsh_multi); + TxIn txin(Txid("544d545a1e53becbf9dd9b0e424e1189b8f6e46d6bc36b191816d341c2d32f69"), 0, 0xffffffff); + + try { + psbt.AddTxIn(txin); + psbt.SetTxInUtxo(0, utxo_tx.GetTxOut(0), multisig, KeyData()); + psbt.SetTxInSighashType(0, SigHashType()); + EXPECT_STREQ("522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae", + psbt.GetTxInRedeemScriptDirect(0, true, true).GetHex().c_str()); + EXPECT_STREQ("00209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9", + psbt.GetTxInRedeemScriptDirect(0, true, false).GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + Amount amount(499993360); + try { + psbt.AddTxOut(addr.GetLockingScript(), amount); + psbt.SetTxOutData(0, out_multisig, std::vector{key1, key2}); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + Psbt psbt1; + try { + psbt1.AddTxIn(txin); + psbt1.SetTxInUtxo(0, utxo_tx.GetTxOut(0), multisig, key_in1); + EXPECT_STREQ("cHNidP8BADMCAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////AAAAAAAAAQEgCFjNHQAAAAAXqRSUX7UDkacGN8H/xat/tlMIwvIxdYcBBCIAIJxNrLJeu4rai7sa3bhp3qTYFwzJUfHZaUshVOFYMnbJAQVHUiEDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/MhA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHUq4iBgOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8xgqcEdgLAAAgAAAAIAAAACAAAAAAAsAAAAA", psbt1.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + Psbt psbt2; + try { + psbt2.AddTxIn(txin); + psbt2.SetTxInUtxo(0, utxo_tx.GetTxOut(0), multisig, key_in2); + EXPECT_STREQ("cHNidP8BADMCAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////AAAAAAAAAQEgCFjNHQAAAAAXqRSUX7UDkacGN8H/xat/tlMIwvIxdYcBBCIAIJxNrLJeu4rai7sa3bhp3qTYFwzJUfHZaUshVOFYMnbJAQVHUiEDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/MhA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHUq4iBgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7Rxida22GLAAAgAAAAIAAAACAAAAAAAsAAAAA", psbt2.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + psbt.Join(psbt1); + psbt.Join(psbt2); + EXPECT_STREQ("70736274ff01005e0200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc00000000000101200858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f23175870103040100000001042200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9010547522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae220603a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3182a7047602c0000800000008000000080000000000b000000220603f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47189d6b6d862c0000800000008000000080000000000b00000000010147522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae220202622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242189d6b6d862c0000800000008000000080000000000c000000220202906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b182a7047602c0000800000008000000080000000000c00000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQMEAQAAAAEEIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBBUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSriIGA6US9fWcDnkB/EeO1jU+73b0T5yywYW/vPfwubt2cWvzGCpwR2AsAACAAAAAgAAAAIAAAAAACwAAACIGA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHGJ1rbYYsAACAAAAAgAAAAIAAAAAACwAAAAABAUdSIQKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGeyECYix5dOwy3nWvo3M7CabDPQ3qUUsp+qzPsJkXdPRiIkJSriICAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCGJ1rbYYsAACAAAAAgAAAAIAAAAAADAAAACICApBtOZ9tu+zImNS43j9JdHTIakHyzzbXH5XlygawdIZ7GCpwR2AsAACAAAAAgAAAAIAAAAAADAAAAAA=", psbt.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + psbt1 = psbt; + psbt1.Sign(key_in1.GetPrivkey()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + psbt2 = psbt; + // psbt2.Sign(key_in2.GetPrivkey()); + // Same processing. + auto sighash_type = psbt.GetTxInSighashType(0); + auto utxo_txout = psbt.GetTxInUtxo(0); + auto tx = psbt.GetTransaction(); + auto sighash = tx.GetSignatureHash(0, psbt.GetTxInRedeemScript(0).GetData(), + sighash_type, utxo_txout.GetValue(), WitnessVersion::kVersion0); + auto sig = key_in2.GetPrivkey().CalculateEcSignature(sighash); + sig = CryptoUtil::ConvertSignatureToDer(sig, sighash_type); + psbt2.SetTxInSignature(0, key_in2, sig); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + psbt.Combine(psbt1); + psbt.Combine(psbt2); + EXPECT_STREQ("70736274ff01005e0200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc00000000000101200858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f2317587220203a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3473044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b8401220203f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4747304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc010103040100000001042200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9010547522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae220603a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3182a7047602c0000800000008000000080000000000b000000220603f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47189d6b6d862c0000800000008000000080000000000b00000000010147522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae220202622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242189d6b6d862c0000800000008000000080000000000c000000220202906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b182a7047602c0000800000008000000080000000000c00000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHIgIDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/NHMEQCICBjWTfgUXDYPcMhOts6bq5mcTAI5KvDi0kSxWgN7E8MAiAzwIpxowdXsIRj1TDsBY7XQBlo+zC+9j1FSXIaDkhbhAEiAgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7R0cwRAIgXSdKeIfePvrehKSjScTDb1ibVWI7ECe32m2sicF4VjQCIGoDr+u7tgifHjf6yPmZpAFRYciSAUT9UxEtoFgEzUPMAQEDBAEAAAABBCIAIJxNrLJeu4rai7sa3bhp3qTYFwzJUfHZaUshVOFYMnbJAQVHUiEDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/MhA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHUq4iBgOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8xgqcEdgLAAAgAAAAIAAAACAAAAAAAsAAAAiBgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7Rxida22GLAAAgAAAAIAAAACAAAAAAAsAAAAAAQFHUiECkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnshAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCUq4iAgJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQhida22GLAAAgAAAAIAAAACAAAAAAAwAAAAiAgKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGexgqcEdgLAAAgAAAAIAAAACAAAAAAAwAAAAA", psbt.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + auto sig1 = psbt.GetTxInSignature(0, key_in1.GetPubkey()); + auto sig2 = psbt.GetTxInSignature(0, key_in2.GetPubkey()); + auto script = psbt.GetTxInRedeemScript(0); + EXPECT_FALSE(psbt.IsFinalizedInput(0)); + EXPECT_FALSE(psbt.IsFinalized()); + psbt.SetTxInFinalScript( + 0, std::vector{ByteData(), sig1, sig2, script.GetData()}); + // psbt.Finalize(); + EXPECT_TRUE(psbt.IsFinalizedInput(0)); + psbt.ClearTxInSignData(0); + + auto wit_script = psbt.GetTxInFinalScript(0, true); + auto wsh_script = psbt.GetTxInFinalScript(0, false); + EXPECT_EQ(4, wit_script.size()); + if (4 == wit_script.size()) { + EXPECT_EQ(script.GetHex(), wit_script[3].GetHex()); + } + EXPECT_EQ(1, wsh_script.size()); + if (1 == wsh_script.size()) { + auto exp_script = ScriptUtil::CreateP2wshLockingScript(script); + EXPECT_EQ(exp_script.GetData().Serialize().GetHex(), wsh_script[0].GetHex()); + } + EXPECT_STREQ("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQcjIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBCNoEAEcwRAIgIGNZN+BRcNg9wyE62zpurmZxMAjkq8OLSRLFaA3sTwwCIDPAinGjB1ewhGPVMOwFjtdAGWj7ML72PUVJchoOSFuEAUcwRAIgXSdKeIfePvrehKSjScTDb1ibVWI7ECe32m2sicF4VjQCIGoDr+u7tgifHjf6yPmZpAFRYciSAUT9UxEtoFgEzUPMAUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSrgABAUdSIQKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGeyECYix5dOwy3nWvo3M7CabDPQ3qUUsp+qzPsJkXdPRiIkJSriICAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCGJ1rbYYsAACAAAAAgAAAAIAAAAAADAAAACICApBtOZ9tu+zImNS43j9JdHTIakHyzzbXH5XlygawdIZ7GCpwR2AsAACAAAAAgAAAAIAAAAAADAAAAAA=", psbt.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + try { + EXPECT_TRUE(psbt.IsFinalized()); + auto tx = psbt.ExtractTransaction(); + EXPECT_STREQ("02000000000101692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d5400000000232200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc0400473044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b840147304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc0147522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae00000000", tx.GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } +} + +static constexpr uint8_t kProprietary = 0xfc; + +TEST(Psbt, SetGlobalRecordTest) { + Transaction tx("0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000"); + Psbt psbt(tx); + + ByteData global_key1 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy1"); + ByteData global_value1 = ByteData("01020304"); + ByteData global_key2 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy2"); + ByteData global_value2 = ByteData("00"); + psbt.SetGlobalRecord(global_key1, global_value1); + EXPECT_TRUE(psbt.IsFindGlobalRecord(global_key1)); + EXPECT_FALSE(psbt.IsFindGlobalRecord(global_key2)); + psbt.SetGlobalRecord(global_key2, global_value2); + EXPECT_TRUE(psbt.IsFindGlobalRecord(global_key2)); + + EXPECT_STREQ("70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000dfc03636664000664756d6d793104010203040dfc03636664000664756d6d793201000000000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAAAAAA==", psbt.GetBase64().c_str()); + + Psbt psbt2(psbt.GetData()); + auto get_gval1 = psbt2.GetGlobalRecord(global_key1); + auto get_gval2 = psbt2.GetGlobalRecord(global_key2); + EXPECT_EQ(get_gval1.GetHex(), global_value1.GetHex()); + EXPECT_EQ(get_gval2.GetHex(), global_value2.GetHex()); + + auto key_list = psbt2.GetGlobalRecordKeyList(); + EXPECT_EQ(2, key_list.size()); + if (key_list.size() == 2) { + EXPECT_EQ(global_key1.GetHex(), key_list[0].GetHex()); + EXPECT_EQ(global_key2.GetHex(), key_list[1].GetHex()); + } + + ByteData global_tx_key = Psbt::CreateRecordKey(Psbt::kPsbtGlobalUnsignedTx); + EXPECT_STREQ( + "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + psbt2.GetGlobalRecord(global_tx_key).GetHex().c_str()); +} + +TEST(Psbt, SetOutputRecordTest) { + Psbt psbt("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/0/2"; + std::string path2 = "44h/0h/0h/0/2"; + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + auto addr1 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key1.GetPubkey()); + auto addr2 = Address(NetType::kTestnet, WitnessVersion::kVersion0, key2.GetPubkey()); + + std::vector txout_key_bytes(34); + std::vector txout_val_bytes; + txout_key_bytes[0] = 2; + auto pk_bytes1 = key1.GetPubkey().GetData().GetBytes(); + auto pk_bytes2 = key2.GetPubkey().GetData().GetBytes(); + auto fp1 = key1.GetFingerprint().GetBytes(); + auto fp2 = key2.GetFingerprint().GetBytes(); + auto plist1 = key1.GetChildNumArray(); + auto plist2 = key2.GetChildNumArray(); + memcpy(&txout_key_bytes[1], pk_bytes1.data(), 33); + txout_val_bytes.resize(4 + (plist1.size() * 4)); + memcpy(txout_val_bytes.data(), fp1.data(), 4); + memcpy(&txout_val_bytes.data()[4], plist1.data(), 4 * plist1.size()); + ByteData txout_key1 = ByteData(txout_key_bytes); + ByteData txout_value1 = ByteData(txout_val_bytes); + + memcpy(&txout_key_bytes[1], pk_bytes2.data(), 33); + txout_val_bytes.resize(4 + (plist2.size() * 4)); + memcpy(txout_val_bytes.data(), fp2.data(), 4); + memcpy(&txout_val_bytes.data()[4], plist2.data(), 4 * plist2.size()); + ByteData txout_key2 = ByteData(txout_key_bytes); + ByteData txout_value2 = ByteData(txout_val_bytes); + + psbt.SetTxOutRecord(0, txout_key1, txout_value1); + // EXPECT_TRUE(psbt.IsFindTxOutRecord(0, txout_key1)); + // EXPECT_FALSE(psbt.IsFindTxOutRecord(1, txout_key2)); + psbt.SetTxOutRecord(1, txout_key2, txout_value2); + // EXPECT_TRUE(psbt.IsFindTxOutRecord(1, txout_key2)); + + EXPECT_STREQ("70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a5550000000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", psbt.GetBase64().c_str()); + + Psbt psbt2(psbt.GetData()); + auto get_val1 = psbt2.GetTxOutKeyData(0); + auto get_val2 = psbt2.GetTxOutKeyData(1); + EXPECT_EQ(get_val1.ToString(), key1.ToString()); + EXPECT_EQ(get_val2.ToString(), key2.ToString()); + + ByteData global_key1 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy1"); + ByteData global_value1 = ByteData("01020304"); + ByteData global_key2 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy2"); + ByteData global_value2 = ByteData("00"); + psbt.SetTxOutRecord(0, global_key1, global_value1); + EXPECT_TRUE(psbt.IsFindTxOutRecord(0, global_key1)); + EXPECT_FALSE(psbt.IsFindTxOutRecord(0, global_key2)); + psbt.SetTxOutRecord(0, global_key2, global_value2); + EXPECT_TRUE(psbt.IsFindTxOutRecord(0, global_key2)); + + psbt2 = Psbt(psbt.GetData()); + auto get_gval1 = psbt2.GetTxOutRecord(0, global_key1); + auto get_gval2 = psbt2.GetTxOutRecord(0, global_key2); + EXPECT_EQ(get_gval1.GetHex(), global_value1.GetHex()); + EXPECT_EQ(get_gval2.GetHex(), global_value2.GetHex()); + + auto key_list = psbt2.GetTxOutRecordKeyList(0); + EXPECT_EQ(2, key_list.size()); + if (key_list.size() == 2) { + EXPECT_EQ(global_key1.GetHex(), key_list[0].GetHex()); + EXPECT_EQ(global_key2.GetHex(), key_list[1].GetHex()); + } + EXPECT_STREQ("cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", psbt2.GetBase64().c_str()); + + ByteData output_bip32_key1 = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtOutputBip32Derivation, key1.GetPubkey()); + EXPECT_STREQ( + "2a7047602c00008000000080000000800000000002000000", + psbt2.GetTxOutRecord(0, output_bip32_key1).GetHex().c_str()); +} + +TEST(Psbt, SetInputRecordTest) { + Psbt psbt("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA=="); + + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/1"; + std::string path2 = "44h/0h/0h/0/1"; + + auto key1 = wallet1.GeneratePubkeyData(NetType::kTestnet, path1); + auto key2 = wallet2.GeneratePubkeyData(NetType::kTestnet, path2); + + std::vector txin_key_bytes(34); + std::vector txin_val_bytes; + txin_key_bytes[0] = 6; + auto pk_bytes1 = key1.GetPubkey().GetData().GetBytes(); + auto pk_bytes2 = key2.GetPubkey().GetData().GetBytes(); + auto fp1 = key1.GetFingerprint().GetBytes(); + auto fp2 = key2.GetFingerprint().GetBytes(); + auto plist1 = key1.GetChildNumArray(); + auto plist2 = key2.GetChildNumArray(); + memcpy(&txin_key_bytes[1], pk_bytes1.data(), 33); + txin_val_bytes.resize(4 + (plist1.size() * 4)); + memcpy(txin_val_bytes.data(), fp1.data(), 4); + memcpy(&txin_val_bytes.data()[4], plist1.data(), 4 * plist1.size()); + ByteData txout_key1 = ByteData(txin_key_bytes); + ByteData txout_value1 = ByteData(txin_val_bytes); + + memcpy(&txin_key_bytes[1], pk_bytes2.data(), 33); + txin_val_bytes.resize(4 + (plist2.size() * 4)); + memcpy(txin_val_bytes.data(), fp2.data(), 4); + memcpy(&txin_val_bytes.data()[4], plist2.data(), 4 * plist2.size()); + ByteData txout_key2 = ByteData(txin_key_bytes); + ByteData txout_value2 = ByteData(txin_val_bytes); + + psbt.SetTxInRecord(0, ByteData("00"), Transaction(g_psbt_utxo_witness).GetData()); + try { + psbt.SetTxInRecord(0, ByteData("01"), ByteData("00e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787")); + psbt.SetTxInRecord(0, ByteData("03"), ByteData("01000000")); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + psbt.SetTxInRecord(0, ByteData("04"), ByteData("0014962c4e08f336d3afbc3415c9d359ae1040470520")); + psbt.SetTxInRecord(0, txout_key1, txout_value1); + + try { + psbt.SetTxInRecord(1, ByteData("00"), Transaction(g_psbt_utxo_legacy).GetData()); + psbt.SetTxInRecord(1, txout_key2, txout_value2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + EXPECT_STREQ("70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000", psbt.GetData().GetHex().c_str()); + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", psbt.GetBase64().c_str()); + + Psbt psbt2(psbt.GetData()); + auto get_val1 = psbt2.GetTxInKeyData(0); + auto get_val2 = psbt2.GetTxInKeyData(1); + EXPECT_EQ(get_val1.ToString(), key1.ToString()); + EXPECT_EQ(get_val2.ToString(), key2.ToString()); + + auto get_tx1 = psbt2.GetTxInUtxoFull(0); + auto get_tx2 = psbt2.GetTxInUtxoFull(1); + EXPECT_EQ(get_tx1.GetHex(), g_psbt_utxo_witness); + EXPECT_EQ(get_tx2.GetHex(), g_psbt_utxo_legacy); + + ByteData global_key1 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy1"); + ByteData global_value1 = ByteData("01020304"); + ByteData global_key2 = Psbt::CreateRecordKey(kProprietary, "cfd", 0, "dummy2"); + ByteData global_value2 = ByteData("00"); + psbt.SetTxInRecord(0, global_key1, global_value1); + EXPECT_TRUE(psbt.IsFindTxInRecord(0, global_key1)); + EXPECT_FALSE(psbt.IsFindTxInRecord(0, global_key2)); + psbt.SetTxInRecord(0, global_key2, global_value2); + EXPECT_TRUE(psbt.IsFindTxInRecord(0, global_key2)); + + psbt2 = Psbt(psbt.GetData()); + auto get_gval1 = psbt2.GetTxInRecord(0, global_key1); + auto get_gval2 = psbt2.GetTxInRecord(0, global_key2); + EXPECT_EQ(get_gval1.GetHex(), global_value1.GetHex()); + EXPECT_EQ(get_gval2.GetHex(), global_value2.GetHex()); + + auto key_list = psbt2.GetTxInRecordKeyList(0); + EXPECT_EQ(2, key_list.size()); + if (key_list.size() == 2) { + EXPECT_EQ(global_key1.GetHex(), key_list[0].GetHex()); + EXPECT_EQ(global_key2.GetHex(), key_list[1].GetHex()); + } + EXPECT_STREQ("cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAA", psbt2.GetBase64().c_str()); + + ByteData input_utxo_key1 = Psbt::CreateRecordKey( + Psbt::kPsbtInputNonWitnessUtxo); + EXPECT_STREQ( + "02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000", + psbt2.GetTxInRecord(0, input_utxo_key1).GetHex().c_str()); +} + +TEST(Psbt, GetSignaturePubkeyList) { + Psbt psbt("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHIgIDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/NHMEQCICBjWTfgUXDYPcMhOts6bq5mcTAI5KvDi0kSxWgN7E8MAiAzwIpxowdXsIRj1TDsBY7XQBlo+zC+9j1FSXIaDkhbhAEiAgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7R0cwRAIgXSdKeIfePvrehKSjScTDb1ibVWI7ECe32m2sicF4VjQCIGoDr+u7tgifHjf6yPmZpAFRYciSAUT9UxEtoFgEzUPMAQEDBAEAAAABBCIAIJxNrLJeu4rai7sa3bhp3qTYFwzJUfHZaUshVOFYMnbJAQVHUiEDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/MhA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHUq4iBgOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8xgqcEdgLAAAgAAAAIAAAACAAAAAAAsAAAAiBgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7Rxida22GLAAAgAAAAIAAAACAAAAAAAsAAAAAAQAiACA8rQYZ3mfmJHp2oQKBNjXAU0V8a6T95Kwf/YFI1w5LzAEBR1IhApBtOZ9tu+zImNS43j9JdHTIakHyzzbXH5XlygawdIZ7IQJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQlKuIgICYix5dOwy3nWvo3M7CabDPQ3qUUsp+qzPsJkXdPRiIkIYnWtthiwAAIAAAACAAAAAgAAAAAAMAAAAIgICkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnsYKnBHYCwAAIAAAACAAAAAgAAAAAAMAAAAAA=="); + + Psbt psbt2(psbt); + EXPECT_TRUE(psbt2.IsFindTxInSignature(0, Pubkey("03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47"))); + EXPECT_FALSE(psbt2.IsFindTxInSignature(0, Pubkey("03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b48"))); + + auto pk_list = psbt2.GetTxInSignaturePubkeyList(0); + EXPECT_EQ(2, pk_list.size()); + if (pk_list.size() == 2) { + EXPECT_STREQ("03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3", + pk_list[0].GetHex().c_str()); + EXPECT_STREQ("03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47", + pk_list[1].GetHex().c_str()); + } +} + +TEST(Psbt, CreateRecordKey) { + auto key1 = Psbt::CreateRecordKey(1); + auto key2 = Psbt::CreateRecordKey(2, ByteData("f1f2")); + auto key3 = Psbt::CreateRecordKey(3, "abc"); + auto key4 = Psbt::CreateRecordKey(4, ByteData("d1d2"), 5); + auto key5 = Psbt::CreateRecordKey(5, "def", 9); + auto key6 = Psbt::CreateFixRecordKey(6, ByteData("f1f2")); + + EXPECT_STREQ("01", key1.GetHex().c_str()); + EXPECT_STREQ("0202f1f2", key2.GetHex().c_str()); + EXPECT_STREQ("0303616263", key3.GetHex().c_str()); + EXPECT_STREQ("0402d1d205", key4.GetHex().c_str()); + EXPECT_STREQ("050364656609", key5.GetHex().c_str()); + EXPECT_STREQ("06f1f2", key6.GetHex().c_str()); +} + +TEST(Psbt, GlobalXpubTest) { + Psbt psbt("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHIgIDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/NHMEQCICBjWTfgUXDYPcMhOts6bq5mcTAI5KvDi0kSxWgN7E8MAiAzwIpxowdXsIRj1TDsBY7XQBlo+zC+9j1FSXIaDkhbhAEiAgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7R0cwRAIgXSdKeIfePvrehKSjScTDb1ibVWI7ECe32m2sicF4VjQCIGoDr+u7tgifHjf6yPmZpAFRYciSAUT9UxEtoFgEzUPMAQEDBAEAAAABBCIAIJxNrLJeu4rai7sa3bhp3qTYFwzJUfHZaUshVOFYMnbJAQVHUiEDpRL19ZwOeQH8R47WNT7vdvRPnLLBhb+89/C5u3Zxa/MhA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHUq4iBgOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8xgqcEdgLAAAgAAAAIAAAACAAAAAAAsAAAAiBgP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7Rxida22GLAAAgAAAAIAAAACAAAAAAAsAAAAAAQAiACA8rQYZ3mfmJHp2oQKBNjXAU0V8a6T95Kwf/YFI1w5LzAEBR1IhApBtOZ9tu+zImNS43j9JdHTIakHyzzbXH5XlygawdIZ7IQJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQlKuIgICYix5dOwy3nWvo3M7CabDPQ3qUUsp+qzPsJkXdPRiIkIYnWtthiwAAIAAAACAAAAAgAAAAAAMAAAAIgICkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnsYKnBHYCwAAIAAAACAAAAAgAAAAAAMAAAAAA=="); + + ExtPubkey ext_pubkey("xpub6EkLrUTiaMiLbMAkbLN2BdH4hWkCQT7fLQf3Q6Ymx3gAqbuFeSKHfTMVDtjcsuRtEFqJbAsjYFZMrqeDLgRSsn4yuQygK44HWPrnA7gZC2C"); + + try { + EXPECT_FALSE(psbt.IsFindGlobalXpubkey(ext_pubkey)); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + KeyData key_data(ext_pubkey, "0/44", ByteData("b7665978")); + psbt.SetGlobalXpubkey(key_data); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + EXPECT_TRUE(psbt.IsFindGlobalXpubkey(ext_pubkey)); + auto get_data = psbt.GetGlobalXpubkeyBip32(ext_pubkey); + EXPECT_STREQ("0/44", get_data.GetBip32Path().c_str()); + EXPECT_STREQ("b7665978", get_data.GetFingerprint().GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + ExtPubkey ext_pubkey2("xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX"); + KeyData key_data2(ext_pubkey2, "0/44'", ByteData("ae05dbb7")); + psbt.SetGlobalXpubkey(key_data2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + try { + auto extkey_list = psbt.GetGlobalXpubkeyDataList(); + EXPECT_EQ(2, extkey_list.size()); + if (extkey_list.size() == 2) { + EXPECT_STREQ("xpub6EkLrUTiaMiLbMAkbLN2BdH4hWkCQT7fLQf3Q6Ymx3gAqbuFeSKHfTMVDtjcsuRtEFqJbAsjYFZMrqeDLgRSsn4yuQygK44HWPrnA7gZC2C", extkey_list[0].GetExtPubkey().ToString().c_str()); + EXPECT_STREQ("0/44", extkey_list[0].GetBip32Path().c_str()); + EXPECT_STREQ("b7665978", extkey_list[0].GetFingerprint().GetHex().c_str()); + + EXPECT_STREQ("xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", extkey_list[1].GetExtPubkey().ToString().c_str()); + EXPECT_STREQ("0/44'", extkey_list[1].GetBip32Path().c_str()); + EXPECT_STREQ("ae05dbb7", extkey_list[1].GetFingerprint().GetHex().c_str()); + } + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + ByteData key("010488b21e0691fe4d298000002cb26a08008723cc8f19ac08bce635c087d63d738b63c33e62186d43cf3a5805f302e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b"); + auto value = psbt.GetGlobalRecord(key); + EXPECT_STREQ("ae05dbb7000000002c000080", value.GetHex().c_str()); +} + +TEST(Psbt, ParseSingleOutputTx) { + std::string tx_hex = "0200000000010000000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef00000000"; + std::string psbt_hex = "70736274ff0100350200000000010000000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef000000000000"; + std::string psbt_base64 = "cHNidP8BADUCAAAAAAEAAAAAAAAAACIAIMWuT/F87AVelktXNgEyjz+Hn6RB5T74is39TY6N9CnvAAAAAAAA"; + Psbt psbt(psbt_base64); + EXPECT_EQ(tx_hex, psbt.GetTransaction().GetHex()); + EXPECT_EQ(psbt_hex, psbt.GetData().GetHex()); + + Transaction tx(tx_hex); + psbt = Psbt(tx); + EXPECT_EQ(tx_hex, psbt.GetTransaction().GetHex()); + EXPECT_EQ(psbt_hex, psbt.GetData().GetHex()); +} diff --git a/test/test_pubkey.cpp b/test/test_pubkey.cpp index 114edd6d..de216bac 100644 --- a/test/test_pubkey.cpp +++ b/test/test_pubkey.cpp @@ -207,6 +207,13 @@ TEST(Pubkey, CompressUncompressTest) { EXPECT_STREQ(uncomp_pubkey.GetHex().c_str(), ext_key_uncompressed.c_str()); } +TEST(Pubkey, FingerprintTest) { + std::string key = "036468efc14b8512007bb720d6e7d4217a6686095a79b57e50dd48355110422955"; + Pubkey pubkey = Pubkey(key); + auto fingerprint = pubkey.GetFingerprint(); + EXPECT_STREQ("aa0ccb72", fingerprint.GetHex().c_str()); +} + TEST(Pubkey, VerifyEcSignature) { Pubkey pubkey( "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"); diff --git a/test/test_schnorrsig.cpp b/test/test_schnorrsig.cpp index b66faac4..33401680 100644 --- a/test/test_schnorrsig.cpp +++ b/test/test_schnorrsig.cpp @@ -238,3 +238,34 @@ TEST(SchnorrPubkey, TweakTest) { EXPECT_EQ(exp_pk_c, pk_c12.GetHex()); EXPECT_EQ(exp_sk_c, sk_c.GetHex()); } + +TEST(SchnorrUtil, ComputeSigPointBatch) { + std::vector data = { + ByteData256( + "e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614"), + ByteData256( + "80a1c2125d13d6b2d639f2da507772040719d36c6228ec141befd1aecb901b17"), + ByteData256( + "375a7aec74bba181ffca89ef03bd8a10d7ddae7813190d4616652d9e91bcff20"), + }; + + std::vector nonces = { + SchnorrPubkey( + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"), + SchnorrPubkey( + "f14d7e54ff58c5d019ce9986be4a0e8b7d643bd08ef2cdf1099e1a457865b547"), + SchnorrPubkey( + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54")}; + + std::vector sig_points; + for (size_t i = 0; i < data.size(); i++) { + sig_points.push_back( + SchnorrUtil::ComputeSigPoint(data[i], nonces[i], pubkey)); + } + auto expected_sig_point = Pubkey::CombinePubkey(sig_points); + + auto actual_sig_point = + SchnorrUtil::ComputeSigPointBatch(data, nonces, pubkey); + + ASSERT_EQ(expected_sig_point.GetHex(), actual_sig_point.GetHex()); +} diff --git a/test/test_script.cpp b/test/test_script.cpp index ff51884f..bda6662a 100644 --- a/test/test_script.cpp +++ b/test/test_script.cpp @@ -20,6 +20,7 @@ using cfd::core::ByteData; using cfd::core::Privkey; using cfd::core::Pubkey; using cfd::core::NetType; +using cfd::core::WitnessVersion; TEST(Script, Script) { size_t size = 0; @@ -38,6 +39,7 @@ TEST(Script, Script_hex) { "76a91498e977b2259a85278aa51188bd863a3df0ad31ba88ac"); EXPECT_EQ(script.IsEmpty(), false); EXPECT_EQ(script.GetElementList().size(), size); + EXPECT_EQ(script.GetWitnessVersion(), WitnessVersion::kVersionNone); } TEST(Script, Script_hex_exception) { @@ -79,6 +81,7 @@ TEST(Script, SetStackData_OP0) { EXPECT_STREQ( script.ToString().c_str(), "0 96376230fbeec4d1e703c3a2d1efe975ccf650a40f6ca2ec2d6cce44fc6bb2b3"); + EXPECT_EQ(script.GetWitnessVersion(), WitnessVersion::kVersion0); } TEST(Script, SetStackData_kUseScriptNum1) { @@ -412,6 +415,73 @@ TEST(Script, IsMultisigScriptTest) { EXPECT_FALSE(script.IsP2wpkhScript()); EXPECT_FALSE(script.IsP2wshScript()); EXPECT_FALSE(script.IsPegoutScript()); + + // 17-of-20 + builder = ScriptBuilder(); + builder << 17; + builder << Pubkey("02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0"); + builder << Pubkey("0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c"); + builder << Pubkey("024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82"); + builder << Pubkey("03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38"); + builder << Pubkey("020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1"); + builder << Pubkey("0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147"); + builder << Pubkey("0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c"); + builder << Pubkey("020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9"); + builder << Pubkey("0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37"); + builder << Pubkey("0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e"); + builder << Pubkey("03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b"); + builder << Pubkey("02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd"); + builder << Pubkey("0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d"); + builder << Pubkey("0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2"); + builder << Pubkey("025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db"); + builder << Pubkey("030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55"); + builder << Pubkey("0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e"); + builder << Pubkey("02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8"); + builder << Pubkey("03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950"); + builder << Pubkey("024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561"); + builder << 20; + builder << ScriptOperator::OP_CHECKMULTISIG; + script = builder.Build(); + EXPECT_TRUE(script.IsMultisigScript()); + + // invalid multisig1 + builder = ScriptBuilder(); + builder.AppendData(ByteData("02")); // binary value + builder.AppendData(Pubkey("0288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b81")); + builder.AppendData(Pubkey("03af2df16372b687457c4e522141ca5a600d64c61f3d7a19a465c051d060bdd727")); + builder.AppendData(Pubkey("02582b60250c5f99ab33faaec09c047f68e81bc267e4da7f136dc7b72afdaf0183")); + builder.AppendData(ByteData("03")); // binary value + builder.AppendOperator(ScriptOperator::OP_CHECKMULTISIG); + script = builder.Build(); + EXPECT_EQ("0102210288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b812103af2df16372b687457c4e522141ca5a600d64c61f3d7a19a465c051d060bdd7272102582b60250c5f99ab33faaec09c047f68e81bc267e4da7f136dc7b72afdaf01830103ae", script.GetHex()); + EXPECT_FALSE(script.IsMultisigScript()); + + // invalid multisig2 + builder = ScriptBuilder(); + builder << 2; + builder << Pubkey("0288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b81"); + builder << 1 << ScriptOperator::OP_CHECKMULTISIG; + script = builder.Build(); + EXPECT_EQ("52210288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b8151ae", script.GetHex()); + EXPECT_FALSE(script.IsMultisigScript()); + + // invalid multisig3 + builder = ScriptBuilder(); + builder << 0; + builder << Pubkey("0288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b81"); + builder << 1 << ScriptOperator::OP_CHECKMULTISIG; + script = builder.Build(); + EXPECT_EQ("00210288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b8151ae", script.GetHex()); + EXPECT_FALSE(script.IsMultisigScript()); + + // invalid multisig4 + builder = ScriptBuilder(); + builder << 1; + builder << Pubkey("0288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b81"); + builder << 2 << ScriptOperator::OP_CHECKMULTISIG; + script = builder.Build(); + EXPECT_EQ("51210288b03ce954e6eccfd9bdfd8cea71f80957e20d37d020b1b99973ea9f897f2b8152ae", script.GetHex()); + EXPECT_FALSE(script.IsMultisigScript()); } TEST(Script, IsP2wpkhScriptTest) { diff --git a/test/test_scriptbuilder.cpp b/test/test_scriptbuilder.cpp index 929caeca..8b8e1037 100644 --- a/test/test_scriptbuilder.cpp +++ b/test/test_scriptbuilder.cpp @@ -126,6 +126,32 @@ TEST(ScriptBuilder, AppendFunctionTest1) { EXPECT_EQ(expect_size, actual.GetElementList().size()); } +TEST(ScriptBuilder, AppendFunctionTest1ByOperator) { + std::string sig = + "304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e"; // NOLINT + std::string pubkey = + "042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0";// NOLINT + std::string pubkey_hash = "aea58b2d64af22fe06b95c46af4e471e6280226c"; + + // ref: https://en.bitcoin.it/wiki/Script#Script_examples + // Freezing funds until a time in the future + Script actual = (ScriptBuilder() << sig << Pubkey(pubkey) << 144 + << ScriptType::kOpCheckLockTimeVerify + << ScriptOperator::OP_DROP << ScriptOperator::OP_DUP + << ScriptOperator::OP_HASH160 << ByteData160(pubkey_hash) + << ScriptOperator::OP_EQUALVERIFY << ScriptOperator::OP_CHECKSIG).Build(); + + std::string expect_hex = + "46304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e41042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0029000b17576a914aea58b2d64af22fe06b95c46af4e471e6280226c88ac";// NOLINT + std::string expect_asm = + "304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e 042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0 144 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 aea58b2d64af22fe06b95c46af4e471e6280226c OP_EQUALVERIFY OP_CHECKSIG";// NOLINT + size_t expect_size = 10; + + EXPECT_STREQ(expect_hex.c_str(), actual.GetHex().c_str()); + EXPECT_STREQ(expect_asm.c_str(), actual.ToString().c_str()); + EXPECT_EQ(expect_size, actual.GetElementList().size()); +} + TEST(ScriptBuilder, AppendFunctionTest2) { ScriptBuilder sb = ScriptBuilder(); std::string sig = @@ -254,3 +280,43 @@ TEST(ScriptBuilder, StringBuildTest) { "0 17 8738 3355443"); } +TEST(ScriptBuilder, StringBuildByOperator) { + Script script = (ScriptBuilder() << "5" << "2" << "OP_ADD" << "OP_CHECKSIG").Build(); + EXPECT_STREQ(script.GetHex().c_str(), "555293ac"); + EXPECT_STREQ(script.ToString().c_str(), "5 2 OP_ADD OP_CHECKSIG"); + + script = (ScriptBuilder() << "0" << "17" << "8738" + << "3355443" << "17476" << "85" << "26214" << "7829367" + << "-2184" << "-25").Build(); + EXPECT_STREQ(script.GetHex().c_str(), + "000111022222033333330244440155026666037777770288880199"); + EXPECT_STREQ(script.ToString().c_str(), + "0 17 8738 3355443 17476 85 26214 7829367 -2184 -25"); + + std::string expect_hex = + "46304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e41042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0029000b1750455529357a820f6116d61351c05df34e116f1cc63fcacbd4f1a3882d2f629e7a0986ac03005c488ac";// NOLINT + std::string expect_asm = + "304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e 042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0 144 OP_CHECKLOCKTIMEVERIFY OP_DROP 1469272661 OP_SHA256 f6116d61351c05df34e116f1cc63fcacbd4f1a3882d2f629e7a0986ac03005c4 OP_EQUALVERIFY OP_CHECKSIG";// NOLINT + + script = (ScriptBuilder() + << "304402203dd0c408e173d6b7252eabc7e3f6a0c632d930a7b343eaf60e7ebee9eb01adcc02204a567cb6a941c88f24f4c4201633468d53810fae9cdb90f35571e6b52bed005e" + << "042322ed12f2779cae32ca89f15d61d10e3bd725d74d45269b05a34abb91b45a2ca19cc8734300deaf74d006871b5cd0730f2384037d16843663a0327fce24aef0" + << "144" + << "OP_CHECKLOCKTIMEVERIFY" << "OP_DROP" << "1469272661" + << "OP_SHA256" + << "f6116d61351c05df34e116f1cc63fcacbd4f1a3882d2f629e7a0986ac03005c4" + << "OP_EQUALVERIFY" << "OP_CHECKSIG").Build(); + EXPECT_STREQ(script.GetHex().c_str(), expect_hex.c_str()); + EXPECT_STREQ(script.ToString().c_str(), expect_asm.c_str()); + + // hex test + script = (ScriptBuilder() + << "0x00" + << "0x11" + << "0x2222" + << "0x333333").Build(); + EXPECT_STREQ(script.GetHex().c_str(), + "0100011102222203333333"); + EXPECT_STREQ(script.ToString().c_str(), + "0 17 8738 3355443"); +} diff --git a/test/test_scripthash.cpp b/test/test_scripthash.cpp index 735c74d9..56f64b15 100644 --- a/test/test_scripthash.cpp +++ b/test/test_scripthash.cpp @@ -7,7 +7,6 @@ using cfd::core::ByteData; using cfd::core::CfdException; using cfd::core::Script; -using cfd::core::ScriptBuilder; using cfd::core::ScriptElement; using cfd::core::ScriptHash; using cfd::core::ScriptType; diff --git a/test/test_scriptoperator.cpp b/test/test_scriptoperator.cpp index 38a008f6..fc185c41 100644 --- a/test/test_scriptoperator.cpp +++ b/test/test_scriptoperator.cpp @@ -7,7 +7,6 @@ using cfd::core::ByteData; using cfd::core::CfdException; using cfd::core::Script; -using cfd::core::ScriptBuilder; using cfd::core::ScriptElement; using cfd::core::ScriptOperator; using cfd::core::ScriptType; diff --git a/test/test_scriptutil.cpp b/test/test_scriptutil.cpp index f2c996ac..97afe678 100644 --- a/test/test_scriptutil.cpp +++ b/test/test_scriptutil.cpp @@ -169,6 +169,7 @@ struct MultisigTestVector { std::vector input_pubkeys; Script expect_multisig_script; std::string expect_message; + bool is_witness; }; TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { @@ -180,7 +181,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { Pubkey("02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0"), }, Script("512102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a051ae"), - "" + "", + false }, // 1-of-2 Multisig { @@ -190,7 +192,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { Pubkey("0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c"), }, Script("512102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c52ae"), - "" + "", + false }, // 2-of-3 Multisig { @@ -201,7 +204,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { Pubkey("024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82"), }, Script("522102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b8253ae"), - "" + "", + false }, // 12-of-15 Multisig { @@ -224,7 +228,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { Pubkey("025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db"), }, Script("5c2102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b822103ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb3821020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1210289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147210210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c21020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9210289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37210254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e2103a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b2102ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd210289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d210396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd221025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db5fae"), - "" + "", + false }, // 15-of-15 Multisig { @@ -247,14 +252,51 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptTest) { Pubkey("025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db"), }, Script("5f2102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b822103ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb3821020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1210289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147210210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c21020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9210289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37210254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e2103a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b2102ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd210289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d210396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd221025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db5fae"), - "" + "", + false + }, + // 20-of-20 Multisig on witness + { + 20, + { + Pubkey("02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0"), + Pubkey("0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c"), + Pubkey("024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82"), + Pubkey("03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38"), + Pubkey("020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1"), + Pubkey("0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147"), + Pubkey("0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c"), + Pubkey("020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9"), + Pubkey("0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37"), + Pubkey("0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e"), + Pubkey("03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b"), + Pubkey("02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd"), + Pubkey("0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d"), + Pubkey("0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2"), + Pubkey("025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db"), + Pubkey("030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55"), + Pubkey("0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e"), + Pubkey("02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8"), + Pubkey("03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950"), + Pubkey("024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561"), + }, + Script("01142102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b822103ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb3821020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1210289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147210210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c21020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9210289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37210254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e2103a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b2102ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd210289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d210396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd221025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db21030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55210267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e2102f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b82103968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb595021024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd25610114ae"), + "", + true }, }; Script actual; for (MultisigTestVector test_vector : test_vectors) { - EXPECT_NO_THROW((actual = ScriptUtil::CreateMultisigRedeemScript(test_vector.req_sig, test_vector.input_pubkeys))); - EXPECT_STREQ(actual.GetHex().c_str(), test_vector.expect_multisig_script.GetHex().c_str()); + try { + actual = ScriptUtil::CreateMultisigRedeemScript( + test_vector.req_sig, test_vector.input_pubkeys, + test_vector.is_witness); + EXPECT_STREQ(actual.GetHex().c_str(), test_vector.expect_multisig_script.GetHex().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + EXPECT_EQ(test_vector.req_sig, 0); + } } } @@ -267,7 +309,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptErrorTest) { Pubkey("02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0"), }, Script(), - "CreateMultisigScript require_num is 0." + "CreateMultisigScript require_num is 0.", + false }, // 1-of-0 Multisig { @@ -275,7 +318,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptErrorTest) { { }, Script(), - "CreateMultisigScript empty pubkey array." + "CreateMultisigScript empty pubkey array.", + false }, // 3-of-2 Multisig { @@ -285,7 +329,8 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptErrorTest) { Pubkey("0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c"), }, Script(), - "CreateMultisigScript require_num is over." + "CreateMultisigScript require_num is over.", + false }, // 1-of-16 Multisig { @@ -309,14 +354,47 @@ TEST(ScriptUtil, CreateMultisigRedeemScriptErrorTest) { Pubkey("030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55"), }, Script(), - "CreateMultisigScript pubkeys array size is over." + "CreateMultisigScript pubkeys array size is over.", + false + }, + // 1-of-21 Multisig on witness + { + 1, + { + Pubkey("02522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0"), + Pubkey("0340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c"), + Pubkey("024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b82"), + Pubkey("03ce982e13798960b7c23fd2c1676f64ff6df80f75324d0e566432e2a884dafb38"), + Pubkey("020bac40bcc23dd9b33a32b8183d2e9e79eb976bcfb2247141da1e58b2970bfde1"), + Pubkey("0289d8f0fb8cbd369a9aad28070edf2e99544384c122b8af825e50ea219193f147"), + Pubkey("0210fcaf81018c3f304ca792c9c1809ec00b159e23ebde669486c62787818f315c"), + Pubkey("020847e443a4d6b9ea577b776ca232c5dc9a3cbbd6c82dde0ef5100ac6c5a36cf9"), + Pubkey("0289e210d82121823dc5af09a0ab8c23d4a52273358295f4e4596b0f98e4973e37"), + Pubkey("0254de5471d6c8b36c26a62e0b54385fe0e88563e34127c18e97e705f83172326e"), + Pubkey("03a9c473d65af0420e600e085be058f98ac0634d13390e5d8d4962cbcfeb75422b"), + Pubkey("02ebcde0a7ece63e607287af1542efddeb008b0d1693da2ca06b622ebaf92051dd"), + Pubkey("0289b2b5852ffd7b89266338d746e05e7afe33e6005dab198b6a4b13065b93a89d"), + Pubkey("0396436fd20f3c5d3638c8ed4195cf63b4467701c5d4de660bd9bced68f4588cd2"), + Pubkey("025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db"), + Pubkey("030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55"), + Pubkey("0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e"), + Pubkey("02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8"), + Pubkey("03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950"), + Pubkey("024b64115bff6cc3718867114f7594fad535344f27ebe17ffa0e66288eb7bd2561"), + Pubkey("03f3aba2366b71f8473dd8dd4186005a9e3c6f9a32f76fc45493fd2a78b78c0d8d"), + }, + Script(), + "CreateMultisigScript pubkeys array size is over.", + true }, }; Script actual; for (MultisigTestVector test_vector : test_vectors) { try{ - EXPECT_THROW((actual = ScriptUtil::CreateMultisigRedeemScript(test_vector.req_sig, test_vector.input_pubkeys)), CfdException); + EXPECT_THROW((actual = ScriptUtil::CreateMultisigRedeemScript( + test_vector.req_sig, test_vector.input_pubkeys, + test_vector.is_witness)), CfdException); } catch (CfdException &e) { EXPECT_STREQ(e.what(), test_vector.expect_message.c_str()); } diff --git a/test/test_serializer.cpp b/test/test_serializer.cpp new file mode 100644 index 00000000..46904162 --- /dev/null +++ b/test/test_serializer.cpp @@ -0,0 +1,25 @@ +#include "gtest/gtest.h" +#include + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_exception.h" + +using cfd::core::ByteData; +using cfd::core::ByteData160; +using cfd::core::ByteData256; +using cfd::core::Serializer; + +TEST(Serializer, Normal) { + Serializer builder; + builder.AddDirectNumber(uint32_t{1}); + builder.AddDirectNumber(uint64_t{2}); + builder.AddDirectByte(3); + builder.AddVariableInt(0x01ffff); + builder.AddVariableBuffer(ByteData("f1f2")); + builder.AddPrefixBuffer(0xe1e2e3e4, ByteData("d1d2d3d4")); + builder.AddDirectBytes(ByteData("c1c2c3c4")); + + EXPECT_STREQ("01000000020000000000000003feffff010002f1f209fee4e3e2e1d1d2d3d4c1c2c3c4", + builder.Output().GetHex().c_str()); +} diff --git a/test/test_sighashtype.cpp b/test/test_sighashtype.cpp index c00627d4..ca8b477a 100644 --- a/test/test_sighashtype.cpp +++ b/test/test_sighashtype.cpp @@ -13,11 +13,13 @@ TEST(SigHashType, Constructor_GetSigHashFlag) { SigHashType type2; EXPECT_NO_THROW(type = SigHashType()); EXPECT_EQ(type.GetSigHashFlag(), 1); + EXPECT_STREQ(type.ToString().c_str(), "ALL"); EXPECT_NO_THROW(type2 = SigHashType(SigHashAlgorithm::kSigHashNone, true, false)); EXPECT_EQ(type2.GetSigHashFlag(), 0x82); EXPECT_TRUE(type2.IsAnyoneCanPay()); EXPECT_FALSE(type2.IsForkId()); + EXPECT_STREQ(type2.ToString().c_str(), "NONE|ANYONECANPAY"); EXPECT_NO_THROW(type = type2); EXPECT_EQ(type.GetSigHashFlag(), 0x82); diff --git a/test/test_taproot_merkletree.cpp b/test/test_taproot_merkletree.cpp new file mode 100644 index 00000000..9568f8c6 --- /dev/null +++ b/test/test_taproot_merkletree.cpp @@ -0,0 +1,559 @@ +#include "gtest/gtest.h" +#include + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_taproot.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_bytedata.h" + +using cfd::core::TaprootScriptTree; +using cfd::core::TapBranch; +using cfd::core::CfdException; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::ByteData256; +using cfd::core::Script; +using cfd::core::ScriptBuilder; +using cfd::core::ScriptOperator; +using cfd::core::SchnorrUtil; + +TEST(TaprootScriptTree, Empty) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + TaprootScriptTree tree; + uint8_t ver = TaprootScriptTree::kTapScriptLeafVersion; + EXPECT_EQ(ver, tree.GetLeafVersion()); + EXPECT_EQ("83d956a5b36109f8f667aa9b366e8479942e32396455b5f43b6df917768e4d45", + tree.GetTapLeafHash().GetHex()); + EXPECT_EQ("83d956a5b36109f8f667aa9b366e8479942e32396455b5f43b6df917768e4d45", + tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ("350105043b07771830fe4e4bd1a694d6aba22eb6e7f953d530f49b581d816bec", + tree.GetTweakedPubkey(schnorr_pubkey).GetHex()); + EXPECT_EQ("023534977a61f3167b576ee7e636a4041d6451a58f708da24fac8bbd2d9e6b25", + tree.GetTweakedPrivkey(key).GetHex()); + + ByteData256 msg("e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02"); + auto pk = tree.GetTweakedPubkey(schnorr_pubkey); + auto sk = tree.GetTweakedPrivkey(key); + auto sig = SchnorrUtil::Sign(msg, sk); + EXPECT_TRUE(pk.Verify(sig, msg)); +} + +TEST(TaprootScriptTree, Branch) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + Script script = (ScriptBuilder() << ScriptOperator::OP_TRUE).Build(); + uint8_t leaf_version = 0xc4; + std::vector nodes = { + ByteData256("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"), + ByteData256("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54") + }; + TaprootScriptTree tree(leaf_version, script); + tree.AddBranch(nodes[0]); + tree.AddBranch(SchnorrPubkey(nodes[1])); + + EXPECT_EQ(leaf_version, tree.GetLeafVersion()); + EXPECT_EQ(script.GetHex(), tree.GetScript().GetHex()); + EXPECT_EQ(nodes.size(), tree.GetNodeList().size()); + if (nodes.size() == tree.GetNodeList().size()) { + for (size_t index=0; index nodes = { + ByteData256("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"), + ByteData256("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d57") + }; + TaprootScriptTree tree(leaf_version, script); + for (const auto& node : nodes) { + tree.AddBranch(node); + } + + EXPECT_EQ(leaf_version, tree.GetLeafVersion()); + EXPECT_EQ(script.GetHex(), tree.GetScript().GetHex()); + EXPECT_EQ(nodes.size(), tree.GetNodeList().size()); + if (nodes.size() == tree.GetNodeList().size()) { + for (size_t index=0; index CHECKSIGVERIFY ... CHECKSIGVERIFY CHECKSIG + Script tree_2_of_2_sig = (ScriptBuilder() << schnorr_pubkey2.GetData() + << ScriptOperator::OP_CHECKSIGVERIFY << schnorr_pubkey3.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + TaprootScriptTree tree3(tree_2_of_2_sig); + + auto exp_hash = "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec"; + auto exp_str = "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}"; + TaprootScriptTree root = tree1; + root.AddBranch(tree2); + root.AddBranch(tree3); + EXPECT_EQ(exp_hash, root.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, root.ToString()); + + root = tree2; + root.AddBranch(tree1); + root.AddBranch(tree3); + EXPECT_EQ(exp_hash, root.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, root.ToString()); + + auto branch = tree2; + branch.AddBranch(tree1); + root = tree3; + root.AddBranch(branch); + EXPECT_EQ(exp_hash, root.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, root.ToString()); + + // blind leaf + root = tree3; + root.AddBranch(branch.GetCurrentBranchHash()); + EXPECT_EQ(exp_hash, root.GetCurrentBranchHash().GetHex()); + EXPECT_EQ( + "{af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4,tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}", + root.ToString()); + + root = TaprootScriptTree::FromString(exp_str, script); + EXPECT_EQ(script.GetHex(), root.GetScript().GetHex()); + EXPECT_EQ(2, root.GetBranchList().size()); + + root = TaprootScriptTree::FromString(exp_str, script_true); + EXPECT_EQ(script_true.GetHex(), root.GetScript().GetHex()); + EXPECT_EQ(2, root.GetBranchList().size()); + + root = TaprootScriptTree::FromString(exp_str, tree_2_of_2_sig); + EXPECT_EQ(tree_2_of_2_sig.GetHex(), root.GetScript().GetHex()); + EXPECT_EQ(1, root.GetBranchList().size()); +} + +TEST(TaprootScriptTree, TreeTest2) { + // /\ // + // /\ H // + // / \ // + // /\ /\ // + // / D E \ // + // / \ /\ // + // A /\ F G // + // B C // + Privkey key("dd43698cf5f96d33bf895c28d67b5ffbd736c2d4cef91e1f8ce0e38c31a709c8"); + ByteData256 tweak1("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"); + ByteData256 tweak2("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d57"); + ByteData256 tweak3("a78120a2d338fce91a49230935e8f000672f9511ee6fa5fc35ef22f0dfc89475"); + ByteData256 tweak4("4b79048979258d39c31b10f2bda70a433daa6e42f987089053f00db1d0f94a8e"); + Pubkey pubkey1 = key.GeneratePubkey(); + Pubkey pubkey2 = pubkey1 + tweak3; + Pubkey pubkey3 = pubkey1 + tweak4; + SchnorrPubkey schnorr_pubkey1 = SchnorrPubkey::FromPubkey(pubkey1); + SchnorrPubkey schnorr_pubkey11 = schnorr_pubkey1 + tweak1; + SchnorrPubkey schnorr_pubkey12 = schnorr_pubkey1 + tweak2; + SchnorrPubkey schnorr_pubkey2 = SchnorrPubkey::FromPubkey(pubkey2); + SchnorrPubkey schnorr_pubkey21 = schnorr_pubkey2 + tweak1; + SchnorrPubkey schnorr_pubkey22 = schnorr_pubkey2 + tweak2; + SchnorrPubkey schnorr_pubkey3 = SchnorrPubkey::FromPubkey(pubkey3); + SchnorrPubkey schnorr_pubkey31 = schnorr_pubkey3 + tweak1; + SchnorrPubkey schnorr_pubkey32 = schnorr_pubkey3 + tweak2; + + Script script_a = (ScriptBuilder() << schnorr_pubkey1.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_b = (ScriptBuilder() << ScriptOperator::OP_TRUE).Build(); + Script script_c = (ScriptBuilder() << schnorr_pubkey11.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + // CHECKSIGVERIFY ... CHECKSIGVERIFY CHECKSIG + Script script_d = (ScriptBuilder() << schnorr_pubkey11.GetData() + << ScriptOperator::OP_CHECKSIGVERIFY << schnorr_pubkey12.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + // Script script_e = (ScriptBuilder() << ScriptOperator::OP_TRUE).Build(); + Script script_e = (ScriptBuilder() << schnorr_pubkey2.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_f = (ScriptBuilder() << schnorr_pubkey21.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_g = (ScriptBuilder() << schnorr_pubkey22.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + // CHECKSIGVERIFY ... CHECKSIGVERIFY CHECKSIG + Script script_h = (ScriptBuilder() << schnorr_pubkey31.GetData() + << ScriptOperator::OP_CHECKSIGVERIFY << schnorr_pubkey32.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_j = (ScriptBuilder() << schnorr_pubkey32.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + EXPECT_EQ("20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", script_a.GetHex()); + EXPECT_EQ("51", script_b.GetHex()); + EXPECT_EQ("2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac", script_c.GetHex()); + EXPECT_EQ("2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", script_d.GetHex()); + EXPECT_EQ("20a6573124a479ab188b063bc383aa599da8ccc3b8f90fc18d570a8b367276eaf5ac", script_e.GetHex()); + EXPECT_EQ("2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac", script_f.GetHex()); + EXPECT_EQ("204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac", script_g.GetHex()); + EXPECT_EQ("2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac", script_h.GetHex()); + EXPECT_EQ("20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac", script_j.GetHex()); + + struct TestScriptTree2Data { + std::string name; + Script script; + size_t depth; + std::string nodes; + }; + std::vector exp_list = { + { + "a", script_a, 4, + "4b3bb79ea92e0b4f2bfa7e8c88d81133e347da393d72a37fe9cdcf1f5f56b5e0e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb0db59f44e1394f15d0f0332e106865849b6dff25aa6a9bf7fe82362d7637be55d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "b", script_b, 5, + "06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fde47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb0db59f44e1394f15d0f0332e106865849b6dff25aa6a9bf7fe82362d7637be55d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "c", script_c, 5, + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d6754691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fde47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb0db59f44e1394f15d0f0332e106865849b6dff25aa6a9bf7fe82362d7637be55d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "d", script_d, 3, + "7da36533760cede4c164d5c00eb1500a27dd86ca76914a9874112c43e0c1b9450db59f44e1394f15d0f0332e106865849b6dff25aa6a9bf7fe82362d7637be55d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "e", script_e, 3, + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27ed7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "f", script_f, 4, + "1aac269b1edaa45c69fb8d1a703a1bb69e90129cef7b7cfe9e676b28e6d1175d7f0ebfee6d06410937c4fd9284a322d1ca33bd1dc315a04e44c4b7df65cfccffaeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27ed7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "g", script_g, 4, + "e82da59bb829eb21f7cb8eb9eb128626da9a9a31f3dfdeb29766faf14468e9967f0ebfee6d06410937c4fd9284a322d1ca33bd1dc315a04e44c4b7df65cfccffaeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27ed7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + }, + { + "h", script_h, 1, "8f43855f8d9916a2cece54e67b4ce08950a60cc3cce8907d34e03788ade5a977" + }, + }; + + auto exp_hash = "ca0e12942fdb00ad71e84e02c44c0b9136e60ff2c25bcb3cade4d7dc53d246df"; + auto exp_str = "{{{tl(20a6573124a479ab188b063bc383aa599da8ccc3b8f90fc18d570a8b367276eaf5ac),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}"; + TaprootScriptTree tree_efg(script_f); + tree_efg.AddBranch(TaprootScriptTree(script_g)); + tree_efg.AddBranch(TaprootScriptTree(script_e)); + + TaprootScriptTree tree_b(script_b); + tree_b.AddBranch(TaprootScriptTree(script_c)); + tree_b.AddBranch(TaprootScriptTree(script_a)); + tree_b.AddBranch(TaprootScriptTree(script_d)); + tree_b.AddBranch(tree_efg); + tree_b.AddBranch(TaprootScriptTree(script_h)); + + TaprootScriptTree tree = tree_b; + EXPECT_EQ(exp_hash, tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, tree.ToString()); + + for (const auto& test_data : exp_list) { + SCOPED_TRACE("script_" + test_data.name); + tree = TaprootScriptTree::FromString(exp_str, test_data.script); + EXPECT_EQ(exp_hash, tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, tree.ToString()); + EXPECT_EQ(test_data.script.GetHex(), tree.GetScript().GetHex()); + EXPECT_EQ(test_data.depth, tree.GetBranchList().size()); + std::string nodes; + for (const auto& node : tree.GetNodeList()) nodes += node.GetHex(); + EXPECT_EQ(test_data.nodes, nodes); + } + + // invalid leaf + try { + tree = TaprootScriptTree::FromString(exp_str, script_j); + EXPECT_EQ("xxx", tree.GetScript().GetHex()); + EXPECT_EQ("xxx", script_j.GetHex()); + } catch (const CfdException& except) { + EXPECT_STREQ( + "This tapscript not exist in this tree.", + except.what()); + } +} + +TEST(TaprootScriptTree, TreeTest3) { + // /\ // + // /\ H // + // / \ // + // /\ /\ // + // / D E \ // + // / \ /\ // + // A /\ F G // + // B C // + Privkey key("dd43698cf5f96d33bf895c28d67b5ffbd736c2d4cef91e1f8ce0e38c31a709c8"); + ByteData256 tweak1("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"); + ByteData256 tweak2("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d57"); + ByteData256 tweak3("a78120a2d338fce91a49230935e8f000672f9511ee6fa5fc35ef22f0dfc89475"); + ByteData256 tweak4("4b79048979258d39c31b10f2bda70a433daa6e42f987089053f00db1d0f94a8e"); + Pubkey pubkey1 = key.GeneratePubkey(); + Pubkey pubkey2 = pubkey1 + tweak3; + Pubkey pubkey3 = pubkey1 + tweak4; + SchnorrPubkey schnorr_pubkey1 = SchnorrPubkey::FromPubkey(pubkey1); + SchnorrPubkey schnorr_pubkey11 = schnorr_pubkey1 + tweak1; + SchnorrPubkey schnorr_pubkey12 = schnorr_pubkey1 + tweak2; + SchnorrPubkey schnorr_pubkey2 = SchnorrPubkey::FromPubkey(pubkey2); + SchnorrPubkey schnorr_pubkey21 = schnorr_pubkey2 + tweak1; + SchnorrPubkey schnorr_pubkey22 = schnorr_pubkey2 + tweak2; + SchnorrPubkey schnorr_pubkey3 = SchnorrPubkey::FromPubkey(pubkey3); + SchnorrPubkey schnorr_pubkey31 = schnorr_pubkey3 + tweak1; + SchnorrPubkey schnorr_pubkey32 = schnorr_pubkey3 + tweak2; + + Script script_a = (ScriptBuilder() << schnorr_pubkey1.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_b = (ScriptBuilder() << ScriptOperator::OP_TRUE).Build(); + Script script_c = (ScriptBuilder() << schnorr_pubkey11.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + // CHECKSIGVERIFY ... CHECKSIGVERIFY CHECKSIG + Script script_d = (ScriptBuilder() << schnorr_pubkey11.GetData() + << ScriptOperator::OP_CHECKSIGVERIFY << schnorr_pubkey12.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_e = (ScriptBuilder() << ScriptOperator::OP_TRUE).Build(); + // Script script_e = (ScriptBuilder() << schnorr_pubkey2.GetData() + // << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_f = (ScriptBuilder() << schnorr_pubkey21.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_g = (ScriptBuilder() << schnorr_pubkey22.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + // CHECKSIGVERIFY ... CHECKSIGVERIFY CHECKSIG + Script script_h = (ScriptBuilder() << schnorr_pubkey31.GetData() + << ScriptOperator::OP_CHECKSIGVERIFY << schnorr_pubkey32.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + Script script_j = (ScriptBuilder() << schnorr_pubkey32.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + + EXPECT_EQ("20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", script_a.GetHex()); + EXPECT_EQ("51", script_b.GetHex()); + EXPECT_EQ("2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac", script_c.GetHex()); + EXPECT_EQ("2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", script_d.GetHex()); + EXPECT_EQ("51", script_e.GetHex()); + EXPECT_EQ("2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac", script_f.GetHex()); + EXPECT_EQ("204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac", script_g.GetHex()); + EXPECT_EQ("2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac", script_h.GetHex()); + EXPECT_EQ("20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac", script_j.GetHex()); + + struct TestScriptTree3Data { + std::string name; + Script script; + size_t depth; + std::vector nodes; + }; + std::vector exp_list = { + { + "b", script_b, 5, + { + ByteData256("06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df"), + ByteData256("4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd"), + ByteData256("e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb"), + ByteData256("32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7c"), + ByteData256("d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75") + } + }, + { + "e", script_e, 3, + { + ByteData256("aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086"), + ByteData256("aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e"), + ByteData256("d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75") + } + }, + }; + + auto exp_hash = "0c1bebfc9a508bf4d5835d401d96d71b72f1873fd338aebfff06d7adbe0c0cc3"; + auto exp_str = "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}"; + + TaprootScriptTree tree_fg(script_f); + tree_fg.AddBranch(TaprootScriptTree(script_g)); + TaprootScriptTree tree_efg(script_e); + tree_efg.AddBranch(tree_fg); + EXPECT_EQ("aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", tree_fg.GetCurrentBranchHash().GetHex()); + + TaprootScriptTree tree_b(script_b); + tree_b.AddBranch(TaprootScriptTree(script_c)); + tree_b.AddBranch(TaprootScriptTree(script_a)); + tree_b.AddBranch(TaprootScriptTree(script_d)); + auto hash_abcd = tree_b.GetCurrentBranchHash(); + tree_b.AddBranch(tree_efg); + tree_b.AddBranch(TaprootScriptTree(script_h)); + + TaprootScriptTree tree = tree_b; + EXPECT_EQ(exp_hash, tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, tree.ToString()); + std::string nodes_str; + for (const auto& node : tree.GetNodeList()) nodes_str += node.GetHex(); + EXPECT_EQ("06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fde47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7cd7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75", nodes_str); + EXPECT_EQ("aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", hash_abcd.GetHex()); + + for (const auto& test_data : exp_list) { + SCOPED_TRACE("script_" + test_data.name); + std::vector nodes; + tree = TaprootScriptTree::FromString(exp_str, test_data.script); + EXPECT_EQ(exp_hash, tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ(exp_str, tree.ToString()); + if (test_data.name == "b") { + // see: script_e (duplicated script) + EXPECT_EQ(exp_list[1].script.GetHex(), tree.GetScript().GetHex()); + EXPECT_EQ(exp_list[1].depth, tree.GetBranchList().size()); + nodes = tree.GetNodeList(); + EXPECT_EQ(exp_list[1].nodes.size(), nodes.size()); + for (size_t index=0; index + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_taproot.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_util.h" +#include "cfdcore/cfdcore_bytedata.h" + +using cfd::core::TaprootScriptTree; +using cfd::core::TaprootUtil; +using cfd::core::ByteData; +using cfd::core::ByteData256; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::ScriptBuilder; +using cfd::core::ScriptOperator; +using cfd::core::Script; +using cfd::core::ScriptBuilder; +using cfd::core::Transaction; +using cfd::core::Amount; +using cfd::core::Txid; +using cfd::core::TxIn; +using cfd::core::ScriptUtil; +using cfd::core::CryptoUtil; +using cfd::core::SigHashType; +using cfd::core::WitnessVersion; +using cfd::core::Address; +using cfd::core::TxOut; +using cfd::core::NetType; +using cfd::core::SchnorrUtil; +using cfd::core::SchnorrSignature; +using cfd::core::TapScriptData; + +TEST(TaprootUtil, ValidLeafVersion) { + EXPECT_FALSE(TaprootUtil::IsValidLeafVersion(0)); + EXPECT_TRUE(TaprootUtil::IsValidLeafVersion(0x66)); + EXPECT_TRUE(TaprootUtil::IsValidLeafVersion(0xc8)); + EXPECT_FALSE(TaprootUtil::IsValidLeafVersion(0xc9)); +} + +TEST(TaprootUtil, CreateTapScriptControl) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + Script redeem_script = (ScriptBuilder() << schnorr_pubkey.GetData() + << ScriptOperator::OP_CHECKSIG).Build(); + std::vector nodes = { + ByteData256("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"), + ByteData256("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54") + }; + TaprootScriptTree tree(redeem_script); + tree.AddBranch(nodes[0]); + tree.AddBranch(SchnorrPubkey(nodes[1])); + // auto pk = tree.GetTweakedPubkey(schnorr_pubkey); + // Script locking_script = ScriptUtil::CreateTaprootLockingScript( + // pk.GetByteData256()); + Script locking_script; + SchnorrPubkey pk0; + auto taproot_control = TaprootUtil::CreateTapScriptControl( + schnorr_pubkey, tree, &pk0, &locking_script); + Address addr01(NetType::kRegtest, WitnessVersion::kVersion1, pk0); + EXPECT_EQ("bcrt1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fq50z666", addr01.GetAddress()); + EXPECT_EQ(locking_script.GetHex(), addr01.GetLockingScript().GetHex()); + EXPECT_EQ("51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", addr01.GetLockingScript().GetHex()); + + Transaction tx1(2, 0); + tx1.AddTxIn( + Txid("cd6adc252632eb0768ac6407e586cc74bfed739d6c8b9efa55305eb37cbd76dd"), + 0, 0xffffffff); // bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40 + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(amt1, locking_script); + EXPECT_EQ("0200000001dd76bd7cb35e3055fa9e8b6c9d73edbf74cc86e50764ac6807eb322625dc6acd0000000000ffffffff0118f50295000000002251203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d200000000", tx1.GetHex()); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + Pubkey pk1("023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44"); + auto pkh_script1 = ScriptUtil::CreateP2pkhLockingScript(pk1); + SigHashType sighash_type; + auto sighash = tx1.GetSignatureHash(0, pkh_script1.GetData(), + sighash_type, Amount(int64_t{2500000000}), WitnessVersion::kVersion0); + auto sig = key1.CalculateEcSignature(sighash); + auto der_sig = CryptoUtil::ConvertSignatureToDer(sig, sighash_type); + tx1.AddScriptWitnessStack(0, der_sig); + tx1.AddScriptWitnessStack(0, pk1.GetData()); + EXPECT_EQ("02000000000101dd76bd7cb35e3055fa9e8b6c9d73edbf74cc86e50764ac6807eb322625dc6acd0000000000ffffffff0118f50295000000002251203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d20247304402201db912bc61dab1c6117b0aec2965ea1b2d1caa42a1372adc16c8cf673f1187d7022062667d8a976b197f7ba33299365eeb68c1e45fa2a255411672d89f7afab12cb20121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + Transaction tx2(2, 0); + tx2.AddTxIn(tx1.GetTxid(), 0, 0xffffffff); // taproot + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(Amount(int64_t{2499998000}), addr2.GetLockingScript()); + std::vector utxo_list(1); + utxo_list[0] = TxOut(amt1, locking_script); + TapScriptData script_data; + script_data.tap_leaf_hash = tree.GetTapLeafHash(); + auto sighash2 = tx2.GetSchnorrSignatureHash(0, sighash_type, utxo_list, &script_data); + EXPECT_EQ("80e53eaee13048aee9c6c13fa5a8529aad7fe2c362bfc16f1e2affc71f591d36", sighash2.GetHex()); + auto sig2 = SchnorrUtil::Sign(sighash2, key); + EXPECT_EQ("f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee9", sig2.GetHex()); + SchnorrSignature schnorr_sig(sig2); + schnorr_sig.SetSigHashType(sighash_type); + tx2.AddScriptWitnessStack(0, schnorr_sig.GetData(true)); + tx2.AddScriptWitnessStack(0, redeem_script.GetData()); + tx2.AddScriptWitnessStack(0, taproot_control); + EXPECT_EQ("020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000", tx2.GetHex()); + + EXPECT_TRUE(schnorr_pubkey.Verify(schnorr_sig, sighash2)); +} + +TEST(TaprootUtil, CreateTapScriptControlParityBit) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + ScriptBuilder builder; + builder.AppendData(schnorr_pubkey.GetData()); + builder.AppendOperator(ScriptOperator::OP_CHECKSIG); + Script redeem_script = builder.Build(); + std::vector nodes = { + ByteData256("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d"), + ByteData256("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d57") + }; + TaprootScriptTree tree(redeem_script); + tree.AddBranch(nodes[0]); + tree.AddBranch(SchnorrPubkey(nodes[1])); + // auto pk = tree.GetTweakedPubkey(schnorr_pubkey); + // Script locking_script = ScriptUtil::CreateTaprootLockingScript( + // pk.GetByteData256()); + Script locking_script; + auto taproot_control = TaprootUtil::CreateTapScriptControl( + schnorr_pubkey, tree, nullptr, &locking_script); + + Transaction tx1(2, 0); + tx1.AddTxIn( + Txid("fee03a31ddbe8f8af75f9ccea23d2d49c27538b1c183aa90c4e35529161a78df"), + 0, 0xffffffff); // bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40 + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(amt1, locking_script); + EXPECT_EQ("0200000001df781a162955e3c490aa83c1b13875c2492d3da2ce9c5ff78a8fbedd313ae0fe0000000000ffffffff0118f5029500000000225120262d16c95b41f6a90a360837b5e9c3e213334deacffaec0413f8b6e98ad4016500000000", tx1.GetHex()); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + Pubkey pk1("023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44"); + auto pkh_script1 = ScriptUtil::CreateP2pkhLockingScript(pk1); + SigHashType sighash_type; + auto sighash = tx1.GetSignatureHash(0, pkh_script1.GetData(), + sighash_type, Amount(int64_t{2500000000}), WitnessVersion::kVersion0); + auto sig = key1.CalculateEcSignature(sighash); + auto der_sig = CryptoUtil::ConvertSignatureToDer(sig, sighash_type); + tx1.AddScriptWitnessStack(0, der_sig); + tx1.AddScriptWitnessStack(0, pk1.GetData()); + EXPECT_EQ("02000000000101df781a162955e3c490aa83c1b13875c2492d3da2ce9c5ff78a8fbedd313ae0fe0000000000ffffffff0118f5029500000000225120262d16c95b41f6a90a360837b5e9c3e213334deacffaec0413f8b6e98ad4016502473044022068e673ac6db21d612864f432c5cfb64f3652e37be27de412c62dd6127ad63ce1022028507107481ad4fe97da3402eeff0540b0cc1677f9a53e87a7facf07c2696e500121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + Transaction tx2(2, 0); + tx2.AddTxIn(tx1.GetTxid(), 0, 0xffffffff); // taproot + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(Amount(int64_t{2499998000}), addr2.GetLockingScript()); + std::vector utxo_list(1); + utxo_list[0] = TxOut(amt1, locking_script); + TapScriptData script_data; + script_data.tap_leaf_hash = tree.GetTapLeafHash(); + auto sighash2 = tx2.GetSchnorrSignatureHash(0, sighash_type, utxo_list, &script_data); + EXPECT_EQ("194c654c0547d805c158711fcf96ed9bc4afbd48556a0632cd2b18ec94c3f773", sighash2.GetHex()); + auto sig2 = SchnorrUtil::Sign(sighash2, key); + EXPECT_EQ("bf55a5d15cc7dd2b583f571db0d59be9b1838a81191ad0e057caf9670d1b7de599864d6bd5e68bd56bb6da4d44e1dfd2deec3a03792c066613c4f6560d4876e0", sig2.GetHex()); + SchnorrSignature schnorr_sig(sig2); + schnorr_sig.SetSigHashType(sighash_type); + tx2.AddScriptWitnessStack(0, schnorr_sig.GetData(true)); + tx2.AddScriptWitnessStack(0, redeem_script.GetData()); + tx2.AddScriptWitnessStack(0, taproot_control); + EXPECT_EQ("020000000001010513391eb3cb6529b86485dbee924a070a7e556c084ed6f0ff338d7a80335c450000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341bf55a5d15cc7dd2b583f571db0d59be9b1838a81191ad0e057caf9670d1b7de599864d6bd5e68bd56bb6da4d44e1dfd2deec3a03792c066613c4f6560d4876e00122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c11777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5700000000", tx2.GetHex()); + + EXPECT_TRUE(schnorr_pubkey.Verify(schnorr_sig, sighash2)); +} + +TEST(TaprootUtil, ParseTaprootSignDataByPubkey) { + Transaction tx2("0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000"); + auto stack = tx2.GetTxIn(0).GetScriptWitness().GetWitness(); + SchnorrSignature sig; + bool parity = false; + uint8_t tapleaf_flag = 0; + SchnorrPubkey pk; + std::vector nodes; + Script tapscript; + std::vector script_stack; + ByteData annex; + TaprootUtil::ParseTaprootSignData(stack, &sig, &parity, &tapleaf_flag, + &pk, &nodes, &tapscript, &script_stack, &annex); + EXPECT_EQ("61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f2087201", sig.GetHex(true)); + EXPECT_FALSE(parity); + EXPECT_EQ(0, tapleaf_flag); + EXPECT_FALSE(pk.IsValid()); + EXPECT_TRUE(nodes.empty()); + EXPECT_TRUE(tapscript.IsEmpty()); + EXPECT_TRUE(script_stack.empty()); + EXPECT_TRUE(annex.IsEmpty()); +} + +TEST(TaprootUtil, ParseAndVerifyTapScript) { + Transaction tx2("020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000"); + auto stack = tx2.GetTxIn(0).GetScriptWitness().GetWitness(); + SchnorrSignature sig; + bool parity = false; + uint8_t tapleaf_flag = 0; + SchnorrPubkey pk; + std::vector nodes; + Script tapscript; + std::vector script_stack; + ByteData annex; + TaprootUtil::ParseTaprootSignData(stack, &sig, &parity, &tapleaf_flag, + &pk, &nodes, &tapscript, &script_stack, &annex); + EXPECT_EQ("", sig.GetHex()); + EXPECT_FALSE(parity); + uint8_t ver = TaprootScriptTree::kTapScriptLeafVersion; + EXPECT_EQ(ver, tapleaf_flag); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", pk.GetHex()); + EXPECT_EQ(2, nodes.size()); + if (nodes.size() == 2) { + EXPECT_EQ("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + nodes[0].GetHex()); + EXPECT_EQ("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + nodes[1].GetHex()); + } + EXPECT_EQ("201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + tapscript.GetHex()); + EXPECT_EQ(1, script_stack.size()); + if (script_stack.size() == 1) { + EXPECT_EQ( + "f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee901", + script_stack[0].GetHex()); + } + EXPECT_TRUE(annex.IsEmpty()); + + EXPECT_TRUE(TaprootUtil::VerifyTaprootCommitment( + parity, tapleaf_flag, + SchnorrPubkey("3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2"), + pk, nodes, tapscript)); +} + +TEST(TaprootUtil, ParseAndVerifyTapScriptParityBit) { + Transaction tx2("020000000001010513391eb3cb6529b86485dbee924a070a7e556c084ed6f0ff338d7a80335c450000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341bf55a5d15cc7dd2b583f571db0d59be9b1838a81191ad0e057caf9670d1b7de599864d6bd5e68bd56bb6da4d44e1dfd2deec3a03792c066613c4f6560d4876e00122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c11777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5700000000"); + auto stack = tx2.GetTxIn(0).GetScriptWitness().GetWitness(); + SchnorrSignature sig; + bool parity = false; + uint8_t tapleaf_flag = 0; + SchnorrPubkey pk; + std::vector nodes; + Script tapscript; + std::vector script_stack; + ByteData annex; + TaprootUtil::ParseTaprootSignData(stack, &sig, &parity, &tapleaf_flag, + &pk, &nodes, &tapscript, &script_stack, &annex); + EXPECT_EQ("", sig.GetHex()); + EXPECT_TRUE(parity); + uint8_t ver = TaprootScriptTree::kTapScriptLeafVersion; + EXPECT_EQ(ver, tapleaf_flag); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", pk.GetHex()); + EXPECT_EQ(2, nodes.size()); + if (nodes.size() == 2) { + EXPECT_EQ("4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + nodes[0].GetHex()); + EXPECT_EQ("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d57", + nodes[1].GetHex()); + } + EXPECT_EQ("201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + tapscript.GetHex()); + EXPECT_EQ(1, script_stack.size()); + if (script_stack.size() == 1) { + EXPECT_EQ( + "bf55a5d15cc7dd2b583f571db0d59be9b1838a81191ad0e057caf9670d1b7de599864d6bd5e68bd56bb6da4d44e1dfd2deec3a03792c066613c4f6560d4876e001", + script_stack[0].GetHex()); + } + EXPECT_TRUE(annex.IsEmpty()); + + EXPECT_TRUE(TaprootUtil::VerifyTaprootCommitment( + parity, tapleaf_flag, + SchnorrPubkey("262d16c95b41f6a90a360837b5e9c3e213334deacffaec0413f8b6e98ad40165"), + pk, nodes, tapscript)); +} diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 99d1ea30..9d782b25 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -9,20 +9,34 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_script.h" #include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_util.h" using cfd::core::AbstractTransaction; +using cfd::core::Address; using cfd::core::Amount; using cfd::core::ByteData; using cfd::core::ByteData160; using cfd::core::ByteData256; using cfd::core::CfdException; +using cfd::core::CryptoUtil; using cfd::core::HashType; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::SchnorrSignature; +using cfd::core::SchnorrUtil; using cfd::core::Script; +using cfd::core::ScriptBuilder; +using cfd::core::ScriptOperator; +using cfd::core::ScriptUtil; using cfd::core::SigHashAlgorithm; using cfd::core::SigHashType; using cfd::core::Transaction; using cfd::core::Txid; using cfd::core::TxInReference; +using cfd::core::TxOut; using cfd::core::TxOutReference; using cfd::core::WitnessVersion; @@ -452,3 +466,108 @@ TEST(Transaction, CheckTxOutBuffer) { EXPECT_EQ(tx.GetTxInCount(), 0); EXPECT_EQ(tx.GetTxOutCount(), 1); } + +TEST(Transaction, GetSchnorrSignatureHash) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + Script locking_script = (ScriptBuilder() << ScriptOperator::OP_1 + << schnorr_pubkey.GetData()).Build(); + + Transaction tx1(2, 0); + tx1.AddTxIn( + Txid("1f9866dc0a19c427347c2db0b5910bdc2c20b78fa9f74f8756b21db890dba8ff"), + 0, 0xffffffff); // bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40 + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(amt1, locking_script); + EXPECT_EQ("0200000001ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", tx1.GetHex()); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + Pubkey pk1("023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44"); + auto pkh_script1 = ScriptUtil::CreateP2pkhLockingScript(pk1); + SigHashType sighash_type; + auto sighash = tx1.GetSignatureHash(0, pkh_script1.GetData(), + sighash_type, Amount(int64_t{2500000000}), WitnessVersion::kVersion0); + auto sig = key1.CalculateEcSignature(sighash); + auto der_sig = CryptoUtil::ConvertSignatureToDer(sig, sighash_type); + tx1.AddScriptWitnessStack(0, der_sig); + tx1.AddScriptWitnessStack(0, pk1.GetData()); + EXPECT_EQ("02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + Transaction tx2(2, 0); + tx2.AddTxIn( + Txid("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"), + 0, 0xffffffff); // taproot + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(Amount(int64_t{2499998000}), addr2.GetLockingScript()); + std::vector utxo_list(1); + TxOut utxo(amt1, locking_script); + utxo_list[0] = utxo; + auto sighash2 = tx2.GetSchnorrSignatureHash(0, sighash_type, utxo_list); + EXPECT_EQ("e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02", sighash2.GetHex()); + auto sig2 = SchnorrUtil::Sign(sighash2, key); + EXPECT_EQ("61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f20872", sig2.GetHex()); + SchnorrSignature schnorr_sig(sig2); + schnorr_sig.SetSigHashType(sighash_type); + tx2.AddScriptWitnessStack(0, schnorr_sig.GetData(true)); + EXPECT_EQ("0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000", tx2.GetHex()); + + EXPECT_TRUE(schnorr_pubkey.Verify(schnorr_sig, sighash2)); +} + +TEST(Transaction, GetSchnorrSignatureHashNonce) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + Script locking_script = (ScriptBuilder() << ScriptOperator::OP_1 + << schnorr_pubkey.GetData()).Build(); + + Transaction tx1(2, 0); + tx1.AddTxIn( + Txid("1f9866dc0a19c427347c2db0b5910bdc2c20b78fa9f74f8756b21db890dba8ff"), + 0, 0xffffffff); // bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40 + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(amt1, locking_script); + EXPECT_EQ("0200000001ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", tx1.GetHex()); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + Pubkey pk1("023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44"); + auto pkh_script1 = ScriptUtil::CreateP2pkhLockingScript(pk1); + SigHashType sighash_type; + auto sighash = tx1.GetSignatureHash(0, pkh_script1.GetData(), + sighash_type, Amount(int64_t{2500000000}), WitnessVersion::kVersion0); + auto sig = key1.CalculateEcSignature(sighash); + auto der_sig = CryptoUtil::ConvertSignatureToDer(sig, sighash_type); + tx1.AddScriptWitnessStack(0, der_sig); + tx1.AddScriptWitnessStack(0, pk1.GetData()); + EXPECT_EQ("02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + Transaction tx2(2, 0); + tx2.AddTxIn( + Txid("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"), + 0, 0xffffffff); // taproot + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(Amount(int64_t{2499998000}), addr2.GetLockingScript()); + std::vector utxo_list(1); + TxOut utxo(amt1, locking_script); + utxo_list[0] = utxo; + auto sighash2 = tx2.GetSchnorrSignatureHash(0, sighash_type, utxo_list); + EXPECT_EQ("e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02", sighash2.GetHex()); + ByteData256 nonce("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"); + auto sig2 = SchnorrUtil::Sign(sighash2, key, nonce); + EXPECT_EQ("51df55894d1a024c244e20ecedc39cae39fa6d43653305b7f32605eea6359415a7ceef44c52a2f26be2e06d33d79c2e90b5dfaebcb4f79e242134121e0b9579e", sig2.GetHex()); + SchnorrSignature schnorr_sig(sig2); + schnorr_sig.SetSigHashType(sighash_type); + tx2.AddScriptWitnessStack(0, schnorr_sig.GetData(true)); + EXPECT_EQ("0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014151df55894d1a024c244e20ecedc39cae39fa6d43653305b7f32605eea6359415a7ceef44c52a2f26be2e06d33d79c2e90b5dfaebcb4f79e242134121e0b9579e0100000000", tx2.GetHex()); + + EXPECT_TRUE(schnorr_pubkey.Verify(schnorr_sig, sighash2)); +} diff --git a/test/test_txin_txinreference.cpp b/test/test_txin_txinreference.cpp index e8285eb3..45fd6a17 100644 --- a/test/test_txin_txinreference.cpp +++ b/test/test_txin_txinreference.cpp @@ -48,12 +48,12 @@ TEST(TxIn, Constractor) { TEST(TxIn, EstimateTxInSize) { static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 149, 0, Script()}, - {AddressType::kP2shAddress, 207, 0, exp_script}, - {AddressType::kP2shP2wpkhAddress, 171, 108, Script()}, - {AddressType::kP2shP2wshAddress, 217, 142, Script("51")}, - {AddressType::kP2wpkhAddress, 149, 108, Script()}, - {AddressType::kP2wshAddress, 207, 166, exp_script}, + {AddressType::kP2pkhAddress, 150, 0, Script()}, + {AddressType::kP2shAddress, 208, 0, exp_script}, + {AddressType::kP2shP2wpkhAddress, 173, 109, Script()}, + {AddressType::kP2shP2wshAddress, 219, 143, Script("51")}, + {AddressType::kP2wpkhAddress, 150, 109, Script()}, + {AddressType::kP2wshAddress, 208, 167, exp_script}, }; for (const auto& test_data : test_vector) { @@ -68,11 +68,11 @@ TEST(TxIn, EstimateTxInSize) { TEST(TxIn, EstimateTxInVsize) { static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 149, 0, Script()}, - {AddressType::kP2shAddress, 207, 0, exp_script}, - {AddressType::kP2shP2wpkhAddress, 90, 0, Script()}, - {AddressType::kP2shP2wshAddress, 111, 0, Script("51")}, - {AddressType::kP2wpkhAddress, 68, 0, Script()}, + {AddressType::kP2pkhAddress, 150, 0, Script()}, + {AddressType::kP2shAddress, 208, 0, exp_script}, + {AddressType::kP2shP2wpkhAddress, 92, 0, Script()}, + {AddressType::kP2shP2wshAddress, 112, 0, Script("51")}, + {AddressType::kP2wpkhAddress, 69, 0, Script()}, {AddressType::kP2wshAddress, 83, 0, exp_script}, }; diff --git a/tools/create_opsuccess.js b/tools/create_opsuccess.js new file mode 100644 index 00000000..a0ba03c2 --- /dev/null +++ b/tools/create_opsuccess.js @@ -0,0 +1,47 @@ + +function addListRange(list, start, end) { + let index = start; + while (index <= end) { + list.push(index); + index += 1; + } +} + + +function dumpScriptType(list) { + for (const target of list) { + const hex = target.toString(16) + console.log(` kOpSuccess${target} = 0x${hex},` + + ` //!< kOpSuccess${target} (BIP-342)`); + } +} + +function dumpScriptOperatorDefine(list) { + for (const target of list) { + console.log(`static const ScriptOperator ` + + `OP_SUCCESS${target}; //!< OP_SUCCESS${target} (BIP-342)`); + } +} + +function dumpScriptOperatorImpl(list) { + for (const target of list) { + console.log(`const ScriptOperator ScriptOperator::` + + `OP_SUCCESS${target}(kOpSuccess${target}, "OP_SUCCESS${target}");`); + } +} + +const main = function() { + const targetList = [80, 98]; + addListRange(targetList, 126, 129); + addListRange(targetList, 131, 134); + addListRange(targetList, 137, 138); + addListRange(targetList, 141, 142); + addListRange(targetList, 149, 153); + addListRange(targetList, 187, 254); + + dumpScriptType(targetList); + dumpScriptOperatorDefine(targetList); + dumpScriptOperatorImpl(targetList); +}; + +main(); diff --git a/tools/format.bat b/tools/format.bat new file mode 100644 index 00000000..ed70a8c3 --- /dev/null +++ b/tools/format.bat @@ -0,0 +1,7 @@ +@echo off + +if exist "format.bat" ( + cd .. +) + +call clang-format -i --style=file src/*.cpp src/*.h src/include/cfdcore/*.h include/cfdcore/*.h diff --git a/tools/format.sh b/tools/format.sh new file mode 100755 index 00000000..34a861fa --- /dev/null +++ b/tools/format.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd `git rev-parse --show-toplevel` + +clang-format -i --style=file src/*.cpp src/*.h src/include/cfdcore/*.h include/cfdcore/*.h