From bcbf7454a432eed9329f14ed767832bd7eea0268 Mon Sep 17 00:00:00 2001 From: "K.Matsuzawa" <49175372+k-matsuzawa@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:24:55 +0900 Subject: [PATCH] update to v0.3.0 (#26) update from cryptogarageinc v0.3.9 --- .github/workflows/check_pre-merge_master.yml | 3 +- .github/workflows/check_pre-merge_sprint.yml | 38 +- .github/workflows/code_scanner.yml | 5 - .../workflows/create_release-and-upload.yml | 68 +- CMakeLists.txt | 3 + README.md | 63 +- cmake/CfdCommonOption.cmake | 1 + cmake/CfdWallyOption.cmake | 3 +- cmake/Cpp11Setting.cmake | 5 + external/CMakeLists.txt | 2 +- include/cfd/cfd_address.h | 171 +- include/cfd/cfd_common.h | 20 +- include/cfd/cfd_elements_address.h | 70 +- include/cfd/cfd_elements_transaction.h | 267 +- include/cfd/cfd_fee.h | 18 +- include/cfd/cfd_json_mapping_api.h | 27 +- include/cfd/cfd_psbt.h | 568 ++ include/cfd/cfd_transaction.h | 249 +- include/cfd/cfd_transaction_common.h | 150 +- include/cfd/cfd_utxo.h | 269 +- include/cfd/cfdapi_address.h | 30 +- include/cfd/cfdapi_coin.h | 1 + include/cfdc/cfdcapi_address.h | 16 +- include/cfdc/cfdcapi_common.h | 134 +- include/cfdc/cfdcapi_key.h | 39 + include/cfdc/cfdcapi_psbt.h | 762 ++ include/cfdc/cfdcapi_script.h | 224 + include/cfdc/cfdcapi_transaction.h | 183 +- package.json | 18 +- src/CMakeLists.txt | 8 + src/Makefile.srclist | 3 + src/capi/cfdc_internal.h | 5 +- src/capi/cfdcapi_address.cpp | 101 +- src/capi/cfdcapi_common.cpp | 383 +- src/capi/cfdcapi_elements_address.cpp | 3 +- src/capi/cfdcapi_elements_transaction.cpp | 16 +- src/capi/cfdcapi_key.cpp | 119 +- src/capi/cfdcapi_psbt.cpp | 2753 ++++++ src/capi/cfdcapi_script.cpp | 768 +- src/capi/cfdcapi_transaction.cpp | 788 +- src/cfd_address.cpp | 360 +- src/cfd_elements_address.cpp | 9 +- src/cfd_elements_transaction.cpp | 91 +- src/cfd_psbt.cpp | 933 ++ src/cfd_transaction.cpp | 324 +- src/cfd_transaction_common.cpp | 145 +- src/cfd_transaction_internal.cpp | 104 +- src/cfd_transaction_internal.h | 5 +- src/cfd_utxo.cpp | 87 +- src/cfdapi_address.cpp | 23 +- src/cfdapi_coin.cpp | 2 +- src/cfdapi_elements_transaction.cpp | 33 +- src/cfdapi_transaction.cpp | 25 +- src/jsonapi/autogen/cfd_api_json_autogen.cpp | 1498 +++- src/jsonapi/autogen/cfd_api_json_autogen.h | 7688 ++++++++++++----- src/jsonapi/cfd_json_mapping_api.cpp | 7 + src/jsonapi/cfd_json_psbt.cpp | 366 + src/jsonapi/cfd_json_psbt.h | 37 + src/jsonapi/cfd_json_transaction.cpp | 134 +- src/jsonapi/cfd_json_transaction.h | 30 +- src/jsonapi/cfd_struct.h | 314 +- .../input_json_format/cfdapi_decode_psbt.json | 502 ++ .../cfdapi_decode_transaction.json | 70 +- test/Makefile.srclist | 2 + test/capi/test_cfdcapi_address.cpp | 72 +- test/capi/test_cfdcapi_coin.cpp | 180 +- test/capi/test_cfdcapi_common.cpp | 372 + test/capi/test_cfdcapi_key.cpp | 111 + test/capi/test_cfdcapi_psbt.cpp | 1469 ++++ test/capi/test_cfdcapi_script.cpp | 543 ++ test/capi/test_cfdcapi_transaction.cpp | 313 +- test/test_cfd_address.cpp | 260 +- test/test_cfd_coin_selection.cpp | 20 +- test/test_cfd_confidentialtx_context.cpp | 2 +- test/test_cfd_psbt.cpp | 1485 ++++ test/test_cfd_transaction_context.cpp | 215 +- test/test_cfdapi_address.cpp | 2 +- test/test_cfdapi_elements_transaction.cpp | 106 +- test/test_cfdapi_transaction.cpp | 46 +- tools/format.bat | 7 + tools/format.sh | 4 + tools/generate_json_map_class.ts | 2176 +++++ 82 files changed, 25020 insertions(+), 3506 deletions(-) create mode 100644 include/cfd/cfd_psbt.h create mode 100644 include/cfdc/cfdcapi_psbt.h create mode 100644 src/capi/cfdcapi_psbt.cpp create mode 100644 src/cfd_psbt.cpp create mode 100644 src/jsonapi/cfd_json_psbt.cpp create mode 100644 src/jsonapi/cfd_json_psbt.h create mode 100644 src/jsonapi/input_json_format/cfdapi_decode_psbt.json create mode 100644 test/capi/test_cfdcapi_psbt.cpp create mode 100644 test/test_cfd_psbt.cpp create mode 100644 tools/format.bat create mode 100755 tools/format.sh create mode 100644 tools/generate_json_map_class.ts 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 4a429742..e30c20f9 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 @@ -99,6 +100,41 @@ jobs: name: output-lcov-cfd-${{ matrix.os }} path: ./build/lcov_cfd_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/cfd_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/.github/workflows/create_release-and-upload.yml b/.github/workflows/create_release-and-upload.yml index 35ebc8ba..a61b25ee 100644 --- a/.github/workflows/create_release-and-upload.yml +++ b/.github/workflows/create_release-and-upload.yml @@ -168,10 +168,14 @@ jobs: asset_name: cfd-${{ steps.get_version.outputs.VERSION }}-ubuntu${{ matrix.os_ver }}04-${{ matrix.bin }}-x86_64.zip asset_content_type: application/zip - upload-object-alpine-3-10: - name: upload-object-alpine-3.10 + upload-object-alpine: + name: upload-object-alpine needs: create_releases runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + alpine: ['3.10', '3.12', '3.13'] steps: - name: checkout @@ -181,57 +185,21 @@ jobs: run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - name: list run: ls -a $GITHUB_WORKSPACE - - name: docker setup + - name: docker setup 3.10 + if: matrix.alpine == '3.10' uses: docker://alpine:3.10 with: entrypoint: /github/workspace/.github/workflows/docker/alpine_build_entrypoint.sh - - name: create archive file - run: | - echo "---- dump output data ----" - ls -l $GITHUB_WORKSPACE/dist/usr/local/* - mkdir -p /tmp/cfd - sudo chmod 777 /tmp/cfd - sudo chown runner /tmp/cfd - sudo cp -r $GITHUB_WORKSPACE/dist/usr /tmp/cfd - cd /tmp/cfd - sudo zip -r /tmp/cfd/cfd.zip usr - sudo chmod 777 /tmp/cfd/cfd.zip - sudo chown runner /tmp/cfd/cfd.zip - sudo cp -rp /tmp/cfd/cfd.zip $GITHUB_WORKSPACE/dist/cfd.zip - echo "---- dump zip file ----" - sudo ls -l /tmp/cfd - - name: output url - id: get_url - run: echo "::set-output name=upload_url::${{ needs.create_releases.outputs.release_url }}" - - name: Upload Release Asset - id: upload-release-asset -# if: success() - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.get_url.outputs.upload_url }} - asset_path: /tmp/cfd/cfd.zip - asset_name: cfd-${{ steps.get_version.outputs.VERSION }}-alpine-3.10-x86_64.zip - asset_content_type: application/zip - - upload-object-alpine-3-12: - name: upload-object-alpine-3.12 - needs: create_releases - runs-on: ubuntu-18.04 - - steps: - - name: checkout - uses: actions/checkout@v2 - - name: Get the version - id: get_version - run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - name: list - run: ls -a $GITHUB_WORKSPACE - - name: docker setup + - name: docker setup 3.12 + if: matrix.alpine == '3.12' uses: docker://alpine:3.12 with: entrypoint: /github/workspace/.github/workflows/docker/alpine_build_entrypoint.sh + - name: docker setup 3.13 + if: matrix.alpine == '3.13' + uses: docker://alpine:3.13 + with: + entrypoint: /github/workspace/.github/workflows/docker/alpine_build_entrypoint.sh - name: create archive file run: | echo "---- dump output data ----" @@ -259,7 +227,7 @@ jobs: with: upload_url: ${{ steps.get_url.outputs.upload_url }} asset_path: /tmp/cfd/cfd.zip - asset_name: cfd-${{ steps.get_version.outputs.VERSION }}-alpine-3.12-x86_64.zip + asset_name: cfd-${{ steps.get_version.outputs.VERSION }}-alpine-${{ matrix.alpine }}-x86_64.zip asset_content_type: application/zip upload-object-macos: @@ -269,7 +237,7 @@ jobs: strategy: fail-fast: false matrix: - xcode: [10.3, 11.6] + xcode: [10.3, 11.7, 12.4] shared: [on, off] include: - shared: on @@ -279,7 +247,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Get the version id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} diff --git a/CMakeLists.txt b/CMakeLists.txt index 804430aa..3bded8ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ option(CFD_SHARED "force shared build (ON or OFF. default:OFF)" OFF) option(ENABLE_CAPI "enable c-api (ON or OFF. default:ON)" ON) option(ENABLE_JSONAPI "enable json-api (ON or OFF. default:ON)" ON) +set(GENERATE_WALLY ON CACHE BOOL "" FORCE) +set(EXCLUDE_WALLYCORE_LIB ON CACHE BOOL "" FORCE) + #################### # common setting #################### diff --git a/README.md b/README.md index 9c3acb00..731232b1 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,53 @@ # Crypto Finance Development Kit (CFD) -cfd library in C/C++ - - +This library is development kit for crypto finance application. +Useful when developing applications for cryptocurrencies. + +### Target Network + +- Bitcoin +- Liquid Network + +### Support function by cfd + +- Estimate Fee +- Coin Selection (FundRawTransaction) +- Simple pubkey-hash sign / verify +- C language API (for reference from other languages) +- from cfd-core function: + - 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 @@ -20,7 +61,8 @@ cfd library in C/C++ ### Windows -download and install files. +download and install files: + - [CMake](https://cmake.org/) (3.14.3 or higher) - Compiler or development environment (One of the following) - MSVC @@ -153,14 +195,14 @@ cmake version is 3.15 or higher: `cmake --install build` sudo ./tools/cleanup_install_files.sh (download) -wget https://github.com/p2pderivatives/cfd/releases/download/v0.1.5/cfd-v0.1.5-ubuntu1804-gcc-x86_64.zip +wget https://github.com/p2pderivatives/cfd/releases/download/v0.3.0/cfd-v0.3.0-ubuntu2004-gcc-x86_64.zip (unzip) -sudo unzip -q cfd-v0.1.5-ubuntu1804-gcc-x86_64.zip -d / +sudo unzip -q cfd-v0.3.0-ubuntu2004-gcc-x86_64.zip -d / ``` - Windows - 1. get releases asset. (ex. https://github.com/p2pderivatives/cfd/releases/download/v0.1.5/cfd-v0.1.5-win-vs2019-x86_64.zip ) + 1. get releases asset. (ex. https://github.com/p2pderivatives/cfd/releases/download/v0.3.0/cfd-v0.3.0-win-vs2019-x86_64.zip ) 2. Expand to PATH ### uninstall @@ -197,6 +239,7 @@ npm run ctest - cfd-core - [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) @@ -275,3 +318,7 @@ set CFD_CMAKE_GIT_SKIP_UPDATE=1 ``` export CFD_CMAKE_GIT_SKIP_UPDATE=1 ``` + +### Visula C++ & Debug build: + +When debugging build with Visual C++, std::map related may not work properly. 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 d789343d..13a7f032 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -50,7 +50,7 @@ if(CFDCORE_TARGET_VERSION) set(CFDCORE_TARGET_TAG ${CFDCORE_TARGET_VERSION}) message(STATUS "[external project local] cfd-core target=${CFDCORE_TARGET_VERSION}") else() -set(CFDCORE_TARGET_TAG v0.2.2) +set(CFDCORE_TARGET_TAG v0.3.0) endif() if(CFDCORE_TARGET_URL) set(CFDCORE_TARGET_REP ${CFDCORE_TARGET_URL}) diff --git a/include/cfd/cfd_address.h b/include/cfd/cfd_address.h index 3ef5efe6..efd2a412 100644 --- a/include/cfd/cfd_address.h +++ b/include/cfd/cfd_address.h @@ -2,7 +2,7 @@ /** * @file cfd_address.h * - * @brief Address操作の関連クラス定義 + * @brief Related class definition for Address operation */ #ifndef CFD_INCLUDE_CFD_CFD_ADDRESS_H_ #define CFD_INCLUDE_CFD_CFD_ADDRESS_H_ @@ -13,8 +13,11 @@ #include "cfd/cfd_common.h" #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_descriptor.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" namespace cfd { @@ -23,36 +26,65 @@ using cfd::core::AddressFormatData; using cfd::core::AddressType; using cfd::core::ByteData; using cfd::core::ByteData160; +using cfd::core::ByteData256; +using cfd::core::DescriptorKeyType; +using cfd::core::DescriptorScriptType; +using cfd::core::KeyData; using cfd::core::NetType; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; using cfd::core::Script; +using cfd::core::TaprootScriptTree; using cfd::core::WitnessVersion; /** - * @brief Addressを生成するFactoryクラス + * @brief Descriptor Script information structure + */ +struct DescriptorScriptData { + DescriptorScriptType type; //!< script type + Script locking_script; //!< locking script + uint32_t depth; //!< depth + Address address; //!< address + AddressType address_type; //!< address type + Script redeem_script; //!< redeem script + DescriptorKeyType key_type; //!< key type + std::string key; //!< key string + uint32_t multisig_req_sig_num; //!< multisig num of require signatures +}; + +/** + * @brief Descriptor Key information structure + */ +struct DescriptorKeyData { + DescriptorKeyType type; //!< key type + std::string key; //!< key string +}; + +/** + * @brief Factory class that generates Address */ class CFD_EXPORT AddressFactory { public: /** - * @brief コンストラクタ. + * @brief Constructor. */ AddressFactory(); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type */ explicit AddressFactory(NetType type); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] wit_ver witness version */ explicit AddressFactory(NetType type, WitnessVersion wit_ver); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] wit_ver witness version * @param[in] prefix_list address prefix list @@ -62,7 +94,7 @@ class CFD_EXPORT AddressFactory { const std::vector& prefix_list); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] prefix_list address prefix list */ @@ -70,28 +102,28 @@ class CFD_EXPORT AddressFactory { NetType type, const std::vector& prefix_list); /** - * @brief デストラクタ. + * @brief Destructor. */ virtual ~AddressFactory() { // do nothing } /** - * @brief アドレスを作成する - * @param[in] address_string address文字列 + * @brief Create an address + * @param[in] address_string address string * @return address */ Address GetAddress(const std::string& address_string) const; /** - * @brief LockingScriptからアドレスを作成する + * @brief Create an address from locking script * @param[in] locking_script locking script * @return address */ Address GetAddressByLockingScript(const Script& locking_script) const; /** - * @brief Hash情報からアドレスを作成する + * @brief Create an address from hash data * @param[in] address_type address type * @param[in] hash hash data * @return address @@ -99,7 +131,7 @@ class CFD_EXPORT AddressFactory { Address GetAddressByHash( AddressType address_type, const ByteData& hash) const; /** - * @brief Hash情報からアドレスを作成する + * @brief Create an address from hash data * @param[in] address_type address type * @param[in] hash hash data * @return address @@ -108,49 +140,82 @@ class CFD_EXPORT AddressFactory { AddressType address_type, const ByteData160& hash) const; /** - * @brief Hash情報から segwit native アドレスを作成する + * @brief Create a segwit native address from hash data. * @param[in] hash hash data * @return address */ Address GetSegwitAddressByHash(const ByteData& hash) const; /** - * @brief P2PKHアドレスを生成する. - * @param[in] pubkey Pubkeyインスタンス - * @return P2PKHアドレスのAddressインスタンス + * @brief Create a segwit native address from hash data. + * @param[in] hash hash data + * @param[in] version witness version + * @return address + */ + Address GetSegwitAddressByHash( + const ByteData& hash, WitnessVersion version) const; + + /** + * @brief Create a P2PKH address. + * @param[in] pubkey Pubkey + * @return address */ Address CreateP2pkhAddress(const Pubkey& pubkey) const; /** - * @brief P2SHのアドレスを生成する. - * @param[in] script Scriptインスタンス - * @return P2SHアドレスのAddressインスタンス + * @brief Create a P2SH address. + * @param[in] script Redeem script + * @return address */ Address CreateP2shAddress(const Script& script) const; /** - * @brief P2WPKHのアドレスを生成する. - * @param[in] pubkey Pubkeyインスタンス - * @return P2WPKHのAddressインスタンス + * @brief Create a P2WPKH address. + * @param[in] pubkey Pubkey + * @return address */ Address CreateP2wpkhAddress(const Pubkey& pubkey) const; /** - * @brief P2WSHのアドレスを生成する. - * @param[in] script Scriptインスタンス - * @return P2WSHのAddressインスタンス + * @brief Create a P2WSH address. + * @param[in] script Redeem script + * @return address */ Address CreateP2wshAddress(const Script& script) const; /** - * @brief P2WSHのMultisig(n of m)アドレスを生成する. - * @param[in] require_num signature要求数(n) - * @param[in] pubkeys Pubkeyリスト(m) - * @return P2WSH MultisigのAddressインスタンス + * @brief Create a P2WSH Multisig (n of m) address. + * @param[in] require_num signature require num(n) + * @param[in] pubkeys Pubkey list(m) + * @return address */ Address CreateP2wshMultisigAddress( uint32_t require_num, const std::vector& pubkeys) const; + /** + * @brief Create taproot address by schnorr pubkey. + * @param[in] pubkey schnorr pubkey + * @return Address by taproot + */ + Address CreateTaprootAddress(const SchnorrPubkey& pubkey) const; + + /** + * @brief Create taproot address by tapscript. + * @param[in] tree merkle tree + * @param[in] internal_pubkey internal schnorr pubkey + * @return Address by taproot + */ + Address CreateTaprootAddress( + const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey) const; + + /** + * @brief Create taproot address by hash. + * @param[in] hash hash + * @return Address by taproot + */ + Address CreateTaprootAddress(const ByteData256& hash) const; + /** * @brief check address's network type is valid. * @param[in] address address which is checked network type @@ -159,6 +224,52 @@ class CFD_EXPORT AddressFactory { */ bool CheckAddressNetType(const Address& address, NetType net_type) const; + /** + * @brief Create address. + * @param[in] address_type address type + * @param[in] pubkey public key (default: nullptr) + * @param[in] script script (default: nullptr) + * @param[out] locking_script locking script + * @param[out] redeem_script redeem script + * @return Address + */ + Address CreateAddress( + AddressType address_type, const Pubkey* pubkey, const Script* script, + Script* locking_script = nullptr, Script* redeem_script = nullptr) const; + + /** + * @brief Create a Pubkey Address list from Multisig Script. + * @param[in] address_type address type + * @param[in] redeem_script multisig script + * @param[out] pubkey_list pubkey list + * @return pubkey address list + */ + std::vector
GetAddressesFromMultisig( + AddressType address_type, const Script& redeem_script, + std::vector* pubkey_list = nullptr) const; + + /** + * @brief Extract information from Output descriptor. + * @param[in] descriptor output descriptor + * @param[in] bip32_derivation_path bip32 derivation path + * @param[out] script_list descriptor script list + * @param[out] multisig_key_list descriptor multisig key list + * @param[out] key_list key data list + * @return descriptor script data (top level or high security) + */ + DescriptorScriptData ParseOutputDescriptor( + const std::string& descriptor, + const std::string& bip32_derivation_path = "", + std::vector* script_list = nullptr, + std::vector* multisig_key_list = nullptr, + std::vector* key_list = nullptr) const; + + /** + * @brief Get current address prefix list. + * @return address prefix list. + */ + std::vector GetPrefixList() const; + protected: NetType type_; //!< network type WitnessVersion wit_ver_; //!< witness version diff --git a/include/cfd/cfd_common.h b/include/cfd/cfd_common.h index 3a5748c9..0afb92c4 100644 --- a/include/cfd/cfd_common.h +++ b/include/cfd/cfd_common.h @@ -1,7 +1,7 @@ // Copyright 2019 CryptoGarage /** * @file cfd_common.h - * @brief cfdの共通定義ファイル。 + * @brief Common definition file for cfd. */ #ifndef CFD_INCLUDE_CFD_CFD_COMMON_H_ #define CFD_INCLUDE_CFD_CFD_COMMON_H_ @@ -9,7 +9,7 @@ #include /** - * @brief APIのDLLエクスポート定義 + * @brief API DLL export definition */ #ifndef CFD_API #if defined(_WIN32) @@ -28,7 +28,7 @@ #endif /** - * @brief クラスのDLLエクスポート定義 + * @brief DLL export definition for class */ #ifndef CFD_EXPORT #if defined(_WIN32) @@ -47,12 +47,12 @@ #endif /** - * @brief cfd名前空間 + * @brief cfd namespace */ namespace cfd { /** - * @brief ライブラリがサポートしている機能の定義値 + * @brief Definition of the function supported by the library */ enum LibraryFunction { kEnableBitcoin = 0x0001, //!< enable bitcoin function @@ -61,17 +61,17 @@ enum LibraryFunction { // API /** - * @brief ライブラリがサポートしている機能の値を取得する。 - * @return LibraryFunctionのビットフラグ + * @brief Get the supported function by the library. + * @return Function bit flag */ CFD_API uint64_t GetSupportedFunction(); /** - * @brief cfdの初期化を行う。 + * @brief Initialize cfd. */ CFD_API void Initialize(void); /** - * @brief cfdの終了処理を行う。 - * @param[in] is_finish_process プロセス終了時かどうか + * @brief Performs cfd termination processing. + * @param[in] is_finish_process Whether at the end of the process */ CFD_API void Finalize(bool is_finish_process = false); diff --git a/include/cfd/cfd_elements_address.h b/include/cfd/cfd_elements_address.h index 7d28e74f..d0a53125 100644 --- a/include/cfd/cfd_elements_address.h +++ b/include/cfd/cfd_elements_address.h @@ -2,7 +2,7 @@ /** * @file cfd_elements_address.h * - * @brief Elements用Address操作関連クラスの定義 + * @brief Definition of Address operation related class for Elements */ #ifndef CFD_INCLUDE_CFD_CFD_ELEMENTS_ADDRESS_H_ #define CFD_INCLUDE_CFD_CFD_ELEMENTS_ADDRESS_H_ @@ -32,23 +32,23 @@ using cfd::core::Script; using cfd::core::WitnessVersion; /** - * @brief Elements用Addressを生成するFactoryクラス + * @brief Factory class that generates address for Elements */ class CFD_EXPORT ElementsAddressFactory : public AddressFactory { public: /** - * @brief コンストラクタ. + * @brief Constructor. */ ElementsAddressFactory(); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type */ explicit ElementsAddressFactory(NetType type); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] prefix_list address prefix list */ @@ -56,14 +56,14 @@ class CFD_EXPORT ElementsAddressFactory : public AddressFactory { NetType type, const std::vector& prefix_list); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] wit_ver witness version */ explicit ElementsAddressFactory(NetType type, WitnessVersion wit_ver); /** - * @brief コンストラクタ. + * @brief Constructor. * @param[in] type network type * @param[in] wit_ver witness version * @param[in] prefix_list address prefix list @@ -73,75 +73,75 @@ class CFD_EXPORT ElementsAddressFactory : public AddressFactory { const std::vector& prefix_list); /** - * @brief デストラクタ. + * @brief Destructor. */ virtual ~ElementsAddressFactory() { // do nothing } /** - * @brief UnblindedAddressをconfidential - * keyでブラインドしたConfidentialAddressを取得する. - * @param[in] unblinded_address Addressインスタンス - * @param[in] confidential_key ConfidentialKeyインスタンス(ec public key) - * @return BlindされたElementsConfidentialAddressインスタンス + * @brief Get the Confidential Address that blinded \ + * the Unblinded Address with the confidential key. + * @param[in] unblinded_address Address + * @param[in] confidential_key ConfidentialKey (ec public key) + * @return ElementsConfidentialAddress instance */ static ElementsConfidentialAddress GetConfidentialAddress( const Address& unblinded_address, const ConfidentialKey& confidential_key); /** - * @brief ConfidentialAddressを取得する. - * @param[in] address Address文字列 - * @return BlindされたElementsConfidentialAddressインスタンス + * @brief Get ConfidentialAddress. + * @param[in] address Address string + * @return ElementsConfidentialAddress instance */ ElementsConfidentialAddress GetConfidentialAddress( const std::string& address) const; /** - * @brief fedpegscriptとpubkeyから、net_typeに応じたmainchain用のpeg-in - * addressを作成する - * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) - * @param[in] pubkey 公開鍵 - * @param[in] fedpegscript elementsのfedpegscript - * @return mainchain用peg-in address + * @brief Create peg-in address for mainchain according \ + * to net_type from fedpegscript and pubkey + * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) + * @param[in] pubkey public key + * @param[in] fedpegscript elements fedpegscript + * @return peg-in address for mainchain */ Address CreatePegInAddress( AddressType address_type, const Pubkey& pubkey, const Script& fedpegscript) const; /** - * @brief fedpegscriptとredeemScriptから、net_typeに応じた - * mainchain用のpeg-in addressを作成する + * @brief Create peg-in address for mainchain according to \ + * net_type from fedpegscript and redeemScript * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) * @param[in] redeem_script redeem script * @param[in] fedpegscript elements fedpegscript - * @return mainchain用peg-in address + * @return peg-in address for mainchain */ Address CreatePegInAddressWithScript( AddressType address_type, const Script& redeem_script, const Script& fedpegscript) const; /** - * @brief fedpegscriptとclaim_scriptから、net_typeに応じたmainchain用のpeg-in - * addressを作成する - * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) - * @param[in] claim_script sidechainでの資産引取りに必要なclaim script - * @param[in] fedpegscript elementsのfedpegscript - * @return mainchain用peg-in address + * @brief Create peg-in address for mainchain according \ + * to net_type from fedpegscript and claim_script + * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) + * @param[in] claim_script Claim script required for asset collection on sidechain + * @param[in] fedpegscript elements fedpegscript + * @return peg-in address for mainchain */ Address CreatePegInAddress( AddressType address_type, const Script& claim_script, const Script& fedpegscript) const; /** - * @brief tweakが足されたfedpegscriptから、net_typeに応じたmainchain用のpeg-in - * addressを作成する + * @brief Create peg-in address for mainchain according \ + * to net_type from fedpegscript with tweak added * @param[in] address_type address type (p2sh, p2wsh, p2sh-p2wsh) * @param[in] tweak_fedpegscript - * fedpegscript内部のpubkeyをtweakと合成させたscript. + * A script that combines pubkey inside fedpegscript with tweak. * (ref: cfd::core::ContractHashUtil) - * @return mainchain用peg-in address + * @return peg-in address for mainchain */ Address CreatePegInAddress( AddressType address_type, const Script& tweak_fedpegscript) const; diff --git a/include/cfd/cfd_elements_transaction.h b/include/cfd/cfd_elements_transaction.h index e35a76d2..36df1dcc 100644 --- a/include/cfd/cfd_elements_transaction.h +++ b/include/cfd/cfd_elements_transaction.h @@ -2,7 +2,7 @@ /** * @file cfd_elements_transaction.h * - * @brief Elements Transaction操作の関連クラス定義 + * @brief Related class definitions for Elements Transaction operations */ #ifndef CFD_INCLUDE_CFD_CFD_ELEMENTS_TRANSACTION_H_ #define CFD_INCLUDE_CFD_CFD_ELEMENTS_TRANSACTION_H_ @@ -26,7 +26,7 @@ #include "cfdcore/cfdcore_util.h" /** - * @brief cfd名前空間 + * @brief cfd namespace */ namespace cfd { @@ -136,9 +136,9 @@ class CFD_EXPORT ConfidentialTransactionContext */ using ConfidentialTransaction::GetTxInIndex; /** - * @brief TxInのindexを取得する. + * @brief Get the index of TxIn. * @param[in] outpoint TxIn txid and vout - * @return 条件に合致するTxInのindex番号 + * @return TxIn index */ virtual uint32_t GetTxInIndex(const OutPoint& outpoint) const; /** @@ -146,34 +146,34 @@ class CFD_EXPORT ConfidentialTransactionContext */ using ConfidentialTransaction::GetTxOutIndex; /** - * @brief TxOutのindexを取得する. + * @brief Get the index of TxOut. * @param[in] address address - * @return 条件に合致するTxOutのindex番号 + * @return TxOut index */ virtual uint32_t GetTxOutIndex(const Address& address) const; /** - * @brief TxInの有無を確認する. + * @brief Check if TxIn exists. * @param[in] outpoint TxIn txid and vout * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxIn(const OutPoint& outpoint, uint32_t* index = nullptr) const; /** - * @brief TxOutの有無を確認する. + * @brief Check if TxOut exists. * @param[in] locking_script locking script * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxOut( const Script& locking_script, uint32_t* index = nullptr) const; /** - * @brief TxOutの有無を確認する. + * @brief Check if TxOut exists. * @param[in] address address * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxOut(const Address& address, uint32_t* index = nullptr) const; /** @@ -202,9 +202,9 @@ class CFD_EXPORT ConfidentialTransactionContext */ using ConfidentialTransaction::AddTxIn; /** - * @brief TxInを追加する. + * @brief Add TxIn. * @param[in] outpoint TxIn txid and vout - * @return 追加したTxInのindex位置 + * @return Index position of added TxIn */ virtual uint32_t AddTxIn(const OutPoint& outpoint); /** @@ -212,22 +212,22 @@ class CFD_EXPORT ConfidentialTransactionContext */ using ConfidentialTransaction::AddTxOut; /** - * @brief TxOutを追加する. - * @param[in] address 送金先unblindedアドレス - * @param[in] value 送金額 + * @brief Add TxOut. + * @param[in] address Remittance destination unblinded address + * @param[in] value Amount * @param[in] asset AssetID - * @return 追加したTxOutのIndex番号 + * @return TxOut Index */ virtual uint32_t AddTxOut( const Address& address, const Amount& value, const ConfidentialAssetId& asset); /** - * @brief TxOutを追加する. - * @param[in] address 送金先confidentialアドレス - * @param[in] value 送金額 + * @brief Add TxOut. + * @param[in] address Remittance destination confidential address + * @param[in] value Amount * @param[in] asset AssetID - * @param[in] remove_nonce nonceの強制削除フラグ - * @return 追加したTxOutのIndex番号 + * @param[in] remove_nonce Nonce forced delete flag + * @return TxOut Index */ virtual uint32_t AddTxOut( const ElementsConfidentialAddress& address, @@ -235,16 +235,16 @@ class CFD_EXPORT ConfidentialTransactionContext const ConfidentialAssetId& asset, bool remove_nonce = false); /** - * @brief TxInを除外したサイズを取得する。 - * @param[in] is_blinded blind時の想定サイズを取得するフラグ - * @param[out] witness_area_size witness area size - * @param[out] no_witness_area_size no witness area size - * @param[in] exponent rangeproof exponent value. + * @brief Get the size excluding TxIn. + * @param[in] is_blinded Flag to get the expected size when blind + * @param[out] witness_area_size witness area size + * @param[out] no_witness_area_size no witness area size + * @param[in] exponent rangeproof exponent value. * -1 to 18. -1 is public value. 0 is most private. - * @param[in] minimum_bits rangeproof blinding bits. + * @param[in] minimum_bits rangeproof blinding bits. * 0 to 64. Number of bits of the value to keep private. 0 is auto. - * @param[in] asset_count input asset count. - * @return TxInを除外したTxサイズ(Serialize) + * @param[in] asset_count input asset count. + * @return Tx size excluding TxIn (Serialize) */ uint32_t GetSizeIgnoreTxIn( bool is_blinded = false, uint32_t* witness_area_size = nullptr, @@ -253,14 +253,14 @@ class CFD_EXPORT ConfidentialTransactionContext uint32_t asset_count = 0) const; /** - * @brief TxInを除外した仮想サイズを取得する。 - * @param[in] is_blinded blind時の想定サイズを取得するフラグ - * @param[in] exponent rangeproof exponent value. + * @brief Get the virtual size excluding TxIn. + * @param[in] is_blinded Flag to get the expected size when blind + * @param[in] exponent rangeproof exponent value. * -1 to 18. -1 is public value. 0 is most private. - * @param[in] minimum_bits rangeproof blinding bits. + * @param[in] minimum_bits rangeproof blinding bits. * 0 to 64. Number of bits of the value to keep private. 0 is auto. - * @param[in] asset_count input asset count. - * @return TxInを除外したTx仮想サイズ(Serialize) + * @param[in] asset_count input asset count. + * @return Tx virtual size excluding TxIn (Serialize) */ uint32_t GetVsizeIgnoreTxIn( bool is_blinded = false, int exponent = 0, @@ -268,33 +268,33 @@ class CFD_EXPORT ConfidentialTransactionContext uint32_t asset_count = 0) const; /** - * @brief feeのtxout indexを取得する. + * @brief Get the txout index of fee. * @param[out] index txout index. - * @retval true fee存在 - * @retval false fee未存在 + * @retval true fee exist + * @retval false fee not found */ bool IsFindFeeTxOut(uint32_t* index = nullptr) const; /** - * @brief TxOutのFee額を取得する. + * @brief Get the Fee amount for TxOut. * @param[out] asset fee asset. - * @return Fee額 + * @return Fee amount */ Amount GetFeeAmount(ConfidentialAssetId* asset = nullptr) const; /** - * @brief TxOutのFee情報を更新する. Fee未存在時は自動追加する。 - * @param[in] value Fee額 + * @brief Update Fee information of TxOut. If Fee does not exist, it will be added automatically. + * @param[in] value Fee amount * @param[in] asset AssetID - * @return 更新したTxOutのIndex番号 + * @return Updated TxOut index */ uint32_t UpdateFeeAmount( const Amount& value, const ConfidentialAssetId& asset); /** - * @brief 簡易のFee計算を行う. - * @param[in] append_feature_signed_size sign分のサイズ加算フラグ - * @param[in] append_signed_witness sign分の加算をwitness扱いとするか + * @brief Perform a simple Fee calculation. + * @param[in] append_feature_signed_size size addition flag for sign + * @param[in] append_signed_witness Whether to treat the addition of sign as witness * @return Fee Amount */ Amount CalculateSimpleFee( @@ -329,14 +329,14 @@ class CFD_EXPORT ConfidentialTransactionContext Address* btc_derive_address = nullptr); /** - * @brief PeginWitnessに文字列から得られるバイトデータを追加する. - * @param[in] outpoint TxIn txid and vout - * @param[in] amount peginしたinputのamount - * @param[in] asset_id 送金先アセットID - * @param[in] mainchain_genesis_block_hash mainchainのgenesis_blockのhash値 - * @param[in] claim_script pegin請求用スクリプト - * @param[in] mainchain_pegin_transaction peginしたmainchainのtransaction - * @param[in] tx_out_proof peginしたtransactionが取り込まれた際のproof値 + * @brief Add byte data obtained from the string to PeginWitness. + * @param[in] outpoint TxIn txid and vout + * @param[in] amount amount of pegin input + * @param[in] asset_id Remittance destination asset ID + * @param[in] mainchain_genesis_block_hash hash of genesis block in mainchain + * @param[in] claim_script pegin claim script + * @param[in] mainchain_pegin_transaction pegin mainchain transaction + * @param[in] tx_out_proof Proof value when pegin transaction is captured */ void AddPeginTxIn( // AddPeginWitness const OutPoint& outpoint, const Amount& amount, @@ -497,6 +497,13 @@ class CFD_EXPORT ConfidentialTransactionContext */ void CollectInputUtxo(const std::vector& utxos); + /** + * @brief Get UTXO by outpoint + * @param[in] outpoint TxIn txid and vout + * @return UTXO + */ + UtxoData GetTxInUtxoData(const OutPoint& outpoint) const; + /** * @brief Execute blinding txout using utxo data. * @param[in] confidential_addresses txout confidential address list. @@ -567,66 +574,55 @@ class CFD_EXPORT ConfidentialTransactionContext * @return transaction raw data. */ ByteData Finalize(); -#if 0 - /** - * @brief clear sign area all. - */ - void ClearSign(); - /** - * @brief clear sign area on outpoint. - * @param[in] outpoint utxo target. - */ - void ClearSign(const OutPoint& outpoint); -#endif // sign-api /** - * @brief 指定されたPubkeyHash形式のTxInのSignatureHashを計算する. + * @brief Calculates the TxIn Signature Hash in the specified PubkeyHash format. * @param[in] outpoint TxIn txid and vout - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @param[in] pubkey Public key + * @param[in] sighash_type SigHash type + * @param[in] value UTXO amount specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Pubkey& pubkey, SigHashType sighash_type, const Amount& value, WitnessVersion version) const; /** - * @brief 指定されたScriptHash形式のTxInのSignatureHashを計算する. - * @details OP_CODESEPARATORが存在するScriptについては未対応 + * @brief Calculates the TxIn Signature Hash in the specified ScriptHash format. + * @details Not supported for Scripts with OP_CODESEPARATOR * @param[in] outpoint TxIn txid and vout - * @param[in] redeem_script ScriptHashのRedeem Script - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @param[in] redeem_script Script Hash Redeem Script + * @param[in] sighash_type SigHash type + * @param[in] value UTXO amount specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Script& redeem_script, SigHashType sighash_type, const Amount& value, WitnessVersion version) const; /** - * @brief 指定されたPubkeyHash形式のTxInのSignatureHashを計算する. + * @brief Calculates the TxIn Signature Hash in the specified PubkeyHash format. * @param[in] outpoint TxIn txid and vout - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのValueCommitment - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @param[in] pubkey Public key + * @param[in] sighash_type SigHash type + * @param[in] value UTXO Value Commitment specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Pubkey& pubkey, SigHashType sighash_type, const ConfidentialValue& value, WitnessVersion version) const; /** - * @brief 指定されたScriptHash形式のTxInのSignatureHashを計算する. - * @details OP_CODESEPARATORが存在するScriptについては未対応 + * @brief Calculates the TxIn Signature Hash in the specified ScriptHash format. + * @details Not supported for Scripts with OP_CODESEPARATOR * @param[in] outpoint TxIn txid and vout - * @param[in] redeem_script ScriptHashのRedeem Script - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのValueCommitment - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @param[in] redeem_script Script Hash Redeem Script + * @param[in] sighash_type SigHash type + * @param[in] value UTXO Value Commitment specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Script& redeem_script, @@ -634,14 +630,14 @@ class CFD_EXPORT ConfidentialTransactionContext WitnessVersion version) const; /** - * @brief 指定されたPubkeyHash形式のTxInに署名する. - * @param[in] outpoint TxIn txid and vout - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] privkey SignatureHashの秘密鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) - * @param[in] has_grind_r signature計算時のオプション + * @brief Sign the specified TxIn with pubkey hash. + * @param[in] outpoint outpoint + * @param[in] pubkey Public key + * @param[in] privkey Private key + * @param[in] sighash_type SigHash Type + * @param[in] value amount of UTXO + * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) + * @param[in] has_grind_r Grind-R option for sign. */ void SignWithPrivkeySimple( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, @@ -650,14 +646,14 @@ class CFD_EXPORT ConfidentialTransactionContext bool has_grind_r = true); /** - * @brief 指定されたPubkeyHash形式のTxInに署名する. - * @param[in] outpoint TxIn txid and vout - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] privkey SignatureHashの秘密鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのValueCommitment - * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) - * @param[in] has_grind_r signature計算時のオプション + * @brief Sign the specified TxIn with pubkey hash. + * @param[in] outpoint outpoint + * @param[in] pubkey Public key + * @param[in] privkey Private key + * @param[in] sighash_type SigHash Type + * @param[in] value value commitment of UTXO + * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) + * @param[in] has_grind_r Grind-R option for sign. */ void SignWithPrivkeySimple( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, @@ -691,11 +687,12 @@ class CFD_EXPORT ConfidentialTransactionContext /** * @brief add multisig sign data to target outpoint. - * @details 追加するsignatureの順序は、redeem - * scriptのpubkeyとsignatures内のrelatedPubkeyで - * 対応をとって自動的に整列される. - * (relatedPubkeyが設定されていない場合は、relatedPubkeyが - * 設定されているsignatureを追加した後にsignParamの順序でsignatureを追加) + * @details The order of the signatures to be added is \ + * automatically arranged by correspondence between \ + * the pubkey of the redeem script and the relatedPubkey \ + * in the signatures. + * (If relatedPubkey is not set, add signatures in the order of \ + * signParam after adding signatures with relatedPubkey set) * @param[in] outpoint TxIn txid and vout * @param[in] signatures signature list * @param[in] redeem_script redeem script @@ -711,7 +708,6 @@ class CFD_EXPORT ConfidentialTransactionContext * @param[in] sign_params sign data list * @param[in] insert_witness use witness * @param[in] clear_stack clear stack data before add. - * @return SignDataが付与されたTransactionController */ void AddSign( const OutPoint& outpoint, const std::vector& sign_params, @@ -758,15 +754,15 @@ class CFD_EXPORT ConfidentialTransactionContext WitnessVersion version = WitnessVersion::kVersionNone) const; /** - * @brief ロックタイムからデフォルトのシーケンス番号を取得する。 - * @retval 0xffffffff locktime値無効 - * @retval 0xfffffffe locktime値有効 + * @brief Get the default sequence number from the lock time. + * @retval 0xffffffff locktime disable + * @retval 0xfffffffe locktime enable */ uint32_t GetDefaultSequence() const; /** - * @brief ロックタイムからlocktime値無効のシーケンス番号を取得する。 - * @retval 0xffffffff locktime値無効 + * @brief Get the default sequence number from the lock time. + * @retval 0xffffffff locktime disable */ static uint32_t GetLockTimeDisabledSequence(); @@ -787,11 +783,30 @@ class CFD_EXPORT ConfidentialTransactionContext */ virtual void CallbackStateChange(uint32_t type); + /** + * @brief find target outpoint utxo list. + * @param[in] outpoint outpoint + * @param[out] utxo utxo + * @retval true exist + * @retval false not exist + */ + bool IsFindUtxoMap(const OutPoint& outpoint, UtxoData* utxo = nullptr) const; + + /** + * @brief find target outpoint from list. + * @param[in] list outpoint list + * @param[in] outpoint outpoint + * @retval true exist + * @retval false not exist + */ + bool IsFindOutPoint( + const std::vector& list, const OutPoint& outpoint) const; + private: /** * @brief utxo map. */ - std::map utxo_map_; + std::vector utxo_map_; /** * @brief utxo signed map. (outpoint, SigHashType) */ @@ -799,11 +814,11 @@ class CFD_EXPORT ConfidentialTransactionContext /** * @brief utxo verify map. (outpoint) */ - std::set verify_map_; + std::vector verify_map_; /** * @brief utxo verify ignore map. (outpoint) */ - std::set verify_ignore_map_; + std::vector verify_ignore_map_; }; // ---------------------------------------------------------------------------- diff --git a/include/cfd/cfd_fee.h b/include/cfd/cfd_fee.h index 6240a20d..248f5f18 100644 --- a/include/cfd/cfd_fee.h +++ b/include/cfd/cfd_fee.h @@ -2,7 +2,7 @@ /** * @file cfd_fee.h * - * @brief Fee操作の関連クラス定義 + * @brief Related class definition for Fee operations */ #ifndef CFD_INCLUDE_CFD_CFD_FEE_H_ #define CFD_INCLUDE_CFD_CFD_FEE_H_ @@ -19,30 +19,30 @@ namespace cfd { using cfd::core::Amount; /** - * @brief Fee計算を行うクラス + * @brief Class that performs Fee calculation */ class CFD_EXPORT FeeCalculator { public: /** - * @brief 最小のfee + * @brief Smallest fee * @details bitcoin relay minimum fee rate. * @see bitcoin: DEFAULT_INCREMENTAL_RELAY_FEE */ static constexpr const int64_t kRelayMinimumFee = 1000; /** - * @brief 最小のfee + * @brief Smallest fee * @details elements relay minimum fee rate. */ static constexpr const int64_t kElementsRelayMinimumFee = 100; /** - * @brief Feeを計算する. + * @brief Calculate Fee. * @param[in] size Transaction size * @param[in] vsize Transaction virtual size * @param[in] rate rate (default = relay minimum) * @return Fee Amount */ - static Amount CalculateFee( // Fee計算 + static Amount CalculateFee( // Calculate Fee. uint32_t size, uint32_t vsize, uint64_t rate = kRelayMinimumFee); /** @@ -57,21 +57,21 @@ class CFD_EXPORT FeeCalculator { explicit FeeCalculator(uint64_t baserate); /** - * @brief Feeを計算する. + * @brief Calculate Fee. * @param[in] size Transaction size * @return Fee Amount */ Amount GetFee(uint32_t size) const; /** - * @brief Feeを計算する. + * @brief Calculate Fee. * @param[in] utxo unused transaction output * @return Fee Amount */ Amount GetFee(const Utxo& utxo) const; private: - uint32_t baserate_; //!< ベースレート + uint32_t baserate_; //!< base rate }; } // namespace cfd diff --git a/include/cfd/cfd_json_mapping_api.h b/include/cfd/cfd_json_mapping_api.h index c1394cc7..766e422d 100644 --- a/include/cfd/cfd_json_mapping_api.h +++ b/include/cfd/cfd_json_mapping_api.h @@ -2,9 +2,9 @@ /** * @file cfd_json_mapping_api.h * - * @brief cfd-apiで利用するJsonApiクラス定義 + * @brief JsonApi class definition used in cfd-api * - * JSON形式のAPIを提供する. + *Provides a JSON-formatted API. */ #ifndef CFD_INCLUDE_CFD_CFD_JSON_MAPPING_API_H_ #define CFD_INCLUDE_CFD_CFD_JSON_MAPPING_API_H_ @@ -14,30 +14,37 @@ #include "cfd/cfd_common.h" /** - * @brief cfdapi名前空間 + * @brief cfd::api::json namespace */ namespace cfd { namespace api { namespace json { /** - * @brief 共通系の関数群クラス + * @brief Json Information mapping API class. */ class CFD_EXPORT JsonMappingApi { public: /** - * @brief DecodeRawTransactionのJSON API関数(request, response). - * @param[in] request_message リクエストされたjson文字列 - * @return 戻り値(JSON文字列) + * @brief DecodeRawTransaction. + * @param[in] request_message json message + * @return json message */ static std::string DecodeRawTransaction(const std::string &request_message); + /** + * @brief DecodePsbt. + * @param[in] request_message json message + * @return json message + */ + static std::string DecodePsbt(const std::string &request_message); + #ifndef CFD_DISABLE_ELEMENTS /** - * @brief ElementsDecodeRawTransactionのJSON API関数(request, response). - * @param[in] request_message リクエストされたjson文字列 - * @return 戻り値(JSON文字列) + * @brief ElementsDecodeRawTransaction. + * @param[in] request_message json message + * @return json message */ static std::string ElementsDecodeRawTransaction( const std::string &request_message); diff --git a/include/cfd/cfd_psbt.h b/include/cfd/cfd_psbt.h new file mode 100644 index 00000000..ccb41879 --- /dev/null +++ b/include/cfd/cfd_psbt.h @@ -0,0 +1,568 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfd_psbt.h + * + * @brief This file is defines Partially Signed Bitcoin Transaction. + */ +#ifndef CFD_INCLUDE_CFD_CFD_PSBT_H_ +#define CFD_INCLUDE_CFD_CFD_PSBT_H_ + +#include +#include +#include +#include +#include +#include + +#include "cfd/cfd_common.h" +#include "cfd/cfd_transaction.h" +#include "cfd/cfd_transaction_common.h" +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_descriptor.h" +#include "cfdcore/cfdcore_hdwallet.h" +#include "cfdcore/cfdcore_psbt.h" +#include "cfdcore/cfdcore_transaction_common.h" + +namespace cfd { + +using cfd::core::Descriptor; +using cfd::core::KeyData; +using cfd::core::OutPoint; +using cfd::core::Transaction; + +/** + * @brief The class of Partially Signed Bitcoin Transaction. + */ +class CFD_EXPORT Psbt : public cfd::core::Psbt { + public: + /** + * @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] context Transaction object. + */ + explicit Psbt(const TransactionContext& context); + /** + * @brief constructor + * @param[in] psbt_version psbt version + * @param[in] context Transaction object. + */ + explicit Psbt(uint32_t psbt_version, const TransactionContext& context); + /** + * @brief constructor + * @param[in] psbt Psbt object. + */ + Psbt(const Psbt& psbt); + /** + * @brief destructor + */ + virtual ~Psbt() {} + + /** + * @brief copy constructor. + * @param[in] psbt Psbt object. + * @return Psbt object. + */ + Psbt& operator=(const Psbt& psbt) &; + + /** + * @brief Get transaction context base. + * @return transaction context. + */ + TransactionContext GetTransactionContext() const; + + /** + * @brief Get transaction fee. (only if HasAllUtxos is true.) + * @retval true Already set all utxos. + * @retval false There is an input for which utxo is not set. + */ + bool HasAllUtxos() const; + + /** + * @brief Get transaction fee. (only if HasAllUtxos is true.) + * @return fee amount + */ + Amount GetFeeAmount() const; + + using cfd::core::Psbt::IsFinalizedInput; + /** + * @brief check finalized input. + * @param[in] outpoint outpoint + * @retval true already finalized input. + * @retval false not finalized input. + */ + bool IsFinalizedInput(const OutPoint& outpoint) const; + + using cfd::core::Psbt::AddTxIn; + /** + * @brief add transaction input. + * @param[in] outpoint outpoint + * @return add index. + */ + uint32_t AddTxIn(const OutPoint& outpoint); + /** + * @brief add transaction input. + * @param[in] outpoint outpoint + * @param[in] sequence sequence + * @return add index. + */ + uint32_t AddTxIn(const OutPoint& outpoint, uint32_t sequence); + + /** + * @brief add transaction input and utxo data. + * @param[in] utxo utxo data + * @return add index. + */ + uint32_t AddTxInData(const UtxoData& utxo); + /** + * @brief add transaction input and utxo data. + * @param[in] utxo utxo data + * @param[in] sequence sequence + * @return add index. + */ + uint32_t AddTxInData(const UtxoData& utxo, uint32_t sequence); + + /** + * @brief find txin. + * @param[in] outpoint outpoint + * @param[out] index txout index. + * @retval true exist txin + * @retval false txin not found + */ + bool IsFindTxIn(const OutPoint& outpoint, uint32_t* index = nullptr) const; + /** + * @brief Get a txin index. + * @param[in] outpoint outpoint + * @return txin index + */ + uint32_t GetTxInIndex(const OutPoint& outpoint) const; + + using cfd::core::Psbt::SetTxInUtxo; + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @param[in] tx utxo transaction + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + const OutPoint& outpoint, const Transaction& tx, const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @param[in] tx utxo transaction + * @param[in] redeem_script utxo related script (only script hash) + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + const OutPoint& outpoint, const Transaction& tx, + const Script& redeem_script, const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @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( + const OutPoint& outpoint, const Transaction& tx, + const Script& redeem_script, const std::vector& key_list); + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @param[in] txout utxo witness transaction output + * @param[in] key utxo related pubkey + */ + void SetTxInUtxo( + const OutPoint& outpoint, const TxOutReference& txout, + const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @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( + const OutPoint& outpoint, const TxOutReference& txout, + const Script& redeem_script, const KeyData& key); + /** + * @brief set input utxo data. + * @param[in] outpoint outpoint + * @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( + const OutPoint& outpoint, const TxOutReference& txout, + const Script& redeem_script, const std::vector& key_list); + + using cfd::core::Psbt::SetTxInWitnessUtxoDirect; + /** + * @brief set input utxo data on direct. + * @param[in] outpoint outpoint + * @param[in] txout utxo witness transaction output + */ + void SetTxInWitnessUtxoDirect( + const OutPoint& outpoint, const TxOutReference& txout); + + using cfd::core::Psbt::SetTxInBip32KeyDirect; + /** + * @brief set input bip32 key on direct. + * @param[in] outpoint outpoint + * @param[in] key_data key data + */ + void SetTxInBip32KeyDirect( + const OutPoint& outpoint, const KeyData& key_data); + + using cfd::core::Psbt::SetTxInSignature; + /** + * @brief set input signature. + * @param[in] outpoint outpoint + * @param[in] key utxo related pubkey + * @param[in] signature signature data + */ + void SetTxInSignature( + const OutPoint& outpoint, const KeyData& key, const ByteData& signature); + + using cfd::core::Psbt::SetTxInSighashType; + /** + * @brief set input sighash type. + * @param[in] outpoint outpoint + * @param[in] sighash_type sighash type + */ + void SetTxInSighashType( + const OutPoint& outpoint, const SigHashType& sighash_type); + + using cfd::core::Psbt::SetTxInFinalScript; + /** + * @brief set input final script. + * @param[in] outpoint outpoint + * @param[in] unlocking_script unlocking script data list. + */ + void SetTxInFinalScript( + const OutPoint& outpoint, const std::vector& unlocking_script); + + using cfd::core::Psbt::SetTxInRecord; + /** + * @brief set input record. + * @param[in] outpoint outpoint + * @param[in] key key + * @param[in] value value + */ + void SetTxInRecord( + const OutPoint& outpoint, const ByteData& key, const ByteData& value); + + using cfd::core::Psbt::GetTxInUtxoFull; + /** + * @brief get input utxo full transaction. + * @param[in] outpoint outpoint + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return utxo transaction + */ + Transaction GetTxInUtxoFull( + const OutPoint& outpoint, bool ignore_error = false, + bool* is_witness = nullptr) const; + + using cfd::core::Psbt::GetTxInUtxo; + /** + * @brief get input utxo output data. + * @param[in] outpoint outpoint + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return utxo transaction output + */ + TxOut GetTxInUtxo( + const OutPoint& outpoint, bool ignore_error = false, + bool* is_witness = nullptr) const; + + using cfd::core::Psbt::GetTxInRedeemScript; + /** + * @brief get input redeem script. + * @param[in] outpoint outpoint + * @param[in] ignore_error ignore error with empty data. + * @param[out] is_witness has witness. + * @return redeem script (or witness script) + */ + Script GetTxInRedeemScript( + const OutPoint& outpoint, bool ignore_error = false, + bool* is_witness = nullptr) const; + + using cfd::core::Psbt::GetTxInRedeemScriptDirect; + /** + * @brief get input redeem script. + * @param[in] outpoint outpoint + * @param[in] ignore_error ignore error with empty data. + * @param[in] is_witness getting target witness. + * @return redeem script (or witness script) + */ + Script GetTxInRedeemScriptDirect( + const OutPoint& outpoint, bool ignore_error = false, + bool is_witness = true) const; + + using cfd::core::Psbt::GetTxInKeyDataList; + /** + * @brief get input key data list. + * @param[in] outpoint outpoint + * @return key data list + */ + std::vector GetTxInKeyDataList(const OutPoint& outpoint) const; + + using cfd::core::Psbt::GetTxInKeyData; + /** + * @brief get input key data (only list top data). + * @param[in] outpoint outpoint + * @param[in] ignore_error ignore error with empty data. + * @return key data + */ + KeyData GetTxInKeyData( + const OutPoint& outpoint, bool ignore_error = false) const; + + using cfd::core::Psbt::GetTxInSignaturePubkeyList; + /** + * @brief get input key data list related to signature. + * @param[in] outpoint outpoint + * @return key data list + */ + std::vector GetTxInSignaturePubkeyList( + const OutPoint& outpoint) const; + + using cfd::core::Psbt::GetTxInSignature; + /** + * @brief get input signature related pubkey. + * @param[in] outpoint outpoint + * @param[in] pubkey pubkey + * @return signature + */ + ByteData GetTxInSignature( + const OutPoint& outpoint, const Pubkey& pubkey) const; + + using cfd::core::Psbt::IsFindTxInSignature; + /** + * @brief exist input signature related pubkey. + * @param[in] outpoint outpoint + * @param[in] pubkey pubkey + * @retval true exist signature + * @retval false signature not found + */ + bool IsFindTxInSignature( + const OutPoint& outpoint, const Pubkey& pubkey) const; + + using cfd::core::Psbt::GetTxInSighashType; + /** + * @brief get input sighash type. + * @param[in] outpoint outpoint + * @return sighash type + */ + SigHashType GetTxInSighashType(const OutPoint& outpoint) const; + + using cfd::core::Psbt::IsFindTxInSighashType; + /** + * @brief exist input sighash type. + * @param[in] outpoint outpoint + * @retval true exist sighash type + * @retval false sighash type not found + */ + bool IsFindTxInSighashType(const OutPoint& outpoint) const; + + using cfd::core::Psbt::GetTxInFinalScript; + /** + * @brief get input final script. + * @param[in] outpoint outpoint + * @param[in] is_witness_stack target witness flag + * @return witness stack or scriptSig + */ + std::vector GetTxInFinalScript( + const OutPoint& outpoint, bool is_witness_stack = true) const; + + using cfd::core::Psbt::GetTxInRecord; + /** + * @brief get input record value. + * @param[in] outpoint outpoint + * @param[in] key record key + * @return record value + */ + ByteData GetTxInRecord(const OutPoint& outpoint, const ByteData& key) const; + + using cfd::core::Psbt::IsFindTxInRecord; + /** + * @brief exist input record. + * @param[in] outpoint outpoint + * @param[in] key record key + * @retval true exist record + * @retval false record not found + */ + bool IsFindTxInRecord(const OutPoint& outpoint, const ByteData& key) const; + + using cfd::core::Psbt::GetTxInRecordKeyList; + /** + * @brief get record key list. + * @param[in] outpoint outpoint + * @return record key list + */ + std::vector GetTxInRecordKeyList(const OutPoint& outpoint) const; + + using cfd::core::Psbt::ClearTxInSignData; + /** + * @brief clear input sign data. + * @details clear target is redeem script, signature, sighashtype. + * @param[in] outpoint outpoint + */ + void ClearTxInSignData(const OutPoint& outpoint); + + /** + * @brief set utxo data. + * @param[in] utxo utxo + * @param[in] transaction transaction + */ + void SetUtxoData(const UtxoData& utxo, const Transaction& transaction); + /** + * @brief set witness utxo. + * @param[in] utxo witness utxo + */ + void SetWitnessUtxoData(const UtxoData& utxo); + /** + * @brief collect utxo and cache into utxo_map_. + * @param[in] utxos utxo list. + */ + void CollectInputUtxo(const std::vector& utxos); + + using cfd::core::Psbt::AddTxOut; + /** + * @brief Add txout. + * @param[in] amount amount + * @param[in] address address + * @return txout index + */ + uint32_t AddTxOut(const Amount& amount, const Address& address); + /** + * @brief Add txout data. + * @param[in] amount amount + * @param[in] address address + * @param[in] key_data key + * @return txout index + */ + uint32_t AddTxOutData( + const Amount& amount, const Address& address, const KeyData& key_data); + /** + * @brief Add txout data. + * @param[in] amount amount + * @param[in] address address + * @param[in] redeem_script redeem script + * @param[in] key_list key list + * @return txout index + */ + uint32_t AddTxOutData( + const Amount& amount, const Address& address, + const Script& redeem_script, const std::vector& key_list); + + // GetTxOutXXXX is see `cfd::core::Psbt` + + /** + * @brief set ignore verify target. + * @param[in] outpoint utxo target. + */ + void IgnoreVerify(const OutPoint& outpoint); + /** + * @brief verify tx sign (signature). + */ + void Verify() const; + /** + * @brief verify tx sign (signature) on outpoint. + * @param[in] outpoint utxo target. + */ + void Verify(const OutPoint& outpoint) const; + + /** + * @brief fund transaction. + * @param[in] witness_utxos witness utxo list + * @param[in] effective_fee_rate fee rate + * @param[in] change_address change address descriptor. + * @param[out] estimate_fee estimate fee + * @param[in] option_params coin selection option data + * @param[in] filter utxo filter + * @param[in] net_type network type + * @return selected utxo list. + */ + std::vector FundTransaction( + const std::vector& witness_utxos, + double effective_fee_rate = 20.0, + const Descriptor* change_address = nullptr, + Amount* estimate_fee = nullptr, + const CoinSelectionOption* option_params = nullptr, + const UtxoFilter* filter = nullptr, + NetType net_type = NetType::kMainnet); + + /** + * @brief Get utxo. + * @param[in] index txin index + * @param[in] net_type network type + * @return utxo data. + */ + UtxoData GetUtxoData( + uint32_t index, NetType net_type = NetType::kMainnet) const; + + /** + * @brief Get all utxo. + * @param[in] net_type network type + * @return utxo list. + */ + std::vector GetUtxoDataAll( + NetType net_type = NetType::kMainnet) const; + + private: + /** + * @brief utxo verify ignore map. (outpoint) + */ + std::set verify_ignore_map_; + + /** + * @brief Get default sequence from locktime. + * @retval 0xfffffffe enable locktime + * @retval 0xffffffff disable locktime + */ + uint32_t GetDefaultSequence() const; +}; + +} // namespace cfd + +#endif // CFD_INCLUDE_CFD_CFD_PSBT_H_ diff --git a/include/cfd/cfd_transaction.h b/include/cfd/cfd_transaction.h index 73fb7f2a..071e97bd 100644 --- a/include/cfd/cfd_transaction.h +++ b/include/cfd/cfd_transaction.h @@ -2,7 +2,7 @@ /** * @file cfd_transaction.h * - * @brief Transaction操作の関連クラス定義 + * @brief Related class definition for Transaction operation */ #ifndef CFD_INCLUDE_CFD_CFD_TRANSACTION_H_ #define CFD_INCLUDE_CFD_CFD_TRANSACTION_H_ @@ -16,9 +16,12 @@ #include "cfd/cfd_transaction_common.h" #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_bytedata.h" #include "cfdcore/cfdcore_coin.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_transaction.h" #include "cfdcore/cfdcore_util.h" @@ -29,11 +32,15 @@ using cfd::core::Address; using cfd::core::AddressType; using cfd::core::Amount; using cfd::core::ByteData; +using cfd::core::ByteData256; using cfd::core::OutPoint; using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::SchnorrSignature; using cfd::core::Script; using cfd::core::SigHashType; +using cfd::core::TaprootScriptTree; using cfd::core::Transaction; using cfd::core::Txid; using cfd::core::TxInReference; @@ -68,7 +75,7 @@ class CFD_EXPORT TransactionContext : public Transaction { * @brief constructor * @param[in] context Transaction Context */ - explicit TransactionContext(const TransactionContext& context); + TransactionContext(const TransactionContext& context); /** * @brief constructor * @param[in] transaction Transaction @@ -92,9 +99,9 @@ class CFD_EXPORT TransactionContext : public Transaction { */ using Transaction::GetTxInIndex; /** - * @brief TxInのindexを取得する. + * @brief Get the index of TxIn. * @param[in] outpoint TxIn txid and vout - * @return 条件に合致するTxInのindex番号 + * @return TxIn index */ virtual uint32_t GetTxInIndex(const OutPoint& outpoint) const; /** @@ -102,34 +109,34 @@ class CFD_EXPORT TransactionContext : public Transaction { */ using Transaction::GetTxOutIndex; /** - * @brief TxOutのindexを取得する. + * @brief Get the index of TxOut. * @param[in] address address - * @return 条件に合致するTxOutのindex番号 + * @return TxOut index */ virtual uint32_t GetTxOutIndex(const Address& address) const; /** - * @brief TxInの有無を確認する. + * @brief Exist for TxIn. * @param[in] outpoint TxIn txid and vout * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxIn(const OutPoint& outpoint, uint32_t* index = nullptr) const; /** - * @brief TxOutの有無を確認する. + * @brief Check if TxOut exists. * @param[in] locking_script locking script * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxOut( const Script& locking_script, uint32_t* index = nullptr) const; /** - * @brief TxOutの有無を確認する. + * @brief Check if TxOut exists. * @param[in] address address * @param[out] index txout index. - * @retval true 存在 - * @retval false 未存在 + * @retval true exist + * @retval false not exist */ bool IsFindTxOut(const Address& address, uint32_t* index = nullptr) const; /** @@ -156,9 +163,9 @@ class CFD_EXPORT TransactionContext : public Transaction { */ using Transaction::AddTxIn; /** - * @brief TxInを追加する. + * @brief Add TxIn. * @param[in] outpoint TxIn txid and vout - * @return 追加したTxInのindex位置 + * @return Index position of added TxIn */ virtual uint32_t AddTxIn(const OutPoint& outpoint); /** @@ -166,24 +173,26 @@ class CFD_EXPORT TransactionContext : public Transaction { */ using Transaction::AddTxOut; /** - * @brief TxOutを追加する. - * @param[in] address 送金先アドレス - * @param[in] value 送金額 - * @return 追加したTxOutのIndex番号 + * @brief Add TxOut. + * @param[in] address Remittance destination address + * @param[in] value Amount + * @return TxOut Index */ virtual uint32_t AddTxOut(const Address& address, const Amount& value); /** - * @brief TxInを除外したサイズを取得する。 - * @return TxInを除外したTxサイズ(Serialize) + * @brief Get the size excluding TxIn. + * @param[in] use_witness witness use flag. + * @return Tx size excluding TxIn */ - uint32_t GetSizeIgnoreTxIn() const; + uint32_t GetSizeIgnoreTxIn(bool use_witness = true) const; /** - * @brief TxInを除外した仮想サイズを取得する。 - * @return TxInを除外したTx仮想サイズ(Serialize) + * @brief Get the virtual size excluding TxIn. + * @param[in] use_witness witness use flag. + * @return Tx virtual size excluding TxIn */ - uint32_t GetVsizeIgnoreTxIn() const; + uint32_t GetVsizeIgnoreTxIn(bool use_witness = true) const; // state-sequence-api /** @@ -207,6 +216,12 @@ class CFD_EXPORT TransactionContext : public Transaction { * @param[in] utxos utxo list. */ void CollectInputUtxo(const std::vector& utxos); + /** + * @brief Get UTXO by outpoint + * @param[in] outpoint TxIn txid and vout + * @return UTXO + */ + UtxoData GetTxInUtxoData(const OutPoint& outpoint) const; /** * @brief TxOutのFee額を取得する. * @return Fee額 @@ -219,10 +234,13 @@ class CFD_EXPORT TransactionContext : public Transaction { * @param[in] privkey private key. * @param[in] sighash_type sighash type. * @param[in] has_grind_r calcurate signature glind-r flag. (default:true) + * @param[in] aux_rand auxiliary random data used to create the nonce. + * @param[in] annex annex byte data. */ void SignWithKey( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, - SigHashType sighash_type = SigHashType(), bool has_grind_r = true); + SigHashType sighash_type = SigHashType(), bool has_grind_r = true, + const ByteData256* aux_rand = nullptr, const ByteData* annex = nullptr); /** * @brief set ignore verify target. * @param[in] outpoint utxo target. @@ -243,41 +261,29 @@ class CFD_EXPORT TransactionContext : public Transaction { */ ByteData Finalize(); -#if 0 - /** - * @brief clear sign area all. - */ - void ClearSign(); - /** - * @brief clear sign area on outpoint. - * @param[in] outpoint utxo target. - */ - void ClearSign(const OutPoint& outpoint); -#endif - // sign-api /** - * @brief 指定されたPubkeyHash形式のTxInのSignatureHashを計算する. - * @param[in] outpoint SignatureHash算出対象のTxInのtxid&vout - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @brief Calculates the TxIn Signature Hash in the specified PubkeyHash format. + * @param[in] outpoint TxIn txid and vout + * @param[in] pubkey Public key + * @param[in] sighash_type SigHash type + * @param[in] value UTXO amount specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Pubkey& pubkey, SigHashType sighash_type, const Amount& value = Amount(), WitnessVersion version = WitnessVersion::kVersionNone) const; /** - * @brief 指定されたScriptHash形式のTxInのSignatureHashを計算する. - * @details OP_CODESEPARATORが存在するScriptについては未対応 - * @param[in] outpoint SignatureHash算出対象のTxInのtxid&vout - * @param[in] redeem_script ScriptHashのRedeem Script - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] version TxInで指定したUTXOのWitnessVersion - * @return 算出されたSignatureHashのHex文字列 + * @brief Calculates the TxIn Signature Hash in the specified ScriptHash format. + * @details Not supported for Scripts with OP_CODESEPARATOR + * @param[in] outpoint TxIn txid and vout + * @param[in] redeem_script Script Hash Redeem Script + * @param[in] sighash_type SigHash type + * @param[in] value UTXO amount specified by TxIn + * @param[in] version Witness Version of UTXO specified by TxIn + * @return Signature hash */ virtual ByteData CreateSignatureHash( const OutPoint& outpoint, const Script& redeem_script, @@ -285,14 +291,30 @@ class CFD_EXPORT TransactionContext : public Transaction { WitnessVersion version = WitnessVersion::kVersionNone) const; /** - * @brief 指定されたPubkeyHash形式のTxInに署名する. - * @param[in] outpoint TxIn - * @param[in] pubkey SignatureHashの公開鍵 - * @param[in] privkey SignatureHashの秘密鍵 - * @param[in] sighash_type SigHashType値 - * @param[in] value TxInで指定したUTXOのamount - * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) - * @param[in] has_grind_r signature計算時のオプション + * @brief Calculate Taproot Signature Hash. + * @details It is necessary to set UTXO of all Inputs in advance. + * @param[in] outpoint target outpoint + * @param[in] sighash_type sighash type + * @param[in] tap_leaf_hash tapleaf hash. + * @param[in] code_separator_position OP_CODESEPARATOR position. + * @param[in] annex annex byte data. + * @return Signature hash + */ + ByteData256 CreateSignatureHashByTaproot( + const OutPoint& outpoint, const SigHashType& sighash_type, + const ByteData256* tap_leaf_hash = nullptr, + const uint32_t* code_separator_position = nullptr, + const ByteData* annex = nullptr) const; + + /** + * @brief Sign the specified TxIn with pubkey hash. + * @param[in] outpoint outpoint + * @param[in] pubkey Public key + * @param[in] privkey Private key + * @param[in] sighash_type SigHash Type + * @param[in] value amount of UTXO + * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) + * @param[in] has_grind_r Grind-R option for sign. */ void SignWithPrivkeySimple( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, @@ -300,9 +322,23 @@ class CFD_EXPORT TransactionContext : public Transaction { AddressType address_type = AddressType::kP2wpkhAddress, bool has_grind_r = true); + /** + * @brief Sign the specified TxIn with Taproot. + * @details It is necessary to set UTXO of all Inputs in advance. + * @param[in] outpoint outpoint + * @param[in] privkey private key for schnorr pubkey + * @param[in] sighash_type SigHashType + * @param[in] aux_rand auxiliary random data used to create the nonce. + * @param[in] annex annex byte data. + */ + void SignWithSchnorrPrivkeySimple( + const OutPoint& outpoint, const Privkey& privkey, + const SigHashType& sighash_type = SigHashType(), + const ByteData256* aux_rand = nullptr, const ByteData* annex = nullptr); + /** * @brief add pubkey-hash sign data to target outpoint. - * @param[in] outpoint TxIn + * @param[in] outpoint outpoint * @param[in] signature signature * @param[in] pubkey pubkey * @param[in] address_type address-type.(P2WPKH, P2SH-P2WPKH, P2PKH) @@ -326,11 +362,12 @@ class CFD_EXPORT TransactionContext : public Transaction { /** * @brief add multisig sign data to target outpoint. - * @details 追加するsignatureの順序は、redeem - * scriptのpubkeyとsignatures内のrelatedPubkeyで - * 対応をとって自動的に整列される. - * (relatedPubkeyが設定されていない場合は、relatedPubkeyが - * 設定されているsignatureを追加した後にsignParamの順序でsignatureを追加) + * @details The order of the signatures to be added is \ + * automatically arranged by correspondence between \ + * the pubkey of the redeem script and the relatedPubkey \ + * in the signatures. + * (If relatedPubkey is not set, add signatures in the order of \ + * signParam after adding signatures with relatedPubkey set) * @param[in] outpoint TxIn * @param[in] signatures signature list * @param[in] redeem_script redeem script @@ -340,13 +377,36 @@ class CFD_EXPORT TransactionContext : public Transaction { const OutPoint& outpoint, const std::vector& signatures, const Script& redeem_script, AddressType hash_type); + /** + * @brief add schnorr-taproot sign data to target outpoint. + * @param[in] outpoint TxIn outpoint + * @param[in] signature signature + * @param[in] annex annex + */ + void AddSchnorrSign( + const OutPoint& outpoint, const SchnorrSignature& signature, + const ByteData* annex = nullptr); + + /** + * @brief add schnorr-taproot sign data to target outpoint. + * @param[in] outpoint xIn outpoint + * @param[in] tree script tree + * @param[in] internal_pubkey internal schnorr pubkey + * @param[in] sign_data_list sign data list + * @param[in] annex annex + */ + void AddTapScriptSign( + const OutPoint& outpoint, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const std::vector& sign_data_list, + const ByteData* annex = nullptr); + /** * @brief add sign data to target outpoint. * @param[in] outpoint TxIn * @param[in] sign_params sign data list * @param[in] insert_witness use witness * @param[in] clear_stack clear stack data before add. - * @return SignDataが付与されたTransactionController */ void AddSign( const OutPoint& outpoint, const std::vector& sign_params, @@ -391,17 +451,31 @@ class CFD_EXPORT TransactionContext : public Transaction { const OutPoint& outpoint, const Script& script, SigHashType sighash_type, const Amount& value = Amount(), WitnessVersion version = WitnessVersion::kVersionNone) const; + /** + * @brief Verify signature which is specified (taproot) input data. + * @param[in] signature signature to be verified. + * @param[in] outpoint TxIn + * @param[in] utxo_list utxo list for calculate sighash. + * @param[in] pubkey internal schnorr public key. + * @param[in] annex annex + * @retval true correct signature. + * @retval false incorrect signature. + */ + bool VerifyInputSchnorrSignature( + const SchnorrSignature& signature, const OutPoint& outpoint, + const std::vector& utxo_list, const SchnorrPubkey& pubkey, + const ByteData* annex = nullptr) const; /** - * @brief ロックタイムからデフォルトのシーケンス番号を取得する。 - * @retval 0xffffffff locktime値無効 - * @retval 0xfffffffe locktime値有効 + * @brief Get the default sequence number from the lock time. + * @retval 0xffffffff locktime disable + * @retval 0xfffffffe locktime enable */ uint32_t GetDefaultSequence() const; /** - * @brief ロックタイムからlocktime値無効のシーケンス番号を取得する。 - * @retval 0xffffffff locktime値無効 + * @brief Get the default sequence number from the lock time. + * @retval 0xffffffff locktime disable */ static uint32_t GetLockTimeDisabledSequence(); @@ -422,11 +496,30 @@ class CFD_EXPORT TransactionContext : public Transaction { */ virtual void CallbackStateChange(uint32_t type); + /** + * @brief find target outpoint utxo list. + * @param[in] outpoint outpoint + * @param[out] utxo utxo + * @retval true exist + * @retval false not exist + */ + bool IsFindUtxoMap(const OutPoint& outpoint, UtxoData* utxo = nullptr) const; + + /** + * @brief find target outpoint from list. + * @param[in] list outpoint list + * @param[in] outpoint outpoint + * @retval true exist + * @retval false not exist + */ + bool IsFindOutPoint( + const std::vector& list, const OutPoint& outpoint) const; + private: /** * @brief utxo map. */ - std::map utxo_map_; + std::vector utxo_map_; /** * @brief utxo signed map. (outpoint, SigHashType) */ @@ -434,11 +527,11 @@ class CFD_EXPORT TransactionContext : public Transaction { /** * @brief utxo verify map. (outpoint) */ - std::set verify_map_; + std::vector verify_map_; /** * @brief utxo verify ignore map. (outpoint) */ - std::set verify_ignore_map_; + std::vector verify_ignore_map_; }; // ---------------------------------------------------------------------------- diff --git a/include/cfd/cfd_transaction_common.h b/include/cfd/cfd_transaction_common.h index 510581d0..4902cb7a 100644 --- a/include/cfd/cfd_transaction_common.h +++ b/include/cfd/cfd_transaction_common.h @@ -2,7 +2,7 @@ /** * @file cfd_transaction_common.h * - * @brief Transaction操作共通の関連クラス定義 + * @brief Related class definition common to Transaction operations */ #ifndef CFD_INCLUDE_CFD_CFD_TRANSACTION_COMMON_H_ #define CFD_INCLUDE_CFD_CFD_TRANSACTION_COMMON_H_ @@ -47,7 +47,7 @@ using cfd::core::ElementsConfidentialAddress; /** * @typedef SignDataType - * @brief SignData種別 + * @brief Sign data type */ enum SignDataType { kSign = 0, @@ -58,20 +58,23 @@ enum SignDataType { }; /** - * @brief UTXO構造体 + * @brief UTXO structure */ -struct UtxoData { - uint64_t block_height; //!< blick高 - BlockHash block_hash; //!< block hash - Txid txid; //!< txid - uint32_t vout; //!< vout - Script locking_script; //!< locking script - Script redeem_script; //!< script - Address address; //!< address - std::string descriptor; //!< output descriptor - Amount amount; //!< amount - AddressType address_type; //!< address type - void* binary_data; //!< binary data option +struct CFD_EXPORT UtxoData { + public: + uint64_t block_height = 0; //!< blick height + BlockHash block_hash; //!< block hash + Txid txid; //!< txid + uint32_t vout = 0; //!< vout + Script locking_script; //!< locking script + Script redeem_script; //!< script + Address address; //!< address + std::string descriptor; //!< output descriptor + Amount amount; //!< amount + //! address type + AddressType address_type = AddressType::kP2shAddress; + //! binary data option + void* binary_data = nullptr; #ifndef CFD_DISABLE_ELEMENTS ConfidentialAssetId asset; //!< asset // elements @@ -81,11 +84,82 @@ struct UtxoData { ConfidentialValue value_commitment; //!< value commitment #endif // CFD_DISABLE_ELEMENTS Script scriptsig_template; //!< scriptsig template - // int32_t status; //!< utxo status (reserved) + + public: + /** + * @brief constructor. + */ + UtxoData(); +#ifndef CFD_DISABLE_ELEMENTS + /** + * @brief constructor. + * @param[in] block_height block_height + * @param[in] block_hash block_hash + * @param[in] txid txid + * @param[in] vout vout + * @param[in] locking_script locking_script + * @param[in] redeem_script redeem_script + * @param[in] address address + * @param[in] descriptor descriptor + * @param[in] amount amount + * @param[in] address_type address_type + * @param[in] binary_data binary_data + * @param[in] asset asset + * @param[in] confidential_address confidential_address + * @param[in] asset_blind_factor asset_blind_factor + * @param[in] amount_blind_factor amount_blind_factor + * @param[in] value_commitment value_commitment + * @param[in] scriptsig_template scriptsig_template + */ + explicit UtxoData( + uint64_t block_height, const BlockHash& block_hash, const Txid& txid, + uint32_t vout, const Script& locking_script, const Script& redeem_script, + const Address& address, const std::string& descriptor, + const Amount& amount, AddressType address_type, void* binary_data, + const ConfidentialAssetId& asset, + const ElementsConfidentialAddress& confidential_address, + const BlindFactor& asset_blind_factor, + const BlindFactor& amount_blind_factor, + const ConfidentialValue& value_commitment, + const Script& scriptsig_template); +#else + /** + * @brief constructor. + * @param[in] block_height block_height + * @param[in] block_hash block_hash + * @param[in] txid txid + * @param[in] vout vout + * @param[in] locking_script locking_script + * @param[in] redeem_script redeem_script + * @param[in] address address + * @param[in] descriptor descriptor + * @param[in] amount amount + * @param[in] address_type address_type + * @param[in] binary_data binary_data + * @param[in] scriptsig_template scriptsig_template + */ + explicit UtxoData( + uint64_t block_height, const BlockHash& block_hash, const Txid& txid, + uint32_t vout, const Script& locking_script, const Script& redeem_script, + const Address& address, const std::string& descriptor, + const Amount& amount, AddressType address_type, void* binary_data, + const Script& scriptsig_template); +#endif // CFD_DISABLE_ELEMENTS + /** + * @brief copy constructor. + * @param[in] object object + */ + UtxoData(const UtxoData& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + UtxoData& operator=(const UtxoData& object) &; }; /** - * @brief Coin関連のAPIクラス + * @brief Utxo related API classes */ class CFD_EXPORT UtxoUtil { public: @@ -112,16 +186,16 @@ class CFD_EXPORT UtxoUtil { }; /** - * @brief Sign生成のためのデータモデル + * @brief Data model for sign generation */ class CFD_EXPORT SignParameter { public: /** - * @brief コンストラクタ(for vector) + * @brief constructor(for vector) */ SignParameter(); /** - * @brief コンストラクタ(Type: auto) + * @brief constructor(Type: auto) * @param[in] text_message text data * @param[in] der_encode flag of need der encode * @param[in] sighash_type sighash type (SigHashType) @@ -131,7 +205,7 @@ class CFD_EXPORT SignParameter { const SigHashType sighash_type = SigHashType(SigHashAlgorithm::kSigHashAll)); /** - * @brief コンストラクタ(Type: Sign) + * @brief constructor(Type: Sign) * @param[in] data byte data * @param[in] der_encode flag of need der encode * @param[in] sighash_type sighash type (SigHashType) @@ -141,44 +215,44 @@ class CFD_EXPORT SignParameter { const SigHashType sighash_type = SigHashType(SigHashAlgorithm::kSigHashAll)); /** - * @brief コンストラクタ(Type: Binary) + * @brief constructor(Type: Binary) * @param[in] data data */ explicit SignParameter(const ByteData& data); /** - * @brief コンストラクタ(Type: Pubkey) + * @brief constructor(Type: Pubkey) * @param[in] pubkey pubkey data */ explicit SignParameter(const Pubkey& pubkey); /** - * @brief コンストラクタ(Type: RedeemScript) + * @brief constructor(Type: RedeemScript) * @param[in] redeem_script redeem script data */ explicit SignParameter(const Script& redeem_script); /** - * @brief コンストラクタ(Type: ScriptOperator) + * @brief constructor(Type: ScriptOperator) * @param[in] op_code op code */ explicit SignParameter(const ScriptOperator& op_code); /** - * @brief コピーコンストラクタ. - * @param[in] sign_parameter Sign生成情報オブジェクト + * @brief copy constructor. + * @param[in] sign_parameter object */ SignParameter(const SignParameter& sign_parameter); /** - * @brief コピーコンストラクタ. - * @param[in] sign_parameter Sign生成情報オブジェクト - * @return SignParameterオブジェクト + * @brief copy constructor. + * @param[in] sign_parameter object + * @return object */ SignParameter& operator=(const SignParameter& sign_parameter); /** - * @brief RelatedPubkeyのセット + * @brief Set the related pubkey. * @param[in] pubkey realated pubkey */ void SetRelatedPubkey(const Pubkey& pubkey); /** - * @brief dataを取得する + * @brief Get data * @return data */ ByteData GetData() const; @@ -194,27 +268,27 @@ class CFD_EXPORT SignParameter { */ bool IsOpCode() const; /** - * @brief data typeを取得する + * @brief Get data type * @return data */ SignDataType GetDataType() const; /** - * @brief RelatedPubkeyを取得する + * @brief Get RelatedPubkey * @return related pubkey data */ Pubkey GetRelatedPubkey() const; /** - * @brief DerEncodeフラグを取得する - * @return der encode 実施フラグ + * @brief Get DerEncode + * @return der encode use flag */ bool IsDerEncode() const; /** - * @brief SigHashTypeを取得する + * @brief Get sighash type * @return sighash type */ SigHashType GetSigHashType() const; /** - * @brief 格納された情報でdataをsignatureへ変換する + * @brief Convert data to signature with stored information * @return signature data */ ByteData ConvertToSignature() const; diff --git a/include/cfd/cfd_utxo.h b/include/cfd/cfd_utxo.h index 73bea415..5df21897 100644 --- a/include/cfd/cfd_utxo.h +++ b/include/cfd/cfd_utxo.h @@ -2,7 +2,7 @@ /** * @file cfd_utxo.h * - * @brief UTXO操作の関連クラス定義 + * @brief Related class definitions for UTXO operations */ #ifndef CFD_INCLUDE_CFD_CFD_UTXO_H_ #define CFD_INCLUDE_CFD_CFD_UTXO_H_ @@ -30,13 +30,13 @@ using cfd::core::Txid; #ifndef CFD_DISABLE_ELEMENTS using cfd::core::ConfidentialAssetId; #endif // CFD_DISABLE_ELEMENTS -//! Asset Amountマップへのエイリアス (key: asset(str), value: amount) +//! Alias ​​to Asset Amount map (key: asset(str), value: amount) using AmountMap = std::map; /** - * @brief 最小のデータのみを保持するUTXO構造体。 + * @brief A UTXO structure that holds only the smallest data. * @details witness_size_max, uscript_size_max, address_type - * は専用APIで算出する。 + * is calculated by the dedicated API. * @see cfd::CoinSelection::ConvertToUtxo() */ struct Utxo { @@ -55,131 +55,126 @@ struct Utxo { uint8_t asset[33]; //!< asset #endif // CFD_DISABLE_ELEMENTS /** - * @brief 任意のバイナリデータアドレス - * @details cfdでは領域だけ設けておき、アクセスはしない + * @brief Any binary data address + * @details In cfd, only the area is provided and it is not accessed. */ void* binary_data; -#if 0 - int32_t status; //!< utxo status (reserved) - // elements - uint8_t confidential_key[33]; //!< Confidential key -#endif // if 0 // calculate - uint64_t effective_value; //!< amountからfeeを除外した有効額 - uint64_t fee; //!< fee - uint64_t long_term_fee; //!< 長期間後のfee + uint64_t effective_value; //!< Effective amount excluding fee from amount + uint64_t fee; //!< fee + uint64_t long_term_fee; //!< Fee after a long term + int64_t effective_k_value; //!< Effective amount for knapsack }; /** - * @brief UTXOのフィルタリング条件を指定する。 - * utxoのamount上限などの指定に利用することを想定 + * @brief Specify UTXO filtering conditions. */ struct UtxoFilter { - uint32_t reserved; //!< 予約領域 + uint32_t reserved; //!< reserved }; /** - * @brief CoinSelectionのオプション情報を保持するクラス + * @brief Class that holds option information of CoinSelection. */ class CFD_EXPORT CoinSelectionOption { public: /** - * @brief コンストラクタ + * @brief constructor. */ CoinSelectionOption(); /** - * @brief BnB使用フラグを取得します. - * @retval true BnB使用 - * @retval false BnB未使用 + * @brief Get the BnB using flag. + * @retval true use BnB + * @retval false unuse BnB */ bool IsUseBnB() const; /** - * @brief 出力変更サイズを取得します. - * @return 出力変更サイズ + * @brief Get the output change size. + * @return output change size. */ uint32_t GetChangeOutputSize() const; /** - * @brief 出力変更サイズを取得します. - * @return 出力変更サイズ + * @brief Get the spend change size. + * @return spend change size. */ uint32_t GetChangeSpendSize() const; /** - * @brief 効果的なfeeのbaserateを取得します. - * @return 効果的なfeeのbaserate + * @brief Get an effective fee baserate. + * @return effective fee baserate. */ uint64_t GetEffectiveFeeBaserate() const; /** - * @brief 長期的なfeeのbaserateを取得します. - * @return 長期的なfeeのbaserate + * @brief Get a long-term fee baserate. + * @return long-term fee baserate. */ uint64_t GetLongTermFeeBaserate() const; /** - * @brief knapsack探索時の最小の加算値を取得します. + * @brief Get the minimum addition value when searching for knapsack. * @return knapsack minimum change */ int64_t GetKnapsackMinimumChange() const; /** - * @brief DustとしてFeeに取り込まれる上限額を取得します. + * @brief Get the maximum amount that will be included in Fee as dust. * @param[in] address txout address * @return dust fee satoshi */ Amount GetDustFeeAmount(const Address& address) const; /** - * @brief BnB 使用フラグを設定します. - * @param[in] use_bnb BnB 使用フラグ + * @brief Set the BnB using flag. + * @param[in] use_bnb BnB using flag. */ void SetUseBnB(bool use_bnb); /** - * @brief 出力変更サイズを設定します. - * @param[in] size サイズ + * @brief Set the output change size. + * @param[in] size output change size. */ void SetChangeOutputSize(size_t size); /** - * @brief 受入変更サイズを設定します. - * @param[in] size サイズ + * @brief Set the spend change size. + * @param[in] size spend change size. */ void SetChangeSpendSize(size_t size); /** - * @brief 効果的なfeeのbaserateを設定します. + * @brief Set an effective fee baserate. * @param[in] baserate fee baserate (for BTC/byte) */ void SetEffectiveFeeBaserate(double baserate); /** - * @brief 長期的なfeeのbaserateを設定します. + * @brief Set a long-term fee baserate. * @param[in] baserate fee baserate (for BTC/byte) */ void SetLongTermFeeBaserate(double baserate); /** - * @brief knapsack探索時の最小の加算値を設定します. + * @brief Set the minimum addition value when searching for knapsack. * @param[in] min_change knapsack minimum change */ void SetKnapsackMinimumChange(int64_t min_change); /** - * @brief DustとしてFeeに取り込まれる額のrateを設定します. + * @brief Set the maximum amount that will be included in Fee as dust. * @param[in] baserate fee baserate (for BTC/byte) */ void SetDustFeeRate(double baserate); /** - * @brief bitcoin相当でサイズ関連情報を初期化します。 + * @brief Initializes size related information equivalent to bitcoin. */ void InitializeTxSizeInfo(); #ifndef CFD_DISABLE_ELEMENTS /** - * @brief feeに使用するassetを取得します. - * @return feeに使用するasset + * @brief Get the asset to use for the fee. + * @return asset to use for the fee. */ ConfidentialAssetId GetFeeAsset() const; /** - * @brief feeに使用するassetを設定します. + * @brief Set the asset to use for the fee. * @param[in] asset asset */ void SetFeeAsset(const ConfidentialAssetId& asset); /** - * @brief DustとしてFeeに取り込まれる上限額を取得します. + * @brief Get the maximum amount that will be included in Fee as dust. * @param[in] address txout address * @return dust fee satoshi */ @@ -204,53 +199,53 @@ class CFD_EXPORT CoinSelectionOption { void GetBlindInfo(int* exponent, int* minimum_bits) const; /** - * @brief ConfidentialTxベースでサイズ関連情報を初期化します。 + * @brief Initializes size-related information based on Confidential Tx. */ void InitializeConfidentialTxSizeInfo(); #endif // CFD_DISABLE_ELEMENTS private: - bool use_bnb_ = true; //!< BnB 使用フラグ - uint32_t change_output_size_ = 0; //!< 出力変更サイズ - uint32_t change_spend_size_ = 0; //!< 受入変更サイズ + bool use_bnb_ = true; //!< BnB using flag + uint32_t change_output_size_ = 0; //!< output change size + uint32_t change_spend_size_ = 0; //!< spend change size uint64_t effective_fee_baserate_; //!< fee baserate uint64_t long_term_fee_baserate_; //!< longterm fee baserate int64_t knapsack_minimum_change_; //!< knapsack min change int64_t dust_fee_rate_; //!< dust fee rate #ifndef CFD_DISABLE_ELEMENTS - ConfidentialAssetId fee_asset_; //!< feeとして利用するasset + ConfidentialAssetId fee_asset_; //!< asset to be used as a fee int exponent_ = 0; //!< rangeproof exponent value int minimum_bits_ = cfd::core::kDefaultBlindMinimumBits; //!< minimum bits #endif // CFD_DISABLE_ELEMENTS }; /** - * @brief CoinSelection計算を行うクラス + * @brief Class that performs CoinSelection calculation */ class CFD_EXPORT CoinSelection { public: /** - * @brief コンストラクタ + * @brief Constructor */ CoinSelection(); /** - * @brief コンストラクタ + * @brief Constructor * @param[in] use_bnb */ explicit CoinSelection(bool use_bnb); /** - * @brief 最小のCoinを選択する。 - * @param[in] target_value 収集額 - * @param[in] utxos 検索対象UTXO一覧 - * @param[in] filter UTXO収集フィルタ情報 - * @param[in] option_params オプション情報 + * @brief Select the smallest Coin. + * @param[in] target_value Collection amount + * @param[in] utxos List of UTXOs to be searched + * @param[in] filter UTXO collection filter (reserved) + * @param[in] option_params collect Option * @param[in] tx_fee_value transaction fee information - * @param[out] select_value UTXO収集成功時、合計収集額 - * @param[out] utxo_fee_value UTXO収集成功時、utxo分のfee金額 - * @param[out] searched_bnb BnBで検索したかのフラグ - * @return UTXO一覧。空の場合はエラー終了。 + * @param[out] select_value Total collection amount + * @param[out] utxo_fee_value the fee amount for utxo + * @param[out] searched_bnb Flag of whether you searched with BnB + * @return UTXO list. If it is empty, the error ends. */ std::vector SelectCoins( const Amount& target_value, const std::vector& utxos, @@ -260,22 +255,23 @@ class CFD_EXPORT CoinSelection { #ifndef CFD_DISABLE_ELEMENTS /** - * @brief 最小のCoinを選択する。(マルチアセット版) - * @details utxosに対して、map_target_valueのkeyで指定された asset id で - * CoinSelectionを行う。また、option_paramで fee asset が指定されている場合 - * 対象のassetに対してのCoinSelectionも実施する。 - * @param[in] map_target_value Asset毎の収集額map - * bitcoinの場合はkeyに空文字を設定 - * @param[in,out] utxos 検索対象UTXO一覧 - * @param[in] filter UTXO収集フィルタ情報 - * @param[in] option_params オプション情報 + * @brief Select the smallest Coin. (Multi-asset version) + * @details CoinSelection is performed on utxos with the asset id \ + * specified by the key of map_target_value. \ + * In addition, if fee asset is specified in option_param, \ + * CoinSelection for the target asset is also executed. + * @param[in] map_target_value Collection amount map for each Asset. + * For bitcoin, set the key to an empty string + * @param[in,out] utxos List of UTXOs to be searched + * @param[in] filter UTXO collection filter (reserved) + * @param[in] option_params collect Option * @param[in] tx_fee_value transaction fee information - * @param[out] map_select_value UTXO収集成功時、Asset毎の合計収集額map - * map_target_valueで指定されたAsset毎の収集額が格納される - * @param[out] utxo_fee_value UTXO収集成功時、utxo分のfee金額 - * @param[out] map_searched_bnb asset毎にBnBで検索したかのフラグ - * map_target_valueで指定されたAsset毎に結果が格納される - * @return UTXO一覧。空の場合はエラー終了。 + * @param[out] map_select_value Total collection amount map. + * The collection amount for each Asset specified by map_target_value is stored. + * @param[out] utxo_fee_value the fee amount for utxo + * @param[out] map_searched_bnb Flag of whether you searched with BnB. + * The result is stored for each Asset specified by map_target_value. + * @return UTXO list. If it is empty, the error ends. */ std::vector SelectCoins( const AmountMap& map_target_value, const std::vector& utxos, @@ -286,14 +282,14 @@ class CFD_EXPORT CoinSelection { #endif // CFD_DISABLE_ELEMENTS /** - * @brief UTXO構造体への変換を行う。 + * @brief Conversion to UTXO structure. * @param[in] txid txid * @param[in] vout vout * @param[in] output_descriptor output descriptor * @param[in] amount amount * @param[in] asset asset - * @param[in] binary_data 任意のデータアドレス - * @param[out] utxo 変換後のUTXO + * @param[in] binary_data Any data address + * @param[out] utxo Converted UTXO * @param[in] scriptsig_template scriptsig template */ static void ConvertToUtxo( @@ -302,16 +298,16 @@ class CFD_EXPORT CoinSelection { Utxo* utxo, const Script* scriptsig_template = nullptr); /** - * @brief UTXO構造体への変換を行う。 - * @param[in] block_height block高 - * @param[in] block_hash blockハッシュ + * @brief Conversion to UTXO structure. + * @param[in] block_height block height + * @param[in] block_hash block hash * @param[in] txid txid * @param[in] vout vout - * @param[in] locking_script block高 + * @param[in] locking_script locking script * @param[in] output_descriptor output descriptor * @param[in] amount amount - * @param[in] binary_data 任意のデータアドレス - * @param[out] utxo 変換後のUTXO + * @param[in] binary_data Any data address + * @param[out] utxo Converted UTXO * @param[in] scriptsig_template scriptsig template */ static void ConvertToUtxo( @@ -323,17 +319,17 @@ class CFD_EXPORT CoinSelection { #ifndef CFD_DISABLE_ELEMENTS /** - * @brief UTXO構造体への変換を行う。 - * @param[in] block_height block高 - * @param[in] block_hash blockハッシュ + * @brief Conversion to UTXO structure. + * @param[in] block_height block height + * @param[in] block_hash block hash * @param[in] txid txid * @param[in] vout vout - * @param[in] locking_script block高 + * @param[in] locking_script locking script * @param[in] output_descriptor output descriptor * @param[in] amount amount * @param[in] asset asset - * @param[in] binary_data 任意のデータアドレス - * @param[out] utxo 変換後のUTXO + * @param[in] binary_data Any data address + * @param[out] utxo Converted UTXO * @param[in] scriptsig_template scriptsig template */ static void ConvertToUtxo( @@ -346,20 +342,22 @@ class CFD_EXPORT CoinSelection { protected: /** - * @brief 最小のCoinを選択する。 - * @details utxosで指定されるutxoについては、複数のassetが入力されることは - * 想定していない。そのため、複数assetが混在したutxoが入力された場合 - * 返却されるutxoには複数のassetが混在する可能性がある。 - * @param[in] target_value 収集額 - * @param[in,out] utxos 検索対象UTXO一覧 - * @param[in] filter UTXO収集フィルタ情報 - * @param[in] option_params オプション情報 - * @param[in] tx_fee_value transaction fee information - * @param[in] consider_fee feeを考慮したCoinSelectionの実施フラグ (default: true) - * @param[out] select_value UTXO収集成功時、合計収集額 - * @param[out] utxo_fee_value UTXO収集成功時、utxo分のfee金額 - * @param[out] searched_bnb BnBで検索できたかどうか - * @return UTXO一覧。空の場合はエラー終了。 + * @brief Select the smallest Coin. + * @details For utxo specified by utxos, it is not assumed that \ + * multiple assets will be input. Therefore, if a utxo with \ + * a mixture of multiple assets is input, there is a possibility \ + * that multiple assets will be mixed in the returned utxo. + * @param[in] target_value Collection amount + * @param[in] utxos List of UTXOs to be searched + * @param[in] filter UTXO collection filter (reserved) + * @param[in] option_params collect Option + * @param[in] tx_fee_value transaction fee information + * @param[in] consider_fee \ + * Coin Selection implementation flag considering fee (default: true) + * @param[out] select_value Total collection amount + * @param[out] utxo_fee_value the fee amount for utxo + * @param[out] searched_bnb Flag of whether you searched with BnB + * @return UTXO list. If it is empty, the error ends. */ std::vector SelectCoinsMinConf( const int64_t& target_value, const std::vector& utxos, @@ -369,50 +367,51 @@ class CFD_EXPORT CoinSelection { bool* searched_bnb = nullptr); /** - * @brief CoinSelection(BnB)を実施する。 - * @param[in] target_value 収集額 - * @param[in] utxos 検索対象UTXO一覧 - * @param[in] cost_of_change コストの変更範囲。 - * target_value+本値が収集上限値となる。 - * @param[in] not_input_fees TxIn部を除いたfee額 - * @param[out] select_value UTXO収集成功時、合計収集額 - * @param[out] utxo_fee_value UTXO収集成功時、utxo分のfee金額 - * @return UTXO一覧。空の場合はエラー終了。 + * @brief Perform Coin Selection (BnB). + * @param[in] target_value Collection amount + * @param[in] utxos List of UTXOs to be searched + * @param[in] cost_of_change Cost change range. + * target_value + This value is the upper limit of collection. + * @param[in] not_input_fees Fee amount excluding TxIn part + * @param[in] ignore_error ignore throw exception. + * @param[out] select_value Total collection amount + * @param[out] utxo_fee_value the fee amount for utxo + * @return UTXO list. If it is empty, the error ends. */ std::vector SelectCoinsBnB( const int64_t& target_value, const std::vector& utxos, const int64_t& cost_of_change, const Amount& not_input_fees, - int64_t* select_value, Amount* utxo_fee_value); + bool ignore_error, int64_t* select_value, Amount* utxo_fee_value); /** - * @brief CoinSelection(KnapsackSolver)を実施する。 - * @param[in] target_value 収集額 - * @param[in] utxos 検索対象UTXO一覧 - * @param[in] min_change 最小の差額 - * @param[out] select_value UTXO収集成功時、合計収集額 - * @param[out] utxo_fee_value UTXO収集成功時、utxo分のfee金額 - * @return UTXO一覧。空の場合はエラー終了。 + * @brief Perform Coin Selection (Knapsack Solver). + * @param[in] target_value Collection amount + * @param[in] utxos List of UTXOs to be searched + * @param[in] min_change Minimum change amount + * @param[out] select_value Total collection amount + * @param[out] utxo_fee_value the fee amount for utxo + * @return UTXO list. If it is empty, the error ends. */ std::vector KnapsackSolver( const int64_t& target_value, const std::vector& utxos, uint64_t min_change, int64_t* select_value, Amount* utxo_fee_value); private: - bool use_bnb_; //!< BnB 利用フラグ + bool use_bnb_; //!< BnB using flag std::vector randomize_cache_; //!< randomize cache /** - * 収集額に最も近い合計額となるUTXO一覧を決定する - * @param[in] utxos 収集額より小さいUTXO一覧 - * @param[in] n_total_value utxo一覧の合計額 - * @param[in] n_target_value 収集額 - * @param[out] vf_best 収集対象フラグ一覧 - * @param[out] n_best 収集額に最も近い合計額 - * @param[in] iterations 繰り返し数 + * Determine the UTXO list with the total amount closest to the collected amount + * @param[in] utxos UTXO list smaller than the collected amount + * @param[in] n_total_value Total amount of utxo list + * @param[in] n_target_value Collection amount + * @param[out] vf_best List of flags to be collected + * @param[out] n_best Total amount closest to the collected amount + * @param[in] iterations Number of repetitions */ void ApproximateBestSubset( - const std::vector& utxos, uint64_t n_total_value, - uint64_t n_target_value, std::vector* vf_best, uint64_t* n_best, + const std::vector& utxos, int64_t n_total_value, + int64_t n_target_value, std::vector* vf_best, int64_t* n_best, int iterations); }; diff --git a/include/cfd/cfdapi_address.h b/include/cfd/cfdapi_address.h index 953c3926..efc12941 100644 --- a/include/cfd/cfdapi_address.h +++ b/include/cfd/cfdapi_address.h @@ -12,6 +12,7 @@ #include #include +#include "cfd/cfd_address.h" #include "cfd/cfd_common.h" #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_descriptor.h" @@ -25,36 +26,15 @@ using cfd::core::AddressFormatData; using cfd::core::AddressType; using cfd::core::DescriptorKeyType; using cfd::core::DescriptorScriptType; +using cfd::core::KeyData; using cfd::core::NetType; using cfd::core::Pubkey; using cfd::core::Script; -/** - * @brief DescriptorのScript系情報構造体 - */ -struct DescriptorScriptData { - DescriptorScriptType type; //!< script type - Script locking_script; //!< locking script - uint32_t depth; //!< depth - Address address; //!< address - AddressType address_type; //!< address type - Script redeem_script; //!< redeem script - DescriptorKeyType key_type; //!< key type - std::string key; //!< key string - uint32_t multisig_req_sig_num; //!< multisig num of require signatures -}; - -/** - * @brief DescriptorのKey系情報構造体 - */ -struct DescriptorKeyData { - DescriptorKeyType type; //!< key type - std::string key; //!< key string -}; - /** * @brief Address関連の関数群クラス * @details 現状は内部クラス扱い。あとで名称変更予定. + * @deprecated The function has moved to AddressFactory. */ class CFD_EXPORT AddressApi { public: @@ -119,6 +99,7 @@ class CFD_EXPORT AddressApi { * @param[out] script_list descriptor script list * @param[out] multisig_key_list descriptor multisig key list * @param[in] prefix_list address prefix list + * @param[out] key_list key data list * @return descriptor script data (top level or high security) */ DescriptorScriptData ParseOutputDescriptor( @@ -126,7 +107,8 @@ class CFD_EXPORT AddressApi { const std::string& bip32_derivation_path = "", std::vector* script_list = nullptr, std::vector* multisig_key_list = nullptr, - const std::vector* prefix_list = nullptr) const; + const std::vector* prefix_list = nullptr, + std::vector* key_list = nullptr) const; }; } // namespace api diff --git a/include/cfd/cfdapi_coin.h b/include/cfd/cfdapi_coin.h index 1944b095..bcdec8b6 100644 --- a/include/cfd/cfdapi_coin.h +++ b/include/cfd/cfdapi_coin.h @@ -30,6 +30,7 @@ using cfd::core::Txid; #ifndef CFD_DISABLE_ELEMENTS using cfd::core::ConfidentialAssetId; #endif // CFD_DISABLE_ELEMENTS +using cfd::UtxoData; /** * @brief Coin関連のAPIクラス diff --git a/include/cfdc/cfdcapi_address.h b/include/cfdc/cfdcapi_address.h index d8a3c9f2..04ed6b94 100644 --- a/include/cfdc/cfdcapi_address.h +++ b/include/cfdc/cfdcapi_address.h @@ -49,7 +49,11 @@ enum CfdAddressType { /** P2sh wrapped segwit address (Script Hash) */ kCfdP2shP2wshAddress, /** P2sh wrapped segwit address (Pubkey Hash) */ - kCfdP2shP2wpkhAddress + kCfdP2shP2wpkhAddress, + /** Taproot address */ + kCfdTaprootAddress, + /** Witness unknown address */ + kCfdWitnessUnknownAddress = 0xff }; /** @@ -67,7 +71,11 @@ enum CfdHashType { /** P2sh wrapped segwit Script Hash */ kCfdP2shP2wsh, /** P2sh wrapped segwit Pubkey Hash */ - kCfdP2shP2wpkh + kCfdP2shP2wpkh, + /** Taproot */ + kCfdTaproot, + /** Unknown */ + kCfdUnknown = 0xff }; /** @@ -78,7 +86,7 @@ enum CfdWitnessVersion { kCfdWitnessVersionNone = -1, /** version 0 */ kCfdWitnessVersion0 = 0, - /** version 1 (for future use) */ + /** version 1 (taproot) */ kCfdWitnessVersion1, /** version 2 (for future use) */ kCfdWitnessVersion2, @@ -116,6 +124,8 @@ enum CfdWitnessVersion { * @brief sighash type */ enum CfdSighashType { + /** SIGHASH_DEFAULT */ + kCfdSigHashDefault = 0, /** SIGHASH_ALL */ kCfdSigHashAll = 0x01, /** SIGHASH_NONE */ diff --git a/include/cfdc/cfdcapi_common.h b/include/cfdc/cfdcapi_common.h index cf358691..89829978 100644 --- a/include/cfdc/cfdcapi_common.h +++ b/include/cfdc/cfdcapi_common.h @@ -61,7 +61,9 @@ enum CfdErrorCode { /** disk access error */ kCfdDiskAccessError = 6, /** Signature Verification Fail */ - kCfdSignVerificationError = 7 + kCfdSignVerificationError = 7, + /** NotFound error */ + kCfdNotFoundError = 8 }; /** @@ -196,6 +198,136 @@ CFDC_API int CfdRequestExecuteJson( CFDC_API int CfdSerializeByteData( void* handle, const char* buffer, char** output); +/** + * @brief Encrypto by AES. + * @param[in] handle handle pointer. + * @param[in] key key byte array hex. + * @param[in] cbc_iv initial vector (with cbc) hex. + * @param[in] buffer byte array hex. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdEncryptAES( + void* handle, const char* key, const char* cbc_iv, const char* buffer, + char** output); + +/** + * @brief Decrypto by AES. + * @param[in] handle handle pointer. + * @param[in] key key byte array hex. + * @param[in] cbc_iv initial vector (with cbc) hex. + * @param[in] buffer AES byte array hex. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdDecryptAES( + void* handle, const char* key, const char* cbc_iv, const char* buffer, + char** output); + +/** + * @brief Encode base64. + * @param[in] handle handle pointer. + * @param[in] buffer byte array hex. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdEncodeBase64(void* handle, const char* buffer, char** output); + +/** + * @brief Decode base64. + * @param[in] handle handle pointer. + * @param[in] base64 base64 string. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdDecodeBase64(void* handle, const char* base64, char** output); + +/** + * @brief Encode base58. + * @param[in] handle handle pointer. + * @param[in] buffer byte array hex. + * @param[in] use_checksum using checksum. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdEncodeBase58( + void* handle, const char* buffer, bool use_checksum, char** output); + +/** + * @brief Decode base58. + * @param[in] handle handle pointer. + * @param[in] base58 base58 string. + * @param[in] use_checksum using checksum. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdDecodeBase58( + void* handle, const char* base58, bool use_checksum, char** output); + +/** + * @brief Hash with ripemd160. + * @param[in] handle handle pointer. + * @param[in] message message string. (text or hex) + * @param[in] has_text message has text. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdRipemd160( + void* handle, const char* message, bool has_text, char** output); + +/** + * @brief Hash with sha256. + * @param[in] handle handle pointer. + * @param[in] message message string. (text or hex) + * @param[in] has_text message has text. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdSha256( + void* handle, const char* message, bool has_text, char** output); + +/** + * @brief Hash with hash160. + * @param[in] handle handle pointer. + * @param[in] message message string. (text or hex) + * @param[in] has_text message has text. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdHash160( + void* handle, const char* message, bool has_text, char** output); + +/** + * @brief Hash with hash256. + * @param[in] handle handle pointer. + * @param[in] message message string. (text or hex) + * @param[in] has_text message has text. + * @param[out] output output byte array hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdHash256( + void* handle, const char* message, bool has_text, char** output); + #ifdef __cplusplus #if 0 { diff --git a/include/cfdc/cfdcapi_key.h b/include/cfdc/cfdcapi_key.h index 386a1ec0..e6464462 100644 --- a/include/cfdc/cfdcapi_key.h +++ b/include/cfdc/cfdcapi_key.h @@ -221,6 +221,33 @@ CFDC_API int CfdSignSchnorrWithNonce( void* handle, const char* msg, const char* sk, const char* nonce, char** signature); +/** + * @brief Add a sighash type to a schnorr signature. + * + * @param[in] handle cfd handle. + * @param[in] signature the signature. + * @param[in] sighash_type the sighash type. + * @param[in] anyone_can_pay the anyone can pay flag. + * @param[out] added_signature the signature. + * @return CfdErrorCode + */ +CFDC_API int CfdAddSighashTypeInSchnorrSignature( + void* handle, const char* signature, int sighash_type, bool anyone_can_pay, + char** added_signature); + +/** + * @brief Get a sighash type from a schnorr signature. + * + * @param[in] handle cfd handle. + * @param[in] signature the signature. + * @param[out] sighash_type the sighash type. + * @param[out] anyone_can_pay the anyone can pay flag. + * @return CfdErrorCode + */ +CFDC_API int CfdGetSighashTypeFromSchnorrSignature( + void* handle, const char* signature, int* sighash_type, + bool* anyone_can_pay); + /** * @brief Compute a signature point for a Schnorr signature. * @@ -378,6 +405,18 @@ CFDC_API int CfdGetPubkeyFromPrivkey( void* handle, const char* privkey, const char* wif, bool is_compressed, char** pubkey); +/** + * @brief get pubkey fingerprint. + * @param[in] handle cfd handle. + * @param[in] pubkey pubkey hex. + * @param[out] fingerprint fingerprint. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPubkeyFingerprint( + void* handle, const char* pubkey, char** fingerprint); + /** * @brief Compress pubkey. * @param[in] handle cfd handle. diff --git a/include/cfdc/cfdcapi_psbt.h b/include/cfdc/cfdcapi_psbt.h new file mode 100644 index 00000000..3a167e77 --- /dev/null +++ b/include/cfdc/cfdcapi_psbt.h @@ -0,0 +1,762 @@ +/* Copyright 2021 CryptoGarage */ +/** + * @file cfdcapi_psbt.h + * + * @brief API definition file of PSBT for used in cfd-capi + */ +#ifndef CFD_INCLUDE_CFDC_CFDCAPI_PSBT_H_ +#define CFD_INCLUDE_CFDC_CFDCAPI_PSBT_H_ + +#ifdef __cplusplus +extern "C" { +#if 0 +} +#endif +#endif /* __cplusplus */ + +#include "cfdc/cfdcapi_address.h" +#include "cfdc/cfdcapi_common.h" + +/** PSBT record type */ +enum CfdPsbtRecordType { + /** record type: global */ + kCfdPsbtRecordTypeGlobal = 1, + /** record type: input */ + kCfdPsbtRecordTypeInput = 2, + /** record type: output */ + kCfdPsbtRecordTypeOutput = 3, +}; + +/** PSBT record kind */ +enum CfdPsbtRecordKind { + /** record: input signature */ + kCfdPsbtRecordInputSignature = 1, + /** record: input bip32 */ + kCfdPsbtRecordInputBip32 = 2, + /** record: output bip32 */ + kCfdPsbtRecordOutputBip32 = 3, + /** record: global xpub bip32 */ + kCfdPsbtRecordGloalXpub = 4, + /** record: input final witness */ + kCfdPsbtRecordFinalWitness = 5, + /** record: input unknown keys */ + kCfdPsbtRecordInputUnknownKeys = 6, + /** record: output unknown keys */ + kCfdPsbtRecordOutputUnknownKeys = 7, + /** record: global unknown keys */ + kCfdPsbtRecordGlobalUnknownKeys = 8, +}; + +/** fund psbt option */ +enum CfdFundPsbtOption { + /** dust fee rate (double) */ + kCfdPsbtFundEstimateFeeRate = 1, + /** dust fee rate (double) */ + kCfdPsbtFundDustFeeRate = 2, + /** longterm fee rate (double) */ + kCfdPsbtFundLongTermFeeRate = 3, + /** knapsack min change (int64) */ + kCfdPsbtFundKnapsackMinChange = 4, + /** use blind fee (bool) */ + kCfdPsbtFundIsBlind = 11, + /** blind option: exponent */ + kCfdPsbtFundBlindExponent = 12, + /** blind option: minBits */ + kCfdPsbtFundBlindMinimumBits = 13, +}; + +/** + * @brief Create PSBT handle. + * @param[in,out] handle cfd handle. + * @param[in] net_type network type. + * @param[in] psbt_string psbt base64 or hex. (if empty, using tx data) + * @param[in] tx_hex_string transaction hex. (if empty, using tx version) + * @param[in] version transaction version. + * @param[in] locktime transaction lock time. + * @param[out] psbt_handle psbt handle. + * Call 'CfdFreePsbtHandle' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdCreatePsbtHandle( + void* handle, int net_type, const char* psbt_string, + const char* tx_hex_string, uint32_t version, uint32_t locktime, + void** psbt_handle); + +/** + * @brief Free PSBT handle. + * @param[in,out] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFreePsbtHandle(void* handle, void* psbt_handle); + +/** + * @brief Get a base64 encoded PSBT. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[out] psbt_base64 psbt by base64. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] psbt_hex psbt by hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtData( + void* handle, void* psbt_handle, char** psbt_base64, char** psbt_hex); + +/** + * @brief Get PSBT data. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[out] psbt_version psbt version. + * @param[out] base_tx base transaction. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] txin_count num of tx input. + * @param[out] txout_count num of tx output. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtGlobalData( + void* handle, void* psbt_handle, uint32_t* psbt_version, char** base_tx, + uint32_t* txin_count, uint32_t* txout_count); + +/** + * @brief Join a PSBT. + * @param[in,out] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @param[in] psbt_join_base64 join target psbt base64. + * @return CfdErrorCode + */ +CFDC_API int CfdJoinPsbt( + void* handle, void* psbt_handle, const char* psbt_join_base64); + +/** + * @brief Sign to PSBT. + * @param[in,out] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @param[in] privkey privkey. (hex or wif) + * @param[in] has_grind_r grind_r flag. + * @return CfdErrorCode + */ +CFDC_API int CfdSignPsbt( + void* handle, void* psbt_handle, const char* privkey, bool has_grind_r); + +/** + * @brief Combine a PSBT. + * @param[in,out] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @param[in] psbt_combine_base64 combine target psbt base64. + * @return CfdErrorCode + */ +CFDC_API int CfdCombinePsbt( + void* handle, void* psbt_handle, const char* psbt_combine_base64); + +/** + * @brief Finalize a PSBT. + * @param[in,out] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFinalizePsbt(void* handle, void* psbt_handle); + +/** + * @brief Extract a PSBT. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[out] transaction transaction hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdExtractPsbtTransaction( + void* handle, void* psbt_handle, char** transaction); + +/** + * @brief Check finalized PSBT. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @retval kCfdSuccess finalized. + * @retval kCfdSignVerificationError invalid finalized. + * @retval other illegal error. + */ +CFDC_API int CfdIsFinalizedPsbt(void* handle, void* psbt_handle); + +/** + * @brief Check finalized PSBT input. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @retval kCfdSuccess finalized. + * @retval kCfdSignVerificationError invalid finalized. + * @retval other illegal error. + */ +CFDC_API int CfdIsFinalizedPsbtInput( + void* handle, void* psbt_handle, const char* txid, uint32_t vout); + +/** + * @brief Add PSBT input with pubkey. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] sequence sequence. + * @param[in] amount utxo amount. + * @param[in] locking_script utxo locking script. + * @param[in] descriptor descriptor. + * @param[in] full_tx_hex full transaction hex. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtTxInWithPubkey( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t sequence, int64_t amount, const char* locking_script, + const char* descriptor, const char* full_tx_hex); + +/** + * @brief Add PSBT input with script. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] sequence sequence. + * @param[in] amount utxo amount. + * @param[in] locking_script utxo locking script. + * @param[in] redeem_script utxo redeem script. + * @param[in] descriptor descriptor. + * @param[in] full_tx_hex full transaction hex. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtTxInWithScript( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t sequence, int64_t amount, const char* locking_script, + const char* redeem_script, const char* descriptor, + const char* full_tx_hex); + +/** + * @brief set PSBT input utxo. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] amount utxo amount. + * @param[in] locking_script utxo locking script. + * @param[in] full_tx_hex full transaction hex. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtTxInUtxo( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int64_t amount, const char* locking_script, const char* full_tx_hex); + +/** + * @brief Set PSBT input bip32 pubkey. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] pubkey pubkey. (direct or path data) + * @param[in] fingerprint fingerprint. + * @param[in] bip32_path bip32_path. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtTxInBip32Pubkey( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* pubkey, const char* fingerprint, const char* bip32_path); + +/** + * @brief Set PSBT signature. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] pubkey pubkey. + * @param[in] der_signature der encoded signature. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtSignature( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* pubkey, const char* der_signature); + +/** + * @brief Set PSBT sighash type. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] sighash_type sighash type. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtSighashType( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int sighash_type); + +/** + * @brief Set PSBT finalize input script. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[in] scriptsig scripsig format script. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtFinalizeScript( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* scriptsig); + +/** + * @brief Clear PSBT sign data. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @return CfdErrorCode + */ +CFDC_API int CfdClearPsbtSignData( + void* handle, void* psbt_handle, const char* txid, uint32_t vout); + +/** + * @brief Get PSBT sighash type. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[out] sighash_type sighash type. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtSighashType( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int* sighash_type); + +/** + * @brief Get PSBT input utxo data. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[out] amount amount. + * @param[out] locking_script locking script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] redeem_script redeem script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] descriptor descriptor. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] full_tx_hex full transaction hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtUtxoData( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int64_t* amount, char** locking_script, char** redeem_script, + char** descriptor, char** full_tx_hex); + +/** + * @brief Get PSBT input utxo data by index. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] index input index. + * @param[out] txid txid. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] vout vout. + * @param[out] amount amount. + * @param[out] locking_script locking script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] redeem_script redeem script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] descriptor descriptor. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] full_tx_hex full transaction hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtUtxoDataByIndex( + void* handle, void* psbt_handle, uint32_t index, char** txid, + uint32_t* vout, int64_t* amount, char** locking_script, + char** redeem_script, char** descriptor, char** full_tx_hex); + +/** + * @brief Add PSBT output with pubkey. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] amount txout amount. + * @param[in] locking_script txout locking script. (if empty, use descriptor) + * @param[in] descriptor descriptor. + * @param[out] index txout index. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtTxOutWithPubkey( + void* handle, void* psbt_handle, int64_t amount, + const char* locking_script, const char* descriptor, uint32_t* index); + +/** + * @brief Add PSBT output with script. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] amount txout amount. + * @param[in] locking_script txout locking script. (if empty, use descriptor) + * @param[in] redeem_script txout redeem script. + * @param[in] descriptor descriptor. + * @param[out] index txout index. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtTxOutWithScript( + void* handle, void* psbt_handle, int64_t amount, + const char* locking_script, const char* redeem_script, + const char* descriptor, uint32_t* index); + +/** + * @brief Set PSBT output bip32 pubkey. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] index txout index. + * @param[in] pubkey pubkey. (direct or path data) + * @param[in] fingerprint fingerprint. + * @param[in] bip32_path bip32_path. + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtTxOutBip32Pubkey( + void* handle, void* psbt_handle, uint32_t index, const char* pubkey, + const char* fingerprint, const char* bip32_path); + +/** + * @brief Get PSBT input index. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @param[out] index txin index. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtTxInIndex( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t* index); + +/** + * @brief Get PSBT with pubkey record. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] kind PSBT kind. (see CfdPsbtRecordKind) + * @param[in] index input or output index. + * @param[in] pubkey pubkey or xpubkey. + * @param[out] value record value. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtPubkeyRecord( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey, char** value); // (txin:signature, key. txout:key) + +/** + * @brief Find PSBT with pubkey record. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] kind PSBT kind. (see CfdPsbtRecordKind) + * @param[in] index input or output index. + * @param[in] pubkey pubkey or xpubkey. + * @return CfdErrorCode + */ +CFDC_API int CfdIsFindPsbtPubkeyRecord( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey); + +/** + * @brief Get PSBT bip32 key data. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] kind PSBT kind. (see CfdPsbtRecordKind) + * @param[in] index input or output index. + * @param[in] pubkey pubkey or xpubkey. + * @param[out] fingerprint fingerprint. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] bip32_path bip32 path. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtBip32Data( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey, char** fingerprint, char** bip32_path); + +/** + * @brief Get PSBT pubkey list handle. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] kind PSBT kind. (see CfdPsbtRecordKind) + * @param[in] index input or output index. + * @param[out] list_num pubkey list count. + * @param[out] pubkey_list_handle pubkey list handle. + * Call 'CfdFreePsbtPubkeyList' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtPubkeyList( + void* handle, void* psbt_handle, int kind, uint32_t index, + uint32_t* list_num, void** pubkey_list_handle); + +/** + * @brief Get PSBT pubkey list data. + * @param[in,out] handle cfd handle. + * @param[in] pubkey_list_handle pubkey list handle. + * @param[in] index list index. + * @param[out] pubkey pubkey or xpubkey. (require) + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] pubkey_hex pubkey or xpubkey by hex string. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtPubkeyListData( + void* handle, void* pubkey_list_handle, uint32_t index, char** pubkey, + char** pubkey_hex); + +/** + * @brief Get PSBT pubkey list data. + * @param[in,out] handle cfd handle. + * @param[in] pubkey_list_handle pubkey list handle. + * @param[in] index list index. + * @param[out] pubkey pubkey or xpubkey. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] fingerprint fingerprint. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] bip32_path bip32 path. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtPubkeyListBip32Data( + void* handle, void* pubkey_list_handle, uint32_t index, char** pubkey, + char** fingerprint, char** bip32_path); + +/** + * @brief Free PSBT pubkey list. + * @param[in,out] handle cfd handle. + * @param[in] pubkey_list_handle pubkey list handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFreePsbtPubkeyList(void* handle, void* pubkey_list_handle); + +/** + * @brief Get PSBT data list handle. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] kind PSBT kind. (see CfdPsbtRecordKind) + * @param[in] index input or output index. + * @param[out] list_num pubkey list count. + * @param[out] pubkey_list_handle pubkey list handle. + * Call 'CfdFreePsbtPubkeyList' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtByteDataList( + void* handle, void* psbt_handle, int kind, uint32_t index, + uint32_t* list_num, void** pubkey_list_handle); + +/** + * @brief Get PSBT data list item. + * @param[in,out] handle cfd handle. + * @param[in] data_list_handle data list handle. + * @param[in] index list index. + * @param[out] data data. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtByteDataItem( + void* handle, void* data_list_handle, uint32_t index, char** data); + +/** + * @brief Free PSBT data list. + * @param[in,out] handle cfd handle. + * @param[in] data_list_handle data list handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFreePsbtByteDataList(void* handle, void* data_list_handle); + +/** + * @brief Add PSBT global bip32 xpubkey. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] xpubkey xpubkey. (direct or path data) + * @param[in] fingerprint fingerprint. + * @param[in] bip32_path bip32_path. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtGlobalXpubkey( + void* handle, void* psbt_handle, const char* xpubkey, + const char* fingerprint, const char* bip32_path); + +/** + * @brief Set PSBT redeem script or witness script. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] type psbt type. (see CfdPsbtRecordType) + * @param[in] index index. + * @param[in] redeem_script redeem script + * @return CfdErrorCode + */ +CFDC_API int CfdSetPsbtRedeemScript( + void* handle, void* psbt_handle, int type, uint32_t index, + const char* redeem_script); + +/** + * @brief Add PSBT record. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] type psbt type. (see CfdPsbtRecordType) + * @param[in] index index. (if type is global, this field is not use.) + * @param[in] key key hex. + * @param[in] value value hex. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, const char* key, + const char* value); + +/** + * @brief Get PSBT record. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] type psbt type. (see CfdPsbtRecordType) + * @param[in] index index. (if type is global, this field is not use.) + * @param[in] key key hex. + * @param[out] value value hex. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, const char* key, + char** value); + +/** + * @brief Find PSBT record. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] type psbt type. (see CfdPsbtRecordType) + * @param[in] index index. (if type is global, this field is not use.) + * @param[in] key key hex. + * @return CfdErrorCode + */ +CFDC_API int CfdIsFindPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, + const char* key); + +/** + * @brief Verify PSBT input. + * @param[in,out] handle cfd handle. + * @param[in] psbt_handle psbt handle. + * @param[in] txid txid. + * @param[in] vout vout. + * @return CfdErrorCode + */ +CFDC_API int CfdVerifyPsbtTxIn( + void* handle, void* psbt_handle, const char* txid, uint32_t vout); + +/** + * @brief Initialize fund PSBT handle. + * @param[in] handle cfd handle. + * @param[out] fund_handle fund PSBT handle. + * Call 'CfdFreeFundPsbt' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdInitializeFundPsbt(void* handle, void** fund_handle); + +/** + * @brief Add utxo on fund PSBT. + * @param[in] handle cfd handle. + * @param[in] fund_handle fund PSBT handle. + * @param[in] txid utxo txid. + * @param[in] vout utxo vout. + * @param[in] amount utxo amount. + * @param[in] asset utxo asset. + * @param[in] descriptor utxo output descriptor. + * @param[in] scriptsig_template utxo scriptsig template. + * @param[in] full_utxo_tx utxo full transaction hex. + * @return CfdErrorCode + */ +CFDC_API int CfdFundPsbtAddToUtxoList( + void* handle, void* fund_handle, const char* txid, uint32_t vout, + int64_t amount, const char* asset, const char* descriptor, + const char* scriptsig_template, const char* full_utxo_tx); + +/** + * @brief Set fund PSBT optional parameter. + * @param[in] handle cfd handle. + * @param[in] fund_handle fund PSBT handle. + * @param[in] key key. (see CfdFundPsbtOption) + * @param[in] int64_value int64 value. + * @param[in] double_value double value. + * @param[in] bool_value bool value. + * @return CfdErrorCode + */ +CFDC_API int CfdSetOptionFundPsbt( + void* handle, void* fund_handle, int key, int64_t int64_value, + double double_value, bool bool_value); + +/** + * @brief Finalize fund PSBT. + * @param[in] handle cfd handle. + * @param[in,out] psbt_handle psbt handle. + * @param[in] fund_handle fund PSBT handle. + * @param[in] change_address_descriptor change address descriptor. + * @param[out] tx_fee tx fee. + * @param[out] used_utxo_count used utxo count. + * @return CfdErrorCode + */ +CFDC_API int CfdFinalizeFundPsbt( + void* handle, void* psbt_handle, void* fund_handle, + const char* change_address_descriptor, int64_t* tx_fee, + uint32_t* used_utxo_count); + +/** + * @brief Get used utxo by fund PSBT. + * @param[in] handle cfd handle. + * @param[in] fund_handle fund PSBT handle. + * @param[in] index used utxo index. + * @param[out] utxo_index set utxo list index. + * @param[out] txid utxo txid. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] vout utxo vout. + * @param[out] amount utxo amount. + * @param[out] asset utxo asset. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] descriptor utxo descriptor. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] scriptsig_template utxo scriptsig template. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetFundPsbtUsedUtxo( + void* handle, void* fund_handle, uint32_t index, uint32_t* utxo_index, + char** txid, uint32_t* vout, int64_t* amount, char** asset, + char** descriptor, char** scriptsig_template); + +/** + * @brief Free fund PSBT. + * @param[in] handle cfd handle. + * @param[in] fund_handle fund PSBT handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFreeFundPsbt(void* handle, void* fund_handle); + +#ifdef __cplusplus +#if 0 +{ +#endif +} +#endif /* __cplusplus */ + +#endif /* CFD_INCLUDE_CFDC_CFDCAPI_PSBT_H_ NOLINT */ diff --git a/include/cfdc/cfdcapi_script.h b/include/cfdc/cfdcapi_script.h index 6c703471..768257e4 100644 --- a/include/cfdc/cfdcapi_script.h +++ b/include/cfdc/cfdcapi_script.h @@ -121,6 +121,230 @@ CFDC_API int CfdFinalizeMultisigScriptSig( CFDC_API int CfdFreeMultisigScriptSigHandle( void* handle, void* multisig_handle); +/*---------*/ +/* taproot */ +/*---------*/ +/** + * @brief initialized for taproot script tree. + * + * Next call is CfdSetInitialTapLeaf() or + * CfdSetTapScriptByWitnessStack(). + * @param[in] handle cfd handle. + * @param[out] tree_handle taproot script tree handle. + * Call 'CfdFreeTaprootScriptTreeHandle' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdInitializeTaprootScriptTree(void* handle, void** tree_handle); + +/** + * @brief Set a initial tapleaf. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] tapscript taproot script. + * @param[in] leaf_version taproot leaf version. + * @return CfdErrorCode + */ +CFDC_API int CfdSetInitialTapLeaf( + void* handle, void* tree_handle, const char* tapscript, + uint8_t leaf_version); + +/** + * @brief Set a initial tapbranch by hash. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] hash taproot script. + * @return CfdErrorCode + */ +CFDC_API int CfdSetInitialTapBranchByHash( + void* handle, void* tree_handle, const char* hash); + +/** + * @brief Set a script tree from string. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] tree_string tree string. + * @param[in] tapscript taproot script. + * @param[in] leaf_version taproot leaf version. + * @param[in] control_nodes control node list. + * @return CfdErrorCode + */ +CFDC_API int CfdSetScriptTreeFromString( + void* handle, void* tree_handle, const char* tree_string, + const char* tapscript, uint8_t leaf_version, const char* control_nodes); + +/** + * @brief Set a tapscript tree from witness stack. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] control_block control data into witness stack. + * @param[in] tapscript tapscript into witness stack. + * @param[in] internal_pubkey internal schnorr public key from control stack. + * @return CfdErrorCode + */ +CFDC_API int CfdSetTapScriptByWitnessStack( + void* handle, void* tree_handle, const char* control_block, + const char* tapscript, char** internal_pubkey); + +/** + * @brief append for taproot script tree branch hash. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] branch_hash branch hash. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTapBranchByHash( + void* handle, void* tree_handle, const char* branch_hash); + +/** + * @brief append for taproot script tree. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] branch_tree tree handle by branch. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTapBranchByScriptTree( + void* handle, void* tree_handle, void* branch_tree); + +/** + * @brief append for taproot script tree. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] tree_string tree string. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTapBranchByScriptTreeString( + void* handle, void* tree_handle, const char* tree_string); + +/** + * @brief append for tapleaf. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] tapscript taproot script. + * @param[in] leaf_version taproot leaf version. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTapBranchByTapLeaf( + void* handle, void* tree_handle, const char* tapscript, + uint8_t leaf_version); + +/** + * @brief Get base tapleaf data. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[out] leaf_version taproot leaf version. Set to 0 if not tapleaf. + * @param[out] tapscript taproot script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] tap_leaf_hash tapleaf hash. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetBaseTapLeaf( + void* handle, void* tree_handle, uint8_t* leaf_version, char** tapscript, + char** tap_leaf_hash); + +/** + * @brief Get tapbranch count. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[out] branch_count tapbranch count. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTapBranchCount( + void* handle, void* tree_handle, uint32_t* branch_count); + +/** + * @brief Get tapbranch data. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] index_from_leaf index from base tapleaf. + * @param[in] is_root_data true is getting combined hash. + * @param[out] branch_hash tapbranch hash. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] leaf_version taproot leaf version. Set to 0 if not tapleaf. + * @param[out] tapscript taproot script. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] depth_by_leaf_or_end target branch depth. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTapBranchData( + void* handle, void* tree_handle, uint8_t index_from_leaf, + bool is_root_data, char** branch_hash, uint8_t* leaf_version, + char** tapscript, uint8_t* depth_by_leaf_or_end); + +/** + * @brief Get tapbranch handle. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] index_from_leaf index from base tapleaf. + * @param[out] branch_hash tapbranch hash. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] branch_tree_handle tapbranch handle. + * Call 'CfdFreeTaprootScriptTreeHandle' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTapBranchHandle( + void* handle, void* tree_handle, uint8_t index_from_leaf, + char** branch_hash, void** branch_tree_handle); + +/** + * @brief Get tapscript hash. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] internal_pubkey internal schnorr public key. + * @param[out] hash tapscript hash. (witness program) + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] tap_leaf_hash tapleaf hash. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @param[out] control_block taproot control data. (for witness stack) + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTaprootScriptTreeHash( + void* handle, void* tree_handle, const char* internal_pubkey, char** hash, + char** tap_leaf_hash, char** control_block); + +/** + * @brief Get tapscript tweaked privkey. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[in] internal_privkey internal private key. + * @param[out] tweaked_privkey tweaked privkey. + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTaprootTweakedPrivkey( + void* handle, void* tree_handle, const char* internal_privkey, + char** tweaked_privkey); + +/** + * @brief Get tapscript hash. + * @param[in] handle cfd handle. + * @param[in] tree_handle taproot script tree handle. + * @param[out] tree_string taproot string. (proposal) + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdGetTaprootScriptTreeSrting( + void* handle, void* tree_handle, char** tree_string); + +/** + * @brief free taproot script tree handle. + * @param[in] handle handle pointer. + * @param[in] tree_handle taproot script tree handle. + * @return CfdErrorCode + */ +CFDC_API int CfdFreeTaprootScriptTreeHandle(void* handle, void* tree_handle); + #ifdef __cplusplus #if 0 { diff --git a/include/cfdc/cfdcapi_transaction.h b/include/cfdc/cfdcapi_transaction.h index 8c894acc..b9cefdc2 100644 --- a/include/cfdc/cfdcapi_transaction.h +++ b/include/cfdc/cfdcapi_transaction.h @@ -19,8 +19,13 @@ extern "C" { /** txin sequence locktime */ enum CfdSequenceLockTime { - /** disable locktime */ + /** + * @brief disable locktime + * @deprecated rename to kCfdSequenceLockTimeFinal. + */ kCfdSequenceLockTimeDisable = 0xffffffffU, + /** locktime final */ + kCfdSequenceLockTimeFinal = 0xffffffffU, /** enable locktime (maximum time) */ kCfdSequenceLockTimeEnableMax = 0xfffffffeU, }; @@ -95,6 +100,182 @@ CFDC_API int CfdAddTransactionOutput( const char* address, const char* direct_locking_script, const char* asset_string); +/** + * @brief clear witness stack on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @return CfdErrorCode + */ +CFDC_API int CfdClearWitnessStack( + void* handle, void* create_handle, const char* txid, uint32_t vout); + +/** + * @brief update scriptsig on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] script_sig unlocking script(script signature). + * @return CfdErrorCode + */ +CFDC_API int CfdUpdateTxInScriptSig( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* script_sig); + +/** + * @brief set utxo data on transaction input. + * @details for calculate schnorr sighature. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid utxo txid. + * @param[in] vout utxo vout. + * @param[in] amount utxo amount + * @param[in] commitment utxo amount commitment + * @param[in] descriptor utxo descriptor + * @param[in] address utxo address + * @param[in] asset utxo asset + * @param[in] scriptsig_template utxo scriptsig template + * @param[in] can_insert insert mode + * @return CfdErrorCode + */ +CFDC_API int CfdSetTransactionUtxoData( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int64_t amount, const char* commitment, const char* descriptor, + const char* address, const char* asset, const char* scriptsig_template, + bool can_insert); + +/** + * @brief Create sighash on transaction input. + * @details Call CfdSetTransactionUtxoData before calling this function. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] sighash_type sighash type. + * @param[in] sighash_anyone_can_pay anyone can pay flag. + * @param[in] pubkey pubkey + * @param[in] redeem_script redeem script + * @param[in] tapleaf_hash tapleaf hash + * @param[in] code_separator_position code separator position + * @param[in] annex annex bytes. + * @param[out] sighash sighash + * If 'CfdFreeStringBuffer' is implemented, + * Call 'CfdFreeStringBuffer' after you are finished using it. + * @return CfdErrorCode + */ +CFDC_API int CfdCreateSighashByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int sighash_type, bool sighash_anyone_can_pay, const char* pubkey, + const char* redeem_script, const char* tapleaf_hash, + uint32_t code_separator_position, const char* annex, char** sighash); + +/** + * @brief Sign with private key on transaction input. + * @details Call CfdSetTransactionUtxoData before calling this function. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] privkey private key. + * @param[in] sighash_type sighash type. + * @param[in] sighash_anyone_can_pay anyone can pay flag. + * @param[in] has_grind_r Grind-R flag on sign. + * @param[in] aux_rand the auxiliary random data.\ + * used to create the nonce. + * @param[in] annex annex bytes. + * @return CfdErrorCode + */ +CFDC_API int CfdAddSignWithPrivkeyByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* privkey, int sighash_type, bool sighash_anyone_can_pay, + bool has_grind_r, const char* aux_rand, const char* annex); + +/** + * @brief Verify transactin sign. (It does not check the Script itself.) + * @details Call CfdSetTransactionUtxoData before calling this function. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @return CfdErrorCode + */ +CFDC_API int CfdVerifyTxSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout); + +/** + * @brief Add tx sign on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] hash_type hash type. + * @param[in] sign_data_hex sign data. + * @param[in] use_der_encode use der encode. + * @param[in] sighash_type sighash type. + * @param[in] sighash_anyone_can_pay anyone can pay flag. + * @param[in] clear_stack clear witness stack. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTxSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* sign_data_hex, bool use_der_encode, + int sighash_type, bool sighash_anyone_can_pay, bool clear_stack); + +/** + * @brief Add tx taproot sign on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] signature signature. + * It is assumed that sighashType is assigned \ + * by CfdAddSighashTypeInSchnorrSignature(). + * @param[in] tapscript tapscript hex. + * @param[in] control_block taproot control block. + * see CfdGetTaprootScriptTreeHash(). + * @param[in] annex annex bytes. + * @return CfdErrorCode + */ +CFDC_API int CfdAddTaprootSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* signature, const char* tapscript, const char* control_block, + const char* annex); + +/** + * @brief Add tx pubkey hash sign on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] hash_type hash type. + * @param[in] pubkey pubkey. + * @param[in] signature signature. + * @param[in] use_der_encode use der encode. + * @param[in] sighash_type sighash type. + * @param[in] sighash_anyone_can_pay anyone can pay flag. + * @return CfdErrorCode + */ +CFDC_API int CfdAddPubkeyHashSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* pubkey, const char* signature, + bool use_der_encode, int sighash_type, bool sighash_anyone_can_pay); + +/** + * @brief Add tx script hash final sign on transaction input. + * @param[in] handle cfd handle. + * @param[in] create_handle create transaction handle. + * @param[in] txid txin txid. + * @param[in] vout txin vout. + * @param[in] hash_type hash type. + * @param[in] redeem_script redeem script. + * @return CfdErrorCode + */ +CFDC_API int CfdAddScriptHashLastSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* redeem_script); + /** * @brief finalize and execute createrawtransaction. * @param[in] handle cfd handle. diff --git a/package.json b/package.json index 4b122f7a..374f5636 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cfd", - "version": "0.2.0", + "version": "0.3.0", "description": "cfd", "scripts": { "code_format": "clang-format -i --style=file src/*.cpp src/*.h include/cfd/*.h src/capi/*.cpp src/capi/*.h include/cfdc/*.h", @@ -139,21 +139,19 @@ "check": "run-s doxygen_check ctest_quiet lint_check", "check_debug": "run-s doxygen_check lint_check ctest_debug", "check_all": "run-s cmake_clean git_subm_update cmake_quiet doxygen_check ctest_quiet lint_check", - "convert_json": "node ./tools/generate_json_map_class.js" + "convert_json": "ts-node ./tools/generate_json_map_class.ts", + "convert_json_debug": "ts-node ./tools/generate_json_map_class.ts mode=debug" }, "author": "", "license": "MIT", - "gypfile": true, "dependencies": { - "cmake-js": "^5.2.0", - "big-integer": "^1.6.26", - "bindings": "*", - "nan": "^2.11.0", - "node-gyp": "^3.8.0", - "tape": "^4.8.0" + "cmake-js": "^5.2.0" }, "devDependencies": { + "@types/node": "^14.14.14", "npm-run-all": "^4.1.5", - "run-script-os": "^1.0.5" + "run-script-os": "^1.1.3", + "ts-morph": "^9.1.0", + "ts-node": "^9.1.1" } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e269a93..cd295701 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,10 +85,17 @@ add_library(${PROJECT_NAME} STATIC) endif() if(NOT ENABLE_CAPI) +if(NOT ENABLE_JSONAPI) +target_sources(${PROJECT_NAME} + PRIVATE + ${CFD_SOURCES} +) +else() target_sources(${PROJECT_NAME} PRIVATE ${CFD_SOURCES} ${CFD_JSONAPI_SOURCES} ) +endif() else() if(NOT ENABLE_JSONAPI) target_sources(${PROJECT_NAME} @@ -117,6 +124,7 @@ target_compile_options(${PROJECT_NAME} target_compile_definitions(${PROJECT_NAME} PRIVATE CFD_BUILD=1 + ${ELEMENTS_COMP_OPT} ${CFD_ELEMENTS_USE} ${CFD_CAPI_USE} ${CFD_JSONAPI_USE} diff --git a/src/Makefile.srclist b/src/Makefile.srclist index 9a651ab2..9a054e3c 100644 --- a/src/Makefile.srclist +++ b/src/Makefile.srclist @@ -12,6 +12,7 @@ CFD_SOURCES = \ cfd_transaction_common.cpp \ cfd_transaction_internal.cpp \ cfd_transaction.cpp \ + cfd_psbt.cpp \ cfd_address.cpp \ cfd_utxo.cpp \ cfdapi_transaction.cpp \ @@ -32,11 +33,13 @@ CFD_CAPI_SOURCES = \ capi/cfdcapi_elements_transaction.cpp \ capi/cfdcapi_key.cpp \ capi/cfdcapi_ledger.cpp \ + capi/cfdcapi_psbt.cpp \ capi/cfdcapi_transaction.cpp CFD_JSONAPI_SOURCES = \ jsonapi/cfd_json_elements_transaction.cpp \ jsonapi/cfd_json_mapping_api.cpp \ jsonapi/cfd_json_transaction.cpp \ + jsonapi/cfd_json_psbt.cpp \ jsonapi/autogen/cfd_api_json_autogen.cpp diff --git a/src/capi/cfdc_internal.h b/src/capi/cfdc_internal.h index 428c9b89..dc3d783d 100644 --- a/src/capi/cfdc_internal.h +++ b/src/capi/cfdc_internal.h @@ -22,6 +22,7 @@ #include "cfdcore/cfdcore_elements_transaction.h" #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_script.h" using cfd::core::ByteData; using cfd::core::CfdError; @@ -45,6 +46,7 @@ namespace cfd { namespace capi { using cfd::core::CfdException; +using cfd::core::Script; //! empty 32byte data constexpr const char* const kEmpty32Bytes = @@ -71,7 +73,7 @@ constexpr uint32_t kAssetSize = 33; //! prefix: MultisigSign constexpr const char* const kPrefixMultisigSignData = "MultisigSign"; //! multisig max key num -constexpr const uint32_t kMultisigMaxKeyNum = 16; +constexpr const uint32_t kMultisigMaxKeyNum = Script::kMaxMultisigPubkeyNum; //! signature hex size (73 * 2) constexpr const uint32_t kSignatureHexSize = 146; //! pubkey hex size (cfd::core::Pubkey::kPubkeySize * 2) @@ -249,6 +251,7 @@ class CfdCapiManager { /** * @brief ハンドルを作成する。 * @param[in] is_outside create outside handle + * @return handle */ void* CreateHandle(bool is_outside = false); /** diff --git a/src/capi/cfdcapi_address.cpp b/src/capi/cfdcapi_address.cpp index 513388b6..848e9410 100644 --- a/src/capi/cfdcapi_address.cpp +++ b/src/capi/cfdcapi_address.cpp @@ -16,7 +16,6 @@ #include "capi/cfdc_internal.h" #include "cfd/cfd_address.h" #include "cfd/cfd_elements_address.h" -#include "cfd/cfdapi_address.h" #include "cfd/cfdapi_elements_address.h" #include "cfdc/cfdcapi_common.h" #include "cfdcore/cfdcore_address.h" @@ -25,11 +24,11 @@ #include "cfdcore/cfdcore_hdwallet.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" using cfd::AddressFactory; -using cfd::api::AddressApi; -using cfd::api::DescriptorKeyData; -using cfd::api::DescriptorScriptData; +using cfd::DescriptorKeyData; +using cfd::DescriptorScriptData; using cfd::core::Address; using cfd::core::AddressType; using cfd::core::CfdError; @@ -41,14 +40,16 @@ using cfd::core::ExtPrivkey; using cfd::core::ExtPubkey; using cfd::core::NetType; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; using cfd::core::Script; +using cfd::core::ScriptUtil; +using cfd::core::WitnessVersion; using cfd::core::logger::info; using cfd::core::logger::warn; #ifndef CFD_DISABLE_ELEMENTS using cfd::ElementsAddressFactory; -using cfd::api::ElementsAddressApi; #endif // CFD_DISABLE_ELEMENTS // ============================================================================= @@ -130,6 +131,7 @@ int CfdCreateAddress( void* handle, int hash_type, const char* pubkey, const char* redeem_script, int network_type, char** address, char** locking_script, char** p2sh_segwit_locking_script) { + int result = CfdErrorCode::kCfdUnknownError; char* work_address = nullptr; char* work_locking_script = nullptr; char* work_p2sh_segwit_locking_script = nullptr; @@ -152,23 +154,26 @@ int CfdCreateAddress( Script unlocking_script; if ((pubkey != nullptr) && (*pubkey != '\0')) { - pubkey_obj = Pubkey(pubkey); + if (addr_type == AddressType::kTaprootAddress) { + pubkey_obj = + Pubkey(ByteData("02").Concat(SchnorrPubkey(pubkey).GetData())); + } else { + pubkey_obj = Pubkey(pubkey); + } } if ((redeem_script != nullptr) && (*redeem_script != '\0')) { script = Script(redeem_script); } if (is_bitcoin) { - AddressApi api; - addr = api.CreateAddress( - net_type, addr_type, &pubkey_obj, &script, &lock_script, - &unlocking_script); + AddressFactory factory(net_type); + addr = factory.CreateAddress( + addr_type, &pubkey_obj, &script, &lock_script, &unlocking_script); } else { #ifndef CFD_DISABLE_ELEMENTS - ElementsAddressApi e_api; - addr = e_api.CreateAddress( - net_type, addr_type, &pubkey_obj, &script, &lock_script, - &unlocking_script); + ElementsAddressFactory factory(net_type); + addr = factory.CreateAddress( + addr_type, &pubkey_obj, &script, &lock_script, &unlocking_script); #else throw CfdException( CfdError::kCfdIllegalStateError, "Elements not supported."); @@ -194,20 +199,15 @@ int CfdCreateAddress( *p2sh_segwit_locking_script = work_p2sh_segwit_locking_script; return CfdErrorCode::kCfdSuccess; } catch (const CfdException& except) { - FreeBufferOnError( - &work_address, &work_locking_script, &work_p2sh_segwit_locking_script); - return SetLastError(handle, except); + result = SetLastError(handle, except); } catch (const std::exception& std_except) { - FreeBufferOnError( - &work_address, &work_locking_script, &work_p2sh_segwit_locking_script); SetLastFatalError(handle, std_except.what()); - return CfdErrorCode::kCfdUnknownError; } catch (...) { - FreeBufferOnError( - &work_address, &work_locking_script, &work_p2sh_segwit_locking_script); SetLastFatalError(handle, "unknown error."); - return CfdErrorCode::kCfdUnknownError; } + FreeBufferOnError( + &work_address, &work_locking_script, &work_p2sh_segwit_locking_script); + return result; } int CfdInitializeMultisigScript( @@ -329,23 +329,29 @@ int CfdFinalizeMultisigScript( for (uint32_t index = 0; index < data->current_index; ++index) { pubkeys.emplace_back(std::string(data->pubkeys[index])); } + bool has_witness = (addr_type != AddressType::kP2shAddress); + Script multisig_script = ScriptUtil::CreateMultisigRedeemScript( + require_num, pubkeys, has_witness); if (is_bitcoin) { - AddressApi api; - addr = api.CreateMultisig( - net_type, addr_type, require_num, pubkeys, &redeem_script_obj, - &witness_script_obj); + AddressFactory factory(net_type); + addr = factory.CreateAddress( + addr_type, nullptr, &multisig_script, nullptr, &redeem_script_obj); } else { #ifndef CFD_DISABLE_ELEMENTS - ElementsAddressApi e_api; - addr = e_api.CreateMultisig( - net_type, addr_type, require_num, pubkeys, &redeem_script_obj, - &witness_script_obj); + ElementsAddressFactory factory(net_type); + addr = factory.CreateAddress( + addr_type, nullptr, &multisig_script, nullptr, &redeem_script_obj); #else throw CfdException( CfdError::kCfdIllegalStateError, "Elements not supported."); #endif // CFD_DISABLE_ELEMENTS } + if (!has_witness) { + redeem_script_obj = multisig_script; + } else { + witness_script_obj = multisig_script; + } work_address = CreateString(addr.GetAddress()); if (redeem_script != nullptr) { @@ -429,14 +435,14 @@ int CfdParseDescriptor( std::vector multisig_key_list; DescriptorScriptData desc_data; if (is_bitcoin) { - AddressApi api; - desc_data = api.ParseOutputDescriptor( - descriptor, net_type, derive_path, &script_list, &multisig_key_list); + AddressFactory factory(net_type); + desc_data = factory.ParseOutputDescriptor( + descriptor, derive_path, &script_list, &multisig_key_list); } else { #ifndef CFD_DISABLE_ELEMENTS - ElementsAddressApi e_api; - desc_data = e_api.ParseOutputDescriptor( - descriptor, net_type, derive_path, &script_list, &multisig_key_list); + ElementsAddressFactory factory(net_type); + desc_data = factory.ParseOutputDescriptor( + descriptor, derive_path, &script_list, &multisig_key_list); #else throw CfdException( CfdError::kCfdIllegalStateError, "Elements not supported."); @@ -509,8 +515,7 @@ int CfdGetDescriptorData( if ((locking_script != nullptr) && (!desc_data.locking_script.IsEmpty())) { work_locking_script = CreateString(desc_data.locking_script.GetHex()); } - if ((address != nullptr) && - (desc_data.type != DescriptorScriptType::kDescriptorScriptRaw)) { + if ((address != nullptr) && (!desc_data.address.GetAddress().empty())) { std::string addr = desc_data.address.GetAddress(); if (!addr.empty()) work_address = CreateString(addr); } @@ -762,14 +767,14 @@ int CfdGetAddressesFromMultisig( std::vector pubkey_list; std::vector
addr_list; if (is_bitcoin) { - AddressApi api; - addr_list = api.GetAddressesFromMultisig( - net_type, addr_type, redeem_script_obj, &pubkey_list); + AddressFactory factory(net_type); + addr_list = factory.GetAddressesFromMultisig( + addr_type, redeem_script_obj, &pubkey_list); } else { #ifndef CFD_DISABLE_ELEMENTS - ElementsAddressApi e_api; - addr_list = e_api.GetAddressesFromMultisig( - net_type, addr_type, redeem_script_obj, &pubkey_list); + ElementsAddressFactory factory(net_type); + addr_list = factory.GetAddressesFromMultisig( + addr_type, redeem_script_obj, &pubkey_list); #else throw CfdException( CfdError::kCfdIllegalStateError, "Elements not supported."); @@ -992,6 +997,12 @@ int CfdGetAddressInfo( case AddressType::kP2shP2wpkhAddress: *hash_type = kCfdP2shP2wpkh; break; + case AddressType::kTaprootAddress: + *hash_type = kCfdTaproot; + break; + case AddressType::kWitnessUnknown: + *hash_type = kCfdUnknown; + break; default: warn( CFD_LOG_SOURCE, "Illegal hash type.({})", addr.GetAddressType()); diff --git a/src/capi/cfdcapi_common.cpp b/src/capi/cfdcapi_common.cpp index 281a07c9..ccaf77d7 100644 --- a/src/capi/cfdcapi_common.cpp +++ b/src/capi/cfdcapi_common.cpp @@ -20,6 +20,7 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_util.h" using cfd::core::AddressType; using cfd::core::CfdError; @@ -167,6 +168,9 @@ cfd::core::AddressType ConvertHashToAddressType(int hash_type) { case kCfdP2shP2wpkh: addr_type = AddressType::kP2shP2wpkhAddress; break; + case kCfdTaproot: + addr_type = AddressType::kTaprootAddress; + break; default: warn(CFD_LOG_SOURCE, "Illegal hash type.({})", hash_type); throw CfdException( @@ -196,6 +200,12 @@ cfd::core::AddressType ConvertAddressType(int address_type) { case kCfdP2shP2wpkhAddress: addr_type = AddressType::kP2shP2wpkhAddress; break; + case kCfdTaprootAddress: + addr_type = AddressType::kTaprootAddress; + break; + case kCfdWitnessUnknownAddress: + addr_type = AddressType::kWitnessUnknown; + break; default: warn(CFD_LOG_SOURCE, "Illegal address type.({})", address_type); throw CfdException( @@ -217,6 +227,9 @@ cfd::core::WitnessVersion GetWitnessVersion(int hash_type) { case kCfdP2shP2wpkh: version = cfd::core::WitnessVersion::kVersion0; break; + case kCfdTaproot: + version = cfd::core::WitnessVersion::kVersion1; + break; default: warn(CFD_LOG_SOURCE, "Illegal hash type.({})", hash_type); throw CfdException( @@ -347,7 +360,7 @@ void CfdCapiManager::CopyHandle( dest_data->error_message, source_data->error_message, sizeof(dest_data->error_message)); } else { - // TODO(k-matsuzawa): 現在はコピーする情報が無い + // TODO(k-matsuzawa): Currently there is no information to copy // bool is_outside = dest_data->is_outside; // memcpy(dest_data, source_data, sizeof(CfdCapiHandleData)); // dest_data->is_outside = is_outside; @@ -357,7 +370,6 @@ void CfdCapiManager::CopyHandle( void CfdCapiManager::SetLastError( void* handle, int error_code, const char* message) { - // TODO(k-matsuzawa): handle存在チェックすべきかどうか if (handle != nullptr) { CfdCapiHandleData* data = static_cast(handle); memset(data->error_message, 0, sizeof(data->error_message)); @@ -376,7 +388,6 @@ void CfdCapiManager::SetLastFatalError(void* handle, const char* message) { } CfdException CfdCapiManager::GetLastError(void* handle) { - // TODO(k-matsuzawa): handle存在チェックすべきかどうか if (handle != nullptr) { CfdCapiHandleData* data = static_cast(handle); char str_buffer[256]; @@ -608,6 +619,8 @@ extern "C" int CfdRequestExecuteJson( #endif // CFD_DISABLE_ELEMENTS } else if (command == "DecodeRawTransaction") { result = JsonMappingApi::DecodeRawTransaction(std::string(json_string)); + } else if (command == "DecodePsbt") { + result = JsonMappingApi::DecodePsbt(std::string(json_string)); } else { throw CfdException( CfdError::kCfdIllegalArgumentError, "unknown request name."); @@ -631,7 +644,8 @@ extern "C" int CfdRequestExecuteJson( #endif // CFD_DISABLE_JSONAPI } -int CfdSerializeByteData(void* handle, const char* buffer, char** output) { +extern "C" int CfdSerializeByteData( + void* handle, const char* buffer, char** output) { try { cfd::Initialize(); if (buffer == nullptr) { @@ -661,4 +675,365 @@ int CfdSerializeByteData(void* handle, const char* buffer, char** output) { return CfdErrorCode::kCfdUnknownError; } +extern "C" int CfdEncryptAES( + void* handle, const char* key, const char* cbc_iv, const char* buffer, + char** output) { + try { + cfd::Initialize(); + if (key == nullptr) { + warn(CFD_LOG_SOURCE, "key is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is null."); + } + if (buffer == nullptr) { + warn(CFD_LOG_SOURCE, "buffer is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. buffer is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + ByteData key_data(key); + ByteData data(buffer); + + if (cfd::capi::IsEmptyString(cbc_iv)) { + auto aes_data = cfd::core::CryptoUtil::EncryptAes256(key_data, data); + *output = cfd::capi::CreateString(aes_data.GetHex()); + } else { + ByteData iv(cbc_iv); + auto aes_data = + cfd::core::CryptoUtil::EncryptAes256Cbc(key_data, iv, data); + *output = cfd::capi::CreateString(aes_data.GetHex()); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdDecryptAES( + void* handle, const char* key, const char* cbc_iv, const char* buffer, + char** output) { + try { + cfd::Initialize(); + if (key == nullptr) { + warn(CFD_LOG_SOURCE, "key is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is null."); + } + if (buffer == nullptr) { + warn(CFD_LOG_SOURCE, "buffer is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. buffer is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + ByteData key_data(key); + ByteData data(buffer); + + if (cfd::capi::IsEmptyString(cbc_iv)) { + auto aes_data = cfd::core::CryptoUtil::DecryptAes256(key_data, data); + *output = cfd::capi::CreateString(aes_data.GetHex()); + } else { + ByteData iv(cbc_iv); + auto aes_data = + cfd::core::CryptoUtil::DecryptAes256Cbc(key_data, iv, data); + *output = cfd::capi::CreateString(aes_data.GetHex()); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdEncodeBase64( + void* handle, const char* buffer, char** output) { + try { + cfd::Initialize(); + if (buffer == nullptr) { + warn(CFD_LOG_SOURCE, "buffer is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. buffer is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + ByteData data(buffer); + auto base64 = cfd::core::CryptoUtil::EncodeBase64(data); + *output = cfd::capi::CreateString(base64); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdDecodeBase64( + void* handle, const char* base64, char** output) { + try { + cfd::Initialize(); + if (cfd::capi::IsEmptyString(base64)) { + warn(CFD_LOG_SOURCE, "base64 is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. base64 is null or empty."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + auto data = cfd::core::CryptoUtil::DecodeBase64(std::string(base64)); + *output = cfd::capi::CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdEncodeBase58( + void* handle, const char* buffer, bool use_checksum, char** output) { + try { + cfd::Initialize(); + if (buffer == nullptr) { + warn(CFD_LOG_SOURCE, "buffer is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. buffer is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + ByteData data(buffer); + if (use_checksum) { + auto base58 = cfd::core::CryptoUtil::EncodeBase58Check(data); + *output = cfd::capi::CreateString(base58); + } else { + auto base58 = cfd::core::CryptoUtil::EncodeBase58(data); + *output = cfd::capi::CreateString(base58); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdDecodeBase58( + void* handle, const char* base58, bool use_checksum, char** output) { + try { + cfd::Initialize(); + if (cfd::capi::IsEmptyString(base58)) { + warn(CFD_LOG_SOURCE, "base58 is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. base58 is null or empty."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + if (use_checksum) { + auto data = + cfd::core::CryptoUtil::DecodeBase58Check(std::string(base58)); + *output = cfd::capi::CreateString(data.GetHex()); + } else { + auto data = cfd::core::CryptoUtil::DecodeBase58(std::string(base58)); + *output = cfd::capi::CreateString(data.GetHex()); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdRipemd160( + void* handle, const char* message, bool has_text, char** output) { + try { + cfd::Initialize(); + if (message == nullptr) { + warn(CFD_LOG_SOURCE, "message is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. message is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + + cfd::core::ByteData160 data; + if (has_text) { + data = cfd::core::HashUtil::Ripemd160(std::string(message)); + } else { + data = cfd::core::HashUtil::Ripemd160(ByteData(message)); + } + *output = cfd::capi::CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdSha256( + void* handle, const char* message, bool has_text, char** output) { + try { + cfd::Initialize(); + if (message == nullptr) { + warn(CFD_LOG_SOURCE, "message is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. message is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + + cfd::core::ByteData256 data; + if (has_text) { + data = cfd::core::HashUtil::Sha256(std::string(message)); + } else { + data = cfd::core::HashUtil::Sha256(ByteData(message)); + } + *output = cfd::capi::CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdHash160( + void* handle, const char* message, bool has_text, char** output) { + try { + cfd::Initialize(); + if (message == nullptr) { + warn(CFD_LOG_SOURCE, "message is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. message is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + + cfd::core::ByteData160 data; + if (has_text) { + data = cfd::core::HashUtil::Hash160(std::string(message)); + } else { + data = cfd::core::HashUtil::Hash160(ByteData(message)); + } + *output = cfd::capi::CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + +extern "C" int CfdHash256( + void* handle, const char* message, bool has_text, char** output) { + try { + cfd::Initialize(); + if (message == nullptr) { + warn(CFD_LOG_SOURCE, "message is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. message is null."); + } + if (output == nullptr) { + warn(CFD_LOG_SOURCE, "output is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. output is null."); + } + + cfd::core::ByteData256 data; + if (has_text) { + data = cfd::core::HashUtil::Sha256D(std::string(message)); + } else { + data = cfd::core::HashUtil::Sha256D(ByteData(message)); + } + *output = cfd::capi::CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return cfd::capi::SetLastError(handle, except); + } catch (const std::exception& std_except) { + cfd::capi::SetLastFatalError(handle, std_except.what()); + } catch (...) { + cfd::capi::SetLastFatalError(handle, "unknown error."); + } + return CfdErrorCode::kCfdUnknownError; +} + #endif // CFD_DISABLE_CAPI diff --git a/src/capi/cfdcapi_elements_address.cpp b/src/capi/cfdcapi_elements_address.cpp index dff4ab1f..ef57e77e 100644 --- a/src/capi/cfdcapi_elements_address.cpp +++ b/src/capi/cfdcapi_elements_address.cpp @@ -15,8 +15,8 @@ #include #include "capi/cfdc_internal.h" +#include "cfd/cfd_address.h" #include "cfd/cfd_elements_address.h" -#include "cfd/cfdapi_address.h" #include "cfd/cfdapi_elements_address.h" #include "cfdc/cfdcapi_address.h" #include "cfdc/cfdcapi_common.h" @@ -26,7 +26,6 @@ #include "cfdcore/cfdcore_logger.h" using cfd::ElementsAddressFactory; -using cfd::api::AddressApi; using cfd::core::Address; using cfd::core::AddressType; using cfd::core::CfdError; diff --git a/src/capi/cfdcapi_elements_transaction.cpp b/src/capi/cfdcapi_elements_transaction.cpp index 32cd4fe2..41e87f18 100644 --- a/src/capi/cfdcapi_elements_transaction.cpp +++ b/src/capi/cfdcapi_elements_transaction.cpp @@ -1585,8 +1585,8 @@ int CfdAddConfidentialTxDerSign( } // encode to der - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_bytes = ByteData(std::string(signature)); SignParameter param(signature_bytes, true, sighashtype); ByteData signature_der = param.ConvertToSignature(); @@ -1772,8 +1772,8 @@ int CfdAddConfidentialTxSignWithPrivkeySimple( OutPoint outpoint(Txid(txid), vout); ConfidentialTransactionContext tx(tx_hex_string); - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); tx.SignWithPrivkeySimple( outpoint, Pubkey(pubkey), privkey_obj, sighashtype, value, addr_type, has_grind_r); @@ -1874,8 +1874,8 @@ int CfdCreateConfidentialSighash( } ElementsTransactionApi api; - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); std::string sighash_bytes = api.CreateSignatureHash( std::string(tx_hex_string), Txid(std::string(txid)), vout, key_data, value, core_hash_type, sighashtype); @@ -2089,8 +2089,8 @@ CFDC_API int CfdVerifyConfidentialTxSignature( ConfidentialTransactionController ctxc(tx_hex); ByteData signature_obj(signature); Txid txid_obj(txid); - SigHashType sighash_type_obj( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighash_type_obj = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ConfidentialValue value; if (!IsEmptyString(value_commitment)) { value = ConfidentialValue(value_commitment); diff --git a/src/capi/cfdcapi_key.cpp b/src/capi/cfdcapi_key.cpp index 116925cd..656f9d08 100644 --- a/src/capi/cfdcapi_key.cpp +++ b/src/capi/cfdcapi_key.cpp @@ -614,12 +614,6 @@ int CfdSignSchnorr( CfdError::kCfdIllegalArgumentError, "Failed to parameter. msg is null or empty."); } - if (IsEmptyString(aux_rand)) { - warn(CFD_LOG_SOURCE, "aux_rand is null or empty."); - throw CfdException( - CfdError::kCfdIllegalArgumentError, - "Failed to parameter. aux_rand is null or empty."); - } if (IsEmptyString(sk)) { warn(CFD_LOG_SOURCE, "sk is null or empty."); throw CfdException( @@ -633,8 +627,13 @@ int CfdSignSchnorr( "Failed to parameter. signature is null."); } - SchnorrSignature schnorr_sig = SchnorrUtil::Sign( - ByteData256(msg), Privkey(sk), ByteData256(aux_rand)); + SchnorrSignature schnorr_sig; + if (IsEmptyString(aux_rand)) { + schnorr_sig = SchnorrUtil::Sign(ByteData256(msg), Privkey(sk)); + } else { + schnorr_sig = SchnorrUtil::Sign( + ByteData256(msg), Privkey(sk), ByteData256(aux_rand)); + } *signature = CreateString(schnorr_sig.GetHex()); return CfdErrorCode::kCfdSuccess; @@ -694,6 +693,71 @@ int CfdSignSchnorrWithNonce( return result; } +int CfdAddSighashTypeInSchnorrSignature( + void* handle, const char* signature, int sighash_type, bool anyone_can_pay, + char** added_signature) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (IsEmptyString(signature)) { + warn(CFD_LOG_SOURCE, "signature is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. signature is null or empty."); + } + if (added_signature == nullptr) { + warn(CFD_LOG_SOURCE, "added_signature is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. added_signature is null or empty."); + } + SchnorrSignature sig(signature); + SigHashType sighash_type_obj = SigHashType::Create( + static_cast(sighash_type), anyone_can_pay); + sig.SetSigHashType(sighash_type_obj); + *added_signature = CreateString(sig.GetData(true).GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetSighashTypeFromSchnorrSignature( + void* handle, const char* signature, int* sighash_type, + bool* anyone_can_pay) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (IsEmptyString(signature)) { + warn(CFD_LOG_SOURCE, "signature is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. signature is null or empty."); + } + SchnorrSignature sig(signature); + auto sighash_type_obj = sig.GetSigHashType(); + if (sighash_type != nullptr) { + *sighash_type = static_cast(sighash_type_obj.GetSigHashAlgorithm()); + } + if (anyone_can_pay != nullptr) { + *anyone_can_pay = sighash_type_obj.IsAnyoneCanPay(); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + int CfdComputeSchnorrSigPoint( void* handle, const char* msg, const char* nonce, const char* pubkey, char** sigpoint) { @@ -842,8 +906,8 @@ int CfdEncodeSignatureByDer( "Failed to parameter. der_signature is null."); } - SigHashType type = SigHashType( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType type = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData der_sig = CryptoUtil::ConvertSignatureToDer(signature, type); *der_signature = CreateString(der_sig.GetHex()); @@ -1123,8 +1187,39 @@ int CfdGetPubkeyFromPrivkey( } } -CFDC_API int CfdCompressPubkey( - void* handle, const char* pubkey, char** output) { +int CfdGetPubkeyFingerprint( + void* handle, const char* pubkey, char** fingerprint) { + try { + cfd::Initialize(); + if (fingerprint == nullptr) { + warn(CFD_LOG_SOURCE, "fingerprint is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. fingerprint is null."); + } + if (IsEmptyString(pubkey)) { + warn(CFD_LOG_SOURCE, "pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null or empty."); + } + + Pubkey key(pubkey); + *fingerprint = CreateString(key.GetFingerprint().GetHex()); + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdCompressPubkey(void* handle, const char* pubkey, char** output) { try { cfd::Initialize(); if (output == nullptr) { diff --git a/src/capi/cfdcapi_psbt.cpp b/src/capi/cfdcapi_psbt.cpp new file mode 100644 index 00000000..8507a862 --- /dev/null +++ b/src/capi/cfdcapi_psbt.cpp @@ -0,0 +1,2753 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcapi_psbt.cpp + * + * @brief This file is implements Partially Signed Bitcoin Transaction. + */ +#ifndef CFD_DISABLE_CAPI +#include "cfdc/cfdcapi_psbt.h" + +#include +#include + +#include "capi/cfdc_internal.h" +#include "cfd/cfd_address.h" +#include "cfd/cfd_common.h" +#include "cfd/cfd_psbt.h" +#include "cfd/cfd_transaction_common.h" +#include "cfdc/cfdcapi_common.h" +#include "cfdc/cfdcapi_key.h" +#include "cfdcore/cfdcore_address.h" +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_descriptor.h" +#include "cfdcore/cfdcore_hdwallet.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_transaction_common.h" +#include "cfdcore/cfdcore_util.h" + +using cfd::AddressFactory; +using cfd::CoinSelectionOption; +using cfd::UtxoData; +using cfd::UtxoFilter; +using cfd::core::Address; +using cfd::core::AddressType; +using cfd::core::Amount; +using cfd::core::ByteData; +using cfd::core::ByteData256; +using cfd::core::CfdError; +using cfd::core::CfdException; +using cfd::core::CryptoUtil; +using cfd::core::Descriptor; +using cfd::core::DescriptorKeyInfo; +using cfd::core::Deserializer; +using cfd::core::ExtPrivkey; +using cfd::core::ExtPubkey; +using cfd::core::HashUtil; +using cfd::core::HDWallet; +using cfd::core::KeyData; +using cfd::core::NetType; +using cfd::core::OutPoint; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::Script; +using cfd::core::ScriptOperator; +using cfd::core::ScriptUtil; +using cfd::core::SigHashAlgorithm; +using cfd::core::SigHashType; +using cfd::core::SignatureUtil; +using cfd::core::StringUtil; +using cfd::core::Transaction; +using cfd::core::Txid; +using cfd::core::TxOut; +using cfd::core::TxOutReference; + +using cfd::core::logger::info; +using cfd::core::logger::warn; + +using cfd::Psbt; + +// ============================================================================= +// internal file +// ============================================================================= +//! ExtPubkey hex size. +static constexpr size_t kCfdExtPubkeyHexSize = ExtPrivkey::kSerializeSize * 2; + +// ============================================================================= +// internal c-api +// ============================================================================= +namespace cfd { +namespace capi { + +//! prefix: PsbtHandle +constexpr const char* const kPrefixPsbtHandle = "PsbtHandle"; +//! prefix: PsbtPubkeyList +constexpr const char* const kPrefixPsbtPubkeyList = "PsbtPubkeyList"; +//! prefix: PsbtByteDataList +constexpr const char* const kPrefixPsbtByteDataList = "PsbtDataList"; +//! prefix: PsbtFund +constexpr const char* const kPrefixPsbtFundHandle = "PsbtFund"; + +/** + * @brief cfd-capi PsbtHandle struct. + */ +struct CfdCapiPsbtHandle { + char prefix[kPrefixLength]; //!< buffer prefix + NetType net_type; //!< network type + //! psbt object + Psbt* psbt; +}; + +/** + * @brief cfd-capi PsbtPubkeyListHandle struct. + */ +struct CfdCapiPsbtPubkeyListHandle { + char prefix[kPrefixLength]; //!< buffer prefix + bool has_bip32_list; //!< bip32 list + bool has_xpub_list; //!< xpubkey list + std::vector* key_list; //!< key list +}; + +/** + * @brief cfd-capi PsbtByteDataListHandle struct. + */ +struct CfdCapiPsbtByteDataListHandle { + char prefix[kPrefixLength]; //!< buffer prefix + std::vector* data_list; //!< data list +}; + +/** + * @brief cfd-capi PsbtFundHandle struct. + */ +struct CfdCapiPsbtFundHandle { + char prefix[kPrefixLength]; //!< buffer prefix + std::vector* utxos; //!< utxo list + std::vector* indexes; //!< index list + /// fee rate (double) + double fee_rate; + /// dust fee rate (double) + double dust_fee_rate; + /// longterm fee rate (double) + double long_term_fee_rate; + /// knapsack min change (int64) + int64_t knapsack_min_change; +}; + +/** + * @brief parse pubkey. + * @param[in] kind psbt record kind. + * @param[in] pubkey pubkey or xpubkey. + * @param[in] net_type network type. + * @return key data. + */ +static KeyData ParsePubkey(int kind, const char* pubkey, NetType net_type) { + constexpr size_t kCompressPubkeyHexSize = Pubkey::kCompressedPubkeySize * 2; + std::string pubkey_str(pubkey); + + bool has_extpubkey = false; + switch (kind) { + case kCfdPsbtRecordGloalXpub: + has_extpubkey = true; + break; + case kCfdPsbtRecordInputBip32: + case kCfdPsbtRecordInputSignature: + case kCfdPsbtRecordOutputBip32: + default: + break; + } + + bool is_pubkey = false; + if (pubkey_str.length() == kCompressPubkeyHexSize) { + is_pubkey = true; + } + if (is_pubkey == has_extpubkey) { + warn(CFD_LOG_SOURCE, "unmatch pubkey type."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. unmatch pubkey type."); + } + + if (is_pubkey) { + return KeyData(pubkey_str); + } + KeyData data; + if (pubkey_str.length() == kCfdExtPubkeyHexSize) { + data = KeyData(ExtPubkey(ByteData(pubkey_str)), "", ByteData()); + } else { + data = KeyData(ExtPubkey(pubkey_str), "", ByteData()); + } + bool has_mainnet = + (data.GetExtPubkey().GetNetworkType() == NetType::kMainnet); + + if (((net_type == NetType::kMainnet) && (!has_mainnet)) || + ((net_type != NetType::kMainnet) && (has_mainnet))) { + warn(CFD_LOG_SOURCE, "unmatch xpubkey network type."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. unmatch xpubkey network type."); + } + return data; +} + +/** + * @brief convert to keyData. + * @param[in] pubkey pubkey or descriptor. + * @param[in] fingerprint fingerprint. + * @param[in] bip32_path bip32 path. + * @param[out] key_list key data list. + */ +static void ConvertToKeyData( + const std::string& pubkey, const char* fingerprint, const char* bip32_path, + std::vector* key_list) { + KeyData key_data; + try { + auto desc = Descriptor::Parse(pubkey); + key_data = desc.GetKeyData(); + } catch (const CfdException&) { + // fall through + } + if (!key_data.IsValid()) key_data = KeyData(pubkey); + + if (key_data.GetBip32Path().empty() || + (key_data.GetFingerprint().GetDataSize() != 4)) { + if (IsEmptyString(fingerprint)) { + warn(CFD_LOG_SOURCE, "fingerprint is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. fingerprint is null or empty."); + } + if (IsEmptyString(bip32_path)) { + warn(CFD_LOG_SOURCE, "bip32_path is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. bip32_path is null or empty."); + } + key_list->emplace_back( + Pubkey(pubkey), std::string(bip32_path), ByteData(fingerprint)); + } else { + key_list->emplace_back(key_data.ToString()); + } +} + +} // namespace capi +} // namespace cfd + +// ============================================================================= +// extern c-api +// ============================================================================= +// API +using cfd::capi::AllocBuffer; +using cfd::capi::CfdCapiPsbtByteDataListHandle; +using cfd::capi::CfdCapiPsbtFundHandle; +using cfd::capi::CfdCapiPsbtHandle; +using cfd::capi::CfdCapiPsbtPubkeyListHandle; +using cfd::capi::CheckBuffer; +using cfd::capi::ConvertNetType; +using cfd::capi::ConvertToKeyData; +using cfd::capi::CreateString; +using cfd::capi::FreeBuffer; +using cfd::capi::FreeBufferOnError; +using cfd::capi::IsEmptyString; +using cfd::capi::kPrefixPsbtByteDataList; +using cfd::capi::kPrefixPsbtFundHandle; +using cfd::capi::kPrefixPsbtHandle; +using cfd::capi::kPrefixPsbtPubkeyList; +using cfd::capi::ParsePubkey; +using cfd::capi::SetLastError; +using cfd::capi::SetLastFatalError; + +extern "C" { + +int CfdCreatePsbtHandle( + void* handle, int net_type, const char* psbt_string, + const char* tx_hex_string, uint32_t version, uint32_t locktime, + void** psbt_handle) { + int result = CfdErrorCode::kCfdUnknownError; + CfdCapiPsbtHandle* buffer = nullptr; + try { + cfd::Initialize(); + + if (psbt_handle == nullptr) { + warn(CFD_LOG_SOURCE, "psbt_handle is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. psbt_handle is null."); + } + bool is_bitcoin = false; + NetType network_type = ConvertNetType(net_type, &is_bitcoin); + if (!is_bitcoin) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); + } + + Psbt psbt; + if (!IsEmptyString(psbt_string)) { + try { + psbt = Psbt(psbt_string); + } catch (const CfdException& fail_base64) { + std::string err_msg(fail_base64.what()); + if (err_msg != "psbt unmatch magic error.") { + throw fail_base64; + } + psbt = Psbt(ByteData(psbt_string)); + } + } else if (!IsEmptyString(tx_hex_string)) { + psbt = Psbt(Transaction(tx_hex_string)); + } else { + psbt = Psbt(version, locktime); + } + + buffer = static_cast( + AllocBuffer(kPrefixPsbtHandle, sizeof(CfdCapiPsbtHandle))); + buffer->psbt = new Psbt(psbt); + buffer->net_type = network_type; + *psbt_handle = buffer; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + CfdFreePsbtHandle(handle, buffer); + return result; +} + +int CfdFreePsbtHandle(void* handle, void* psbt_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (psbt_handle != nullptr) { + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_data = + static_cast(psbt_handle); + if (psbt_data->psbt != nullptr) { + delete psbt_data->psbt; + psbt_data->psbt = nullptr; + } + FreeBuffer(psbt_handle, kPrefixPsbtHandle, sizeof(CfdCapiPsbtHandle)); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtData( + void* handle, void* psbt_handle, char** psbt_base64, char** psbt_hex) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_base64 = nullptr; + char* work_hex = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + if (psbt_base64 != nullptr) { + work_base64 = CreateString(psbt_obj->psbt->GetBase64()); + } + if (psbt_hex != nullptr) { + work_hex = CreateString(psbt_obj->psbt->GetData().GetHex()); + } + if (psbt_base64 != nullptr) *psbt_base64 = work_base64; + if (psbt_hex != nullptr) *psbt_hex = work_hex; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_base64, &work_hex); + return result; +} + +int CfdGetPsbtGlobalData( + void* handle, void* psbt_handle, uint32_t* psbt_version, char** base_tx, + uint32_t* txin_count, uint32_t* txout_count) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + if (psbt_version != nullptr) { + *psbt_version = psbt_obj->psbt->GetPsbtVersion(); + } + auto tx = psbt_obj->psbt->GetTransaction(); + if (txin_count != nullptr) *txin_count = tx.GetTxInCount(); + if (txout_count != nullptr) *txout_count = tx.GetTxOutCount(); + if (base_tx != nullptr) *base_tx = CreateString(tx.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdJoinPsbt( + void* handle, void* psbt_handle, const char* psbt_join_base64) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(psbt_join_base64)) { + warn(CFD_LOG_SOURCE, "psbt_join_base64 is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. psbt_join_base64 is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Psbt join_psbt(psbt_join_base64); + psbt_obj->psbt->Join(join_psbt); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSignPsbt( + void* handle, void* psbt_handle, const char* privkey, bool has_grind_r) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(privkey)) { + warn(CFD_LOG_SOURCE, "privkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. privkey is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + std::string privkey_str(privkey); + Privkey privkey_obj; + if (Privkey::HasWif(privkey_str)) { + privkey_obj = Privkey::FromWif(privkey_str); + } else { + privkey_obj = Privkey(privkey_str); + } + psbt_obj->psbt->Sign(privkey_obj, has_grind_r); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdCombinePsbt( + void* handle, void* psbt_handle, const char* psbt_combine_base64) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(psbt_combine_base64)) { + warn(CFD_LOG_SOURCE, "psbt_combine_base64 is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. psbt_combine_base64 is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Psbt combine_psbt(psbt_combine_base64); + psbt_obj->psbt->Combine(combine_psbt); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdFinalizePsbt(void* handle, void* psbt_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + psbt_obj->psbt->Finalize(); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdExtractPsbtTransaction( + void* handle, void* psbt_handle, char** transaction) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (transaction == nullptr) { + warn(CFD_LOG_SOURCE, "transaction is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. transaction is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + *transaction = CreateString(psbt_obj->psbt->Extract().GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdIsFinalizedPsbt(void* handle, void* psbt_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + if (!psbt_obj->psbt->IsFinalized()) return kCfdSignVerificationError; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdIsFinalizedPsbtInput( + void* handle, void* psbt_handle, const char* txid, uint32_t vout) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Txid txid_obj(txid); + if (!psbt_obj->psbt->IsFinalizedInput(OutPoint(txid_obj, vout))) { + return kCfdSignVerificationError; + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddPsbtTxInWithPubkey( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t sequence, int64_t amount, const char* locking_script, + const char* descriptor, const char* full_tx_hex) { + int result = CfdErrorCode::kCfdUnknownError; + Psbt backup_psbt; + bool can_restore_psbt = false; + CfdCapiPsbtHandle* psbt_obj = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + backup_psbt = *(psbt_obj->psbt); + + OutPoint outpoint(Txid(txid), vout); + + Script locking_script_obj; + std::vector key_list; + if (!IsEmptyString(descriptor)) { + Descriptor desc = Descriptor::Parse(descriptor); + std::vector path_args; + auto key = desc.GetKeyData(path_args); + auto ref = desc.GetReference(&path_args); + if (IsEmptyString(locking_script)) { + locking_script_obj = ref.GetLockingScript(); + } else { + locking_script_obj = Script(locking_script); + } + key_list.push_back(key); + } else if (!IsEmptyString(locking_script)) { + locking_script_obj = Script(locking_script); + } + + psbt_obj->psbt->AddTxIn(outpoint, sequence); + can_restore_psbt = true; + + if (!IsEmptyString(full_tx_hex)) { + Transaction tx(full_tx_hex); + if (!tx.GetTxOut(vout).GetLockingScript().Equals(locking_script_obj)) { + warn(CFD_LOG_SOURCE, "unmatch locking script."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. unmatch locking script."); + } + psbt_obj->psbt->SetTxInUtxo(outpoint, tx, Script(), key_list); + } else if (!locking_script_obj.IsEmpty()) { + TxOut txout(Amount(amount), locking_script_obj); + psbt_obj->psbt->SetTxInUtxo( + outpoint, TxOutReference(txout), Script(), key_list); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + if (can_restore_psbt) *(psbt_obj->psbt) = backup_psbt; + return result; +} + +int CfdAddPsbtTxInWithScript( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t sequence, int64_t amount, const char* locking_script, + const char* redeem_script, const char* descriptor, + const char* full_tx_hex) { + int result = CfdErrorCode::kCfdUnknownError; + Psbt backup_psbt; + bool can_restore_psbt = false; + CfdCapiPsbtHandle* psbt_obj = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + backup_psbt = *(psbt_obj->psbt); + + OutPoint outpoint(Txid(txid), vout); + std::vector key_list; + + Script script; + Script locking_script_obj; + if (!IsEmptyString(locking_script)) { + locking_script_obj = Script(locking_script); + } + if (!IsEmptyString(redeem_script)) { + script = Script(redeem_script); + } + + if (!IsEmptyString(descriptor)) { + Descriptor desc = Descriptor::Parse(descriptor); + std::vector path_args; + key_list = desc.GetKeyDataAll(&path_args); + auto ref_list = desc.GetReferenceAll(&path_args); + if (!IsEmptyString(locking_script)) { + locking_script_obj = ref_list.front().GetLockingScript(); + } + if (IsEmptyString(redeem_script)) { + auto ref = ref_list.front(); + while (ref.HasChild()) { + if (ref.HasRedeemScript()) script = ref.GetRedeemScript(); + ref = ref.GetChild(); + } + if (ref.HasRedeemScript()) script = ref.GetRedeemScript(); + } + } + + psbt_obj->psbt->AddTxIn(outpoint, sequence); + can_restore_psbt = true; + + if (!IsEmptyString(full_tx_hex)) { + Transaction tx(full_tx_hex); + if (!tx.GetTxOut(vout).GetLockingScript().Equals(locking_script_obj)) { + warn(CFD_LOG_SOURCE, "unmatch locking script."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. unmatch locking script."); + } + psbt_obj->psbt->SetTxInUtxo(outpoint, tx, script, key_list); + } else if (!locking_script_obj.IsEmpty()) { + TxOut txout(Amount(amount), locking_script_obj); + psbt_obj->psbt->SetTxInUtxo( + outpoint, TxOutReference(txout), script, key_list); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + if (can_restore_psbt) *(psbt_obj->psbt) = backup_psbt; + return result; +} + +int CfdSetPsbtTxInUtxo( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int64_t amount, const char* locking_script, const char* full_tx_hex) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + auto redeem_script = psbt_obj->psbt->GetTxInRedeemScript(outpoint, true); + std::vector key_list; + if (!IsEmptyString(full_tx_hex)) { + Transaction tx(full_tx_hex); + psbt_obj->psbt->SetTxInUtxo(outpoint, tx, redeem_script, key_list); + } else if (!IsEmptyString(locking_script)) { + Script script(locking_script); + TxOut txout(Amount(amount), script); + auto pubkeys = psbt_obj->psbt->GetTxInKeyDataList(outpoint); + if (script.IsWitnessProgram() || (!redeem_script.IsEmpty()) || + (!pubkeys.empty())) { + psbt_obj->psbt->SetTxInUtxo( + outpoint, TxOutReference(txout), redeem_script, key_list); + } else { // on P2SH-segwit + psbt_obj->psbt->SetTxInWitnessUtxoDirect( + outpoint, TxOutReference(txout)); + } + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtTxInBip32Pubkey( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* pubkey, const char* fingerprint, const char* bip32_path) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(pubkey)) { + warn(CFD_LOG_SOURCE, "pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + std::vector key_list; + ConvertToKeyData(pubkey, fingerprint, bip32_path, &key_list); + + auto script = psbt_obj->psbt->GetTxInRedeemScript(outpoint, true); + auto tx = psbt_obj->psbt->GetTxInUtxoFull(outpoint, true); + if (tx.GetTxOutCount() > vout) { + psbt_obj->psbt->SetTxInUtxo(outpoint, tx, script, key_list); + } else { + auto txout = psbt_obj->psbt->GetTxInUtxo(outpoint, true); + if (!txout.GetLockingScript().IsEmpty()) { + psbt_obj->psbt->SetTxInUtxo( + outpoint, TxOutReference(txout), script, key_list); + } else { + psbt_obj->psbt->SetTxInBip32KeyDirect(outpoint, key_list[0]); + } + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtSignature( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* pubkey, const char* der_signature) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(pubkey)) { + warn(CFD_LOG_SOURCE, "pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null or empty."); + } + if (IsEmptyString(der_signature)) { + warn(CFD_LOG_SOURCE, "der_signature is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. der_signature is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + KeyData key = KeyData(Pubkey(pubkey), std::string(), ByteData()); + psbt_obj->psbt->SetTxInSignature(outpoint, key, ByteData(der_signature)); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtSighashType( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int sighash_type) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + psbt_obj->psbt->SetTxInSighashType( + outpoint, SigHashType(static_cast(sighash_type))); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtFinalizeScript( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + const char* scriptsig) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(scriptsig)) { + warn(CFD_LOG_SOURCE, "scriptsig is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. scriptsig is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + bool is_witness = false; + auto txout = psbt_obj->psbt->GetTxInUtxo(outpoint, false, &is_witness); + auto script = psbt_obj->psbt->GetTxInRedeemScript(outpoint, true); + std::vector script_stack; + if (is_witness) { + Script scriptsig_obj(scriptsig); + bool is_multi_first = script.IsMultisigScript(); + for (const auto item : scriptsig_obj.GetElementList()) { + if (is_multi_first) { + if (item.GetOpCode() == ScriptOperator::OP_0) { + script_stack.emplace_back(ByteData()); + } else if (item.IsBinary()) { + script_stack.emplace_back(item.GetBinaryData()); + } else { + script_stack.emplace_back(item.GetData()); + } + is_multi_first = false; + } else { + if (item.IsBinary()) { + script_stack.emplace_back(item.GetBinaryData()); + } else { + script_stack.emplace_back(item.GetData()); + } + } + } + } else { + script_stack.emplace_back(ByteData(scriptsig)); + } + psbt_obj->psbt->SetTxInFinalScript(outpoint, script_stack); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdClearPsbtSignData( + void* handle, void* psbt_handle, const char* txid, uint32_t vout) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + psbt_obj->psbt->ClearTxInSignData(outpoint); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtSighashType( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int* sighash_type) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (sighash_type == nullptr) { + warn(CFD_LOG_SOURCE, "sighash_type is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. sighash_type is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + if (!psbt_obj->psbt->IsFindTxInSighashType(outpoint)) { + *sighash_type = 0; + } else { + auto sighash_type_obj = psbt_obj->psbt->GetTxInSighashType(outpoint); + *sighash_type = + static_cast(sighash_type_obj.GetSigHashAlgorithm()); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtUtxoData( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + int64_t* amount, char** locking_script, char** redeem_script, + char** descriptor, char** full_tx_hex) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + uint32_t index = psbt_obj->psbt->GetTxInIndex(outpoint); + + return CfdGetPsbtUtxoDataByIndex( + handle, psbt_handle, index, nullptr, nullptr, amount, locking_script, + redeem_script, descriptor, full_tx_hex); + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtUtxoDataByIndex( + void* handle, void* psbt_handle, uint32_t index, char** txid, + uint32_t* vout, int64_t* amount, char** locking_script, + char** redeem_script, char** descriptor, char** full_tx_hex) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_txid = nullptr; + char* work_locking_script = nullptr; + char* work_redeem_script = nullptr; + char* work_descriptor = nullptr; + char* work_tx = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + auto txin = psbt_obj->psbt->GetTransaction().GetTxIn(index); + OutPoint outpoint = txin.GetOutPoint(); + if (txid != nullptr) { + work_txid = CreateString(outpoint.GetTxid().GetHex()); + } + if (vout != nullptr) *vout = outpoint.GetVout(); + + auto utxo = psbt_obj->psbt->GetUtxoData(index); + if (!utxo.locking_script.IsEmpty()) { + if (locking_script != nullptr) { + work_locking_script = CreateString(utxo.locking_script.GetHex()); + } + if (amount != nullptr) *amount = utxo.amount.GetSatoshiValue(); + } + + Script script = psbt_obj->psbt->GetTxInRedeemScript(index, true); + if ((!script.IsEmpty()) && (!script.IsP2wpkhScript()) && + (redeem_script != nullptr)) { + work_redeem_script = CreateString(script.GetHex()); + } + + auto tx = psbt_obj->psbt->GetTxInUtxoFull(index, true); + TxOutReference txout; + if (tx.GetTxOutCount() > outpoint.GetVout()) { + txout = tx.GetTxOut(outpoint.GetVout()); + if (full_tx_hex != nullptr) work_tx = CreateString(tx.GetHex()); + } + + auto key_list = psbt_obj->psbt->GetTxInKeyDataList(index); + if (utxo.descriptor.empty()) { + Script temp_script = txout.GetLockingScript(); + if (temp_script.IsP2shScript() && script.IsMultisigScript()) { + uint32_t req_num = 0; + auto multisig_key_list = + ScriptUtil::ExtractPubkeysFromMultisigScript(script, &req_num); + utxo.descriptor = "multi(" + std::to_string(req_num); + for (const auto& pubkey : multisig_key_list) { + bool is_find = false; + for (const auto& key : key_list) { + if (pubkey.Equals(key.GetPubkey())) { + utxo.descriptor += "," + key.ToString(); + is_find = true; + } + } + if (!is_find) { + utxo.descriptor += "," + pubkey.GetHex(); + } + } + utxo.descriptor = "sh(" + utxo.descriptor + ")"; + } else if (temp_script.IsP2pkhScript() || temp_script.IsP2pkScript()) { + if (key_list.size() == 1) { + if (temp_script.IsP2pkhScript()) + utxo.descriptor = "pkh("; + else if (temp_script.IsP2pkScript()) + utxo.descriptor = "pk("; + utxo.descriptor += key_list[0].ToString(); + utxo.descriptor += ")"; + } + } + } + if ((!utxo.descriptor.empty()) && (descriptor != nullptr)) { + work_descriptor = CreateString(utxo.descriptor); + } + + if (txid != nullptr) *txid = work_txid; + if (locking_script != nullptr) *locking_script = work_locking_script; + if (redeem_script != nullptr) *redeem_script = work_redeem_script; + if (descriptor != nullptr) *descriptor = work_descriptor; + if (full_tx_hex != nullptr) *full_tx_hex = work_tx; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError( + &work_txid, &work_locking_script, &work_redeem_script, &work_descriptor, + &work_tx); + return result; +} + +int CfdAddPsbtTxOutWithPubkey( + void* handle, void* psbt_handle, int64_t amount, + const char* locking_script, const char* descriptor, uint32_t* index) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Script script; + uint32_t txout_index = 0; + if (!IsEmptyString(locking_script)) { + script = Script(locking_script); + } + + if (!IsEmptyString(descriptor)) { + Descriptor desc = Descriptor::Parse(descriptor); + std::vector path_args; + auto key = desc.GetKeyData(path_args); + auto ref = desc.GetReference(&path_args); + if ((!script.IsEmpty()) && (!script.Equals(ref.GetLockingScript()))) { + std::string err_msg = "locking_script doesn't match the descriptor."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + + txout_index = psbt_obj->psbt->AddTxOutData( + Amount(amount), ref.GenerateAddress(psbt_obj->net_type), key); + } else if (!script.IsEmpty()) { + txout_index = psbt_obj->psbt->AddTxOut(script, Amount(amount)); + } else { + warn(CFD_LOG_SOURCE, "locking_script is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. locking_script is null or empty."); + } + + if (index != nullptr) *index = txout_index; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddPsbtTxOutWithScript( + void* handle, void* psbt_handle, int64_t amount, + const char* locking_script, const char* redeem_script, + const char* descriptor, uint32_t* index) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Script script; + Script redeem_script_obj; + uint32_t txout_index = 0; + if (!IsEmptyString(locking_script)) script = Script(locking_script); + if (!IsEmptyString(redeem_script)) { + redeem_script_obj = Script(redeem_script); + } + + if (!IsEmptyString(descriptor)) { + Descriptor desc = Descriptor::Parse(descriptor); + std::vector path_args; + auto key_list = desc.GetKeyDataAll(); + auto ref_list = desc.GetReferenceAll(); + Script temp_redeem_script; + Address addr; + auto ref = ref_list.front(); + addr = ref.GenerateAddress(psbt_obj->net_type); + while (ref.HasChild()) { + if (ref.HasRedeemScript()) temp_redeem_script = ref.GetRedeemScript(); + ref = ref.GetChild(); + } + if (ref.HasRedeemScript()) temp_redeem_script = ref.GetRedeemScript(); + + if ((!script.IsEmpty()) && (!script.Equals(ref.GetLockingScript()))) { + std::string err_msg = "locking_script doesn't match the descriptor."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + if ((!redeem_script_obj.IsEmpty()) && + (!redeem_script_obj.Equals(temp_redeem_script))) { + std::string err_msg = "redeem_script doesn't match the descriptor."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + + txout_index = psbt_obj->psbt->AddTxOutData( + Amount(amount), addr, temp_redeem_script, key_list); + } else if ((!redeem_script_obj.IsEmpty()) && (!script.IsEmpty())) { + AddressFactory factory(psbt_obj->net_type); + Address addr; + if (script.IsP2wshScript()) { + auto temp_script = + ScriptUtil::CreateP2wshLockingScript(redeem_script_obj); + if (!temp_script.Equals(script)) { + std::string err_msg = "locking_script is not match redeem_script."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + addr = factory.CreateP2wshAddress(redeem_script_obj); + } else if (script.IsP2shScript()) { + auto temp_sh_script = + ScriptUtil::CreateP2shLockingScript(redeem_script_obj); + auto temp_wsh_script = + ScriptUtil::CreateP2wshLockingScript(redeem_script_obj); + auto temp_script = + ScriptUtil::CreateP2shLockingScript(temp_wsh_script); + if (temp_sh_script.Equals(script)) { + addr = factory.CreateP2shAddress(redeem_script_obj); + } else if (temp_script.Equals(script)) { + addr = Address( + psbt_obj->net_type, AddressType::kP2shP2wshAddress, + HashUtil::Hash160(temp_wsh_script)); + } else { + std::string err_msg = "locking_script is not match redeem_script."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + } else { + std::string err_msg = "locking_script is not support format."; + warn(CFD_LOG_SOURCE, "{}", err_msg); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. " + err_msg); + } + std::vector key_list; + txout_index = psbt_obj->psbt->AddTxOutData( + Amount(amount), addr, redeem_script_obj, key_list); + } else if (!script.IsEmpty()) { + txout_index = psbt_obj->psbt->AddTxOut(script, Amount(amount)); + } else { + warn(CFD_LOG_SOURCE, "locking_script is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. locking_script is null or empty."); + } + + if (index != nullptr) *index = txout_index; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtTxOutBip32Pubkey( + void* handle, void* psbt_handle, uint32_t index, const char* pubkey, + const char* fingerprint, const char* bip32_path) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(pubkey)) { + warn(CFD_LOG_SOURCE, "pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + std::vector key_list; + ConvertToKeyData(pubkey, fingerprint, bip32_path, &key_list); + + auto script = psbt_obj->psbt->GetTxOutScript(index, true); + psbt_obj->psbt->SetTxOutData(index, script, key_list); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtTxInIndex( + void* handle, void* psbt_handle, const char* txid, uint32_t vout, + uint32_t* index) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (index == nullptr) { + warn(CFD_LOG_SOURCE, "index is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. index is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + OutPoint outpoint(Txid(txid), vout); + + *index = psbt_obj->psbt->GetTxInIndex(outpoint); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtPubkeyRecord( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey, char** value) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (pubkey == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null."); + } + if (value == nullptr) { + warn(CFD_LOG_SOURCE, "value is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. value is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + KeyData pk_obj = ParsePubkey(kind, pubkey, psbt_obj->net_type); + ByteData key; + ByteData data; + switch (kind) { + case kCfdPsbtRecordInputSignature: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtInputPartialSig, pk_obj.GetPubkey()); + data = psbt_obj->psbt->GetTxInRecord(index, key); + break; + case kCfdPsbtRecordInputBip32: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtInputBip32Derivation, pk_obj.GetPubkey()); + data = psbt_obj->psbt->GetTxInRecord(index, key); + break; + case kCfdPsbtRecordOutputBip32: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtOutputBip32Derivation, pk_obj.GetPubkey()); + data = psbt_obj->psbt->GetTxOutRecord(index, key); + break; + case kCfdPsbtRecordGloalXpub: + key = Psbt::CreateFixRecordKey( + Psbt::kPsbtGlobalXpub, pk_obj.GetExtPubkey().GetData()); + data = psbt_obj->psbt->GetGlobalRecord(key); + break; + default: + warn(CFD_LOG_SOURCE, "kind is invalid: {}", kind); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. kind is invalid."); + } + + *value = CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdIsFindPsbtPubkeyRecord( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (pubkey == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + KeyData pk_obj = ParsePubkey(kind, pubkey, psbt_obj->net_type); + ByteData key; + bool is_find = true; + switch (kind) { + case kCfdPsbtRecordInputSignature: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtInputPartialSig, pk_obj.GetPubkey()); + is_find = psbt_obj->psbt->IsFindTxInRecord(index, key); + break; + case kCfdPsbtRecordInputBip32: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtInputBip32Derivation, pk_obj.GetPubkey()); + is_find = psbt_obj->psbt->IsFindTxInRecord(index, key); + break; + case kCfdPsbtRecordOutputBip32: + key = Psbt::CreatePubkeyRecordKey( + Psbt::kPsbtOutputBip32Derivation, pk_obj.GetPubkey()); + is_find = psbt_obj->psbt->IsFindTxOutRecord(index, key); + break; + case kCfdPsbtRecordGloalXpub: + key = Psbt::CreateFixRecordKey( + Psbt::kPsbtGlobalXpub, pk_obj.GetExtPubkey().GetData()); + is_find = psbt_obj->psbt->IsFindGlobalRecord(key); + break; + default: + warn(CFD_LOG_SOURCE, "kind is invalid: {}", kind); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. kind is invalid."); + } + + if (!is_find) return CfdErrorCode::kCfdNotFoundError; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtBip32Data( + void* handle, void* psbt_handle, int kind, uint32_t index, + const char* pubkey, char** fingerprint, char** bip32_path) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_fingerprint = nullptr; + char* work_path = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (pubkey == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + KeyData pk_obj = ParsePubkey(kind, pubkey, psbt_obj->net_type); + + std::vector key_list; + if (kind == kCfdPsbtRecordInputBip32) { + key_list = psbt_obj->psbt->GetTxInKeyDataList(index); + } else if (kind == kCfdPsbtRecordOutputBip32) { + key_list = psbt_obj->psbt->GetTxOutKeyDataList(index); + } else if (kind == kCfdPsbtRecordGloalXpub) { + key_list = psbt_obj->psbt->GetGlobalXpubkeyDataList(); + } else { + warn(CFD_LOG_SOURCE, "kind is invalid: {}", kind); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. kind is invalid."); + } + + KeyData key; + bool has_extpubkey = pk_obj.HasExtPubkey(); + Pubkey target_pubkey = pk_obj.GetPubkey(); + for (const auto temp_key : key_list) { + if (temp_key.GetPubkey().Equals(target_pubkey)) { + if (has_extpubkey) { + if (temp_key.GetExtPubkey().GetData().Equals( + pk_obj.GetExtPubkey().GetData())) { + key = temp_key; + break; + } + } else { + key = temp_key; + break; + } + } + } + if (!key.IsValid()) { + warn(CFD_LOG_SOURCE, "pubkey not found: {}", pk_obj.ToString()); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey not found"); + } + + if (fingerprint != nullptr) { + work_fingerprint = CreateString(key.GetFingerprint().GetHex()); + } + if (bip32_path != nullptr) work_path = CreateString(key.GetBip32Path()); + + if (fingerprint != nullptr) *fingerprint = work_fingerprint; + if (bip32_path != nullptr) *bip32_path = work_path; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_fingerprint, &work_path); + return result; +} + +int CfdGetPsbtPubkeyList( + void* handle, void* psbt_handle, int kind, uint32_t index, + uint32_t* list_num, void** pubkey_list_handle) { + int result = CfdErrorCode::kCfdUnknownError; + CfdCapiPsbtPubkeyListHandle* buffer = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (pubkey_list_handle == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey_list_handle is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey_list_handle is null."); + } + if (list_num == nullptr) { + warn(CFD_LOG_SOURCE, "list_num is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. list_num is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + std::vector pk_list; + std::vector key_list; + bool has_bip32_list = true; + bool has_xpub_list = false; + switch (kind) { + case kCfdPsbtRecordInputSignature: + pk_list = psbt_obj->psbt->GetTxInSignaturePubkeyList(index); + break; + case kCfdPsbtRecordInputBip32: + key_list = psbt_obj->psbt->GetTxInKeyDataList(index); + break; + case kCfdPsbtRecordOutputBip32: + key_list = psbt_obj->psbt->GetTxOutKeyDataList(index); + break; + case kCfdPsbtRecordGloalXpub: + key_list = psbt_obj->psbt->GetGlobalXpubkeyDataList(); + has_xpub_list = true; + break; + default: + warn(CFD_LOG_SOURCE, "kind is invalid: {}", kind); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. kind is invalid."); + } + if (key_list.empty()) { + has_bip32_list = false; + ByteData empty_fingerprint; + std::string empty_path; + key_list.reserve(pk_list.size()); + for (const auto pubkey : pk_list) { + key_list.emplace_back(pubkey, empty_path, empty_fingerprint); + } + } + + buffer = static_cast(AllocBuffer( + kPrefixPsbtPubkeyList, sizeof(CfdCapiPsbtPubkeyListHandle))); + buffer->key_list = new std::vector(); + buffer->has_bip32_list = has_bip32_list; + buffer->has_xpub_list = has_xpub_list; + *(buffer->key_list) = key_list; + *list_num = static_cast(key_list.size()); + *pubkey_list_handle = buffer; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + CfdFreePsbtPubkeyList(handle, buffer); + return result; +} + +int CfdGetPsbtPubkeyListData( + void* handle, void* pubkey_list_handle, uint32_t index, char** pubkey, + char** pubkey_hex) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_pubkey = nullptr; + char* work_pubkey_hex = nullptr; + try { + cfd::Initialize(); + CheckBuffer(pubkey_list_handle, kPrefixPsbtPubkeyList); + CfdCapiPsbtPubkeyListHandle* list_handle = + static_cast(pubkey_list_handle); + if (pubkey == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null."); + } + if (list_handle->key_list == nullptr) { + warn(CFD_LOG_SOURCE, "key_list is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. key_list is null."); + } + if (index >= list_handle->key_list->size()) { + warn(CFD_LOG_SOURCE, "out of range error."); + throw CfdException( + CfdError::kCfdOutOfRangeError, "Failed to out of range."); + } + + auto key = list_handle->key_list->at(index); + if (list_handle->has_xpub_list) { + auto extkey = key.GetExtPubkey(); + work_pubkey = CreateString(extkey.ToString()); + if (pubkey_hex != nullptr) { + work_pubkey_hex = CreateString(extkey.GetData().GetHex()); + } + } else { + auto pubkey_obj = key.GetPubkey(); + work_pubkey = CreateString(pubkey_obj.GetHex()); + if (pubkey_hex != nullptr) { + work_pubkey_hex = CreateString(pubkey_obj.GetHex()); + } + } + + *pubkey = work_pubkey; + if (pubkey_hex != nullptr) *pubkey_hex = work_pubkey_hex; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_pubkey, &work_pubkey_hex); + return result; +} + +int CfdGetPsbtPubkeyListBip32Data( + void* handle, void* pubkey_list_handle, uint32_t index, char** pubkey, + char** fingerprint, char** bip32_path) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_pubkey = nullptr; + char* work_fingerprint = nullptr; + char* work_path = nullptr; + try { + cfd::Initialize(); + CheckBuffer(pubkey_list_handle, kPrefixPsbtPubkeyList); + CfdCapiPsbtPubkeyListHandle* list_handle = + static_cast(pubkey_list_handle); + if (pubkey == nullptr) { + warn(CFD_LOG_SOURCE, "pubkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null."); + } + if (list_handle->key_list == nullptr) { + warn(CFD_LOG_SOURCE, "key_list is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. key_list is null."); + } + if (index >= list_handle->key_list->size()) { + warn(CFD_LOG_SOURCE, "out of range error."); + throw CfdException( + CfdError::kCfdOutOfRangeError, "Failed to out of range."); + } + if (!list_handle->has_bip32_list) { + warn(CFD_LOG_SOURCE, "bip32 not support error."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Failed to bip32 not support."); + } + + auto key = list_handle->key_list->at(index); + if (list_handle->has_xpub_list) { + work_pubkey = CreateString(key.GetExtPubkey().ToString()); + } else { + work_pubkey = CreateString(key.GetPubkey().GetHex()); + } + if (fingerprint != nullptr) { + work_fingerprint = CreateString(key.GetFingerprint().GetHex()); + } + if (bip32_path != nullptr) work_path = CreateString(key.GetBip32Path()); + + *pubkey = work_pubkey; + if (fingerprint != nullptr) *fingerprint = work_fingerprint; + if (bip32_path != nullptr) *bip32_path = work_path; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_pubkey, &work_fingerprint, &work_path); + return result; +} + +int CfdFreePsbtPubkeyList(void* handle, void* pubkey_list_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (pubkey_list_handle != nullptr) { + CheckBuffer(pubkey_list_handle, kPrefixPsbtPubkeyList); + CfdCapiPsbtPubkeyListHandle* list_handle = + static_cast(pubkey_list_handle); + if (list_handle->key_list != nullptr) { + delete list_handle->key_list; + list_handle->key_list = nullptr; + } + FreeBuffer( + pubkey_list_handle, kPrefixPsbtPubkeyList, + sizeof(CfdCapiPsbtPubkeyListHandle)); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtByteDataList( + void* handle, void* psbt_handle, int kind, uint32_t index, + uint32_t* list_num, void** data_list_handle) { + int result = CfdErrorCode::kCfdUnknownError; + CfdCapiPsbtByteDataListHandle* buffer = nullptr; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (data_list_handle == nullptr) { + warn(CFD_LOG_SOURCE, "data_list_handle is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. data_list_handle is null."); + } + if (list_num == nullptr) { + warn(CFD_LOG_SOURCE, "list_num is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. list_num is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + std::vector data_list; + switch (kind) { + case kCfdPsbtRecordFinalWitness: + data_list = psbt_obj->psbt->GetTxInFinalScript(index, true); + break; + case kCfdPsbtRecordInputUnknownKeys: + data_list = psbt_obj->psbt->GetTxInRecordKeyList(index); + break; + case kCfdPsbtRecordOutputUnknownKeys: + data_list = psbt_obj->psbt->GetTxOutRecordKeyList(index); + break; + case kCfdPsbtRecordGlobalUnknownKeys: + data_list = psbt_obj->psbt->GetGlobalRecordKeyList(); + break; + default: + warn(CFD_LOG_SOURCE, "kind is invalid: {}", kind); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. kind is invalid."); + } + + buffer = static_cast(AllocBuffer( + kPrefixPsbtByteDataList, sizeof(CfdCapiPsbtByteDataListHandle))); + buffer->data_list = new std::vector(); + *(buffer->data_list) = data_list; + *list_num = static_cast(data_list.size()); + *data_list_handle = buffer; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + CfdFreePsbtByteDataList(handle, buffer); + return result; +} + +int CfdGetPsbtByteDataItem( + void* handle, void* data_list_handle, uint32_t index, char** data) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(data_list_handle, kPrefixPsbtByteDataList); + CfdCapiPsbtByteDataListHandle* list_handle = + static_cast(data_list_handle); + if (data == nullptr) { + warn(CFD_LOG_SOURCE, "data is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. data is null."); + } + if (list_handle->data_list == nullptr) { + warn(CFD_LOG_SOURCE, "data_list is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. data_list is null."); + } + if (index >= list_handle->data_list->size()) { + warn(CFD_LOG_SOURCE, "out of range error."); + throw CfdException( + CfdError::kCfdOutOfRangeError, "Failed to out of range."); + } + + auto byte_data = list_handle->data_list->at(index); + *data = CreateString(byte_data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdFreePsbtByteDataList(void* handle, void* data_list_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (data_list_handle != nullptr) { + CheckBuffer(data_list_handle, kPrefixPsbtByteDataList); + CfdCapiPsbtByteDataListHandle* list_handle = + static_cast(data_list_handle); + if (list_handle->data_list != nullptr) { + delete list_handle->data_list; + list_handle->data_list = nullptr; + } + FreeBuffer( + data_list_handle, kPrefixPsbtByteDataList, + sizeof(CfdCapiPsbtByteDataListHandle)); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddPsbtGlobalXpubkey( + void* handle, void* psbt_handle, const char* xpubkey, + const char* fingerprint, const char* bip32_path) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(xpubkey)) { + warn(CFD_LOG_SOURCE, "xpubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. xpubkey is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + std::string xpub_str(xpubkey); + KeyData key_data; + if (xpub_str.length() == kCfdExtPubkeyHexSize) { + key_data = KeyData(ExtPubkey(ByteData(xpub_str)), "", ByteData()); + } else { + key_data = KeyData(xpub_str); + if ((!key_data.HasExtPubkey()) || key_data.HasExtPrivkey()) { + warn(CFD_LOG_SOURCE, "psbt invalid xpubkey format."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. psbt invalid xpubkey format."); + } + } + + bool has_mainnet = + (key_data.GetExtPubkey().GetNetworkType() == NetType::kMainnet); + if (((psbt_obj->net_type == NetType::kMainnet) && (!has_mainnet)) || + ((psbt_obj->net_type != NetType::kMainnet) && (has_mainnet))) { + warn(CFD_LOG_SOURCE, "unmatch xpubkey network type."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. unmatch xpubkey network type."); + } + + if (key_data.GetBip32Path().empty() || + (key_data.GetFingerprint().GetDataSize() != 4)) { + if (IsEmptyString(fingerprint)) { + warn(CFD_LOG_SOURCE, "fingerprint is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. fingerprint is null or empty."); + } + if (IsEmptyString(bip32_path)) { + warn(CFD_LOG_SOURCE, "bip32_path is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. bip32_path is null or empty."); + } + key_data = KeyData( + key_data.GetExtPubkey(), std::string(bip32_path), + ByteData(fingerprint)); + } + psbt_obj->psbt->SetGlobalXpubkey(key_data); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetPsbtRedeemScript( + void* handle, void* psbt_handle, int type, uint32_t index, + const char* redeem_script) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(redeem_script)) { + warn(CFD_LOG_SOURCE, "redeem_script is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. redeem_script is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Script script(redeem_script); + std::vector key_list; + switch (type) { + case kCfdPsbtRecordTypeInput: { + auto txout = psbt_obj->psbt->GetTxInUtxo(index); + psbt_obj->psbt->SetTxInUtxo( + index, TxOutReference(txout), script, key_list); + } break; + case kCfdPsbtRecordTypeOutput: + psbt_obj->psbt->SetTxOutData(index, script, key_list); + break; + default: + warn(CFD_LOG_SOURCE, "type is invalid: {}", type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. type is invalid."); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, const char* key, + const char* value) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(key)) { + warn(CFD_LOG_SOURCE, "key is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is null or empty."); + } + if (value == nullptr) { + warn(CFD_LOG_SOURCE, "value is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. value is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + ByteData key_data(key); + ByteData data(value); + switch (type) { + case kCfdPsbtRecordTypeGlobal: + psbt_obj->psbt->SetGlobalRecord(key_data, data); + break; + case kCfdPsbtRecordTypeInput: + psbt_obj->psbt->SetTxInRecord(index, key_data, data); + break; + case kCfdPsbtRecordTypeOutput: + psbt_obj->psbt->SetTxOutRecord(index, key_data, data); + break; + default: + warn(CFD_LOG_SOURCE, "type is invalid: {}", type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. type is invalid."); + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, const char* key, + char** value) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(key)) { + warn(CFD_LOG_SOURCE, "key is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is null or empty."); + } + if (value == nullptr) { + warn(CFD_LOG_SOURCE, "value is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. value is null."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + ByteData key_data(key); + ByteData data; + switch (type) { + case kCfdPsbtRecordTypeGlobal: + data = psbt_obj->psbt->GetGlobalRecord(key_data); + break; + case kCfdPsbtRecordTypeInput: + data = psbt_obj->psbt->GetTxInRecord(index, key_data); + break; + case kCfdPsbtRecordTypeOutput: + data = psbt_obj->psbt->GetTxOutRecord(index, key_data); + break; + default: + warn(CFD_LOG_SOURCE, "type is invalid: {}", type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. type is invalid."); + } + + *value = CreateString(data.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdIsFindPsbtRecord( + void* handle, void* psbt_handle, int type, uint32_t index, + const char* key) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(key)) { + warn(CFD_LOG_SOURCE, "key is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + ByteData key_data(key); + bool is_find = true; + switch (type) { + case kCfdPsbtRecordTypeGlobal: + is_find = psbt_obj->psbt->IsFindGlobalRecord(key_data); + break; + case kCfdPsbtRecordTypeInput: + is_find = psbt_obj->psbt->IsFindTxInRecord(index, key_data); + break; + case kCfdPsbtRecordTypeOutput: + is_find = psbt_obj->psbt->IsFindTxOutRecord(index, key_data); + break; + default: + warn(CFD_LOG_SOURCE, "type is invalid: {}", type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. type is invalid."); + } + + if (!is_find) return CfdErrorCode::kCfdNotFoundError; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdVerifyPsbtTxIn( + void* handle, void* psbt_handle, const char* txid, uint32_t vout) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + + Txid txid_obj(txid); + psbt_obj->psbt->Verify(OutPoint(txid_obj, vout)); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + if (std::string(except.what()) == "psbt txin not finalized yet.") { + result = kCfdSignVerificationError; + } + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdInitializeFundPsbt(void* handle, void** fund_handle) { + int result = CfdErrorCode::kCfdUnknownError; + CfdCapiPsbtFundHandle* buffer = nullptr; + try { + cfd::Initialize(); + if (fund_handle == nullptr) { + warn(CFD_LOG_SOURCE, "fund handle is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. fund handle is null."); + } + + buffer = static_cast( + AllocBuffer(kPrefixPsbtFundHandle, sizeof(CfdCapiPsbtFundHandle))); + buffer->utxos = new std::vector(); + buffer->indexes = new std::vector(); + buffer->fee_rate = 20.0; + buffer->long_term_fee_rate = 20.0; + buffer->dust_fee_rate = 3.0; + buffer->knapsack_min_change = -1; + *fund_handle = buffer; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + CfdFreeFundPsbt(handle, buffer); + return result; +} + +int CfdFundPsbtAddToUtxoList( + void* handle, void* fund_handle, const char* txid, uint32_t vout, + int64_t amount, const char* asset, const char* descriptor, + const char* scriptsig_template, const char* full_utxo_tx) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(fund_handle, kPrefixPsbtFundHandle); + CfdCapiPsbtFundHandle* buffer = + static_cast(fund_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(descriptor)) { + warn(CFD_LOG_SOURCE, "descriptor is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. descriptor is null or empty."); + } + if ((buffer->indexes == nullptr) || (buffer->utxos == nullptr)) { + warn(CFD_LOG_SOURCE, "invalid handle statement."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to statement. invalid handle statement."); + } + if (!buffer->indexes->empty()) { + warn(CFD_LOG_SOURCE, "already funded."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to statement. already funded."); + } + + UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.block_height = 0; + utxo.binary_data = nullptr; + utxo.txid = Txid(txid); + utxo.vout = vout; + utxo.amount = Amount(amount); + utxo.descriptor = std::string(descriptor); + if (!IsEmptyString(scriptsig_template)) { + utxo.scriptsig_template = Script(std::string(scriptsig_template)); + } + if (!IsEmptyString(asset)) { + // do nothing + } + if (!IsEmptyString(full_utxo_tx)) { + // TODO(k-matsuzawa) add full utxo tx with list + // (If necessary, fix it at the same time as FundPsbt.) + } + buffer->utxos->push_back(utxo); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetOptionFundPsbt( + void* handle, void* fund_handle, int key, int64_t int64_value, + double double_value, bool bool_value) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(fund_handle, kPrefixPsbtFundHandle); + CfdCapiPsbtFundHandle* buffer = + static_cast(fund_handle); + + switch (key) { + case kCfdPsbtFundEstimateFeeRate: + buffer->fee_rate = double_value; + break; + case kCfdPsbtFundDustFeeRate: + buffer->dust_fee_rate = double_value; + break; + case kCfdPsbtFundLongTermFeeRate: + buffer->long_term_fee_rate = double_value; + break; + case kCfdPsbtFundKnapsackMinChange: + buffer->knapsack_min_change = int64_value; + break; + case kCfdPsbtFundIsBlind: + if (bool_value) { + // do nothing + } + break; + case kCfdPsbtFundBlindExponent: + break; + case kCfdPsbtFundBlindMinimumBits: + if (int64_value >= 0) { + // do nothing + } + break; + default: + warn(CFD_LOG_SOURCE, "illegal key {}.", key); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. key is illegal."); + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdFinalizeFundPsbt( + void* handle, void* psbt_handle, void* fund_handle, + const char* change_address_descriptor, int64_t* tx_fee, + uint32_t* used_utxo_count) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(psbt_handle, kPrefixPsbtHandle); + CheckBuffer(fund_handle, kPrefixPsbtFundHandle); + CfdCapiPsbtHandle* psbt_obj = static_cast(psbt_handle); + CfdCapiPsbtFundHandle* buffer = + static_cast(fund_handle); + + if (psbt_obj->psbt == nullptr) { + warn(CFD_LOG_SOURCE, "psbt is null."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to handle statement. psbt is null."); + } + if ((buffer->indexes == nullptr) || (buffer->utxos == nullptr)) { + warn(CFD_LOG_SOURCE, "invalid handle statement."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to statement. invalid handle statement."); + } + + CoinSelectionOption option_params; + option_params.SetEffectiveFeeBaserate(buffer->fee_rate); + option_params.SetLongTermFeeBaserate(buffer->long_term_fee_rate); + option_params.SetDustFeeRate(buffer->dust_fee_rate); + option_params.SetKnapsackMinimumChange(buffer->knapsack_min_change); + + UtxoFilter filter; + Amount tx_fee_value; + option_params.InitializeTxSizeInfo(); // bitcoin + Descriptor* change_address_ptr = nullptr; + Descriptor change_address; + if (!IsEmptyString(change_address_descriptor)) { + change_address = Descriptor::Parse(change_address_descriptor); + change_address_ptr = &change_address; + } + + auto used_utxos = psbt_obj->psbt->FundTransaction( + *(buffer->utxos), buffer->fee_rate, change_address_ptr, &tx_fee_value, + &option_params, &filter, psbt_obj->net_type); + { + buffer->indexes->clear(); + buffer->indexes->reserve(used_utxos.size()); + + bool is_find; + uint32_t utxo_index = 0; + for (const auto& used_utxo : used_utxos) { + is_find = false; + for (size_t index = 0; index < buffer->utxos->size(); ++index) { + const auto& utxo = buffer->utxos->at(index); + if ((used_utxo.vout == utxo.vout) && + (used_utxo.txid.Equals(utxo.txid))) { + is_find = true; + utxo_index = static_cast(index); + break; + } + } + if (!is_find) { + warn(CFD_LOG_SOURCE, "invalid utxo state."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to statement. invalid utxo state."); + } + buffer->indexes->push_back(utxo_index); + } + } + + if (tx_fee != nullptr) *tx_fee = tx_fee_value.GetSatoshiValue(); + if (used_utxo_count != nullptr) { + *used_utxo_count = static_cast(used_utxos.size()); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetFundPsbtUsedUtxo( + void* handle, void* fund_handle, uint32_t index, uint32_t* utxo_index, + char** txid, uint32_t* vout, int64_t* amount, char** asset, + char** descriptor, char** scriptsig_template) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_txid = nullptr; + char* work_descriptor = nullptr; + char* work_asset = nullptr; + char* work_scriptsig = nullptr; + try { + cfd::Initialize(); + CheckBuffer(fund_handle, kPrefixPsbtFundHandle); + CfdCapiPsbtFundHandle* buffer = + static_cast(fund_handle); + + if ((buffer->indexes == nullptr) || (buffer->indexes->size() <= index)) { + warn(CFD_LOG_SOURCE, "index out of range."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. index is out of range."); + } + uint32_t target_index = buffer->indexes->at(index); + if (utxo_index != nullptr) *utxo_index = target_index; + + UtxoData utxo = buffer->utxos->at(target_index); + if (vout != nullptr) *vout = utxo.vout; + if (amount != nullptr) *amount = utxo.amount.GetSatoshiValue(); + + if (txid != nullptr) work_txid = CreateString(utxo.txid.GetHex()); +#ifndef CFD_DISABLE_ELEMENTS + if (asset != nullptr) work_asset = CreateString(utxo.asset.GetHex()); +#endif // CFD_DISABLE_ELEMENTS + if (descriptor != nullptr) work_descriptor = CreateString(utxo.descriptor); + if (scriptsig_template != nullptr) { + work_scriptsig = CreateString(utxo.scriptsig_template.GetHex()); + } + + if (txid != nullptr) *txid = work_txid; +#ifndef CFD_DISABLE_ELEMENTS + if (asset != nullptr) *asset = work_asset; +#endif // CFD_DISABLE_ELEMENTS + if (descriptor != nullptr) *descriptor = work_descriptor; + if (scriptsig_template != nullptr) *scriptsig_template = work_scriptsig; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError( + &work_txid, &work_descriptor, &work_asset, &work_scriptsig); + return result; +} + +int CfdFreeFundPsbt(void* handle, void* fund_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (fund_handle != nullptr) { + CheckBuffer(fund_handle, kPrefixPsbtFundHandle); + CfdCapiPsbtFundHandle* buffer = + static_cast(fund_handle); + if (buffer->utxos != nullptr) { + delete buffer->utxos; + buffer->utxos = nullptr; + } + if (buffer->indexes != nullptr) { + delete buffer->indexes; + buffer->indexes = nullptr; + } + FreeBuffer( + fund_handle, kPrefixPsbtFundHandle, sizeof(CfdCapiPsbtFundHandle)); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +}; // extern "C" + +#endif // CFD_DISABLE_CAPI diff --git a/src/capi/cfdcapi_script.cpp b/src/capi/cfdcapi_script.cpp index 8905d98b..f629a05b 100644 --- a/src/capi/cfdcapi_script.cpp +++ b/src/capi/cfdcapi_script.cpp @@ -2,9 +2,7 @@ /** * @file cfdcapi_script.h * - * @brief cfd-capiで利用するTransaction作成のAPI定義 - * - * C言語のAPIを提供する. + * @brief API implements file of Script function for used in cfd-capi */ #ifndef CFD_DISABLE_CAPI #include "cfdc/cfdcapi_script.h" @@ -23,21 +21,30 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_util.h" using cfd::SignParameter; using cfd::api::TransactionApi; using cfd::core::ByteData; +using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; +using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; using cfd::core::Script; using cfd::core::ScriptBuilder; using cfd::core::ScriptElement; using cfd::core::ScriptElementType; using cfd::core::SigHashAlgorithm; using cfd::core::SigHashType; +using cfd::core::StringUtil; +using cfd::core::TapBranch; +using cfd::core::TaprootScriptTree; +using cfd::core::TaprootUtil; using cfd::core::logger::info; using cfd::core::logger::warn; @@ -50,7 +57,9 @@ namespace capi { //! prefix: ScriptItem constexpr const char* const kPrefixScriptItem = "ScriptItem"; //! prefix: MultisigScriptSig -constexpr const char* const kPrefiMultisigScriptSig = "MultiScriptSig"; +constexpr const char* const kPrefixMultisigScriptSig = "MultiScriptSig"; +//! prefix: TapscriptTree +constexpr const char* const kPrefixTapscriptTree = "TapscriptTree"; //! delimiter of script asm constexpr const char kScriptAsmDelimiter = ' '; //! MultisigScriptSig struct @@ -66,6 +75,17 @@ struct CfdCapiScriptItemHandleData { std::vector* script_items; }; +/** + * @brief cfd-capi TapscriptTree handle data. + */ +struct CfdCapiTapscriptTree { + char prefix[kPrefixLength]; //!< buffer prefix + //! tree container + std::vector* tree_buffer; + //! branch container + std::vector* branch_buffer; +}; + } // namespace capi } // namespace cfd @@ -75,6 +95,7 @@ struct CfdCapiScriptItemHandleData { using cfd::capi::AllocBuffer; using cfd::capi::CfdCapiMultisigScriptSigData; using cfd::capi::CfdCapiScriptItemHandleData; +using cfd::capi::CfdCapiTapscriptTree; using cfd::capi::CheckBuffer; using cfd::capi::ConvertHashToAddressType; using cfd::capi::CreateString; @@ -82,8 +103,9 @@ using cfd::capi::FreeBuffer; using cfd::capi::FreeBufferOnError; using cfd::capi::IsEmptyString; using cfd::capi::kMultisigMaxKeyNum; -using cfd::capi::kPrefiMultisigScriptSig; +using cfd::capi::kPrefixMultisigScriptSig; using cfd::capi::kPrefixScriptItem; +using cfd::capi::kPrefixTapscriptTree; using cfd::capi::kPubkeyHexSize; using cfd::capi::kScriptAsmDelimiter; using cfd::capi::kSignatureHexSize; @@ -257,7 +279,7 @@ int CfdInitializeMultisigScriptSig(void* handle, void** multisig_handle) { } *multisig_handle = AllocBuffer( - kPrefiMultisigScriptSig, sizeof(CfdCapiMultisigScriptSigData)); + kPrefixMultisigScriptSig, sizeof(CfdCapiMultisigScriptSigData)); return CfdErrorCode::kCfdSuccess; } catch (const CfdException& except) { result = SetLastError(handle, except); @@ -275,7 +297,7 @@ int CfdAddMultisigScriptSigData( int result = CfdErrorCode::kCfdUnknownError; try { cfd::Initialize(); - CheckBuffer(multisig_handle, kPrefiMultisigScriptSig); + CheckBuffer(multisig_handle, kPrefixMultisigScriptSig); if (signature == nullptr) { warn(CFD_LOG_SOURCE, "signature is null."); throw CfdException( @@ -333,8 +355,8 @@ int CfdAddMultisigScriptSigDataToDer( } // encode to der - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_bytes = ByteData(std::string(signature)); SignParameter param(signature_bytes, true, sighashtype); ByteData signature_der = param.ConvertToSignature(); @@ -358,7 +380,7 @@ int CfdFinalizeMultisigScriptSig( int result = CfdErrorCode::kCfdUnknownError; try { cfd::Initialize(); - CheckBuffer(multisig_handle, kPrefiMultisigScriptSig); + CheckBuffer(multisig_handle, kPrefixMultisigScriptSig); if (IsEmptyString(redeem_script)) { warn(CFD_LOG_SOURCE, "redeemScript is null or empty."); throw CfdException( @@ -421,7 +443,7 @@ int CfdFreeMultisigScriptSigHandle(void* handle, void* multisig_handle) { try { cfd::Initialize(); FreeBuffer( - multisig_handle, kPrefiMultisigScriptSig, + multisig_handle, kPrefixMultisigScriptSig, sizeof(CfdCapiMultisigScriptSigData)); return CfdErrorCode::kCfdSuccess; } catch (const CfdException& except) { @@ -434,6 +456,730 @@ int CfdFreeMultisigScriptSigHandle(void* handle, void* multisig_handle) { return result; } +int CfdInitializeTaprootScriptTree(void* handle, void** tree_handle) { + int result = CfdErrorCode::kCfdUnknownError; + CfdCapiTapscriptTree* buffer = nullptr; + try { + cfd::Initialize(); + if (tree_handle == nullptr) { + warn(CFD_LOG_SOURCE, "tree_handle is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tree_handle is null."); + } + + buffer = static_cast( + AllocBuffer(kPrefixTapscriptTree, sizeof(CfdCapiTapscriptTree))); + buffer->tree_buffer = new std::vector(1); + buffer->branch_buffer = new std::vector(); + *tree_handle = buffer; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + CfdFreeTaprootScriptTreeHandle(handle, buffer); + return result; +} + +int CfdSetInitialTapLeaf( + void* handle, void* tree_handle, const char* tapscript, + uint8_t leaf_version) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(tapscript)) { + warn(CFD_LOG_SOURCE, "tapscript is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tapscript is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + auto& tree = buffer->tree_buffer->at(0); + + TaprootScriptTree leaf(leaf_version, Script(tapscript)); + tree = leaf; + buffer->branch_buffer->clear(); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetInitialTapBranchByHash( + void* handle, void* tree_handle, const char* hash) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(hash)) { + warn(CFD_LOG_SOURCE, "tapscript is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tapscript is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + auto& tree = buffer->tree_buffer->at(0); + buffer->branch_buffer->clear(); + + TapBranch branch = TapBranch(ByteData256(hash)); + buffer->branch_buffer->emplace_back(branch); + + tree = TaprootScriptTree(); // clear + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetScriptTreeFromString( + void* handle, void* tree_handle, const char* tree_string, + const char* tapscript, uint8_t leaf_version, const char* control_nodes) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(tree_string)) { + warn(CFD_LOG_SOURCE, "tree_string is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tree_string is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + auto& tree = buffer->tree_buffer->at(0); + + if (IsEmptyString(tapscript)) { + auto branch = TapBranch::FromString(tree_string); + buffer->branch_buffer->clear(); + buffer->branch_buffer->emplace_back(branch); + tree = TaprootScriptTree(); + } else { + if (leaf_version != TaprootScriptTree::kTapScriptLeafVersion) { + // TODO(k-matsuzawa): Support in the future. + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. leaf_version is not support."); + } + Script tapscript_obj(tapscript); + std::vector target_nodes; + if (!IsEmptyString(control_nodes)) { + std::string control_str(control_nodes); + if ((control_str.size() % (cfd::core::kByteData256Length * 2)) == 0) { + size_t split_size = cfd::core::kByteData256Length * 2; + size_t max = control_str.size() / split_size; + for (size_t index = 0; index < max; ++index) { + size_t offset = index * split_size; + ByteData256 node(control_str.substr(offset, split_size)); + target_nodes.emplace_back(node); + } + } else { // control block + std::vector stack; + stack.emplace_back(tapscript_obj.GetData()); + stack.emplace_back(ByteData(control_str)); + TaprootUtil::ParseTaprootSignData( + stack, nullptr, nullptr, nullptr, nullptr, &target_nodes, + nullptr); + } + } + + tree = TaprootScriptTree::FromString( + tree_string, tapscript_obj, target_nodes); + buffer->branch_buffer->clear(); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetTapScriptByWitnessStack( + void* handle, void* tree_handle, const char* control_block, + const char* tapscript, char** internal_pubkey) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_internal_pubkey = nullptr; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(control_block)) { + warn(CFD_LOG_SOURCE, "control_block is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. control_block is null or empty."); + } + if (IsEmptyString(tapscript)) { + warn(CFD_LOG_SOURCE, "tapscript is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tapscript is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + auto& tree = buffer->tree_buffer->at(0); + + uint8_t leaf_version = 0; + SchnorrPubkey internal_pubkey_obj; + std::vector nodes; + Script tapscript_obj; + std::vector witness_stack; + witness_stack.emplace_back(std::string(tapscript)); + witness_stack.emplace_back(std::string(control_block)); + TaprootUtil::ParseTaprootSignData( + witness_stack, nullptr, nullptr, &leaf_version, &internal_pubkey_obj, + &nodes, &tapscript_obj); + TaprootScriptTree new_tree(leaf_version, tapscript_obj); + for (const auto& node : nodes) new_tree.AddBranch(node); + + if (internal_pubkey != nullptr) { + work_internal_pubkey = CreateString(internal_pubkey_obj.GetHex()); + } + + tree = new_tree; + buffer->branch_buffer->clear(); + if (internal_pubkey != nullptr) *internal_pubkey = work_internal_pubkey; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_internal_pubkey); + return result; +} + +int CfdAddTapBranchByHash( + void* handle, void* tree_handle, const char* branch_hash) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(branch_hash)) { + warn(CFD_LOG_SOURCE, "branch_hash is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. branch_hash is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + ByteData256 hash(branch_hash); + if (!buffer->branch_buffer->empty()) { + auto& branch = buffer->branch_buffer->at(0); + branch.AddBranch(hash); + } else { + auto& tree = buffer->tree_buffer->at(0); + tree.AddBranch(hash); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddTapBranchByScriptTree( + void* handle, void* tree_handle, void* branch_tree) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CheckBuffer(branch_tree, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + CfdCapiTapscriptTree* branch_buffer = + static_cast(branch_tree); + + TapBranch* src_tree; + if (!branch_buffer->branch_buffer->empty()) { + src_tree = &branch_buffer->branch_buffer->at(0); + } else { + src_tree = &branch_buffer->tree_buffer->at(0); + } + + if (!buffer->branch_buffer->empty()) { + auto& branch = buffer->branch_buffer->at(0); + branch.AddBranch(*src_tree); + } else { + auto& dest_tree = buffer->tree_buffer->at(0); + dest_tree.AddBranch(*src_tree); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddTapBranchByScriptTreeString( + void* handle, void* tree_handle, const char* tree_string) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(tree_string)) { + warn(CFD_LOG_SOURCE, "tree_string is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tree_string is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + + TapBranch add_branch = TapBranch::FromString(tree_string); + if (!buffer->branch_buffer->empty()) { + auto& branch = buffer->branch_buffer->at(0); + branch.AddBranch(add_branch); + } else { + auto& dest_tree = buffer->tree_buffer->at(0); + dest_tree.AddBranch(add_branch); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddTapBranchByTapLeaf( + void* handle, void* tree_handle, const char* tapscript, + uint8_t leaf_version) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (IsEmptyString(tapscript)) { + warn(CFD_LOG_SOURCE, "tapscript is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tapscript is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + TaprootScriptTree leaf(leaf_version, Script(tapscript)); + if (!buffer->branch_buffer->empty()) { + auto& branch = buffer->branch_buffer->at(0); + branch.AddBranch(leaf); + } else { + auto& tree = buffer->tree_buffer->at(0); + tree.AddBranch(leaf); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetBaseTapLeaf( + void* handle, void* tree_handle, uint8_t* leaf_version, char** tapscript, + char** tap_leaf_hash) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_tapscript = nullptr; + char* work_tap_leaf_hash = nullptr; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + if (!buffer->branch_buffer->empty()) { + auto& branch = buffer->branch_buffer->at(0); + if (branch.HasTapLeaf()) { + if (tapscript != nullptr) { + work_tapscript = CreateString(branch.GetScript().GetHex()); + } + if (leaf_version != nullptr) *leaf_version = branch.GetLeafVersion(); + } else { + if (leaf_version != nullptr) *leaf_version = 0; + } + if (tap_leaf_hash != nullptr) { + work_tap_leaf_hash = CreateString(branch.GetBaseHash().GetHex()); + } + } else { + auto& tree = buffer->tree_buffer->at(0); + if (tapscript != nullptr) { + work_tapscript = CreateString(tree.GetScript().GetHex()); + } + if (tap_leaf_hash != nullptr) { + work_tap_leaf_hash = CreateString(tree.GetTapLeafHash().GetHex()); + } + if (leaf_version != nullptr) *leaf_version = tree.GetLeafVersion(); + } + + if (tapscript != nullptr) *tapscript = work_tapscript; + if (tap_leaf_hash != nullptr) *tap_leaf_hash = work_tap_leaf_hash; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_tapscript, &work_tap_leaf_hash); + return result; +} + +int CfdGetTapBranchCount( + void* handle, void* tree_handle, uint32_t* branch_count) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + uint32_t count; + if (!buffer->branch_buffer->empty()) { + count = static_cast( + buffer->branch_buffer->at(0).GetBranchList().size()); + } else { + count = static_cast( + buffer->tree_buffer->at(0).GetBranchList().size()); + } + + if (branch_count != nullptr) *branch_count = count; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetTapBranchData( + void* handle, void* tree_handle, uint8_t index_from_leaf, + bool is_root_data, char** branch_hash, uint8_t* leaf_version, + char** tapscript, uint8_t* depth_by_leaf_or_end) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_tapscript = nullptr; + char* work_branch_hash = nullptr; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + TapBranch* branch; + if (!buffer->branch_buffer->empty()) { + branch = &buffer->branch_buffer->at(0); + } else { + branch = &buffer->tree_buffer->at(0); + } + + ByteData256 hash_obj; + TapBranch branch_data; + bool has_leaf = false; + uint8_t branch_count = 0; + auto branches = branch->GetBranchList(); + uint32_t max = static_cast(branches.size()); + if (max <= index_from_leaf) { + warn(CFD_LOG_SOURCE, "index_from_leaf is out of range."); + throw CfdException( + CfdError::kCfdOutOfRangeError, + "Failed to parameter. index_from_leaf is out of range."); + } + if (is_root_data) { + hash_obj = branch->GetBranchHash(index_from_leaf); + } else { + branch_data = branches.at(index_from_leaf); + hash_obj = branch_data.GetCurrentBranchHash(); + has_leaf = branch_data.HasTapLeaf(); + branch_count = static_cast(branch_data.GetBranchList().size()); + } + + if (has_leaf && (tapscript != nullptr)) { + work_tapscript = CreateString(branch_data.GetScript().GetHex()); + } + if (branch_hash != nullptr) { + work_branch_hash = CreateString(hash_obj.GetHex()); + } + + if (leaf_version != nullptr) { + *leaf_version = (has_leaf) ? branch_data.GetLeafVersion() : 0; + } + if (depth_by_leaf_or_end != nullptr) *depth_by_leaf_or_end = branch_count; + if (tapscript != nullptr) *tapscript = work_tapscript; + if (branch_hash != nullptr) *branch_hash = work_branch_hash; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_tapscript, &work_branch_hash); + return result; +} + +int CfdGetTapBranchHandle( + void* handle, void* tree_handle, uint8_t index_from_leaf, + char** branch_hash, void** branch_tree_handle) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_branch_hash = nullptr; + CfdCapiTapscriptTree* work_branch_buffer = nullptr; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + if (branch_tree_handle == nullptr) { + warn(CFD_LOG_SOURCE, "branch_tree_handle is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. branch_tree_handle is null or empty."); + } + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + TapBranch* branch; + if (!buffer->branch_buffer->empty()) { + branch = &buffer->branch_buffer->at(0); + } else { + branch = &buffer->tree_buffer->at(0); + } + + auto branches = branch->GetBranchList(); + uint32_t max = static_cast(branches.size()); + if (max <= index_from_leaf) { + warn(CFD_LOG_SOURCE, "index_from_leaf is out of range."); + throw CfdException( + CfdError::kCfdOutOfRangeError, + "Failed to parameter. index_from_leaf is out of range."); + } + TapBranch& branch_data = branches.at(index_from_leaf); + ByteData256 hash_obj = branch_data.GetCurrentBranchHash(); + if (branch_hash != nullptr) { + work_branch_hash = CreateString(hash_obj.GetHex()); + } + + work_branch_buffer = static_cast( + AllocBuffer(kPrefixTapscriptTree, sizeof(CfdCapiTapscriptTree))); + work_branch_buffer->tree_buffer = new std::vector(1); + work_branch_buffer->branch_buffer = new std::vector(); + if (branch_data.HasTapLeaf()) { + TaprootScriptTree tree(branch_data); + auto& dest_tree = work_branch_buffer->tree_buffer->at(0); + dest_tree = tree; + } else { + work_branch_buffer->branch_buffer->emplace_back(branch_data); + } + + *branch_tree_handle = work_branch_buffer; + if (branch_hash != nullptr) *branch_hash = work_branch_hash; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_branch_hash); + CfdFreeTaprootScriptTreeHandle(handle, work_branch_buffer); + return result; +} + +int CfdGetTaprootScriptTreeHash( + void* handle, void* tree_handle, const char* internal_pubkey, char** hash, + char** tap_leaf_hash, char** control_block) { + int result = CfdErrorCode::kCfdUnknownError; + char* work_hash = nullptr; + char* work_tap_leaf_hash = nullptr; + char* work_control_block = nullptr; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + if (IsEmptyString(internal_pubkey)) { + warn(CFD_LOG_SOURCE, "internal_pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. internal_pubkey is null or empty."); + } + if (!buffer->branch_buffer->empty()) { + warn( + CFD_LOG_SOURCE, + "This tree root is a tapbranch only. Please set tapleaf."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "This tree root is a tapbranch only. Please set tapleaf."); + } + auto& tree = buffer->tree_buffer->at(0); + SchnorrPubkey tapscript_hash; + auto control = TaprootUtil::CreateTapScriptControl( + SchnorrPubkey(internal_pubkey), tree, &tapscript_hash); + + if (hash != nullptr) { + work_hash = CreateString(tapscript_hash.GetHex()); + } + if (tap_leaf_hash != nullptr) { + work_tap_leaf_hash = CreateString(tree.GetTapLeafHash().GetHex()); + } + if (control_block != nullptr) { + work_control_block = CreateString(control.GetHex()); + } + + if (hash != nullptr) *hash = work_hash; + if (tap_leaf_hash != nullptr) *tap_leaf_hash = work_tap_leaf_hash; + if (control_block != nullptr) *control_block = work_control_block; + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + FreeBufferOnError(&work_hash, &work_tap_leaf_hash, &work_control_block); + return result; +} + +int CfdGetTaprootTweakedPrivkey( + void* handle, void* tree_handle, const char* internal_privkey, + char** tweaked_privkey) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + if (IsEmptyString(internal_privkey)) { + warn(CFD_LOG_SOURCE, "internal_privkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. internal_privkey is null or empty."); + } + if (tweaked_privkey == nullptr) { + warn(CFD_LOG_SOURCE, "tweaked_privkey is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tweaked_privkey is null."); + } + if (!buffer->branch_buffer->empty()) { + warn( + CFD_LOG_SOURCE, + "This tree root is a tapbranch only. Please set tapleaf."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "This tree root is a tapbranch only. Please set tapleaf."); + } + auto& tree = buffer->tree_buffer->at(0); + Privkey privkey; + if (Privkey::HasWif(internal_privkey)) { + privkey = Privkey::FromWif(internal_privkey); + } else { + privkey = Privkey(internal_privkey); + } + auto taproot_privkey = tree.GetTweakedPrivkey(privkey); + *tweaked_privkey = CreateString(taproot_privkey.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdGetTaprootScriptTreeSrting( + void* handle, void* tree_handle, char** tree_string) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* buffer = + static_cast(tree_handle); + std::string tree_str; + if (!buffer->branch_buffer->empty()) { + tree_str = buffer->branch_buffer->at(0).ToString(); + } else { + tree_str = buffer->tree_buffer->at(0).ToString(); + } + + if (tree_string != nullptr) { + *tree_string = CreateString(tree_str); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdFreeTaprootScriptTreeHandle(void* handle, void* tree_handle) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + if (tree_handle != nullptr) { + CheckBuffer(tree_handle, kPrefixTapscriptTree); + CfdCapiTapscriptTree* data = + static_cast(tree_handle); + if (data->tree_buffer != nullptr) { + delete data->tree_buffer; + data->tree_buffer = nullptr; + } + if (data->branch_buffer != nullptr) { + delete data->branch_buffer; + data->branch_buffer = nullptr; + } + FreeBuffer( + tree_handle, kPrefixTapscriptTree, sizeof(CfdCapiTapscriptTree)); + } + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + }; // extern "C" #endif // CFD_DISABLE_CAPI diff --git a/src/capi/cfdcapi_transaction.cpp b/src/capi/cfdcapi_transaction.cpp index 94a4576c..5e5e2304 100644 --- a/src/capi/cfdcapi_transaction.cpp +++ b/src/capi/cfdcapi_transaction.cpp @@ -29,7 +29,9 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_transaction.h" #include "cfdcore/cfdcore_transaction_common.h" #include "cfdcore/cfdcore_util.h" @@ -62,6 +64,7 @@ using cfd::core::Address; using cfd::core::AddressType; using cfd::core::Amount; using cfd::core::ByteData; +using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; using cfd::core::HashType; @@ -69,10 +72,13 @@ using cfd::core::NetType; using cfd::core::OutPoint; using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::SchnorrSignature; using cfd::core::Script; using cfd::core::ScriptUtil; using cfd::core::SigHashAlgorithm; using cfd::core::SigHashType; +using cfd::core::TaprootUtil; using cfd::core::Txid; using cfd::core::TxInReference; using cfd::core::TxOutReference; @@ -381,6 +387,757 @@ int CfdAddTransactionOutput( } } +int CfdClearWitnessStack( + void* handle, void* create_handle, const char* txid, uint32_t vout) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + Txid txid_obj(txid); + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } else if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + auto index = tx->GetTxInIndex(txid_obj, vout); + tx->RemoveScriptWitnessStackAll(index); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + auto index = tx->GetTxInIndex(txid_obj, vout); + tx->RemoveScriptWitnessStackAll(index); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdUpdateTxInScriptSig( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* script_sig) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + Txid txid_obj(txid); + Script script; + if (!IsEmptyString(script_sig)) script = Script(script_sig); + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } else if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + auto index = tx->GetTxInIndex(txid_obj, vout); + tx->SetUnlockingScript(index, script); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + auto index = tx->GetTxInIndex(txid_obj, vout); + tx->SetUnlockingScript(index, script); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + result = SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdSetTransactionUtxoData( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int64_t amount, const char* commitment, const char* descriptor, + const char* address, const char* asset, const char* scriptsig_template, + bool can_insert) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + bool is_bitcoin = false; + NetType network = ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + + UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.block_height = 0; + utxo.binary_data = nullptr; + utxo.vout = vout; + utxo.txid = Txid(txid); + utxo.amount = Amount(amount); + if (!IsEmptyString(scriptsig_template)) { + utxo.scriptsig_template = Script(scriptsig_template); + } + if (!IsEmptyString(descriptor)) { + utxo.descriptor = std::string(descriptor); + } + + OutPoint outpoint(utxo.txid, utxo.vout); + if (is_bitcoin) { + AddressFactory factory(network); + if (!IsEmptyString(address)) { + utxo.address = factory.GetAddress(address); + } + TransactionContext* tx = + static_cast(tx_data->tx_obj); + if (tx->IsFindTxIn(outpoint)) { + tx->CollectInputUtxo({utxo}); + } else if (!can_insert) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Txid is not found."); + } else { + tx->AddInput(utxo); + } + } else { +#ifndef CFD_DISABLE_ELEMENTS + ElementsAddressFactory factory(network); + if (!IsEmptyString(asset)) { + utxo.asset = ConfidentialAssetId(asset); + } + if (!IsEmptyString(commitment)) { + utxo.value_commitment = ConfidentialValue(commitment); + } + if (!IsEmptyString(address)) { + utxo.address = factory.GetAddress(address); + } + + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + if (tx->IsFindTxIn(outpoint)) { + tx->CollectInputUtxo({utxo}); + } else { + tx->AddInput(utxo); + } +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdCreateSighashByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int sighash_type, bool sighash_anyone_can_pay, const char* pubkey, + const char* redeem_script, const char* tapleaf_hash, + uint32_t code_separator_position, const char* annex, char** sighash) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (sighash == nullptr) { + warn(CFD_LOG_SOURCE, "sighash is null."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. sighash is null."); + } + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + ByteData sighash_bytes; + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + auto utxo = tx->GetTxInUtxoData(outpoint); + if (utxo.address_type == AddressType::kTaprootAddress) { + ByteData256 tapleaf_hash_obj; + ByteData annex_data; + bool has_tapscript = !IsEmptyString(tapleaf_hash); + if (has_tapscript) { + tapleaf_hash_obj = ByteData256(tapleaf_hash); + } + if (!IsEmptyString(annex)) annex_data = ByteData(annex); + sighash_bytes = + tx->CreateSignatureHashByTaproot( + outpoint, sighashtype, + (has_tapscript) ? &tapleaf_hash_obj : nullptr, + (has_tapscript) ? &code_separator_position : nullptr, + &annex_data) + .GetData(); + } else { + WitnessVersion version = WitnessVersion::kVersionNone; + Script script; + Amount amount; + if (IsEmptyString(redeem_script)) { + Pubkey pubkey_obj(pubkey); + if (utxo.address_type != AddressType::kP2pkhAddress) { + version = WitnessVersion::kVersion0; + amount = utxo.amount; + } + sighash_bytes = tx->CreateSignatureHash( + outpoint, pubkey_obj, sighashtype, amount, version); + } else { + script = Script(redeem_script); + auto wsh_script = ScriptUtil::CreateP2wshLockingScript(script); + auto segwit_script = ScriptUtil::CreateP2shLockingScript(wsh_script); + if (utxo.locking_script.Equals(wsh_script) || + utxo.locking_script.Equals(segwit_script)) { + version = WitnessVersion::kVersion0; + amount = utxo.amount; + } + sighash_bytes = tx->CreateSignatureHash( + outpoint, script, sighashtype, amount, version); + } + } + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + auto utxo = tx->GetTxInUtxoData(outpoint); + ConfidentialValue value; + auto utxo_value = utxo.value_commitment; + if (!value.HasBlinding()) utxo_value = ConfidentialValue(utxo.amount); + if (utxo.address_type == AddressType::kTaprootAddress) { + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to parameter. unsupported type."); + } else { + WitnessVersion version = WitnessVersion::kVersionNone; + Pubkey pubkey_obj; + Script script; + if (!IsEmptyString(pubkey)) pubkey_obj = Pubkey(pubkey); + if (!IsEmptyString(redeem_script)) script = Script(redeem_script); + if (pubkey_obj.IsValid()) { + if (utxo.address_type != AddressType::kP2pkhAddress) { + version = WitnessVersion::kVersion0; + value = utxo_value; + } + sighash_bytes = tx->CreateSignatureHash( + outpoint, pubkey_obj, sighashtype, value, version); + } else { + auto wsh_script = ScriptUtil::CreateP2wshLockingScript(script); + auto segwit_script = ScriptUtil::CreateP2shLockingScript(wsh_script); + if (utxo.locking_script.Equals(wsh_script) || + utxo.locking_script.Equals(segwit_script)) { + version = WitnessVersion::kVersion0; + value = utxo_value; + } + sighash_bytes = tx->CreateSignatureHash( + outpoint, script, sighashtype, value, version); + } + } +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + *sighash = CreateString(sighash_bytes.GetHex()); + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdAddSignWithPrivkeyByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* privkey, int sighash_type, bool sighash_anyone_can_pay, + bool has_grind_r, const char* aux_rand, const char* annex) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(privkey)) { + warn(CFD_LOG_SOURCE, "privkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. privkey is null or empty."); + } + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + ByteData sighash_bytes; + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); + + Privkey privkey_obj; + std::string privkey_str(privkey); + if (Privkey::HasWif(privkey_str)) { + privkey_obj = Privkey::FromWif(privkey_str); + } else { + privkey_obj = Privkey(privkey_str); + } + + ByteData annex_data; + bool has_annex = !IsEmptyString(aux_rand); + if (has_annex) annex_data = ByteData(annex); + ByteData256 aux_rand_data; + bool has_aux_rand = !IsEmptyString(aux_rand); + if (has_aux_rand) aux_rand_data = ByteData256(aux_rand); + + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->SignWithKey( + outpoint, privkey_obj.GetPubkey(), privkey_obj, sighashtype, + has_grind_r, (has_aux_rand) ? &aux_rand_data : nullptr, + (has_annex) ? &annex_data : nullptr); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->SignWithKey( + outpoint, privkey_obj.GetPubkey(), privkey_obj, sighashtype, + has_grind_r); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdVerifyTxSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout) { + int result = CfdErrorCode::kCfdUnknownError; + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->Verify(outpoint); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->Verify(outpoint); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + if (result != CfdErrorCode::kCfdSignVerificationError) { + result = SetLastError(handle, except); + } else { + SetLastError(handle, except); // collect error message + } + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + } catch (...) { + SetLastFatalError(handle, "unknown error."); + } + return result; +} + +int CfdAddTxSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* sign_data_hex, bool use_der_encode, + int sighash_type, bool sighash_anyone_can_pay, bool clear_stack) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + AddressType addr_type = ConvertHashToAddressType(hash_type); + bool is_witness = true; + if ((addr_type == AddressType::kP2shAddress) || + (addr_type == AddressType::kP2pkhAddress)) { + is_witness = false; + } + + if ((sign_data_hex == nullptr) || + (IsEmptyString(sign_data_hex) && (use_der_encode || !is_witness))) { + warn(CFD_LOG_SOURCE, "sign data is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. sign data is null or empty."); + } + + SignParameter param; + if (use_der_encode) { + // encode to der + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); + ByteData signature_bytes = ByteData(std::string(sign_data_hex)); + param = SignParameter(signature_bytes, true, sighashtype); + } else { + param = SignParameter(std::string(sign_data_hex)); + } + std::vector sign_list = {param}; + + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddSign(outpoint, sign_list, is_witness, clear_stack); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddSign(outpoint, sign_list, is_witness, clear_stack); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdAddTaprootSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + const char* signature, const char* tapscript, const char* control_block, + const char* annex) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + ByteData annex_data; + bool has_annex = !IsEmptyString(annex); + if (has_annex) annex_data = ByteData(annex); + + if (!IsEmptyString(signature)) { // single sign + // do nothing + } else if (IsEmptyString(tapscript)) { + warn(CFD_LOG_SOURCE, "tapscript is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. tapscript is null or empty."); + } else if (IsEmptyString(control_block)) { + warn(CFD_LOG_SOURCE, "control_block is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. control_block is null or empty."); + } + + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + if (!IsEmptyString(signature)) { + SchnorrSignature sig(signature); + tx->AddSchnorrSign(outpoint, sig, (has_annex) ? &annex_data : nullptr); + } else { + Script tapscript_obj(tapscript); + ByteData control_obj(control_block); + std::vector sign_params; + sign_params.emplace_back(tapscript_obj.GetData()); + sign_params.emplace_back(control_obj); + if (has_annex) sign_params.emplace_back(annex_data); + tx->AddSign(outpoint, sign_params, true, false); + } + } else { +#ifndef CFD_DISABLE_ELEMENTS + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Elements is not supported yet."); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdAddPubkeyHashSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* pubkey, const char* signature, + bool use_der_encode, int sighash_type, bool sighash_anyone_can_pay) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(pubkey)) { + warn(CFD_LOG_SOURCE, "pubkey is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. pubkey is null or empty."); + } + if (IsEmptyString(signature)) { + warn(CFD_LOG_SOURCE, "signature is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. signature is null or empty."); + } + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + AddressType addr_type = ConvertHashToAddressType(hash_type); + SignParameter param; + if (use_der_encode) { + // encode to der + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); + ByteData signature_bytes = ByteData(std::string(signature)); + param = SignParameter(signature_bytes, true, sighashtype); + } else { + param = SignParameter(std::string(signature)); + } + + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddPubkeyHashSign(outpoint, param, Pubkey(pubkey), addr_type); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddPubkeyHashSign(outpoint, param, Pubkey(pubkey), addr_type); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + +int CfdAddScriptHashLastSignByHandle( + void* handle, void* create_handle, const char* txid, uint32_t vout, + int hash_type, const char* redeem_script) { + try { + cfd::Initialize(); + CheckBuffer(create_handle, kPrefixTransactionData); + CfdCapiTransactionData* tx_data = + static_cast(create_handle); + if (IsEmptyString(txid)) { + warn(CFD_LOG_SOURCE, "txid is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. txid is null or empty."); + } + if (IsEmptyString(redeem_script)) { + warn(CFD_LOG_SOURCE, "redeem_script is null or empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to parameter. redeem_script is null or empty."); + } + bool is_bitcoin = false; + ConvertNetType(tx_data->net_type, &is_bitcoin); + if (tx_data->tx_obj == nullptr) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Invalid handle state. tx is null"); + } + OutPoint outpoint(Txid(txid), vout); + + AddressType addr_type = ConvertHashToAddressType(hash_type); + std::vector list; + Script script = Script(std::string(redeem_script)); + if (is_bitcoin) { + TransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddScriptHashSign(outpoint, list, script, addr_type); + } else { +#ifndef CFD_DISABLE_ELEMENTS + ConfidentialTransactionContext* tx = + static_cast(tx_data->tx_obj); + tx->AddScriptHashSign(outpoint, list, script, addr_type); +#else + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Elements is not supported."); +#endif // CFD_DISABLE_ELEMENTS + } + + return CfdErrorCode::kCfdSuccess; + } catch (const CfdException& except) { + return SetLastError(handle, except); + } catch (const std::exception& std_except) { + SetLastFatalError(handle, std_except.what()); + return CfdErrorCode::kCfdUnknownError; + } catch (...) { + SetLastFatalError(handle, "unknown error."); + return CfdErrorCode::kCfdUnknownError; + } +} + int CfdFinalizeTransaction( void* handle, void* create_handle, char** tx_hex_string) { return CfdGetModifiedTxByHandle(handle, create_handle, tx_hex_string); @@ -481,8 +1238,8 @@ int CfdAddTxSign( SignParameter param; if (use_der_encode) { // encode to der - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_bytes = ByteData(std::string(sign_data_hex)); param = SignParameter(signature_bytes, true, sighashtype); } else { @@ -563,8 +1320,8 @@ int CfdAddPubkeyHashSign( SignParameter param; if (use_der_encode) { // encode to der - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_bytes = ByteData(std::string(signature)); param = SignParameter(signature_bytes, true, sighashtype); } else { @@ -738,8 +1495,8 @@ int CfdAddSignWithPrivkeySimple( } OutPoint outpoint(Txid(txid), vout); - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); bool is_bitcoin = false; ConvertNetType(net_type, &is_bitcoin); @@ -861,8 +1618,8 @@ int CfdAddMultisigSignDataToDer( } // encode to der - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_bytes = ByteData(std::string(signature)); SignParameter param(signature_bytes, true, sighashtype); ByteData signature_der = param.ConvertToSignature(); @@ -1039,8 +1796,8 @@ int CfdVerifySignature( WitnessVersion version = GetWitnessVersion(hash_type); Amount amount = Amount(value_satoshi); OutPoint outpoint(Txid(txid), vout); - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); ByteData signature_obj(signature); bool is_verify = false; @@ -1125,6 +1882,7 @@ int CfdVerifyTxSign( ConvertNetType(net_type, &is_bitcoin); UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; utxo.block_height = 0; utxo.binary_data = nullptr; utxo.descriptor = ""; @@ -1253,8 +2011,8 @@ int CfdCreateSighash( ConvertNetType(net_type, &is_bitcoin); Amount value(value_satoshi); ByteData sighash_bytes; - SigHashType sighashtype( - static_cast(sighash_type), sighash_anyone_can_pay); + SigHashType sighashtype = SigHashType::Create( + static_cast(sighash_type), sighash_anyone_can_pay); if ((core_hash_type == HashType::kP2sh) || (core_hash_type == HashType::kP2wsh)) { if (IsEmptyString(redeem_script)) { @@ -1883,6 +2641,9 @@ int CfdAddTxInTemplateForFundRawTx( static_cast(fund_handle); UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.block_height = 0; + utxo.binary_data = nullptr; utxo.txid = Txid(txid); utxo.vout = vout; utxo.amount = Amount(amount); @@ -1963,6 +2724,9 @@ int CfdAddUtxoTemplateForFundRawTx( static_cast(fund_handle); UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.block_height = 0; + utxo.binary_data = nullptr; utxo.txid = Txid(txid); utxo.vout = vout; utxo.amount = Amount(amount); diff --git a/src/cfd_address.cpp b/src/cfd_address.cpp index 03cc012c..a6a29d81 100644 --- a/src/cfd_address.cpp +++ b/src/cfd_address.cpp @@ -2,8 +2,7 @@ /** * @file cfd_address.cpp * - * @brief \~english implementation of classes related to address operation - * \~japanese Address操作の関連クラスの実装ファイル + * @brief implementation of classes related to address operation */ #include "cfd/cfd_address.h" @@ -13,8 +12,10 @@ #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_descriptor.h" #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_logger.h" #include "cfdcore/cfdcore_script.h" namespace cfd { @@ -26,13 +27,21 @@ using cfd::core::ByteData; using cfd::core::ByteData160; using cfd::core::CfdError; using cfd::core::CfdException; +using cfd::core::Descriptor; +using cfd::core::DescriptorKeyReference; +using cfd::core::DescriptorNode; +using cfd::core::DescriptorScriptReference; using cfd::core::GetBitcoinAddressFormatList; using cfd::core::NetType; using cfd::core::Pubkey; using cfd::core::Script; using cfd::core::ScriptElement; +using cfd::core::ScriptOperator; +using cfd::core::ScriptType; using cfd::core::ScriptUtil; +using cfd::core::TaprootScriptTree; using cfd::core::WitnessVersion; +using cfd::core::logger::warn; AddressFactory::AddressFactory() : type_(NetType::kMainnet), @@ -89,6 +98,15 @@ Address AddressFactory::GetAddressByLockingScript( } else if ( locking_script.IsP2wpkhScript() || locking_script.IsP2wshScript()) { return GetSegwitAddressByHash(items[1].GetBinaryData()); + } else if (locking_script.IsTaprootScript()) { + return GetSegwitAddressByHash( + items[1].GetBinaryData(), WitnessVersion::kVersion1); + } else if ( + locking_script.IsWitnessProgram() && + (items[0].GetOpCode() != ScriptOperator::OP_0)) { + uint8_t var = items[0].GetOpCode().GetDataType() - ScriptType::kOp_1; + return GetSegwitAddressByHash( + items[1].GetBinaryData(), static_cast(var + 1)); } else if (locking_script.IsMultisigScript()) { throw CfdException( CfdError::kCfdIllegalArgumentError, "script type is multisig script."); @@ -112,6 +130,11 @@ Address AddressFactory::GetSegwitAddressByHash(const ByteData& hash) const { return Address(type_, wit_ver_, hash, prefix_list_); } +Address AddressFactory::GetSegwitAddressByHash( + const ByteData& hash, WitnessVersion version) const { + return Address(type_, version, hash, prefix_list_); +} + Address AddressFactory::CreateP2pkhAddress(const Pubkey& pubkey) const { return Address(type_, pubkey, prefix_list_); } @@ -121,17 +144,33 @@ Address AddressFactory::CreateP2shAddress(const Script& script) const { } Address AddressFactory::CreateP2wpkhAddress(const Pubkey& pubkey) const { - return Address(type_, wit_ver_, pubkey, prefix_list_); + return Address(type_, WitnessVersion::kVersion0, pubkey, prefix_list_); } Address AddressFactory::CreateP2wshAddress(const Script& script) const { - return Address(type_, wit_ver_, script, prefix_list_); + return Address(type_, WitnessVersion::kVersion0, script, prefix_list_); } Address AddressFactory::CreateP2wshMultisigAddress( uint32_t require_num, const std::vector& pubkeys) const { Script script = ScriptUtil::CreateMultisigRedeemScript(require_num, pubkeys); - return Address(type_, wit_ver_, script, prefix_list_); + return Address(type_, WitnessVersion::kVersion0, script, prefix_list_); +} + +Address AddressFactory::CreateTaprootAddress( + const SchnorrPubkey& pubkey) const { + return Address(type_, WitnessVersion::kVersion1, pubkey, prefix_list_); +} + +Address AddressFactory::CreateTaprootAddress( + const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey) const { + auto pk = tree.GetTweakedPubkey(internal_pubkey); + return CreateTaprootAddress(pk); +} + +Address AddressFactory::CreateTaprootAddress(const ByteData256& hash) const { + return CreateTaprootAddress(SchnorrPubkey(hash)); } bool AddressFactory::CheckAddressNetType( @@ -158,7 +197,12 @@ bool AddressFactory::CheckAddressNetType( result = (prefix.GetP2shPrefix() == addr_format.GetP2shPrefix()); break; case AddressType::kP2wpkhAddress: + // fall-through case AddressType::kP2wshAddress: + // fall-through + case AddressType::kTaprootAddress: + // fall-through + case AddressType::kWitnessUnknown: result = (prefix.GetBech32Hrp() == addr_format.GetBech32Hrp()); break; default: @@ -172,4 +216,310 @@ bool AddressFactory::CheckAddressNetType( return result; } +Address AddressFactory::CreateAddress( + AddressType address_type, const Pubkey* pubkey, const Script* script, + Script* locking_script, Script* redeem_script) const { + if ((pubkey == nullptr) || (!pubkey->IsValid())) { + if (address_type == AddressType::kP2pkhAddress || + address_type == AddressType::kP2wpkhAddress || + address_type == AddressType::kP2shP2wpkhAddress || + address_type == AddressType::kTaprootAddress) { + warn( + CFD_LOG_SOURCE, + "Failed to CreateAddress. Invalid pubkey hex: pubkey is empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "pubkey hex is empty."); + } + } + if ((script == nullptr) || (script->IsEmpty())) { + if (address_type == AddressType::kP2shAddress || + address_type == AddressType::kP2wshAddress || + address_type == AddressType::kP2shP2wshAddress) { + warn( + CFD_LOG_SOURCE, + "Failed to CreateAddress. Invalid script hex: script is empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, "script hex is empty."); + } + } + Address addr; + + Script temp_script; + switch (address_type) { + case AddressType::kP2pkhAddress: + addr = CreateP2pkhAddress(*pubkey); + break; + case AddressType::kP2shAddress: + addr = CreateP2shAddress(*script); + break; + case AddressType::kP2wpkhAddress: + addr = CreateP2wpkhAddress(*pubkey); + break; + case AddressType::kP2wshAddress: + addr = CreateP2wshAddress(*script); + break; + case AddressType::kP2shP2wpkhAddress: + temp_script = ScriptUtil::CreateP2wpkhLockingScript(*pubkey); + addr = CreateP2shAddress(temp_script); + if (redeem_script != nullptr) { + *redeem_script = temp_script; + } + break; + case AddressType::kP2shP2wshAddress: + temp_script = ScriptUtil::CreateP2wshLockingScript(*script); + addr = CreateP2shAddress(temp_script); + if (redeem_script != nullptr) { + *redeem_script = temp_script; + } + break; + case AddressType::kTaprootAddress: + addr = CreateTaprootAddress(SchnorrPubkey::FromPubkey(*pubkey)); + break; + default: + warn( + CFD_LOG_SOURCE, + "Failed to CreateAddress. Invalid address type: address_type={}", + address_type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid address_type. address_type must be \"p2pkh\" or " + "\"p2sh\" or \"p2wpkh\" or \"p2wsh\" or \"p2sh-p2wpkh\" or " + "\"p2sh-p2wsh\"."); // NOLINT + break; + } + if (locking_script != nullptr) { + *locking_script = addr.GetLockingScript(); + } + + return addr; +} + +std::vector
AddressFactory::GetAddressesFromMultisig( + AddressType address_type, const Script& redeem_script, + std::vector* pubkey_list) const { + if ((address_type != AddressType::kP2pkhAddress) && + (address_type != AddressType::kP2wpkhAddress) && + (address_type != AddressType::kP2shP2wpkhAddress)) { + warn( + CFD_LOG_SOURCE, + "Failed to GetAddressesFromMultisig. Invalid address_type passed: " + "addressType={}", // NOLINT + address_type); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid address_type. address_type must be \"p2pkh\" " + "\"p2wpkh\" or \"p2sh-p2wpkh\"."); // NOLINT + } + + std::vector pubkeys = + ScriptUtil::ExtractPubkeysFromMultisigScript(redeem_script); + + std::vector
addr_list; + Address addr; + Script script; + for (const auto& pubkey : pubkeys) { + if (address_type == AddressType::kP2pkhAddress) { + addr = CreateP2pkhAddress(pubkey); + } else if (address_type == AddressType::kP2shP2wpkhAddress) { + script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); + addr = CreateP2shAddress(script); + } else if (address_type == AddressType::kP2wpkhAddress) { + // Currently we support only witness version 0. + addr = CreateP2wpkhAddress(pubkey); + } + addr_list.push_back(addr); + } + + if (pubkey_list) *pubkey_list = pubkeys; + return addr_list; +} + +DescriptorScriptData AddressFactory::ParseOutputDescriptor( + const std::string& descriptor, const std::string& bip32_derivation_path, + std::vector* script_list, + std::vector* multisig_key_list, + std::vector* key_list) const { + if (script_list != nullptr) script_list->clear(); + if (multisig_key_list != nullptr) multisig_key_list->clear(); + + static auto get_keystr_function = + [](const DescriptorKeyReference& key_ref) -> std::string { + if (key_ref.HasExtPrivkey()) { + return key_ref.GetExtPrivkey().ToString(); + } else if (key_ref.HasExtPubkey()) { + return key_ref.GetExtPubkey().ToString(); + } else { + return key_ref.GetPubkey().GetHex(); + } + }; + + DescriptorScriptData result; + result.key_type = DescriptorKeyType::kDescriptorKeyNull; // dummy init + result.address_type = AddressType::kP2shAddress; // dummy init + result.multisig_req_sig_num = 0; + Descriptor desc = Descriptor::Parse(descriptor, &prefix_list_); + std::vector args; + for (uint32_t index = 0; index < desc.GetNeedArgumentNum(); ++index) { + args.push_back(bip32_derivation_path); + } + if (key_list != nullptr) { + auto temp_list = desc.GetKeyDataAll(&args); + for (const auto& key : temp_list) { + key_list->push_back(key); + } + } + std::vector script_refs = + desc.GetReferenceAll(&args); + DescriptorNode node = desc.GetNode(); + + result.type = node.GetScriptType(); + result.key_type = DescriptorKeyType::kDescriptorKeyNull; + result.depth = 0; + result.locking_script = script_refs[0].GetLockingScript(); + if (script_refs[0].HasAddress()) { + result.address = script_refs[0].GenerateAddress(type_); + result.address_type = script_refs[0].GetAddressType(); + } + if (script_refs[0].HasRedeemScript()) { + result.redeem_script = script_refs[0].GetRedeemScript(); + } + + std::vector multisig_keys; + uint32_t multisig_req_num = 0; + bool use_script_list = false; + DescriptorKeyReference key_ref; + switch (result.type) { + case DescriptorScriptType::kDescriptorScriptCombo: + for (const auto& ref : script_refs) { + DescriptorScriptData ref_data; + ref_data.type = ref.GetScriptType(); + ref_data.depth = 0; + ref_data.locking_script = ref.GetLockingScript(); + if (ref.HasAddress()) { + ref_data.address = ref.GenerateAddress(type_); + ref_data.address_type = ref.GetAddressType(); + } else { + ref_data.address_type = AddressType::kP2shAddress; // dummy init + } + if (ref.HasRedeemScript()) { + ref_data.redeem_script = ref.GetRedeemScript(); + ref_data.key_type = DescriptorKeyType::kDescriptorKeyNull; + } else { + key_ref = ref.GetKeyList()[0]; + ref_data.key_type = key_ref.GetKeyType(); + ref_data.key = get_keystr_function(key_ref); + if (result.key_type == DescriptorKeyType::kDescriptorKeyNull) { + result.key_type = ref_data.key_type; + result.key = ref_data.key; + } + } + if (script_list != nullptr) script_list->push_back(ref_data); + } + break; + case DescriptorScriptType::kDescriptorScriptSh: + case DescriptorScriptType::kDescriptorScriptWsh: + use_script_list = true; + break; + case DescriptorScriptType::kDescriptorScriptPk: + case DescriptorScriptType::kDescriptorScriptPkh: + case DescriptorScriptType::kDescriptorScriptWpkh: + key_ref = script_refs[0].GetKeyList()[0]; + result.key_type = key_ref.GetKeyType(); + result.key = get_keystr_function(key_ref); + break; + case DescriptorScriptType::kDescriptorScriptMulti: + case DescriptorScriptType::kDescriptorScriptSortedMulti: + multisig_keys = script_refs[0].GetKeyList(); + result.multisig_req_sig_num = script_refs[0].GetReqNum(); + break; + case DescriptorScriptType::kDescriptorScriptRaw: + case DescriptorScriptType::kDescriptorScriptAddr: + default: + break; + } + + if (use_script_list) { + DescriptorScriptReference script_ref = script_refs[0]; + bool is_loop = script_ref.HasChild(); + uint32_t depth = 0; + result.redeem_script = (is_loop) ? Script() : script_ref.GetRedeemScript(); + while (is_loop) { + DescriptorScriptReference child; + DescriptorScriptData data; + switch (script_ref.GetScriptType()) { + case DescriptorScriptType::kDescriptorScriptSh: + case DescriptorScriptType::kDescriptorScriptWsh: + if (!script_ref.HasChild()) { + result.redeem_script = script_ref.GetRedeemScript(); + } else { + child = script_ref.GetChild(); + if ((child.GetScriptType() == + DescriptorScriptType::kDescriptorScriptMulti) || + (child.GetScriptType() == + DescriptorScriptType::kDescriptorScriptSortedMulti)) { + multisig_keys = child.GetKeyList(); + multisig_req_num = child.GetReqNum(); + result.multisig_req_sig_num = multisig_req_num; + is_loop = false; + } else if ( + child.GetScriptType() == + DescriptorScriptType::kDescriptorScriptMiniscript) { + is_loop = false; + } + if (child.GetScriptType() != + DescriptorScriptType::kDescriptorScriptWpkh) { + result.redeem_script = script_ref.GetRedeemScript(); + } + } + break; + // case DescriptorScriptType::kDescriptorScriptPk: + // case DescriptorScriptType::kDescriptorScriptPkh: + // case DescriptorScriptType::kDescriptorScriptWpkh: + default: + is_loop = false; + break; + } + data.type = script_ref.GetScriptType(); + data.depth = depth; + data.locking_script = script_ref.GetLockingScript(); + data.address = script_ref.GenerateAddress(type_); + data.address_type = script_ref.GetAddressType(); + if (script_ref.HasRedeemScript()) { + data.redeem_script = script_ref.GetRedeemScript(); + } + if (script_ref.HasKey()) { + key_ref = script_ref.GetKeyList()[0]; + data.key_type = key_ref.GetKeyType(); + data.key = get_keystr_function(key_ref); + result.key_type = key_ref.GetKeyType(); + result.key = get_keystr_function(key_ref); + } else { + data.key_type = DescriptorKeyType::kDescriptorKeyNull; + } + data.multisig_req_sig_num = multisig_req_num; + + if (script_list != nullptr) script_list->push_back(data); + if (is_loop && script_ref.HasChild()) { + child = script_ref.GetChild(); + script_ref = child; + ++depth; + } else { + is_loop = false; + } + } + } + + if ((!multisig_keys.empty()) && multisig_key_list) { + for (const auto& ref : multisig_keys) { + DescriptorKeyData key_data{ref.GetKeyType(), get_keystr_function(ref)}; + multisig_key_list->push_back(key_data); + } + } + return result; +} + +std::vector AddressFactory::GetPrefixList() const { + return prefix_list_; +} + } // namespace cfd diff --git a/src/cfd_elements_address.cpp b/src/cfd_elements_address.cpp index 963098c9..32c85aee 100644 --- a/src/cfd_elements_address.cpp +++ b/src/cfd_elements_address.cpp @@ -2,8 +2,7 @@ /** * @file cfd_elements_address.cpp * - * @brief \~english implementation of classes related to address operation for Elements - * \~japanese Elements用Address操作関連クラスの実装 + * @brief implementation of classes related to address operation for Elements */ #ifndef CFD_DISABLE_ELEMENTS #include "cfd/cfd_elements_address.h" @@ -70,12 +69,14 @@ ElementsAddressFactory::ElementsAddressFactory( ElementsConfidentialAddress ElementsAddressFactory::GetConfidentialAddress( const Address& unblinded_address, const ConfidentialKey& confidential_key) { - return ElementsConfidentialAddress(unblinded_address, confidential_key); + ElementsConfidentialAddress obj(unblinded_address, confidential_key); + return obj; } ElementsConfidentialAddress ElementsAddressFactory::GetConfidentialAddress( const std::string& address) const { - return ElementsConfidentialAddress(address, prefix_list_); + ElementsConfidentialAddress obj(address, prefix_list_); + return obj; } Address ElementsAddressFactory::CreatePegInAddress( diff --git a/src/cfd_elements_transaction.cpp b/src/cfd_elements_transaction.cpp index ff6c8f26..77ebc222 100644 --- a/src/cfd_elements_transaction.cpp +++ b/src/cfd_elements_transaction.cpp @@ -90,7 +90,8 @@ static ByteData256 CreateConfidentialTxSighash( const ConfidentialTransactionContext* transaction, const OutPoint& outpoint, const UtxoData& utxo, const SigHashType& sighash_type, const Pubkey& pubkey, - const Script& redeem_script, WitnessVersion version) { + const Script& redeem_script, WitnessVersion version, const ByteData*, + const TaprootScriptTree*) { ConfidentialValue value; if (utxo.value_commitment.IsEmpty()) { value = ConfidentialValue(utxo.amount); @@ -145,11 +146,13 @@ ConfidentialTransactionContext::ConfidentialTransactionContext( ConfidentialTransactionContext& ConfidentialTransactionContext::operator=( const ConfidentialTransactionContext& context) & { - SetFromHex(context.GetHex()); - utxo_map_ = context.utxo_map_; - signed_map_ = context.signed_map_; - verify_map_ = context.verify_map_; - verify_ignore_map_ = context.verify_ignore_map_; + if (this != &context) { + SetFromHex(context.GetHex()); + utxo_map_ = context.utxo_map_; + signed_map_ = context.signed_map_; + verify_map_ = context.verify_map_; + verify_ignore_map_ = context.verify_ignore_map_; + } return *this; } @@ -659,7 +662,7 @@ void ConfidentialTransactionContext::AddInput( UtxoUtil::ConvertToUtxo(utxo, &temp, &dest); AddTxIn(utxo.txid, utxo.vout, sequence, Script::Empty); - utxo_map_.emplace(OutPoint(utxo.txid, utxo.vout), dest); + utxo_map_.emplace_back(dest); } void ConfidentialTransactionContext::AddInputs( @@ -677,10 +680,10 @@ void ConfidentialTransactionContext::CollectInputUtxo( uint32_t vout = txin_ref.GetVout(); OutPoint outpoint(txid, vout); - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + if (!IsFindUtxoMap(outpoint)) { for (const auto& utxo : utxos) { if ((utxo.vout == vout) && utxo.txid.Equals(txid)) { - utxo_map_.emplace(outpoint, utxo); + utxo_map_.emplace_back(utxo); break; } } @@ -689,6 +692,17 @@ void ConfidentialTransactionContext::CollectInputUtxo( } } +UtxoData ConfidentialTransactionContext::GetTxInUtxoData( + const OutPoint& outpoint) const { + UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.vout = 0; + utxo.block_height = 0; + utxo.binary_data = nullptr; + IsFindUtxoMap(outpoint, &utxo); + return utxo; +} + void ConfidentialTransactionContext::Blind( const std::vector* confidential_addresses, int64_t minimum_range_value, int exponent, int minimum_bits, @@ -704,13 +718,13 @@ void ConfidentialTransactionContext::BlindIssuance( int64_t minimum_range_value, int exponent, int minimum_bits, std::vector* blinder_list) { std::map utxo_info_map; + UtxoData utxo; for (const auto& txin_ref : vin_) { OutPoint outpoint = txin_ref.GetOutPoint(); - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. blind fail."); } - UtxoData utxo = utxo_map_.find(outpoint)->second; BlindParameter param; param.asset = utxo.asset; param.abf = utxo.asset_blind_factor; @@ -733,11 +747,11 @@ void ConfidentialTransactionContext::BlindIssuance( void ConfidentialTransactionContext::SignWithKey( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, SigHashType sighash_type, bool has_grind_r) { - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + UtxoData utxo; + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. sign fail."); } - UtxoData utxo = utxo_map_.find(outpoint)->second; if (utxo.amount_blind_factor.IsEmpty() && (!utxo.value_commitment.HasBlinding())) { @@ -753,29 +767,33 @@ void ConfidentialTransactionContext::SignWithKey( void ConfidentialTransactionContext::IgnoreVerify(const OutPoint& outpoint) { GetTxInIndex(outpoint.GetTxid(), outpoint.GetVout()); - verify_ignore_map_.emplace(outpoint); + if (!IsFindOutPoint(verify_ignore_map_, outpoint)) { + verify_ignore_map_.emplace_back(outpoint); + } } void ConfidentialTransactionContext::Verify() { for (const auto& vin : vin_) { OutPoint outpoint = vin.GetOutPoint(); - if (verify_ignore_map_.find(outpoint) == std::end(verify_ignore_map_)) { + if (!IsFindOutPoint(verify_ignore_map_, outpoint)) { Verify(outpoint); } } } void ConfidentialTransactionContext::Verify(const OutPoint& outpoint) { - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + UtxoData utxo; + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. verify fail."); } - const auto& utxo = utxo_map_.find(outpoint)->second; const auto& txin = vin_[GetTxInIndex(outpoint)]; TransactionContextUtil::Verify( this, outpoint, utxo, &txin, CreateConfidentialTxSighash); - verify_map_.emplace(outpoint); + if (!IsFindOutPoint(verify_map_, outpoint)) { + verify_map_.emplace_back(outpoint); + } } ByteData ConfidentialTransactionContext::Finalize() { @@ -783,16 +801,6 @@ ByteData ConfidentialTransactionContext::Finalize() { return AbstractTransaction::GetData(); } -#if 0 -// priority: low -void ConfidentialTransactionContext::ClearSign() { return; } - -// priority: low -void ConfidentialTransactionContext::ClearSign(const OutPoint& outpoint) { - return; -} -#endif - ByteData ConfidentialTransactionContext::CreateSignatureHash( const OutPoint& outpoint, const Pubkey& pubkey, SigHashType sighash_type, const Amount& value, WitnessVersion version) const { @@ -822,8 +830,7 @@ ByteData ConfidentialTransactionContext::CreateSignatureHash( const OutPoint& outpoint, const Script& redeem_script, SigHashType sighash_type, const ConfidentialValue& value, WitnessVersion version) const { - // TODO(soejima): OP_CODESEPARATOR存在時、Scriptの分割が必要。 - // TODO(k-matsuzawa): 現状は利用側で分割し、適用する箇所だけ指定してもらう。 + // TODO(k-matsuzawa): For now, when using OP_CODESEPARATOR, divide it on the user side and ask them to specify only the applicable part. // NOLINT ByteData256 sighash = GetElementsSignatureHash( GetTxInIndex(outpoint), redeem_script.GetData(), sighash_type, value, version); @@ -928,6 +935,26 @@ void ConfidentialTransactionContext::CallbackStateChange(uint32_t type) { verify_map_.clear(); } +bool ConfidentialTransactionContext::IsFindUtxoMap( + const OutPoint& outpoint, UtxoData* utxo) const { + for (const auto& utxo_data : utxo_map_) { + if ((outpoint.GetVout() == utxo_data.vout) && + utxo_data.txid.Equals(outpoint.GetTxid())) { + if (utxo != nullptr) *utxo = utxo_data; + return true; + } + } + return false; +} + +bool ConfidentialTransactionContext::IsFindOutPoint( + const std::vector& list, const OutPoint& outpoint) const { + for (const auto& target : list) { + if (outpoint == target) return true; + } + return false; +} + // ----------------------------------------------------------------------------- // ConfidentialTransactionController // ----------------------------------------------------------------------------- @@ -952,7 +979,9 @@ ConfidentialTransactionController::ConfidentialTransactionController( ConfidentialTransactionController& ConfidentialTransactionController::operator=( const ConfidentialTransactionController& transaction) & { - transaction_ = transaction.transaction_; + if (this != &transaction) { + transaction_ = transaction.transaction_; + } return *this; } diff --git a/src/cfd_psbt.cpp b/src/cfd_psbt.cpp new file mode 100644 index 00000000..9ac182a5 --- /dev/null +++ b/src/cfd_psbt.cpp @@ -0,0 +1,933 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfd_psbt.cpp + * + * @brief This file is implements Partially Signed Bitcoin Transaction. + */ + +#include "cfd/cfd_psbt.h" + +#include +#include +#include + +#include "cfd/cfd_transaction.h" +#include "cfd/cfdapi_address.h" +#include "cfd/cfdapi_transaction.h" +#include "cfd_transaction_internal.h" // NOLINT +#include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_psbt.h" +#include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" +#include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_transaction_common.h" +#include "wally_psbt.h" // NOLINT + +namespace cfd { + +using cfd::core::AbstractTransaction; +using cfd::core::Amount; +using cfd::core::CfdError; +using cfd::core::CfdException; +using cfd::core::Script; +using cfd::core::ScriptUtil; +using cfd::core::TaprootScriptTree; +using cfd::core::TxIn; +using cfd::core::logger::warn; + +// ----------------------------------------------------------------------------- +// File internal function +// ----------------------------------------------------------------------------- +/** + * @brief Collect sighash by transaction. + * @param[in] transaction transaction + * @param[in] outpoint outpoint + * @param[in] utxo utxo + * @param[in] sighash_type sighash_type + * @param[in] pubkey pubkey + * @param[in] redeem_script redeem_script + * @param[in] witness_version witness_version + * @return sighash + */ +static ByteData256 CollectSighashByTx( + const Transaction* transaction, const OutPoint& outpoint, + const UtxoData& utxo, const SigHashType& sighash_type, + const Pubkey& pubkey, const Script& redeem_script, + WitnessVersion witness_version, const ByteData*, + const TaprootScriptTree*) { + uint32_t index = + transaction->GetTxInIndex(outpoint.GetTxid(), outpoint.GetVout()); + Script script_data = redeem_script; + if (script_data.IsEmpty()) { + script_data = ScriptUtil::CreateP2pkhLockingScript(pubkey); + } + auto sighash = transaction->GetSignatureHash( + index, script_data.GetData(), sighash_type, utxo.amount, + witness_version); + return sighash; +} + +/** + * @brief parse descriptor + * @param[in] descriptor descriptor string. + * @param[out] key_list key list. + * @param[in] net_type network type + * @return descriptor script data + */ +cfd::DescriptorScriptData ParseDescriptor( + const std::string& descriptor, std::vector* key_list, + NetType* net_type = nullptr) { + std::vector temp_key_list; + cfd::DescriptorScriptData script_data; + if (net_type == nullptr) { + NetType target_list[] = { + NetType::kMainnet, NetType::kTestnet, NetType::kRegtest}; + size_t max = (sizeof(target_list) / sizeof(NetType)) - 1; + for (size_t index = 0; index <= max; ++index) { + try { + cfd::AddressFactory addr_factory(target_list[index]); + script_data = addr_factory.ParseOutputDescriptor( + descriptor, "", nullptr, nullptr, &temp_key_list); + break; + } catch (const CfdException& except) { + if (index == max) { + throw except; + } + } + } + } else { + cfd::AddressFactory addr_factory(*net_type); + script_data = addr_factory.ParseOutputDescriptor( + descriptor, "", nullptr, nullptr, &temp_key_list); + } + if (key_list != nullptr) { + for (const auto& key : temp_key_list) { + if (key.IsValid() && (!key.GetFingerprint().IsEmpty())) { + key_list->push_back(key); + } + } + } + return script_data; +} + +/** + * @brief Convert from utxo data. + * @param[in] utxo utxo + * @param[out] outpoint outpoint + * @param[out] txout txout + * @param[out] key_list key data list + * @param[out] redeem_script redeem script + * @param[in] ignore_error ignore error flag + * @param[in] net_type network type + * @retval true witness + * @retval flse other + */ +bool ConvertFromUtxoData( + const UtxoData& utxo, OutPoint* outpoint, TxOut* txout, + std::vector* key_list, Script* redeem_script, + bool ignore_error = false, NetType* net_type = nullptr) { + if (utxo.descriptor.empty()) { + warn(CFD_LOG_SOURCE, "psbt not supported utxo data. need descriptor."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "psbt not supported utxo data. need descriptor."); + } + if (outpoint != nullptr) *outpoint = OutPoint(utxo.txid, utxo.vout); + + std::vector work_key_list; + cfd::DescriptorScriptData script_data = + ParseDescriptor(utxo.descriptor, &work_key_list, net_type); + + if (key_list != nullptr) { + for (const auto& key : work_key_list) { + key_list->push_back(key); + } + } + if (redeem_script != nullptr) *redeem_script = script_data.redeem_script; + + if (script_data.redeem_script.IsEmpty() && work_key_list.empty()) { + warn( + CFD_LOG_SOURCE, + "psbt not supported pubkey format. need fingerprint and bip32 path."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "psbt not supported pubkey format. need fingerprint and bip32 path."); + } + + if (txout != nullptr) { + TxOut tmp_txout(utxo.amount, script_data.locking_script); + *txout = tmp_txout; + } + + bool is_witness = false; + if (script_data.locking_script.IsWitnessProgram() || + (script_data.address_type == AddressType::kP2shP2wpkhAddress) || + (script_data.address_type == AddressType::kP2shP2wshAddress)) { + is_witness = true; + } else if (!ignore_error) { + warn(CFD_LOG_SOURCE, "psbt not supported address type. need segwit."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "psbt not supported address type. need segwit."); + } + return is_witness; +} + +// ----------------------------------------------------------------------------- +// Psbt +// ----------------------------------------------------------------------------- +Psbt::Psbt() : cfd::core::Psbt() {} + +Psbt::Psbt(uint32_t version, uint32_t lock_time) + : cfd::core::Psbt(version, lock_time) {} + +Psbt::Psbt(uint32_t psbt_version, uint32_t version, uint32_t lock_time) + : cfd::core::Psbt(psbt_version, version, lock_time) {} + +Psbt::Psbt(const std::string& base64) : cfd::core::Psbt(base64) {} +Psbt::Psbt(const ByteData& byte_data) : cfd::core::Psbt(byte_data) {} +Psbt::Psbt(const Transaction& transaction) : cfd::core::Psbt(transaction) {} +Psbt::Psbt(uint32_t psbt_version, const Transaction& transaction) + : cfd::core::Psbt(psbt_version, transaction) {} + +Psbt::Psbt(const TransactionContext& context) + : Psbt(GetDefaultVersion(), context) {} + +Psbt::Psbt(uint32_t psbt_version, const TransactionContext& context) + : cfd::core::Psbt(psbt_version, static_cast(context)) { + std::vector utxo_list; + for (const auto& txin : context.GetTxInList()) { + utxo_list.push_back(context.GetTxInUtxoData(txin.GetOutPoint())); + } + CollectInputUtxo(utxo_list); +} + +Psbt::Psbt(const Psbt& psbt) : cfd::core::Psbt(psbt.GetData()) { + verify_ignore_map_ = psbt.verify_ignore_map_; +} + +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(CfdError::kCfdInternalError, "psbt clone error."); + } + cfd::core::Psbt::FreeWallyPsbtAddress(wally_psbt_pointer_); // free + wally_psbt_pointer_ = psbt_pointer; + base_tx_ = cfd::core::Psbt::RebuildTransaction(wally_psbt_pointer_); + verify_ignore_map_ = psbt.verify_ignore_map_; + } + return *this; +} + +TransactionContext Psbt::GetTransactionContext() const { + TransactionContext tx(GetTransaction().GetHex()); + std::vector utxo_list = GetUtxoDataAll(); + tx.CollectInputUtxo(utxo_list); + return tx; +} + +bool Psbt::HasAllUtxos() 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( + CfdError::kCfdIllegalStateError, "psbt pointer is null."); + } else if (psbt_pointer->tx == nullptr) { + warn(CFD_LOG_SOURCE, "psbt base tx is null"); + throw CfdException( + CfdError::kCfdIllegalStateError, "psbt base tx is null."); + } + + for (size_t index = 0; index < psbt_pointer->num_inputs; ++index) { + if ((psbt_pointer->inputs[index].witness_utxo == nullptr) && + (psbt_pointer->inputs[index].utxo == nullptr)) { + return false; + } + } + return (psbt_pointer->num_inputs != 0); +} + +Amount Psbt::GetFeeAmount() const { + if (!HasAllUtxos()) return Amount(); + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + + Amount total_input; + for (size_t index = 0; index < psbt_pointer->num_inputs; ++index) { + if (psbt_pointer->inputs[index].witness_utxo != nullptr) { + total_input += psbt_pointer->inputs[index].witness_utxo->satoshi; + } else if (psbt_pointer->inputs[index].utxo != nullptr) { + uint32_t vout = psbt_pointer->tx->inputs[index].index; + if (psbt_pointer->inputs[index].utxo->num_outputs <= vout) { + warn(CFD_LOG_SOURCE, "psbt txin vout is invalid."); + throw CfdException( + CfdError::kCfdIllegalStateError, "psbt txin vout is invalid."); + } + total_input += psbt_pointer->inputs[index].utxo->outputs[vout].satoshi; + } else { + warn(CFD_LOG_SOURCE, "psbt txin utxo is empty."); + throw CfdException( + CfdError::kCfdIllegalStateError, "psbt txin utxo is empty."); + } + } + + Amount total_output; + for (size_t index = 0; index < psbt_pointer->tx->num_outputs; ++index) { + total_output += psbt_pointer->tx->outputs[index].satoshi; + } + + if (total_input <= total_output) return Amount(); + return total_input - total_output; +} + +bool Psbt::IsFinalizedInput(const OutPoint& outpoint) const { + return cfd::core::Psbt::IsFinalizedInput(GetTxInIndex(outpoint)); +} + +uint32_t Psbt::AddTxIn(const OutPoint& outpoint) { + return AddTxIn(outpoint, GetDefaultSequence()); +} + +uint32_t Psbt::AddTxIn(const OutPoint& outpoint, uint32_t sequence) { + return cfd::core::Psbt::AddTxIn( + outpoint.GetTxid(), outpoint.GetVout(), sequence); +} + +uint32_t Psbt::AddTxInData(const UtxoData& utxo) { + return AddTxInData(utxo, GetDefaultSequence()); +} + +uint32_t Psbt::AddTxInData(const UtxoData& utxo, uint32_t sequence) { + OutPoint outpoint(utxo.txid, utxo.vout); + TxOut txout; + std::vector key_list; + Script redeem_script; + ConvertFromUtxoData(utxo, nullptr, &txout, &key_list, &redeem_script); + + uint32_t index = AddTxIn(outpoint, sequence); + try { + cfd::core::Psbt::SetTxInUtxo( + index, TxOutReference(txout), redeem_script, key_list); + } catch (const CfdException& except) { + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + wally_psbt_remove_input(psbt_pointer, index); + base_tx_ = cfd::core::Psbt::RebuildTransaction(wally_psbt_pointer_); + throw except; + } + return index; +} + +bool Psbt::IsFindTxIn(const OutPoint& outpoint, uint32_t* index) const { + static constexpr const char* const kErrorMessage = "Txid is not found."; + try { + uint32_t temp_index = + base_tx_.GetTxInIndex(outpoint.GetTxid(), outpoint.GetVout()); + if (index != nullptr) *index = temp_index; + return true; + } catch (const CfdException& except) { + std::string errmsg(except.what()); + if (errmsg == kErrorMessage) { + return false; + } else { + throw except; + } + } +} + +uint32_t Psbt::GetTxInIndex(const OutPoint& outpoint) const { + return base_tx_.GetTxInIndex(outpoint.GetTxid(), outpoint.GetVout()); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const Transaction& tx, const KeyData& key) { + cfd::core::Psbt::SetTxInUtxo(GetTxInIndex(outpoint), tx, key); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const Transaction& tx, + const Script& redeem_script, const KeyData& key) { + cfd::core::Psbt::SetTxInUtxo(GetTxInIndex(outpoint), tx, redeem_script, key); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const Transaction& tx, + const Script& redeem_script, const std::vector& key_list) { + cfd::core::Psbt::SetTxInUtxo( + GetTxInIndex(outpoint), tx, redeem_script, key_list); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const TxOutReference& txout, + const KeyData& key) { + cfd::core::Psbt::SetTxInUtxo(GetTxInIndex(outpoint), txout, key); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const TxOutReference& txout, + const Script& redeem_script, const KeyData& key) { + cfd::core::Psbt::SetTxInUtxo( + GetTxInIndex(outpoint), txout, redeem_script, key); +} + +void Psbt::SetTxInUtxo( + const OutPoint& outpoint, const TxOutReference& txout, + const Script& redeem_script, const std::vector& key_list) { + cfd::core::Psbt::SetTxInUtxo( + GetTxInIndex(outpoint), txout, redeem_script, key_list); +} + +void Psbt::SetTxInWitnessUtxoDirect( + const OutPoint& outpoint, const TxOutReference& txout) { + cfd::core::Psbt::SetTxInWitnessUtxoDirect(GetTxInIndex(outpoint), txout); +} + +void Psbt::SetTxInBip32KeyDirect( + const OutPoint& outpoint, const KeyData& key_data) { + cfd::core::Psbt::SetTxInBip32KeyDirect(GetTxInIndex(outpoint), key_data); +} + +void Psbt::SetTxInSignature( + const OutPoint& outpoint, const KeyData& key, const ByteData& signature) { + cfd::core::Psbt::SetTxInSignature(GetTxInIndex(outpoint), key, signature); +} + +void Psbt::SetTxInSighashType( + const OutPoint& outpoint, const SigHashType& sighash_type) { + cfd::core::Psbt::SetTxInSighashType(GetTxInIndex(outpoint), sighash_type); +} + +void Psbt::SetTxInFinalScript( + const OutPoint& outpoint, const std::vector& unlocking_script) { + cfd::core::Psbt::SetTxInFinalScript( + GetTxInIndex(outpoint), unlocking_script); +} + +void Psbt::SetTxInRecord( + const OutPoint& outpoint, const ByteData& key, const ByteData& value) { + cfd::core::Psbt::SetTxInRecord(GetTxInIndex(outpoint), key, value); +} + +Transaction Psbt::GetTxInUtxoFull( + const OutPoint& outpoint, bool ignore_error, bool* is_witness) const { + return cfd::core::Psbt::GetTxInUtxoFull( + GetTxInIndex(outpoint), ignore_error, is_witness); +} + +TxOut Psbt::GetTxInUtxo( + const OutPoint& outpoint, bool ignore_error, bool* is_witness) const { + return cfd::core::Psbt::GetTxInUtxo( + GetTxInIndex(outpoint), ignore_error, is_witness); +} +Script Psbt::GetTxInRedeemScript( + const OutPoint& outpoint, bool ignore_error, bool* is_witness) const { + return cfd::core::Psbt::GetTxInRedeemScript( + GetTxInIndex(outpoint), ignore_error, is_witness); +} + +Script Psbt::GetTxInRedeemScriptDirect( + const OutPoint& outpoint, bool ignore_error, bool is_witness) const { + return cfd::core::Psbt::GetTxInRedeemScriptDirect( + GetTxInIndex(outpoint), ignore_error, is_witness); +} +std::vector Psbt::GetTxInKeyDataList(const OutPoint& outpoint) const { + return cfd::core::Psbt::GetTxInKeyDataList(GetTxInIndex(outpoint)); +} +KeyData Psbt::GetTxInKeyData( + const OutPoint& outpoint, bool ignore_error) const { + return cfd::core::Psbt::GetTxInKeyData(GetTxInIndex(outpoint), ignore_error); +} +std::vector Psbt::GetTxInSignaturePubkeyList( + const OutPoint& outpoint) const { + return cfd::core::Psbt::GetTxInSignaturePubkeyList(GetTxInIndex(outpoint)); +} +ByteData Psbt::GetTxInSignature( + const OutPoint& outpoint, const Pubkey& pubkey) const { + return cfd::core::Psbt::GetTxInSignature(GetTxInIndex(outpoint), pubkey); +} +bool Psbt::IsFindTxInSignature( + const OutPoint& outpoint, const Pubkey& pubkey) const { + return cfd::core::Psbt::IsFindTxInSignature(GetTxInIndex(outpoint), pubkey); +} + +SigHashType Psbt::GetTxInSighashType(const OutPoint& outpoint) const { + return cfd::core::Psbt::GetTxInSighashType(GetTxInIndex(outpoint)); +} + +bool Psbt::IsFindTxInSighashType(const OutPoint& outpoint) const { + return cfd::core::Psbt::IsFindTxInSighashType(GetTxInIndex(outpoint)); +} + +std::vector Psbt::GetTxInFinalScript( + const OutPoint& outpoint, bool is_witness_stack) const { + return cfd::core::Psbt::GetTxInFinalScript( + GetTxInIndex(outpoint), is_witness_stack); +} + +ByteData Psbt::GetTxInRecord( + const OutPoint& outpoint, const ByteData& key) const { + return cfd::core::Psbt::GetTxInRecord(GetTxInIndex(outpoint), key); +} + +bool Psbt::IsFindTxInRecord( + const OutPoint& outpoint, const ByteData& key) const { + return cfd::core::Psbt::IsFindTxInRecord(GetTxInIndex(outpoint), key); +} + +std::vector Psbt::GetTxInRecordKeyList( + const OutPoint& outpoint) const { + return cfd::core::Psbt::GetTxInRecordKeyList(GetTxInIndex(outpoint)); +} + +void Psbt::ClearTxInSignData(const OutPoint& outpoint) { + cfd::core::Psbt::ClearTxInSignData(GetTxInIndex(outpoint)); +} + +void Psbt::SetUtxoData(const UtxoData& utxo, const Transaction& transaction) { + OutPoint outpoint; + std::vector key_list; + Script redeem_script; + ConvertFromUtxoData( + utxo, &outpoint, nullptr, &key_list, &redeem_script, true); + + SetTxInUtxo(outpoint, transaction, redeem_script, key_list); +} + +void Psbt::SetWitnessUtxoData(const UtxoData& utxo) { + OutPoint outpoint; + std::vector key_list; + Script redeem_script; + TxOut txout; + ConvertFromUtxoData(utxo, &outpoint, &txout, &key_list, &redeem_script); + SetTxInUtxo(outpoint, TxOutReference(txout), redeem_script, key_list); +} + +void Psbt::CollectInputUtxo(const std::vector& utxos) { + OutPoint outpoint; + TxOut txout; + std::vector key_list; + Script redeem_script; + for (const auto& utxo : utxos) { + if (utxo.descriptor.empty()) continue; + key_list.clear(); + bool is_witness = ConvertFromUtxoData( + utxo, &outpoint, &txout, &key_list, &redeem_script, true); + if (is_witness) { + SetTxInUtxo(outpoint, TxOutReference(txout), redeem_script, key_list); + } + } +} + +uint32_t Psbt::AddTxOut(const Amount& amount, const Address& address) { + return cfd::core::Psbt::AddTxOut(address.GetLockingScript(), amount); +} + +uint32_t Psbt::AddTxOutData( + const Amount& amount, const Address& address, const KeyData& key_data) { + uint32_t index = + cfd::core::Psbt::AddTxOut(address.GetLockingScript(), amount); + try { + SetTxOutData(index, key_data); + } catch (const CfdException& except) { + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + wally_psbt_remove_output(psbt_pointer, index); + base_tx_ = cfd::core::Psbt::RebuildTransaction(wally_psbt_pointer_); + throw except; + } + return index; +} + +uint32_t Psbt::AddTxOutData( + const Amount& amount, const Address& address, const Script& redeem_script, + const std::vector& key_list) { + uint32_t index = + cfd::core::Psbt::AddTxOut(address.GetLockingScript(), amount); + try { + SetTxOutData(index, redeem_script, key_list); + } catch (const CfdException& except) { + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + wally_psbt_remove_output(psbt_pointer, index); + base_tx_ = cfd::core::Psbt::RebuildTransaction(wally_psbt_pointer_); + throw except; + } + return 0; +} + +void Psbt::IgnoreVerify(const OutPoint& outpoint) { + verify_ignore_map_.emplace(outpoint); +} + +void Psbt::Verify() const { + if (!HasAllUtxos()) { + warn(CFD_LOG_SOURCE, "psbt utxo not set."); + throw CfdException(CfdError::kCfdIllegalStateError, "psbt utxo not set."); + } + + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint32_t max = static_cast(psbt_pointer->tx->num_inputs); + for (uint32_t index = 0; index < max; ++index) { + OutPoint outpoint( + Txid(ByteData256(ByteData( + psbt_pointer->tx->inputs[index].txhash, + sizeof(psbt_pointer->tx->inputs[index].txhash)))), + psbt_pointer->tx->inputs[index].index); + Verify(outpoint); + } +} + +void Psbt::Verify(const OutPoint& outpoint) const { + if (verify_ignore_map_.find(outpoint) == verify_ignore_map_.end()) { + auto tx = GetTransaction(); + uint32_t index = GetTxInIndex(outpoint); + if (!IsFinalizedInput(index)) { + warn(CFD_LOG_SOURCE, "psbt txin not finalized yet."); + throw CfdException( + CfdError::kCfdIllegalStateError, "psbt txin not finalized yet."); + } + + UtxoData utxo = GetUtxoData(index); + TxIn txin(utxo.txid, utxo.vout, tx.GetTxIn(index).GetSequence()); + ByteData scriptsig = cfd::core::Psbt::GetTxInFinalScript(index, false)[0]; + if (!scriptsig.IsEmpty()) txin.SetUnlockingScript(Script(scriptsig)); + auto witness_stack = cfd::core::Psbt::GetTxInFinalScript(index, true); + for (const auto& stack : witness_stack) { + txin.AddScriptWitnessStack(stack); + } + + TransactionContextUtil::Verify( + &tx, outpoint, utxo, &txin, CollectSighashByTx); + } +} + +std::vector Psbt::FundTransaction( + const std::vector& witness_utxos, double effective_fee_rate, + const Descriptor* change_address, Amount* estimate_fee, + const CoinSelectionOption* option_params, const UtxoFilter* filter, + NetType net_type) { + std::vector input_utxos = GetUtxoDataAll(); + for (const auto& utxo : witness_utxos) { + ConvertFromUtxoData( + utxo, nullptr, nullptr, nullptr, nullptr, false, &net_type); + } + + std::string reserve_txout_address; + if (change_address != nullptr) { + auto key_list = change_address->GetKeyDataAll(); + if (key_list.empty()) { + warn(CFD_LOG_SOURCE, "psbt change address's KeyData is empty."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "psbt change address's KeyData is empty."); + } + auto ref = change_address->GetReference(); + switch (ref.GetAddressType()) { + case AddressType::kP2wpkhAddress: + case AddressType::kP2wshAddress: + case AddressType::kP2shP2wpkhAddress: + case AddressType::kP2shP2wshAddress: + // do nothing + break; + case AddressType::kP2pkhAddress: + case AddressType::kP2shAddress: + default: + warn(CFD_LOG_SOURCE, "psbt change address's address not segwit."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "psbt change address's address not segwit."); + break; + } + reserve_txout_address = ref.GenerateAddress(net_type).GetAddress(); + } + cfd::api::TransactionApi api; + std::vector append_txout_addresses; + // TODO(k-matsuzawa) move to TransactionContext + auto fund_tx = api.FundRawTransaction( + base_tx_.GetHex(), witness_utxos, Amount(), input_utxos, + reserve_txout_address, effective_fee_rate, estimate_fee, filter, + option_params, &append_txout_addresses, net_type); + auto fund_basetx = fund_tx.GetTransaction(); + + std::vector append_utxos; + if (base_tx_.GetTxInCount() < fund_basetx.GetTxInCount()) { + auto txin_list = fund_basetx.GetTxInList(); + for (const auto& txin : txin_list) { + if (!IsFindTxIn(txin.GetOutPoint())) { + auto outpoint = txin.GetOutPoint(); + for (const auto& utxo : witness_utxos) { + if ((utxo.vout == outpoint.GetVout()) && + (utxo.txid.Equals(outpoint.GetTxid()))) { + append_utxos.push_back(utxo); + break; + } + } + } + } + } + if (base_tx_.GetTxOutCount() < fund_basetx.GetTxOutCount()) { + if ((fund_basetx.GetTxOutCount() - base_tx_.GetTxOutCount()) > 1) { + warn( + CFD_LOG_SOURCE, "psbt txout count invalid. [{},{}]", + fund_basetx.GetTxOutCount(), base_tx_.GetTxOutCount()); + throw CfdException( + CfdError::kCfdInternalError, "psbt txout count invalid."); + } + if (append_txout_addresses.size() != 1) { + warn( + CFD_LOG_SOURCE, "psbt txout append address invalid. [{}]", + append_txout_addresses.size()); + throw CfdException( + CfdError::kCfdInternalError, "psbt txout append address invalid."); + } + if (change_address != nullptr) { + std::vector key_list; + cfd::DescriptorScriptData script_data = + ParseDescriptor(change_address->ToString(), &key_list, &net_type); + auto txout = fund_basetx.GetTxOut(base_tx_.GetTxOutCount()); + if (!script_data.locking_script.Equals(txout.GetLockingScript())) { + warn( + CFD_LOG_SOURCE, "psbt txout append locking script invalid. [{}]", + txout.GetLockingScript().GetHex()); + throw CfdException( + CfdError::kCfdInternalError, + "psbt txout append locking script invalid."); + } + AddTxOutData( + txout.GetValue(), script_data.address, script_data.redeem_script, + key_list); + } + } + for (const auto& utxo : append_utxos) { + AddTxInData(utxo); + } + base_tx_ = cfd::core::Psbt::RebuildTransaction(wally_psbt_pointer_); + return append_utxos; +} + +uint32_t Psbt::GetDefaultSequence() const { + return (base_tx_.GetLockTime() == 0) ? kSequenceDisableLockTime + : kSequenceEnableLockTimeMax; +} + +UtxoData Psbt::GetUtxoData(uint32_t index, NetType net_type) const { + CheckTxInIndex(index, __LINE__, __FUNCTION__); + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + UtxoData utxo; + utxo.block_height = 0; + utxo.address_type = AddressType::kP2shAddress; + + utxo.txid = Txid(ByteData256(ByteData( + psbt_pointer->tx->inputs[index].txhash, + sizeof(psbt_pointer->tx->inputs[index].txhash)))); + utxo.vout = psbt_pointer->tx->inputs[index].index; + + bool witness_only = false; + if (psbt_pointer->inputs[index].witness_utxo != nullptr) { + utxo.amount = Amount(static_cast( + psbt_pointer->inputs[index].witness_utxo->satoshi)); + utxo.locking_script = Script(ByteData( + psbt_pointer->inputs[index].witness_utxo->script, + static_cast( + psbt_pointer->inputs[index].witness_utxo->script_len))); + witness_only = true; + } else if (psbt_pointer->inputs[index].utxo != nullptr) { + if (psbt_pointer->inputs[index].utxo->num_outputs > utxo.vout) { + auto output = &psbt_pointer->inputs[index].utxo->outputs[utxo.vout]; + utxo.amount = Amount(static_cast(output->satoshi)); + utxo.locking_script = Script( + ByteData(output->script, static_cast(output->script_len))); + } + } + if (utxo.locking_script.IsP2shScript()) { + utxo.address_type = AddressType::kP2shAddress; + } else if (utxo.locking_script.IsP2pkhScript()) { + utxo.address_type = AddressType::kP2pkhAddress; + } else if (utxo.locking_script.IsP2wpkhScript()) { + utxo.address_type = AddressType::kP2wpkhAddress; + } else if (utxo.locking_script.IsP2wshScript()) { + utxo.address_type = AddressType::kP2wshAddress; + } + + std::vector key_list; + bool is_finalized = IsFinalizedInput(index); + if (is_finalized) { + if (witness_only && + (psbt_pointer->inputs[index].final_witness != nullptr)) { + auto witness_stack = psbt_pointer->inputs[index].final_witness; + uint32_t last_index = static_cast(witness_stack->num_items); + if (last_index > 0) { + ByteData last_data( + witness_stack->items[last_index - 1].witness, + static_cast( + witness_stack->items[last_index - 1].witness_len)); + if (Pubkey::IsValid(last_data)) { + key_list.emplace_back(KeyData(Pubkey(last_data), "", ByteData())); + if (utxo.address_type == AddressType::kP2shAddress) { + utxo.address_type = AddressType::kP2shP2wpkhAddress; + } + } else { + utxo.redeem_script = Script(last_data); + if (utxo.address_type == AddressType::kP2shAddress) { + utxo.address_type = AddressType::kP2shP2wshAddress; + } + if (utxo.redeem_script.IsP2pkScript()) { + key_list.emplace_back(KeyData( + Pubkey(utxo.redeem_script.GetElementList()[0].GetData()), "", + ByteData())); + } + } + } + } else if (psbt_pointer->inputs[index].final_scriptsig != nullptr) { + Script scriptsig(ByteData( + psbt_pointer->inputs[index].final_scriptsig, + static_cast( + psbt_pointer->inputs[index].final_scriptsig_len))); + auto items = scriptsig.GetElementList(); + uint32_t last_index = static_cast(items.size()); + if (last_index > 0) { + ByteData last_data = items[last_index - 1].GetBinaryData(); + if (Pubkey::IsValid(last_data)) { + key_list.emplace_back(KeyData(Pubkey(last_data), "", ByteData())); + } else { + utxo.redeem_script = Script(last_data); + if (utxo.redeem_script.IsP2pkScript()) { + key_list.emplace_back(KeyData( + Pubkey(utxo.redeem_script.GetElementList()[0].GetData()), "", + ByteData())); + } + } + } + } + } else if (witness_only) { + if (psbt_pointer->inputs[index].witness_script != nullptr) { + utxo.redeem_script = Script(ByteData( + psbt_pointer->inputs[index].witness_script, + static_cast( + psbt_pointer->inputs[index].witness_script_len))); + if (utxo.address_type == AddressType::kP2shAddress) { + utxo.address_type = AddressType::kP2shP2wshAddress; + } + } else if (utxo.address_type == AddressType::kP2shAddress) { + utxo.address_type = AddressType::kP2shP2wpkhAddress; + } + + key_list = cfd::core::Psbt::GetTxInKeyDataList(index); + } else { + if (psbt_pointer->inputs[index].redeem_script != nullptr) { + utxo.redeem_script = Script(ByteData( + psbt_pointer->inputs[index].redeem_script, + static_cast( + psbt_pointer->inputs[index].redeem_script_len))); + } + key_list = cfd::core::Psbt::GetTxInKeyDataList(index); + } + + if ((!is_finalized) && + (psbt_pointer->inputs[index].keypaths.num_items == 0)) { + // unset descriptor + } else if ( + utxo.locking_script.IsP2wpkhScript() || + (utxo.locking_script.IsP2shScript() && utxo.redeem_script.IsEmpty())) { + if (key_list.size() != 1) { + warn(CFD_LOG_SOURCE, "psbt not supported. many pubkey for p2wpkh."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "psbt not supported. many pubkey for p2wpkh."); + } + utxo.descriptor = "wpkh(" + key_list[0].ToString() + ")"; + if (utxo.locking_script.IsP2shScript()) { + utxo.descriptor = "sh(" + utxo.descriptor + ")"; + } + } else if ( + utxo.locking_script.IsP2wshScript() || + utxo.locking_script.IsP2shScript()) { + if (utxo.redeem_script.IsMultisigScript()) { + uint32_t req_num = 0; + auto multisig_key_list = ScriptUtil::ExtractPubkeysFromMultisigScript( + utxo.redeem_script, &req_num); + utxo.descriptor = "multi(" + std::to_string(req_num); + for (const auto& pubkey : multisig_key_list) { + bool is_find = false; + for (const auto& key : key_list) { + if (pubkey.Equals(key.GetPubkey())) { + utxo.descriptor += "," + key.ToString(); + is_find = true; + } + } + if (!is_find) { + utxo.descriptor += "," + pubkey.GetHex(); + } + } + utxo.descriptor += ")"; + } else if (utxo.redeem_script.IsP2pkScript()) { + if (key_list.size() != 1) { + warn(CFD_LOG_SOURCE, "psbt not supported. many pubkey for p2pkh."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "psbt not supported. many pubkey for p2pkh."); + } + utxo.descriptor = "pkh(" + key_list[0].ToString() + ")"; + } else { + // not key support + utxo.descriptor = utxo.redeem_script.GetHex(); + } + if (witness_only) { + utxo.descriptor = "wsh(" + utxo.descriptor + ")"; + } + if (utxo.locking_script.IsP2shScript()) { + utxo.descriptor = "sh(" + utxo.descriptor + ")"; + } + } else if (utxo.locking_script.IsP2pkhScript()) { + if (key_list.size() != 1) { + warn(CFD_LOG_SOURCE, "psbt not supported. many pubkey for p2pkh."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "psbt not supported. many pubkey for p2pkh."); + } + utxo.descriptor = "pkh(" + key_list[0].ToString() + ")"; + } else { + // unsupported format + } + + if (!utxo.descriptor.empty()) { + Descriptor desc = Descriptor::Parse(utxo.descriptor); + auto ref = desc.GetReference(); + if (!ref.GetLockingScript().Equals(utxo.locking_script)) { + warn( + CFD_LOG_SOURCE, + "psbt invalid state. unmatch descriptor & lockingScript."); + throw CfdException( + CfdError::kCfdIllegalStateError, + "psbt invalid state. unmatch descriptor & lockingScript."); + } + utxo.address = ref.GenerateAddress(net_type); + utxo.address_type = ref.GetAddressType(); + } + + return utxo; +} + +std::vector Psbt::GetUtxoDataAll(NetType net_type) const { + std::vector list; + if (!HasAllUtxos()) return list; + + struct wally_psbt* psbt_pointer; + psbt_pointer = static_cast(wally_psbt_pointer_); + uint32_t max = static_cast(psbt_pointer->num_inputs); + for (uint32_t index = 0; index < max; ++index) { + list.emplace_back(GetUtxoData(index, net_type)); + } + return list; +} + +} // namespace cfd diff --git a/src/cfd_transaction.cpp b/src/cfd_transaction.cpp index df4f9e90..4bd2e76c 100644 --- a/src/cfd_transaction.cpp +++ b/src/cfd_transaction.cpp @@ -2,8 +2,7 @@ /** * @file cfd_transaction.cpp * - * @brief \~english implementation of classes related to transaction operation - * \~japanese Transaction操作の関連クラスの実装ファイル + * @brief implementation of classes related to transaction operation */ #include "cfd/cfd_transaction.h" @@ -17,10 +16,13 @@ #include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_amount.h" #include "cfdcore/cfdcore_coin.h" +#include "cfdcore/cfdcore_descriptor.h" #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_transaction.h" namespace cfd { @@ -33,16 +35,21 @@ using cfd::core::ByteData; using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; +using cfd::core::Descriptor; using cfd::core::HashType; using cfd::core::NetType; using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SchnorrUtil; using cfd::core::Script; using cfd::core::ScriptBuilder; using cfd::core::ScriptOperator; using cfd::core::ScriptUtil; using cfd::core::SigHashType; using cfd::core::SignatureUtil; +using cfd::core::TaprootScriptTree; +using cfd::core::TaprootUtil; +using cfd::core::TapScriptData; using cfd::core::Transaction; using cfd::core::Txid; using cfd::core::TxIn; @@ -65,15 +72,23 @@ using cfd::TransactionController; * @param[in] pubkey pubkey on pubkey hash. * @param[in] redeem_script redeem script on script hash. * @param[in] version witness version. + * @param[in] annex annex + * @param[in] script_tree taproot script tree. * @return signature hash. */ static ByteData256 CreateTxSighash( const TransactionContext* transaction, const OutPoint& outpoint, const UtxoData& utxo, const SigHashType& sighash_type, - const Pubkey& pubkey, const Script& redeem_script, - WitnessVersion version) { + const Pubkey& pubkey, const Script& redeem_script, WitnessVersion version, + const ByteData* annex, const TaprootScriptTree* script_tree) { ByteData sig; - if (redeem_script.IsEmpty()) { + if (version == WitnessVersion::kVersion1) { + ByteData256 tapleaf_hash; + if (script_tree != nullptr) tapleaf_hash = script_tree->GetTapLeafHash(); + return transaction->CreateSignatureHashByTaproot( + outpoint, sighash_type, + (script_tree != nullptr) ? &tapleaf_hash : nullptr, 0, annex); + } else if (redeem_script.IsEmpty()) { sig = transaction->CreateSignatureHash( outpoint, pubkey, sighash_type, utxo.amount, version); } else { @@ -88,19 +103,55 @@ static ByteData256 CreateTxSighash( const Script&, WitnessVersion)> create_sighash_func; */ +/** + * @brief Get locking script from utxo. + * @param[in] utxo utxo data. + * @return locking script + */ +static Script GetLockingScriptFromUtxoData(const UtxoData& utxo) { + Script locking_script = utxo.locking_script; + if (!utxo.address.GetAddress().empty()) { + locking_script = utxo.address.GetLockingScript(); + } else if (!utxo.descriptor.empty()) { + auto desc = Descriptor::Parse(utxo.descriptor); + locking_script = desc.GetLockingScript(); + } + return locking_script; +} + // ----------------------------------------------------------------------------- // TransactionController // ----------------------------------------------------------------------------- -TransactionContext::TransactionContext() {} +TransactionContext::TransactionContext() { + utxo_map_.clear(); + signed_map_.clear(); + verify_map_.clear(); + verify_ignore_map_.clear(); +} TransactionContext::TransactionContext(uint32_t version, uint32_t locktime) - : Transaction(version, locktime) {} + : Transaction(version, locktime) { + utxo_map_.clear(); + signed_map_.clear(); + verify_map_.clear(); + verify_ignore_map_.clear(); +} TransactionContext::TransactionContext(const std::string& tx_hex) - : Transaction(tx_hex) {} + : Transaction(tx_hex) { + utxo_map_.clear(); + signed_map_.clear(); + verify_map_.clear(); + verify_ignore_map_.clear(); +} TransactionContext::TransactionContext(const ByteData& byte_data) - : Transaction(byte_data.GetHex()) {} + : Transaction(byte_data.GetHex()) { + utxo_map_.clear(); + signed_map_.clear(); + verify_map_.clear(); + verify_ignore_map_.clear(); +} TransactionContext::TransactionContext(const TransactionContext& context) : Transaction(context.GetHex()) { @@ -115,11 +166,13 @@ TransactionContext::TransactionContext(const Transaction& transaction) TransactionContext& TransactionContext::operator=( const TransactionContext& context) & { - SetFromHex(context.GetHex()); - utxo_map_ = context.utxo_map_; - signed_map_ = context.signed_map_; - verify_map_ = context.verify_map_; - verify_ignore_map_ = context.verify_ignore_map_; + if (this != &context) { + SetFromHex(context.GetHex()); + utxo_map_ = context.utxo_map_; + signed_map_ = context.signed_map_; + verify_map_ = context.verify_map_; + verify_ignore_map_ = context.verify_ignore_map_; + } return *this; } @@ -207,8 +260,9 @@ uint32_t TransactionContext::AddTxOut( return AddTxOut(value, address.GetLockingScript()); } -uint32_t TransactionContext::GetSizeIgnoreTxIn() const { +uint32_t TransactionContext::GetSizeIgnoreTxIn(bool use_witness) const { uint32_t result = AbstractTransaction::kTransactionMinimumSize; + if (use_witness) result += 2; std::vector txouts = GetTxOutList(); for (const auto& txout : txouts) { result += txout.GetSerializeSize(); @@ -216,8 +270,9 @@ uint32_t TransactionContext::GetSizeIgnoreTxIn() const { return result; } -uint32_t TransactionContext::GetVsizeIgnoreTxIn() const { - return AbstractTransaction::GetVsizeFromSize(GetSizeIgnoreTxIn(), 0); +uint32_t TransactionContext::GetVsizeIgnoreTxIn(bool use_witness) const { + return AbstractTransaction::GetVsizeFromSize( + GetSizeIgnoreTxIn(use_witness), 0); } void TransactionContext::AddInput(const UtxoData& utxo) { @@ -237,7 +292,7 @@ void TransactionContext::AddInput(const UtxoData& utxo, uint32_t sequence) { UtxoUtil::ConvertToUtxo(utxo, &temp, &dest); AddTxIn(utxo.txid, utxo.vout, sequence); - utxo_map_.emplace(OutPoint(utxo.txid, utxo.vout), dest); + utxo_map_.emplace_back(dest); } void TransactionContext::AddInputs(const std::vector& utxos) { @@ -253,10 +308,14 @@ void TransactionContext::CollectInputUtxo(const std::vector& utxos) { uint32_t vout = txin_ref.GetVout(); OutPoint outpoint(txid, vout); - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + if (!IsFindUtxoMap(outpoint)) { for (const auto& utxo : utxos) { if ((utxo.vout == vout) && utxo.txid.Equals(txid)) { - utxo_map_.emplace(outpoint, utxo); + UtxoData dest; + Utxo temp; + memset(&temp, 0, sizeof(temp)); + UtxoUtil::ConvertToUtxo(utxo, &temp, &dest); + utxo_map_.emplace_back(dest); break; } } @@ -265,16 +324,27 @@ void TransactionContext::CollectInputUtxo(const std::vector& utxos) { } } +UtxoData TransactionContext::GetTxInUtxoData(const OutPoint& outpoint) const { + UtxoData utxo; + utxo.address_type = AddressType::kP2shAddress; + utxo.vout = 0; + utxo.block_height = 0; + utxo.binary_data = nullptr; + IsFindUtxoMap(outpoint, &utxo); + return utxo; +} + Amount TransactionContext::GetFeeAmount() const { Amount input; + UtxoData utxo; for (const auto& txin_ref : vin_) { OutPoint outpoint(txin_ref.GetTxid(), txin_ref.GetVout()); - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. GetFeeAmount fail."); } - input += utxo_map_.find(outpoint)->second.amount; + input += utxo.amount; } Amount output; @@ -290,43 +360,68 @@ Amount TransactionContext::GetFeeAmount() const { void TransactionContext::SignWithKey( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, - SigHashType sighash_type, bool has_grind_r) { - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + SigHashType sighash_type, bool has_grind_r, const ByteData256* aux_rand, + const ByteData* annex) { + UtxoData utxo; + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. sign fail."); } - UtxoData utxo = utxo_map_.find(outpoint)->second; - - SignWithPrivkeySimple( - outpoint, pubkey, privkey, sighash_type, utxo.amount, utxo.address_type, - has_grind_r); + utxo.locking_script = GetLockingScriptFromUtxoData(utxo); + if (utxo.locking_script.IsTaprootScript()) { + SignWithSchnorrPrivkeySimple( + outpoint, privkey, sighash_type, aux_rand, annex); + } else { + SignWithPrivkeySimple( + outpoint, pubkey, privkey, sighash_type, utxo.amount, + utxo.address_type, has_grind_r); + } } void TransactionContext::IgnoreVerify(const OutPoint& outpoint) { GetTxInIndex(outpoint.GetTxid(), outpoint.GetVout()); - verify_ignore_map_.emplace(outpoint); + if (!IsFindOutPoint(verify_ignore_map_, outpoint)) { + verify_ignore_map_.emplace_back(outpoint); + } } void TransactionContext::Verify() { for (const auto& vin : vin_) { OutPoint outpoint = vin.GetOutPoint(); - if (verify_ignore_map_.find(outpoint) == std::end(verify_ignore_map_)) { + if (!IsFindOutPoint(verify_ignore_map_, outpoint)) { Verify(outpoint); } } } void TransactionContext::Verify(const OutPoint& outpoint) { - if (utxo_map_.find(outpoint) == std::end(utxo_map_)) { + UtxoData utxo; + if (!IsFindUtxoMap(outpoint, &utxo)) { throw CfdException( CfdError::kCfdIllegalStateError, "Utxo is not found. verify fail."); } - const auto& utxo = utxo_map_.find(outpoint)->second; const auto& txin = vin_[GetTxInIndex(outpoint)]; + std::vector utxo_list; + utxo.locking_script = GetLockingScriptFromUtxoData(utxo); + if (utxo.locking_script.IsTaprootScript()) { + UtxoData work_utxo; + for (const auto& txin_ref : vin_) { + OutPoint target_outpoint(txin_ref.GetTxid(), txin_ref.GetVout()); + if (!IsFindUtxoMap(target_outpoint, &work_utxo)) { + throw CfdException( + CfdError::kCfdIllegalStateError, + "Utxo is not found. Verify fail."); + } + work_utxo.locking_script = GetLockingScriptFromUtxoData(work_utxo); + utxo_list.emplace_back(work_utxo); + } + } TransactionContextUtil::Verify( this, outpoint, utxo, &txin, CreateTxSighash); - verify_map_.emplace(outpoint); + if (!IsFindOutPoint(verify_map_, outpoint)) { + verify_map_.emplace_back(outpoint); + } } ByteData TransactionContext::Finalize() { @@ -334,14 +429,6 @@ ByteData TransactionContext::Finalize() { return AbstractTransaction::GetData(); } -#if 0 -// priority: low -void TransactionContext::ClearSign() { return; } - -// priority: low -void TransactionContext::ClearSign(const OutPoint& outpoint) { return; } -#endif - ByteData TransactionContext::CreateSignatureHash( const OutPoint& outpoint, const Pubkey& pubkey, SigHashType sighash_type, const Amount& value, WitnessVersion version) const { @@ -355,14 +442,49 @@ ByteData TransactionContext::CreateSignatureHash( const OutPoint& outpoint, const Script& redeem_script, SigHashType sighash_type, const Amount& value, WitnessVersion version) const { - // TODO(soejima): OP_CODESEPARATOR存在時、Scriptの分割が必要。 - // TODO(k-matsuzawa): 現状は利用側で分割し、適用する箇所だけ指定してもらう。 + // TODO(k-matsuzawa): For now, when using OP_CODESEPARATOR, divide it on the user side and ask them to specify only the applicable part. // NOLINT ByteData256 sighash = GetSignatureHash( GetTxInIndex(outpoint), redeem_script.GetData(), sighash_type, value, version); return ByteData(sighash.GetBytes()); } +ByteData256 TransactionContext::CreateSignatureHashByTaproot( + const OutPoint& outpoint, const SigHashType& sighash_type, + const ByteData256* tap_leaf_hash, const uint32_t* code_separator_position, + const ByteData* annex) const { + UtxoData utxo; + std::vector utxos; + // utxos.reserve(vin_.size()); + // for (const auto& txin_ref : vin_) { + for (size_t index = 0; index < vin_.size(); ++index) { + const auto& txin_ref = vin_[index]; + OutPoint target_outpoint(txin_ref.GetTxid(), txin_ref.GetVout()); + if (!IsFindUtxoMap(target_outpoint, &utxo)) { + throw CfdException( + CfdError::kCfdIllegalStateError, + "Utxo is not found. CreateSignatureHashByTaproot fail."); + } + utxo.locking_script = GetLockingScriptFromUtxoData(utxo); + utxos.emplace_back(TxOut(utxo.amount, utxo.locking_script)); + } + + uint32_t txin_index = GetTxInIndex(outpoint); + ByteData annex_data; + if (annex != nullptr) annex_data = *annex; + TapScriptData script_data; + if (tap_leaf_hash != nullptr) { + script_data.tap_leaf_hash = *tap_leaf_hash; + if (code_separator_position != nullptr) { + script_data.code_separator_position = *code_separator_position; + } + } + + return GetSchnorrSignatureHash( + txin_index, sighash_type, utxos, + (tap_leaf_hash != nullptr) ? &script_data : nullptr, annex_data); +} + void TransactionContext::SignWithPrivkeySimple( const OutPoint& outpoint, const Pubkey& pubkey, const Privkey& privkey, SigHashType sighash_type, const Amount& value, AddressType address_type, @@ -379,6 +501,22 @@ void TransactionContext::SignWithPrivkeySimple( AddPubkeyHashSign(outpoint, sign, pubkey, address_type); } +void TransactionContext::SignWithSchnorrPrivkeySimple( + const OutPoint& outpoint, const Privkey& privkey, + const SigHashType& sighash_type, const ByteData256* aux_rand, + const ByteData* annex) { + auto sighash = + CreateSignatureHashByTaproot(outpoint, sighash_type, nullptr, 0, annex); + SchnorrSignature signature; + if (aux_rand != nullptr) { + signature = SchnorrUtil::Sign(sighash, privkey, *aux_rand); + } else { + signature = SchnorrUtil::Sign(sighash, privkey); + } + signature.SetSigHashType(sighash_type); + AddSchnorrSign(outpoint, signature, annex); +} + void TransactionContext::AddPubkeyHashSign( const OutPoint& outpoint, const SignParameter& signature, const Pubkey& pubkey, AddressType address_type) { @@ -408,10 +546,33 @@ void TransactionContext::AddMultisigSign( AddScriptHashSign(outpoint, sign_list, redeem_script, address_type, true); } +void TransactionContext::AddSchnorrSign( + const OutPoint& outpoint, const SchnorrSignature& signature, + const ByteData* annex) { + std::vector sign_params = { + SignParameter(signature.GetData(true))}; + if (annex != nullptr) sign_params.emplace_back(*annex); + AddSign(outpoint, sign_params, true, true); + signed_map_.emplace(outpoint, signature.GetSigHashType()); +} + +void TransactionContext::AddTapScriptSign( + const OutPoint& outpoint, const TaprootScriptTree& tree, + const SchnorrPubkey& internal_pubkey, + const std::vector& sign_data_list, const ByteData* annex) { + std::vector sign_params = sign_data_list; + auto control = TaprootUtil::CreateTapScriptControl(internal_pubkey, tree); + auto script = tree.GetScript(); + sign_params.emplace_back(script); + sign_params.emplace_back(control); + if (annex != nullptr) sign_params.emplace_back(*annex); + AddSign(outpoint, sign_params, true, true); +} + void TransactionContext::AddSign( const OutPoint& outpoint, const std::vector& sign_params, bool insert_witness, bool clear_stack) { - return TransactionContextUtil::AddSign( + TransactionContextUtil::AddSign( this, outpoint, sign_params, insert_witness, clear_stack); } @@ -435,6 +596,55 @@ bool TransactionContext::VerifyInputSignature( ByteData256(sighash.GetBytes()), pubkey, signature); } +bool TransactionContext::VerifyInputSchnorrSignature( + const SchnorrSignature& signature, const OutPoint& outpoint, + const std::vector& utxo_list, const SchnorrPubkey& pubkey, + const ByteData* annex) const { + UtxoData target_utxo; + std::vector utxos; + utxos.reserve(vin_.size()); + for (const auto& txin_ref : vin_) { + OutPoint target_outpoint(txin_ref.GetTxid(), txin_ref.GetVout()); + bool is_find = false; + for (const auto& utxo : utxo_list) { + if (txin_ref.GetTxid().Equals(utxo.txid) && + (txin_ref.GetVout() == utxo.vout)) { + is_find = true; + Script locking_script = GetLockingScriptFromUtxoData(utxo); + utxos.emplace_back(utxo.amount, locking_script); + + if (outpoint == target_outpoint) { + target_utxo = utxo; + target_utxo.locking_script = locking_script; + } + } + } + if (!is_find) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Utxo is not found. VerifyInputSchnorrSignature fail."); + } + } + if (target_utxo.amount == 0) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "OutPoint is not found into utxo_list."); + } else if (!target_utxo.locking_script.IsTaprootScript()) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Target OutPoint is not taproot."); + } + auto hash = target_utxo.locking_script.GetElementList()[1].GetBinaryData(); + if (!hash.Equals(pubkey.GetData())) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "Unmatch locking script."); + } + + auto sighash = GetSchnorrSignatureHash( + GetTxInIndex(outpoint), signature.GetSigHashType(), utxos, nullptr, + (annex != nullptr) ? *annex : ByteData()); + return pubkey.Verify(signature, sighash); +} + uint32_t TransactionContext::GetDefaultSequence() const { if (GetLockTime() == 0) { return kSequenceDisableLockTime; @@ -506,6 +716,26 @@ std::vector TransactionContext::CheckMultisig( return signature_list; } +bool TransactionContext::IsFindUtxoMap( + const OutPoint& outpoint, UtxoData* utxo) const { + for (const auto& utxo_data : utxo_map_) { + if ((outpoint.GetVout() == utxo_data.vout) && + utxo_data.txid.Equals(outpoint.GetTxid())) { + if (utxo != nullptr) *utxo = utxo_data; + return true; + } + } + return false; +} + +bool TransactionContext::IsFindOutPoint( + const std::vector& list, const OutPoint& outpoint) const { + for (const auto& target : list) { + if (outpoint == target) return true; + } + return false; +} + // ----------------------------------------------------------------------------- // TransactionController // ----------------------------------------------------------------------------- @@ -532,7 +762,9 @@ TransactionController::TransactionController( TransactionController& TransactionController::operator=( const TransactionController& transaction) & { - transaction_ = transaction.transaction_; + if (this != &transaction) { + transaction_ = transaction.transaction_; + } return *this; } diff --git a/src/cfd_transaction_common.cpp b/src/cfd_transaction_common.cpp index e8862115..f37dfedb 100644 --- a/src/cfd_transaction_common.cpp +++ b/src/cfd_transaction_common.cpp @@ -63,6 +63,114 @@ constexpr uint32_t kSequenceEnableLockTimeMax = 0xfffffffeU; /// シーケンス値(locktime無効) constexpr uint32_t kSequenceDisableLockTime = 0xffffffffU; +// ----------------------------------------------------------------------------- +// UtxoData +// ----------------------------------------------------------------------------- +UtxoData::UtxoData() { + // do nothing +} + +#ifndef CFD_DISABLE_ELEMENTS +UtxoData::UtxoData( + uint64_t block_height, const BlockHash& block_hash, const Txid& txid, + uint32_t vout, const Script& locking_script, const Script& redeem_script, + const Address& address, const std::string& descriptor, + const Amount& amount, AddressType address_type, void* binary_data, + const ConfidentialAssetId& asset, + const ElementsConfidentialAddress& confidential_address, + const BlindFactor& asset_blind_factor, + const BlindFactor& amount_blind_factor, + const ConfidentialValue& value_commitment, + const Script& scriptsig_template) + : block_height(block_height), + block_hash(block_hash), + txid(txid), + vout(vout), + locking_script(locking_script), + redeem_script(redeem_script), + address(address), + descriptor(descriptor), + amount(amount), + address_type(address_type), + binary_data(binary_data), + asset(asset), + confidential_address(confidential_address), + asset_blind_factor(asset_blind_factor), + amount_blind_factor(amount_blind_factor), + value_commitment(value_commitment), + scriptsig_template(scriptsig_template) { + // do nothing +} +#else +UtxoData::UtxoData( + uint64_t block_height, const BlockHash& block_hash, const Txid& txid, + uint32_t vout, const Script& locking_script, const Script& redeem_script, + const Address& address, const std::string& descriptor, + const Amount& amount, AddressType address_type, void* binary_data, + const Script& scriptsig_template) + : block_height(block_height), + block_hash(block_hash), + txid(txid), + vout(vout), + locking_script(locking_script), + redeem_script(redeem_script), + address(address), + descriptor(descriptor), + amount(amount), + address_type(address_type), + binary_data(binary_data), + scriptsig_template(scriptsig_template) { + // do nothing +} +#endif // CFD_DISABLE_ELEMENTS + +UtxoData::UtxoData(const UtxoData& object) { + block_height = object.block_height; + block_hash = object.block_hash; + txid = object.txid; + vout = object.vout; + locking_script = object.locking_script; + redeem_script = object.redeem_script; + address = object.address; + descriptor = object.descriptor; + amount = object.amount; + address_type = object.address_type; + binary_data = object.binary_data; +#ifndef CFD_DISABLE_ELEMENTS + asset = object.asset; + confidential_address = object.confidential_address; + asset_blind_factor = object.asset_blind_factor; + amount_blind_factor = object.amount_blind_factor; + value_commitment = object.value_commitment; +#endif // CFD_DISABLE_ELEMENTS + scriptsig_template = object.scriptsig_template; +} + +UtxoData& UtxoData::operator=(const UtxoData& object) & { + if (this != &object) { + block_height = object.block_height; + block_hash = object.block_hash; + txid = object.txid; + vout = object.vout; + locking_script = object.locking_script; + redeem_script = object.redeem_script; + address = object.address; + descriptor = object.descriptor; + amount = object.amount; + address_type = object.address_type; + binary_data = object.binary_data; +#ifndef CFD_DISABLE_ELEMENTS + asset = object.asset; + confidential_address = object.confidential_address; + asset_blind_factor = object.asset_blind_factor; + amount_blind_factor = object.amount_blind_factor; + value_commitment = object.value_commitment; +#endif // CFD_DISABLE_ELEMENTS + scriptsig_template = object.scriptsig_template; + } + return *this; +} + // ----------------------------------------------------------------------------- // UtxoUtil // ----------------------------------------------------------------------------- @@ -80,7 +188,7 @@ std::vector UtxoUtil::ConvertToUtxo(const std::vector& utxos) { void UtxoUtil::ConvertToUtxo( const UtxoData& utxo_data, Utxo* utxo, UtxoData* dest) { if (utxo != nullptr) { - UtxoData output = utxo_data; + UtxoData output(utxo_data); memset(utxo, 0, sizeof(Utxo)); utxo->block_height = utxo_data.block_height; utxo->vout = utxo_data.vout; @@ -126,8 +234,9 @@ void UtxoUtil::ConvertToUtxo( DescriptorScriptReference& script_ref = ref_list[0]; output.locking_script = script_ref.GetLockingScript(); locking_script_bytes = output.locking_script.GetData().GetBytes(); - if (script_ref.GetScriptType() != - DescriptorScriptType::kDescriptorScriptRaw) { + if ((script_ref.GetScriptType() != + DescriptorScriptType::kDescriptorScriptRaw) || + script_ref.HasAddress()) { output.address_type = script_ref.GetAddressType(); output.address = script_ref.GenerateAddress(net_type); if (ref_list[ref_list.size() - 1].HasRedeemScript()) { @@ -141,13 +250,13 @@ void UtxoUtil::ConvertToUtxo( if (!locking_script_bytes.empty()) { // do nothing - } else if (!utxo_data.address.GetAddress().empty()) { - output.locking_script = utxo_data.address.GetLockingScript(); + } else if (!output.address.GetAddress().empty()) { + output.locking_script = output.address.GetLockingScript(); locking_script_bytes = output.locking_script.GetData().GetBytes(); - AddressType addr_type = utxo_data.address.GetAddressType(); + AddressType addr_type = output.address.GetAddressType(); if ((addr_type == AddressType::kP2shAddress) && - ((utxo_data.address_type == AddressType::kP2shP2wshAddress) || - (utxo_data.address_type == AddressType::kP2shP2wpkhAddress))) { + ((output.address_type == AddressType::kP2shP2wshAddress) || + (output.address_type == AddressType::kP2shP2wpkhAddress))) { // direct set. output.address_type; } else { output.address_type = addr_type; @@ -161,6 +270,12 @@ void UtxoUtil::ConvertToUtxo( utxo->address_type = AddressType::kP2wshAddress; } else if (utxo_data.locking_script.IsP2pkhScript()) { utxo->address_type = AddressType::kP2pkhAddress; + } else if (utxo_data.locking_script.IsTaprootScript()) { + utxo->address_type = AddressType::kTaprootAddress; + } else if ( + utxo_data.locking_script.IsWitnessProgram() && + (utxo_data.locking_script.GetElementList()[0].GetNumber() != 0)) { + utxo->address_type = AddressType::kWitnessUnknown; } else { // TODO(k-matsuzawa): unbknown type is convert to p2sh utxo->address_type = AddressType::kP2shAddress; } @@ -315,12 +430,14 @@ SignParameter::SignParameter(const SignParameter& sign_parameter) { } SignParameter& SignParameter::operator=(const SignParameter& sign_parameter) { - data_ = sign_parameter.GetData(); - data_type_ = sign_parameter.GetDataType(); - related_pubkey_ = sign_parameter.GetRelatedPubkey(); - der_encode_ = sign_parameter.IsDerEncode(); - sighash_type_ = sign_parameter.GetSigHashType(); - op_code_ = sign_parameter.GetOpCode(); + if (this != &sign_parameter) { + data_ = sign_parameter.GetData(); + data_type_ = sign_parameter.GetDataType(); + related_pubkey_ = sign_parameter.GetRelatedPubkey(); + der_encode_ = sign_parameter.IsDerEncode(); + sighash_type_ = sign_parameter.GetSigHashType(); + op_code_ = sign_parameter.GetOpCode(); + } return *this; } diff --git a/src/cfd_transaction_internal.cpp b/src/cfd_transaction_internal.cpp index d3cf8813..6c952dc3 100644 --- a/src/cfd_transaction_internal.cpp +++ b/src/cfd_transaction_internal.cpp @@ -19,7 +19,9 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_transaction_common.h" #include "cfdcore/cfdcore_util.h" @@ -35,6 +37,8 @@ using cfd::core::CryptoUtil; using cfd::core::OutPoint; using cfd::core::Privkey; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; +using cfd::core::SchnorrSignature; using cfd::core::Script; using cfd::core::ScriptBuilder; using cfd::core::ScriptElement; @@ -43,6 +47,8 @@ using cfd::core::ScriptUtil; using cfd::core::ScriptWitness; using cfd::core::SigHashType; using cfd::core::SignatureUtil; +using cfd::core::TaprootScriptTree; +using cfd::core::TaprootUtil; using cfd::core::Txid; using cfd::core::logger::warn; @@ -74,6 +80,10 @@ WitnessVersion TransactionContextUtil::CheckSignWithPrivkeySimple( case AddressType::kP2wpkhAddress: case AddressType::kP2shP2wpkhAddress: version = WitnessVersion::kVersion0; + break; + case AddressType::kTaprootAddress: + version = WitnessVersion::kVersion1; + break; default: break; } @@ -250,11 +260,13 @@ void TransactionContextUtil::AddSign( } bool has_op_code = false; - for (const SignParameter& sign_param : sign_params) { - if (sign_param.IsOpCode()) { - ScriptOperator op_code = sign_param.GetOpCode(); - if (op_code.IsPushOperator()) { - has_op_code = true; + if (!insert_witness) { + for (const SignParameter& sign_param : sign_params) { + if (sign_param.IsOpCode()) { + ScriptOperator op_code = sign_param.GetOpCode(); + if (op_code.IsPushOperator()) { + has_op_code = true; + } } } } @@ -297,7 +309,8 @@ void TransactionContextUtil::Verify( const AbstractTxIn* txin, std::function + const Pubkey&, const Script&, WitnessVersion, const ByteData*, + const TaprootScriptTree*)> create_sighash_func) { if (TransactionContextUtil::IsOpTrueLockingScript(utxo, txin)) { return; // OP_TRUE only. @@ -324,14 +337,46 @@ void TransactionContextUtil::Verify( TransactionContextUtil::GetVerifySignatureStack( work_utxo, txin, &version, &has_pubkey); - if (has_pubkey) { + if (version == WitnessVersion::kVersion1) { + SchnorrPubkey witness_program( + utxo.locking_script.GetElementList()[1].GetBinaryData()); + SchnorrSignature schnorr_sig; + bool has_parity = false; + uint8_t leaf_version = 0; + SchnorrPubkey internal_pubkey; + std::vector nodes; + Script tapscript; + ByteData annex; + TaprootUtil::ParseTaprootSignData( + signature_stack, &schnorr_sig, &has_parity, &leaf_version, + &internal_pubkey, &nodes, &tapscript, nullptr, &annex); + if (has_pubkey) { + auto sighash = create_sighash_func( + transaction, outpoint, work_utxo, schnorr_sig.GetSigHashType(), + Pubkey(), Script(), version, &annex, nullptr); + bool is_verify = witness_program.Verify(schnorr_sig, sighash); + if (!is_verify) { + throw CfdException( + CfdError::kCfdIllegalStateError, "Verify signature fail."); + } + } else { + TaprootUtil::VerifyTaprootCommitment( + has_parity, leaf_version, witness_program, internal_pubkey, nodes, + tapscript); + // At the moment, there is no function to run Script, + // so no further confirmation is possible. + throw CfdException( + CfdError::kCfdIllegalStateError, + "The script analysis of tapscript is not supported."); + } + } else if (has_pubkey) { SigHashType sighashtype; ByteData signature = CryptoUtil::ConvertSignatureFromDer(signature_stack[0], &sighashtype); Pubkey pubkey(signature_stack[1]); auto sighash = create_sighash_func( transaction, outpoint, work_utxo, sighashtype, pubkey, Script(), - version); + version, nullptr, nullptr); bool is_verify = SignatureUtil::VerifyEcSignature(sighash, pubkey, signature); if (!is_verify) { @@ -342,6 +387,8 @@ void TransactionContextUtil::Verify( // check script format Script redeem_script(signature_stack.back()); if (!redeem_script.IsMultisigScript()) { + // At the moment, there is no function to run Script, + // so no further confirmation is possible. throw CfdException( CfdError::kCfdIllegalStateError, "Unsupported script format. verify support is multisig only."); @@ -382,7 +429,7 @@ void TransactionContextUtil::Verify( signature_stack[index + 1], &sighashtype); auto sighash = create_sighash_func( transaction, outpoint, work_utxo, sighashtype, Pubkey(), - redeem_script, version); + redeem_script, version, nullptr, nullptr); for (size_t key_index = 0; key_index < pubkeys.size(); ++key_index) { const auto& pubkey = pubkeys[key_index]; is_exist = @@ -491,6 +538,8 @@ std::vector TransactionContextUtil::GetVerifySignatureStack( signature_stack.push_back(build.Build().GetData()); } } + } else if (utxo.address_type == AddressType::kTaprootAddress) { + // do nothing } else { if (items.size() != 1) { throw CfdException( @@ -544,6 +593,29 @@ std::vector TransactionContextUtil::GetVerifySignatureStack( locking_script = ScriptUtil::CreateP2shLockingScript(locking_script); } } + } else if (utxo.address_type == AddressType::kTaprootAddress) { + version = WitnessVersion::kVersion1; + + SchnorrSignature schnorr_sig; + bool has_parity = false; + uint8_t leaf_version = 0; + SchnorrPubkey internal_pubkey; + std::vector nodes; + Script tapscript; + ByteData annex; + + TaprootUtil::ParseTaprootSignData( + signature_stack, &schnorr_sig, &has_parity, &leaf_version, + &internal_pubkey, &nodes, &tapscript, nullptr, &annex); + if (internal_pubkey.IsValid()) { + TaprootScriptTree tree(leaf_version, tapscript); + for (const auto& node : nodes) tree.AddBranch(node); + TaprootUtil::CreateTapScriptControl( + internal_pubkey, tree, nullptr, &locking_script); + } else { + has_pubkey = true; + locking_script = utxo.locking_script; // for ignore check + } } else { // check script format Script redeem_script(signature_stack.back()); @@ -596,7 +668,17 @@ template void TransactionContextUtil::Verify( const UtxoData& utxo, const AbstractTxIn* txin, std::function + const SigHashType&, const Pubkey&, const Script&, WitnessVersion, + const ByteData*, const TaprootScriptTree*)> + create_sighash_func); + +template void TransactionContextUtil::Verify( + const Transaction* transaction, const OutPoint& outpoint, + const UtxoData& utxo, const AbstractTxIn* txin, + std::function create_sighash_func); // ----------------------------------------------------------------------------- @@ -626,7 +708,7 @@ template void TransactionContextUtil::Verify( std::function + WitnessVersion, const ByteData*, const TaprootScriptTree*)> create_sighash_func); #endif // CFD_DISABLE_ELEMENTS diff --git a/src/cfd_transaction_internal.h b/src/cfd_transaction_internal.h index 20925960..47b89ccb 100644 --- a/src/cfd_transaction_internal.h +++ b/src/cfd_transaction_internal.h @@ -109,7 +109,7 @@ class TransactionContextUtil { /** * @brief verify tx sign (signature) on outpoint. - * @param[in,out] transaction Transaction + * @param[in] transaction Transaction * @param[in] outpoint utxo target txin & vout. * @param[in] utxo utxo. * @param[in] txin tx input. @@ -121,7 +121,8 @@ class TransactionContextUtil { const AbstractTxIn* txin, std::function + const Pubkey&, const Script&, WitnessVersion, const ByteData*, + const TaprootScriptTree*)> create_sighash_func); /** diff --git a/src/cfd_utxo.cpp b/src/cfd_utxo.cpp index e2c30193..50b688bb 100644 --- a/src/cfd_utxo.cpp +++ b/src/cfd_utxo.cpp @@ -462,6 +462,7 @@ std::vector CoinSelection::SelectCoinsMinConf( } std::vector utxo_pool; + bool ignore_error = false; if (use_bnb_ && option_params.IsUseBnB()) { // Get long term estimate FeeCalculator long_term_fee(option_params.GetLongTermFeeBaserate()); @@ -476,7 +477,7 @@ std::vector CoinSelection::SelectCoinsMinConf( uint64_t fee = effective_fee.GetFee(*utxo).GetSatoshiValue(); // Only include outputs that are positive effective value (i.e. not dust) - if (utxo->amount > fee) { + if ((utxo->amount > fee) || (!consider_fee)) { uint64_t effective_value = utxo->amount; if (use_fee) { if (consider_fee) { @@ -495,16 +496,19 @@ std::vector CoinSelection::SelectCoinsMinConf( utxo->long_term_fee); #endif if (utxo->long_term_fee > utxo->fee) { - utxo->long_term_fee = utxo->fee; // TODO(k-matsuzawa): 後で見直し + utxo->long_term_fee = utxo->fee; // TODO(k-matsuzawa): Check later } utxo->effective_value = effective_value; + utxo->effective_k_value = static_cast(effective_value); utxo_pool.push_back(utxo); + } else { + ignore_error = true; } } // Calculate the fees for things that aren't inputs std::vector result = SelectCoinsBnB( target_value, utxo_pool, cost_of_change.GetSatoshiValue(), - tx_fee_value, select_value, utxo_fee_value); + tx_fee_value, ignore_error, select_value, utxo_fee_value); if (!result.empty()) { if (searched_bnb) *searched_bnb = true; return result; @@ -513,20 +517,22 @@ std::vector CoinSelection::SelectCoinsMinConf( } // Filter by the min conf specs and add to utxo_pool - // TODO(k-matsuzawa): 現状はフィルタリングしていないためUtxo一覧の再作成不要 + // TODO(k-matsuzawa): Currently it is not filtered, so there is no need to recreate the Utxo list. // NOLINT // for (const OutputGroup& group : groups) { // if (!group.EligibleForSpending(eligibility_filter)) continue; // utxo_pool.push_back(group); // } - if (utxo_pool.empty()) { + if (utxo_pool.empty() || ignore_error) { + utxo_pool.clear(); for (auto& utxo : work_utxos) { if (utxo == nullptr) continue; utxo->fee = (use_fee) ? effective_fee.GetFee(*utxo).GetSatoshiValue() : 0; - if (utxo->amount > utxo->fee) { - utxo->effective_value = utxo->amount - utxo->fee; - utxo_pool.push_back(utxo); + utxo->effective_k_value = static_cast(utxo->amount); + if (consider_fee) { + utxo->effective_k_value -= static_cast(utxo->fee); } + utxo_pool.push_back(utxo); } } int64_t search_value = target_value; @@ -573,7 +579,7 @@ std::vector CoinSelection::SelectCoinsMinConf( std::vector CoinSelection::SelectCoinsBnB( const int64_t& target_value, const std::vector& utxos, const int64_t& cost_of_change, const Amount& not_input_fees, - int64_t* select_value, Amount* utxo_fee_value) { + bool ignore_error, int64_t* select_value, Amount* utxo_fee_value) { info( CFD_LOG_SOURCE, "SelectCoinsBnB start. cost_of_change={}, not_input_fees={}", @@ -612,9 +618,14 @@ std::vector CoinSelection::SelectCoinsBnB( ": curr_available_value={}," "actual_target={}", curr_available_value, actual_target); - throw CfdException( - CfdError::kCfdIllegalStateError, - "Failed to select coin. Not enough utxos."); + if (ignore_error) { // go to knapsack route. + info(CFD_LOG_SOURCE, "SelectCoinsBnB end. results={}", results.size()); + return results; + } else { + throw CfdException( + CfdError::kCfdIllegalStateError, + "Failed to select coin. Not enough utxos."); + } } // Sort the utxos @@ -653,6 +664,9 @@ std::vector CoinSelection::SelectCoinsBnB( best_selection = curr_selection; best_selection.resize(p_utxos.size()); best_waste = curr_waste; + if (best_waste == 0) { + break; + } } curr_waste -= (curr_value - actual_target); //NOLINT Remove the excess value as we will be selecting different coins now @@ -726,22 +740,24 @@ std::vector CoinSelection::KnapsackSolver( const int64_t& target_value, const std::vector& utxos, uint64_t min_change, int64_t* select_value, Amount* utxo_fee_value) { std::vector ret_utxos; - uint64_t n_target = target_value; + int64_t n_target = target_value; + int64_t n_min_change = static_cast(min_change); info(CFD_LOG_SOURCE, "KnapsackSolver start. target={}", n_target); // List of values less than target const Utxo* lowest_larger = nullptr; std::vector applicable_groups; - uint64_t n_total = 0; - uint64_t n_effective_total = 0; // amount excluding fee - uint64_t utxo_fee = 0; + // int64_t n_total = 0; + int64_t n_effective_total = 0; // amount excluding fee + int64_t n_effective_total_max = 0; + int64_t utxo_fee = 0; std::vector indexes = RandomNumberUtil::GetRandomIndexes(static_cast(utxos.size())); for (size_t index = 0; index < indexes.size(); ++index) { // if (utxos[index]->amount == n_target) { - if (utxos[index]->effective_value == n_target) { + if (utxos[index]->effective_k_value == n_target) { // that meets the required value ret_utxos.push_back(*utxos[index]); *select_value = utxos[index]->amount; @@ -749,11 +765,14 @@ std::vector CoinSelection::KnapsackSolver( info(CFD_LOG_SOURCE, "KnapsackSolver end. results={}", ret_utxos.size()); return ret_utxos; - } else if (utxos[index]->effective_value < n_target + min_change) { + } else if (utxos[index]->effective_k_value < n_target + n_min_change) { // } else if ((utxos[index]->amount < n_target + min_change) { applicable_groups.push_back(utxos[index]); - n_total += utxos[index]->amount; - n_effective_total += utxos[index]->effective_value; + // n_total += utxos[index]->amount; + if (utxos[index]->effective_k_value > 0) { + n_effective_total_max += utxos[index]->effective_k_value; + } + n_effective_total += utxos[index]->effective_k_value; } else if ( lowest_larger == nullptr || @@ -778,7 +797,7 @@ std::vector CoinSelection::KnapsackSolver( } // if (n_total < n_target) { - if (n_effective_total < n_target) { + if (n_effective_total_max < n_target) { if (lowest_larger == nullptr) { warn( CFD_LOG_SOURCE, "insufficient funds. effective_total:{} target:{}", @@ -797,20 +816,20 @@ std::vector CoinSelection::KnapsackSolver( std::sort( applicable_groups.begin(), applicable_groups.end(), [](const Utxo* a, const Utxo* b) { - return a->effective_value > b->effective_value; + return a->effective_k_value > b->effective_k_value; }); std::vector vf_best; - uint64_t n_best; + int64_t n_best; randomize_cache_.clear(); ApproximateBestSubset( - applicable_groups, n_effective_total, n_target, &vf_best, &n_best, + applicable_groups, n_effective_total_max, n_target, &vf_best, &n_best, kApproximateBestSubsetIterations); - if (n_best != n_target && n_effective_total >= n_target + min_change) { - uint64_t n_best2 = n_best; + if (n_best != n_target && n_effective_total_max >= n_target + n_min_change) { + int64_t n_best2 = n_best; std::vector vf_best2; ApproximateBestSubset( - applicable_groups, n_effective_total, (n_target + min_change), + applicable_groups, n_effective_total_max, (n_target + n_min_change), &vf_best2, &n_best2, kApproximateBestSubsetIterations); if ((n_best2 == n_target) || (n_best > n_best2)) { n_best = n_best2; @@ -821,8 +840,8 @@ std::vector CoinSelection::KnapsackSolver( // NOLINT If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // NOLINT or the next bigger coin is closer), return the bigger coin if (lowest_larger != nullptr && - ((n_best != n_target && n_best < n_target + min_change) || - lowest_larger->effective_value <= n_best)) { + ((n_best != n_target && n_best < n_target + n_min_change) || + lowest_larger->effective_k_value <= n_best)) { // lowest_larger->amount <= n_best)) { ret_utxos.push_back(*lowest_larger); *select_value = static_cast(lowest_larger->amount); @@ -845,8 +864,8 @@ std::vector CoinSelection::KnapsackSolver( } void CoinSelection::ApproximateBestSubset( - const std::vector& utxos, uint64_t n_total_value, - uint64_t n_target_value, std::vector* vf_best, uint64_t* n_best, + const std::vector& utxos, int64_t n_total_value, + int64_t n_target_value, std::vector* vf_best, int64_t* n_best, int iterations) { if (vf_best == nullptr || n_best == nullptr) { warn(CFD_LOG_SOURCE, "Outparameter(select_value) is nullptr."); @@ -862,7 +881,7 @@ void CoinSelection::ApproximateBestSubset( for (int n_rep = 0; n_rep < iterations && *n_best != n_target_value; n_rep++) { vf_includes.assign(utxos.size(), false); - uint64_t n_total = 0; + int64_t n_total = 0; bool is_reached_target = false; for (int n_pass = 0; n_pass < 2 && !is_reached_target; n_pass++) { for (unsigned int i = 0; i < utxos.size(); i++) { @@ -878,7 +897,7 @@ void CoinSelection::ApproximateBestSubset( } if (rand_bool) { // n_total += utxos[i]->amount; - n_total += utxos[i]->effective_value; + n_total += utxos[i]->effective_k_value; vf_includes[i] = true; if (n_total >= n_target_value) { is_reached_target = true; @@ -887,7 +906,7 @@ void CoinSelection::ApproximateBestSubset( *vf_best = vf_includes; } // n_total -= utxos[i]->amount; - n_total -= utxos[i]->effective_value; + n_total -= utxos[i]->effective_k_value; vf_includes[i] = false; } } diff --git a/src/cfdapi_address.cpp b/src/cfdapi_address.cpp index 9f61b2a7..0c81c3bc 100644 --- a/src/cfdapi_address.cpp +++ b/src/cfdapi_address.cpp @@ -2,8 +2,7 @@ /** * @file cfdapi_address.cpp * - * @brief \~english implementation of address operation that uses cfd-api - * \~japanese cfd-apiで利用するAddress操作の実装ファイル + * @brief implementation of address operation that uses cfd-api */ #include "cfd/cfdapi_address.h" @@ -17,6 +16,7 @@ #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_schnorrsig.h" #include "cfdcore/cfdcore_script.h" namespace cfd { @@ -34,8 +34,10 @@ using cfd::core::DescriptorKeyType; using cfd::core::DescriptorNode; using cfd::core::DescriptorScriptReference; using cfd::core::DescriptorScriptType; +using cfd::core::KeyData; using cfd::core::NetType; using cfd::core::Pubkey; +using cfd::core::SchnorrPubkey; using cfd::core::Script; using cfd::core::ScriptUtil; using cfd::core::WitnessVersion; @@ -48,7 +50,8 @@ Address AddressApi::CreateAddress( if ((pubkey == nullptr) || (!pubkey->IsValid())) { if (address_type == AddressType::kP2pkhAddress || address_type == AddressType::kP2wpkhAddress || - address_type == AddressType::kP2shP2wpkhAddress) { + address_type == AddressType::kP2shP2wpkhAddress || + address_type == AddressType::kTaprootAddress) { warn( CFD_LOG_SOURCE, "Failed to CreateAddress. Invalid pubkey hex: pubkey is empty."); @@ -105,6 +108,11 @@ Address AddressApi::CreateAddress( *redeem_script = temp_script; } break; + case AddressType::kTaprootAddress: + addr = Address( + net_type, WitnessVersion::kVersion1, + SchnorrPubkey::FromPubkey(*pubkey), addr_prefixes); + break; default: warn( CFD_LOG_SOURCE, @@ -235,7 +243,8 @@ DescriptorScriptData AddressApi::ParseOutputDescriptor( const std::string& bip32_derivation_path, std::vector* script_list, std::vector* multisig_key_list, - const std::vector* prefix_list) const { + const std::vector* prefix_list, + std::vector* key_list) const { std::vector addr_prefixes; if (prefix_list == nullptr) { addr_prefixes = cfd::core::GetBitcoinAddressFormatList(); @@ -265,6 +274,12 @@ DescriptorScriptData AddressApi::ParseOutputDescriptor( for (uint32_t index = 0; index < desc.GetNeedArgumentNum(); ++index) { args.push_back(bip32_derivation_path); } + if (key_list != nullptr) { + auto temp_list = desc.GetKeyDataAll(&args); + for (const auto& key : temp_list) { + key_list->push_back(key); + } + } std::vector script_refs = desc.GetReferenceAll(&args); DescriptorNode node = desc.GetNode(); diff --git a/src/cfdapi_coin.cpp b/src/cfdapi_coin.cpp index 9069ede5..bf5a521f 100644 --- a/src/cfdapi_coin.cpp +++ b/src/cfdapi_coin.cpp @@ -67,7 +67,7 @@ void CoinApi::ConvertToUtxo(const UtxoData& utxo_data, Utxo* utxo) const { utxo->address_type = AddressType::kP2wshAddress; } else if (utxo_data.locking_script.IsP2pkhScript()) { utxo->address_type = AddressType::kP2pkhAddress; - } else { // TODO(k-matsuzawa): p2shに丸めておく + } else { // TODO(k-matsuzawa): unbknown type is convert to p2sh utxo->address_type = AddressType::kP2shAddress; } } diff --git a/src/cfdapi_elements_transaction.cpp b/src/cfdapi_elements_transaction.cpp index ab202463..9fa56a86 100644 --- a/src/cfdapi_elements_transaction.cpp +++ b/src/cfdapi_elements_transaction.cpp @@ -577,8 +577,13 @@ Amount ElementsTransactionApi::EstimateFee( uint32_t rangeproof_size_cache = 0; uint32_t asset_count = 0; uint32_t not_witness_count = 0; - ElementsAddressApi address_api; for (const auto& utxo : utxos) { + NetType net_type = NetType::kLiquidV1; + if (!utxo.utxo.address.GetAddress().empty()) { + net_type = utxo.utxo.address.GetNetType(); + } + ElementsAddressFactory factory(net_type); + uint32_t pegin_btc_tx_size = 0; txin_size = 0; wit_size = 0; @@ -590,8 +595,7 @@ Amount ElementsTransactionApi::EstimateFee( // check descriptor std::string descriptor = utxo.utxo.descriptor; // set dummy NetType for getting AddressType. - auto data = - address_api.ParseOutputDescriptor(descriptor, NetType::kLiquidV1, ""); + auto data = factory.ParseOutputDescriptor(descriptor, ""); AddressType addr_type; if (utxo.utxo.address.GetAddress().empty() || @@ -668,7 +672,7 @@ Amount ElementsTransactionApi::EstimateFee( witness_size += wit_size; if (wit_size == 0) ++not_witness_count; } - if ((not_witness_count != 0) && + if ((witness_size != 0) && (not_witness_count != 0) && (not_witness_count < static_cast(utxos.size()))) { // append witness size for p2pkh or p2sh witness_size += not_witness_count * 4; @@ -677,13 +681,20 @@ Amount ElementsTransactionApi::EstimateFee( uint32_t utxo_vsize = AbstractTransaction::GetVsizeFromSize(size, witness_size); + uint32_t tx_witness_size = 0; + uint32_t tx_size = 0; + txc.GetSizeIgnoreTxIn( + is_blind, &tx_witness_size, &tx_size, exponent, minimum_bits, + asset_count); uint32_t tx_vsize = - txc.GetVsizeIgnoreTxIn(is_blind, exponent, minimum_bits, asset_count); + AbstractTransaction::GetVsizeFromSize(tx_size, tx_witness_size); FeeCalculator fee_calc(effective_fee_rate); Amount tx_fee_amount = fee_calc.GetFee(tx_vsize); Amount utxo_fee_amount = fee_calc.GetFee(utxo_vsize); - Amount fee = tx_fee_amount + utxo_fee_amount; + uint32_t total_vsize = AbstractTransaction::GetVsizeFromSize( + tx_size + size, tx_witness_size + witness_size); + Amount fee = fee_calc.GetFee(total_vsize); if (txout_fee) *txout_fee = tx_fee_amount; if (utxo_fee) *utxo_fee = utxo_fee_amount; @@ -829,7 +840,6 @@ void CollectUtxoDataByFundRawTransaction( * @param[in] txin_amount_map txin amount map * @param[in] tx_amount_map tx amount map * @param[in] target_values target amounts - * @param[in] input_amount_map input amount map * @param[in] input_max_map input max amount map * @param[in] selected_coins select utxo list * @param[in] utxodata_list utxo list @@ -851,7 +861,6 @@ void CalculateFeeAndFundTransaction( const std::map& txin_amount_map, const std::map& tx_amount_map, const std::map& target_values, - const std::map& input_amount_map, const std::map& input_max_map, const std::vector& selected_coins, const std::vector& utxodata_list, @@ -907,7 +916,7 @@ void CalculateFeeAndFundTransaction( int64_t txin_amount = 0; int64_t tx_amount = 0; int64_t target_value = 0; - int64_t utxo_value = 0; + // int64_t utxo_value = 0; int64_t max_utxo_value = 0; if (txin_amount_map.find(fee_asset_str) != txin_amount_map.end()) txin_amount = txin_amount_map.at(fee_asset_str); @@ -915,8 +924,6 @@ void CalculateFeeAndFundTransaction( tx_amount = tx_amount_map.at(fee_asset_str); if (target_values.find(fee_asset_str) != target_values.end()) target_value = target_values.at(fee_asset_str); - if (input_amount_map.find(fee_asset_str) != input_amount_map.end()) - utxo_value = input_amount_map.at(fee_asset_str); if (input_max_map.find(fee_asset_str) != input_max_map.end()) max_utxo_value = input_max_map.at(fee_asset_str); @@ -1315,8 +1322,8 @@ ConfidentialTransactionController ElementsTransactionApi::FundRawTransaction( if (use_fee) { CalculateFeeAndFundTransaction( *this, addr_factory, txin_amount_map, tx_amount_map, target_values, - input_amount_map, input_max_map, selected_coins, utxodata_list, - fee_asset, selected_txin_utxos, reserve_txout_address, net_type, + input_max_map, selected_coins, utxodata_list, fee_asset, + selected_txin_utxos, reserve_txout_address, net_type, is_blind_estimate_fee, utxo_filter, option, utxo_list, &ctxc, append_txout_addresses, estimate_fee); } diff --git a/src/cfdapi_transaction.cpp b/src/cfdapi_transaction.cpp index d12778ce..767fa0ec 100644 --- a/src/cfdapi_transaction.cpp +++ b/src/cfdapi_transaction.cpp @@ -27,6 +27,7 @@ #include "cfdcore/cfdcore_logger.h" #include "cfdcore/cfdcore_script.h" #include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_transaction_common.h" #include "cfdcore/cfdcore_util.h" namespace cfd { @@ -35,6 +36,7 @@ namespace api { using cfd::FeeCalculator; using cfd::TransactionController; using cfd::api::TransactionApiBase; +using cfd::core::AbstractTransaction; using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; @@ -211,25 +213,23 @@ TransactionController TransactionApi::AddMultisigSign( Amount TransactionApi::EstimateFee( const std::string& tx_hex, const std::vector& utxos, Amount* txout_fee, Amount* utxo_fee, double effective_fee_rate) const { - TransactionController txc(tx_hex); - - uint32_t tx_vsize = txc.GetVsizeIgnoreTxIn(); + TransactionContext txc(tx_hex); uint32_t size = 0; uint32_t witness_size = 0; uint32_t wit_size = 0; uint32_t nowit_size = 0; uint32_t not_witness_count = 0; - AddressApi address_api; for (const auto& utxo : utxos) { NetType net_type = NetType::kMainnet; if (!utxo.address.GetAddress().empty()) { net_type = utxo.address.GetNetType(); } + AddressFactory address_factory(net_type); // check descriptor std::string descriptor = utxo.descriptor; // set dummy NetType for getting AddressType. - auto data = address_api.ParseOutputDescriptor(descriptor, net_type, ""); + auto data = address_factory.ParseOutputDescriptor(descriptor); AddressType addr_type; if (utxo.address.GetAddress().empty() || @@ -247,7 +247,9 @@ Amount TransactionApi::EstimateFee( redeem_script = utxo.redeem_script; } const Script* scriptsig_template = nullptr; - if ((!redeem_script.IsEmpty()) && (!utxo.scriptsig_template.IsEmpty())) { + if (((!redeem_script.IsEmpty()) || + (addr_type == AddressType::kTaprootAddress)) && + (!utxo.scriptsig_template.IsEmpty())) { scriptsig_template = &utxo.scriptsig_template; } @@ -257,7 +259,11 @@ Amount TransactionApi::EstimateFee( witness_size += wit_size; if (wit_size == 0) ++not_witness_count; } - if ((not_witness_count != 0) && + + uint32_t tx_size = txc.GetSizeIgnoreTxIn((witness_size != 0)); + uint32_t tx_vsize = AbstractTransaction::GetVsizeFromSize(tx_size, 0); + + if ((witness_size != 0) && (not_witness_count != 0) && (not_witness_count < static_cast(utxos.size()))) { // append witness size for p2pkh or p2sh witness_size += not_witness_count; @@ -270,7 +276,10 @@ Amount TransactionApi::EstimateFee( FeeCalculator fee_calc(fee_rate); Amount tx_fee_amount = fee_calc.GetFee(tx_vsize); Amount utxo_fee_amount = fee_calc.GetFee(utxo_vsize); - Amount fee = tx_fee_amount + utxo_fee_amount; + uint32_t total_size = tx_size + size; + uint32_t total_vsize = + AbstractTransaction::GetVsizeFromSize(total_size, witness_size); + Amount fee = fee_calc.GetFee(total_vsize); if (txout_fee) *txout_fee = tx_fee_amount; if (utxo_fee) *utxo_fee = utxo_fee_amount; diff --git a/src/jsonapi/autogen/cfd_api_json_autogen.cpp b/src/jsonapi/autogen/cfd_api_json_autogen.cpp index 1d9d9223..9f9fafe2 100644 --- a/src/jsonapi/autogen/cfd_api_json_autogen.cpp +++ b/src/jsonapi/autogen/cfd_api_json_autogen.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfd_api_json_autogen.cpp * @@ -22,54 +22,72 @@ using cfd::core::JsonVector; // @formatter:off // ------------------------------------------------------------------------ -// DecodeRawTransactionRequest +// DecodeLockingScript // ------------------------------------------------------------------------ -cfd::core::JsonTableMap - DecodeRawTransactionRequest::json_mapper; -std::vector DecodeRawTransactionRequest::item_list; +cfd::core::JsonTableMap + DecodeLockingScript::json_mapper; +std::vector DecodeLockingScript::item_list; -void DecodeRawTransactionRequest::CollectFieldName() { +void DecodeLockingScript::CollectFieldName() { if (!json_mapper.empty()) { return; } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT func_table = { - DecodeRawTransactionRequest::GetHexString, - DecodeRawTransactionRequest::SetHexString, - DecodeRawTransactionRequest::GetHexFieldType, + DecodeLockingScript::GetAsmString, + DecodeLockingScript::SetAsmString, + DecodeLockingScript::GetAsmFieldType, + }; + json_mapper.emplace("asm", func_table); + item_list.push_back("asm"); + func_table = { + DecodeLockingScript::GetHexString, + DecodeLockingScript::SetHexString, + DecodeLockingScript::GetHexFieldType, }; json_mapper.emplace("hex", func_table); item_list.push_back("hex"); func_table = { - DecodeRawTransactionRequest::GetNetworkString, - DecodeRawTransactionRequest::SetNetworkString, - DecodeRawTransactionRequest::GetNetworkFieldType, + DecodeLockingScript::GetReqSigsString, + DecodeLockingScript::SetReqSigsString, + DecodeLockingScript::GetReqSigsFieldType, }; - json_mapper.emplace("network", func_table); - item_list.push_back("network"); + json_mapper.emplace("reqSigs", func_table); + item_list.push_back("reqSigs"); func_table = { - DecodeRawTransactionRequest::GetIswitnessString, - DecodeRawTransactionRequest::SetIswitnessString, - DecodeRawTransactionRequest::GetIswitnessFieldType, + DecodeLockingScript::GetTypeString, + DecodeLockingScript::SetTypeString, + DecodeLockingScript::GetTypeFieldType, }; - json_mapper.emplace("iswitness", func_table); - item_list.push_back("iswitness"); + json_mapper.emplace("type", func_table); + item_list.push_back("type"); + func_table = { + DecodeLockingScript::GetAddressesString, + DecodeLockingScript::SetAddressesString, + DecodeLockingScript::GetAddressesFieldType, + }; + json_mapper.emplace("addresses", func_table); + item_list.push_back("addresses"); } -void DecodeRawTransactionRequest::ConvertFromStruct( - const DecodeRawTransactionRequestStruct& data) { +void DecodeLockingScript::ConvertFromStruct( + const DecodeLockingScriptStruct& data) { + asm__ = data.asm_; hex_ = data.hex; - network_ = data.network; - iswitness_ = data.iswitness; + req_sigs_ = data.req_sigs; + type_ = data.type; + addresses_.ConvertFromStruct(data.addresses); ignore_items = data.ignore_items; } -DecodeRawTransactionRequestStruct DecodeRawTransactionRequest::ConvertToStruct() const { // NOLINT - DecodeRawTransactionRequestStruct result; +DecodeLockingScriptStruct DecodeLockingScript::ConvertToStruct() const { // NOLINT + DecodeLockingScriptStruct result; + result.asm_ = asm__; result.hex = hex_; - result.network = network_; - result.iswitness = iswitness_; + result.req_sigs = req_sigs_; + result.type = type_; + result.addresses = addresses_.ConvertToStruct(); result.ignore_items = ignore_items; return result; } @@ -118,6 +136,68 @@ DecodeUnlockingScriptStruct DecodeUnlockingScript::ConvertToStruct() const { // return result; } +// ------------------------------------------------------------------------ +// DecodePsbtLockingScript +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtLockingScript::json_mapper; +std::vector DecodePsbtLockingScript::item_list; + +void DecodePsbtLockingScript::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtLockingScript::GetAsmString, + DecodePsbtLockingScript::SetAsmString, + DecodePsbtLockingScript::GetAsmFieldType, + }; + json_mapper.emplace("asm", func_table); + item_list.push_back("asm"); + func_table = { + DecodePsbtLockingScript::GetHexString, + DecodePsbtLockingScript::SetHexString, + DecodePsbtLockingScript::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); + func_table = { + DecodePsbtLockingScript::GetTypeString, + DecodePsbtLockingScript::SetTypeString, + DecodePsbtLockingScript::GetTypeFieldType, + }; + json_mapper.emplace("type", func_table); + item_list.push_back("type"); + func_table = { + DecodePsbtLockingScript::GetAddressString, + DecodePsbtLockingScript::SetAddressString, + DecodePsbtLockingScript::GetAddressFieldType, + }; + json_mapper.emplace("address", func_table); + item_list.push_back("address"); +} + +void DecodePsbtLockingScript::ConvertFromStruct( + const DecodePsbtLockingScriptStruct& data) { + asm__ = data.asm_; + hex_ = data.hex; + type_ = data.type; + address_ = data.address; + ignore_items = data.ignore_items; +} + +DecodePsbtLockingScriptStruct DecodePsbtLockingScript::ConvertToStruct() const { // NOLINT + DecodePsbtLockingScriptStruct result; + result.asm_ = asm__; + result.hex = hex_; + result.type = type_; + result.address = address_; + result.ignore_items = ignore_items; + return result; +} + // ------------------------------------------------------------------------ // DecodeRawTransactionTxIn // ------------------------------------------------------------------------ @@ -198,77 +278,6 @@ DecodeRawTransactionTxInStruct DecodeRawTransactionTxIn::ConvertToStruct() const return result; } -// ------------------------------------------------------------------------ -// DecodeLockingScript -// ------------------------------------------------------------------------ -cfd::core::JsonTableMap - DecodeLockingScript::json_mapper; -std::vector DecodeLockingScript::item_list; - -void DecodeLockingScript::CollectFieldName() { - if (!json_mapper.empty()) { - return; - } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT - - func_table = { - DecodeLockingScript::GetAsmString, - DecodeLockingScript::SetAsmString, - DecodeLockingScript::GetAsmFieldType, - }; - json_mapper.emplace("asm", func_table); - item_list.push_back("asm"); - func_table = { - DecodeLockingScript::GetHexString, - DecodeLockingScript::SetHexString, - DecodeLockingScript::GetHexFieldType, - }; - json_mapper.emplace("hex", func_table); - item_list.push_back("hex"); - func_table = { - DecodeLockingScript::GetReqSigsString, - DecodeLockingScript::SetReqSigsString, - DecodeLockingScript::GetReqSigsFieldType, - }; - json_mapper.emplace("reqSigs", func_table); - item_list.push_back("reqSigs"); - func_table = { - DecodeLockingScript::GetTypeString, - DecodeLockingScript::SetTypeString, - DecodeLockingScript::GetTypeFieldType, - }; - json_mapper.emplace("type", func_table); - item_list.push_back("type"); - func_table = { - DecodeLockingScript::GetAddressesString, - DecodeLockingScript::SetAddressesString, - DecodeLockingScript::GetAddressesFieldType, - }; - json_mapper.emplace("addresses", func_table); - item_list.push_back("addresses"); -} - -void DecodeLockingScript::ConvertFromStruct( - const DecodeLockingScriptStruct& data) { - asm__ = data.asm_; - hex_ = data.hex; - req_sigs_ = data.req_sigs; - type_ = data.type; - addresses_.ConvertFromStruct(data.addresses); - ignore_items = data.ignore_items; -} - -DecodeLockingScriptStruct DecodeLockingScript::ConvertToStruct() const { // NOLINT - DecodeLockingScriptStruct result; - result.asm_ = asm__; - result.hex = hex_; - result.req_sigs = req_sigs_; - result.type = type_; - result.addresses = addresses_.ConvertToStruct(); - result.ignore_items = ignore_items; - return result; -} - // ------------------------------------------------------------------------ // DecodeRawTransactionTxOut // ------------------------------------------------------------------------ @@ -322,6 +331,50 @@ DecodeRawTransactionTxOutStruct DecodeRawTransactionTxOut::ConvertToStruct() con return result; } +// ------------------------------------------------------------------------ +// DecodePsbtUtxo +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtUtxo::json_mapper; +std::vector DecodePsbtUtxo::item_list; + +void DecodePsbtUtxo::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtUtxo::GetAmountString, + DecodePsbtUtxo::SetAmountString, + DecodePsbtUtxo::GetAmountFieldType, + }; + json_mapper.emplace("amount", func_table); + item_list.push_back("amount"); + func_table = { + DecodePsbtUtxo::GetScriptPubKeyString, + DecodePsbtUtxo::SetScriptPubKeyString, + DecodePsbtUtxo::GetScriptPubKeyFieldType, + }; + json_mapper.emplace("scriptPubKey", func_table); + item_list.push_back("scriptPubKey"); +} + +void DecodePsbtUtxo::ConvertFromStruct( + const DecodePsbtUtxoStruct& data) { + amount_ = data.amount; + script_pub_key_.ConvertFromStruct(data.script_pub_key); + ignore_items = data.ignore_items; +} + +DecodePsbtUtxoStruct DecodePsbtUtxo::ConvertToStruct() const { // NOLINT + DecodePsbtUtxoStruct result; + result.amount = amount_; + result.script_pub_key = script_pub_key_.ConvertToStruct(); + result.ignore_items = ignore_items; + return result; +} + // ------------------------------------------------------------------------ // DecodeRawTransactionResponse // ------------------------------------------------------------------------ @@ -430,161 +483,46 @@ DecodeRawTransactionResponseStruct DecodeRawTransactionResponse::ConvertToStruct } // ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionRequest +// ElementsDecodeIssuance // ------------------------------------------------------------------------ -cfd::core::JsonTableMap - ElementsDecodeRawTransactionRequest::json_mapper; -std::vector ElementsDecodeRawTransactionRequest::item_list; +cfd::core::JsonTableMap + ElementsDecodeIssuance::json_mapper; +std::vector ElementsDecodeIssuance::item_list; -void ElementsDecodeRawTransactionRequest::CollectFieldName() { +void ElementsDecodeIssuance::CollectFieldName() { if (!json_mapper.empty()) { return; } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT func_table = { - ElementsDecodeRawTransactionRequest::GetHexString, - ElementsDecodeRawTransactionRequest::SetHexString, - ElementsDecodeRawTransactionRequest::GetHexFieldType, + ElementsDecodeIssuance::GetAssetBlindingNonceString, + ElementsDecodeIssuance::SetAssetBlindingNonceString, + ElementsDecodeIssuance::GetAssetBlindingNonceFieldType, }; - json_mapper.emplace("hex", func_table); - item_list.push_back("hex"); + json_mapper.emplace("assetBlindingNonce", func_table); + item_list.push_back("assetBlindingNonce"); func_table = { - ElementsDecodeRawTransactionRequest::GetNetworkString, - ElementsDecodeRawTransactionRequest::SetNetworkString, - ElementsDecodeRawTransactionRequest::GetNetworkFieldType, + ElementsDecodeIssuance::GetAssetEntropyString, + ElementsDecodeIssuance::SetAssetEntropyString, + ElementsDecodeIssuance::GetAssetEntropyFieldType, }; - json_mapper.emplace("network", func_table); - item_list.push_back("network"); + json_mapper.emplace("assetEntropy", func_table); + item_list.push_back("assetEntropy"); func_table = { - ElementsDecodeRawTransactionRequest::GetMainchainNetworkString, - ElementsDecodeRawTransactionRequest::SetMainchainNetworkString, - ElementsDecodeRawTransactionRequest::GetMainchainNetworkFieldType, + ElementsDecodeIssuance::GetContractHashString, + ElementsDecodeIssuance::SetContractHashString, + ElementsDecodeIssuance::GetContractHashFieldType, }; - json_mapper.emplace("mainchainNetwork", func_table); - item_list.push_back("mainchainNetwork"); + json_mapper.emplace("contractHash", func_table); + item_list.push_back("contractHash"); func_table = { - ElementsDecodeRawTransactionRequest::GetIswitnessString, - ElementsDecodeRawTransactionRequest::SetIswitnessString, - ElementsDecodeRawTransactionRequest::GetIswitnessFieldType, + ElementsDecodeIssuance::GetIsreissuanceString, + ElementsDecodeIssuance::SetIsreissuanceString, + ElementsDecodeIssuance::GetIsreissuanceFieldType, }; - json_mapper.emplace("iswitness", func_table); - item_list.push_back("iswitness"); - func_table = { - ElementsDecodeRawTransactionRequest::GetFullDumpString, - ElementsDecodeRawTransactionRequest::SetFullDumpString, - ElementsDecodeRawTransactionRequest::GetFullDumpFieldType, - }; - json_mapper.emplace("fullDump", func_table); - item_list.push_back("fullDump"); -} - -void ElementsDecodeRawTransactionRequest::ConvertFromStruct( - const ElementsDecodeRawTransactionRequestStruct& data) { - hex_ = data.hex; - network_ = data.network; - mainchain_network_ = data.mainchain_network; - iswitness_ = data.iswitness; - full_dump_ = data.full_dump; - ignore_items = data.ignore_items; -} - -ElementsDecodeRawTransactionRequestStruct ElementsDecodeRawTransactionRequest::ConvertToStruct() const { // NOLINT - ElementsDecodeRawTransactionRequestStruct result; - result.hex = hex_; - result.network = network_; - result.mainchain_network = mainchain_network_; - result.iswitness = iswitness_; - result.full_dump = full_dump_; - result.ignore_items = ignore_items; - return result; -} - -// ------------------------------------------------------------------------ -// ElementsDecodeUnlockingScript -// ------------------------------------------------------------------------ -cfd::core::JsonTableMap - ElementsDecodeUnlockingScript::json_mapper; -std::vector ElementsDecodeUnlockingScript::item_list; - -void ElementsDecodeUnlockingScript::CollectFieldName() { - if (!json_mapper.empty()) { - return; - } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT - - func_table = { - ElementsDecodeUnlockingScript::GetAsmString, - ElementsDecodeUnlockingScript::SetAsmString, - ElementsDecodeUnlockingScript::GetAsmFieldType, - }; - json_mapper.emplace("asm", func_table); - item_list.push_back("asm"); - func_table = { - ElementsDecodeUnlockingScript::GetHexString, - ElementsDecodeUnlockingScript::SetHexString, - ElementsDecodeUnlockingScript::GetHexFieldType, - }; - json_mapper.emplace("hex", func_table); - item_list.push_back("hex"); -} - -void ElementsDecodeUnlockingScript::ConvertFromStruct( - const ElementsDecodeUnlockingScriptStruct& data) { - asm__ = data.asm_; - hex_ = data.hex; - ignore_items = data.ignore_items; -} - -ElementsDecodeUnlockingScriptStruct ElementsDecodeUnlockingScript::ConvertToStruct() const { // NOLINT - ElementsDecodeUnlockingScriptStruct result; - result.asm_ = asm__; - result.hex = hex_; - result.ignore_items = ignore_items; - return result; -} - -// ------------------------------------------------------------------------ -// ElementsDecodeIssuance -// ------------------------------------------------------------------------ -cfd::core::JsonTableMap - ElementsDecodeIssuance::json_mapper; -std::vector ElementsDecodeIssuance::item_list; - -void ElementsDecodeIssuance::CollectFieldName() { - if (!json_mapper.empty()) { - return; - } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT - - func_table = { - ElementsDecodeIssuance::GetAssetBlindingNonceString, - ElementsDecodeIssuance::SetAssetBlindingNonceString, - ElementsDecodeIssuance::GetAssetBlindingNonceFieldType, - }; - json_mapper.emplace("assetBlindingNonce", func_table); - item_list.push_back("assetBlindingNonce"); - func_table = { - ElementsDecodeIssuance::GetAssetEntropyString, - ElementsDecodeIssuance::SetAssetEntropyString, - ElementsDecodeIssuance::GetAssetEntropyFieldType, - }; - json_mapper.emplace("assetEntropy", func_table); - item_list.push_back("assetEntropy"); - func_table = { - ElementsDecodeIssuance::GetContractHashString, - ElementsDecodeIssuance::SetContractHashString, - ElementsDecodeIssuance::GetContractHashFieldType, - }; - json_mapper.emplace("contractHash", func_table); - item_list.push_back("contractHash"); - func_table = { - ElementsDecodeIssuance::GetIsreissuanceString, - ElementsDecodeIssuance::SetIsreissuanceString, - ElementsDecodeIssuance::GetIsreissuanceFieldType, - }; - json_mapper.emplace("isreissuance", func_table); - item_list.push_back("isreissuance"); + json_mapper.emplace("isreissuance", func_table); + item_list.push_back("isreissuance"); func_table = { ElementsDecodeIssuance::GetTokenString, ElementsDecodeIssuance::SetTokenString, @@ -678,113 +616,6 @@ ElementsDecodeIssuanceStruct ElementsDecodeIssuance::ConvertToStruct() const { return result; } -// ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionTxIn -// ------------------------------------------------------------------------ -cfd::core::JsonTableMap - ElementsDecodeRawTransactionTxIn::json_mapper; -std::vector ElementsDecodeRawTransactionTxIn::item_list; - -void ElementsDecodeRawTransactionTxIn::CollectFieldName() { - if (!json_mapper.empty()) { - return; - } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT - - func_table = { - ElementsDecodeRawTransactionTxIn::GetCoinbaseString, - ElementsDecodeRawTransactionTxIn::SetCoinbaseString, - ElementsDecodeRawTransactionTxIn::GetCoinbaseFieldType, - }; - json_mapper.emplace("coinbase", func_table); - item_list.push_back("coinbase"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetTxidString, - ElementsDecodeRawTransactionTxIn::SetTxidString, - ElementsDecodeRawTransactionTxIn::GetTxidFieldType, - }; - json_mapper.emplace("txid", func_table); - item_list.push_back("txid"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetVoutString, - ElementsDecodeRawTransactionTxIn::SetVoutString, - ElementsDecodeRawTransactionTxIn::GetVoutFieldType, - }; - json_mapper.emplace("vout", func_table); - item_list.push_back("vout"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetScriptSigString, - ElementsDecodeRawTransactionTxIn::SetScriptSigString, - ElementsDecodeRawTransactionTxIn::GetScriptSigFieldType, - }; - json_mapper.emplace("scriptSig", func_table); - item_list.push_back("scriptSig"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetIs_peginString, - ElementsDecodeRawTransactionTxIn::SetIs_peginString, - ElementsDecodeRawTransactionTxIn::GetIs_peginFieldType, - }; - json_mapper.emplace("is_pegin", func_table); - item_list.push_back("is_pegin"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetSequenceString, - ElementsDecodeRawTransactionTxIn::SetSequenceString, - ElementsDecodeRawTransactionTxIn::GetSequenceFieldType, - }; - json_mapper.emplace("sequence", func_table); - item_list.push_back("sequence"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetTxinwitnessString, - ElementsDecodeRawTransactionTxIn::SetTxinwitnessString, - ElementsDecodeRawTransactionTxIn::GetTxinwitnessFieldType, - }; - json_mapper.emplace("txinwitness", func_table); - item_list.push_back("txinwitness"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetPegin_witnessString, - ElementsDecodeRawTransactionTxIn::SetPegin_witnessString, - ElementsDecodeRawTransactionTxIn::GetPegin_witnessFieldType, - }; - json_mapper.emplace("pegin_witness", func_table); - item_list.push_back("pegin_witness"); - func_table = { - ElementsDecodeRawTransactionTxIn::GetIssuanceString, - ElementsDecodeRawTransactionTxIn::SetIssuanceString, - ElementsDecodeRawTransactionTxIn::GetIssuanceFieldType, - }; - json_mapper.emplace("issuance", func_table); - item_list.push_back("issuance"); -} - -void ElementsDecodeRawTransactionTxIn::ConvertFromStruct( - const ElementsDecodeRawTransactionTxInStruct& data) { - coinbase_ = data.coinbase; - txid_ = data.txid; - vout_ = data.vout; - script_sig_.ConvertFromStruct(data.script_sig); - is_pegin_ = data.is_pegin; - sequence_ = data.sequence; - txinwitness_.ConvertFromStruct(data.txinwitness); - pegin_witness_.ConvertFromStruct(data.pegin_witness); - issuance_.ConvertFromStruct(data.issuance); - ignore_items = data.ignore_items; -} - -ElementsDecodeRawTransactionTxInStruct ElementsDecodeRawTransactionTxIn::ConvertToStruct() const { // NOLINT - ElementsDecodeRawTransactionTxInStruct result; - result.coinbase = coinbase_; - result.txid = txid_; - result.vout = vout_; - result.script_sig = script_sig_.ConvertToStruct(); - result.is_pegin = is_pegin_; - result.sequence = sequence_; - result.txinwitness = txinwitness_.ConvertToStruct(); - result.pegin_witness = pegin_witness_.ConvertToStruct(); - result.issuance = issuance_.ConvertToStruct(); - result.ignore_items = ignore_items; - return result; -} - // ------------------------------------------------------------------------ // ElementsDecodeLockingScript // ------------------------------------------------------------------------ @@ -911,36 +742,621 @@ ElementsDecodeLockingScriptStruct ElementsDecodeLockingScript::ConvertToStruct() } // ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionTxOut +// ElementsDecodeUnlockingScript // ------------------------------------------------------------------------ -cfd::core::JsonTableMap - ElementsDecodeRawTransactionTxOut::json_mapper; -std::vector ElementsDecodeRawTransactionTxOut::item_list; +cfd::core::JsonTableMap + ElementsDecodeUnlockingScript::json_mapper; +std::vector ElementsDecodeUnlockingScript::item_list; -void ElementsDecodeRawTransactionTxOut::CollectFieldName() { +void ElementsDecodeUnlockingScript::CollectFieldName() { if (!json_mapper.empty()) { return; } - cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT func_table = { - ElementsDecodeRawTransactionTxOut::GetValueString, - ElementsDecodeRawTransactionTxOut::SetValueString, - ElementsDecodeRawTransactionTxOut::GetValueFieldType, - }; - json_mapper.emplace("value", func_table); - item_list.push_back("value"); - func_table = { - ElementsDecodeRawTransactionTxOut::GetValue_minimumString, - ElementsDecodeRawTransactionTxOut::SetValue_minimumString, - ElementsDecodeRawTransactionTxOut::GetValue_minimumFieldType, + ElementsDecodeUnlockingScript::GetAsmString, + ElementsDecodeUnlockingScript::SetAsmString, + ElementsDecodeUnlockingScript::GetAsmFieldType, }; - json_mapper.emplace("value-minimum", func_table); - item_list.push_back("value-minimum"); + json_mapper.emplace("asm", func_table); + item_list.push_back("asm"); func_table = { - ElementsDecodeRawTransactionTxOut::GetValue_maximumString, - ElementsDecodeRawTransactionTxOut::SetValue_maximumString, - ElementsDecodeRawTransactionTxOut::GetValue_maximumFieldType, + ElementsDecodeUnlockingScript::GetHexString, + ElementsDecodeUnlockingScript::SetHexString, + ElementsDecodeUnlockingScript::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); +} + +void ElementsDecodeUnlockingScript::ConvertFromStruct( + const ElementsDecodeUnlockingScriptStruct& data) { + asm__ = data.asm_; + hex_ = data.hex; + ignore_items = data.ignore_items; +} + +ElementsDecodeUnlockingScriptStruct ElementsDecodeUnlockingScript::ConvertToStruct() const { // NOLINT + ElementsDecodeUnlockingScriptStruct result; + result.asm_ = asm__; + result.hex = hex_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// PsbtBip32Data +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + PsbtBip32Data::json_mapper; +std::vector PsbtBip32Data::item_list; + +void PsbtBip32Data::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + PsbtBip32Data::GetPubkeyString, + PsbtBip32Data::SetPubkeyString, + PsbtBip32Data::GetPubkeyFieldType, + }; + json_mapper.emplace("pubkey", func_table); + item_list.push_back("pubkey"); + func_table = { + PsbtBip32Data::GetMaster_fingerprintString, + PsbtBip32Data::SetMaster_fingerprintString, + PsbtBip32Data::GetMaster_fingerprintFieldType, + }; + json_mapper.emplace("master_fingerprint", func_table); + item_list.push_back("master_fingerprint"); + func_table = { + PsbtBip32Data::GetPathString, + PsbtBip32Data::SetPathString, + PsbtBip32Data::GetPathFieldType, + }; + json_mapper.emplace("path", func_table); + item_list.push_back("path"); + func_table = { + PsbtBip32Data::GetDescriptorString, + PsbtBip32Data::SetDescriptorString, + PsbtBip32Data::GetDescriptorFieldType, + }; + json_mapper.emplace("descriptor", func_table); + item_list.push_back("descriptor"); +} + +void PsbtBip32Data::ConvertFromStruct( + const PsbtBip32DataStruct& data) { + pubkey_ = data.pubkey; + master_fingerprint_ = data.master_fingerprint; + path_ = data.path; + descriptor_ = data.descriptor; + ignore_items = data.ignore_items; +} + +PsbtBip32DataStruct PsbtBip32Data::ConvertToStruct() const { // NOLINT + PsbtBip32DataStruct result; + result.pubkey = pubkey_; + result.master_fingerprint = master_fingerprint_; + result.path = path_; + result.descriptor = descriptor_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// PsbtMapData +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + PsbtMapData::json_mapper; +std::vector PsbtMapData::item_list; + +void PsbtMapData::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + PsbtMapData::GetKeyString, + PsbtMapData::SetKeyString, + PsbtMapData::GetKeyFieldType, + }; + json_mapper.emplace("key", func_table); + item_list.push_back("key"); + func_table = { + PsbtMapData::GetValueString, + PsbtMapData::SetValueString, + PsbtMapData::GetValueFieldType, + }; + json_mapper.emplace("value", func_table); + item_list.push_back("value"); +} + +void PsbtMapData::ConvertFromStruct( + const PsbtMapDataStruct& data) { + key_ = data.key; + value_ = data.value; + ignore_items = data.ignore_items; +} + +PsbtMapDataStruct PsbtMapData::ConvertToStruct() const { // NOLINT + PsbtMapDataStruct result; + result.key = key_; + result.value = value_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// PsbtScriptData +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + PsbtScriptData::json_mapper; +std::vector PsbtScriptData::item_list; + +void PsbtScriptData::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + PsbtScriptData::GetAsmString, + PsbtScriptData::SetAsmString, + PsbtScriptData::GetAsmFieldType, + }; + json_mapper.emplace("asm", func_table); + item_list.push_back("asm"); + func_table = { + PsbtScriptData::GetHexString, + PsbtScriptData::SetHexString, + PsbtScriptData::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); + func_table = { + PsbtScriptData::GetTypeString, + PsbtScriptData::SetTypeString, + PsbtScriptData::GetTypeFieldType, + }; + json_mapper.emplace("type", func_table); + item_list.push_back("type"); +} + +void PsbtScriptData::ConvertFromStruct( + const PsbtScriptDataStruct& data) { + asm__ = data.asm_; + hex_ = data.hex; + type_ = data.type; + ignore_items = data.ignore_items; +} + +PsbtScriptDataStruct PsbtScriptData::ConvertToStruct() const { // NOLINT + PsbtScriptDataStruct result; + result.asm_ = asm__; + result.hex = hex_; + result.type = type_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// PsbtSignatureData +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + PsbtSignatureData::json_mapper; +std::vector PsbtSignatureData::item_list; + +void PsbtSignatureData::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + PsbtSignatureData::GetPubkeyString, + PsbtSignatureData::SetPubkeyString, + PsbtSignatureData::GetPubkeyFieldType, + }; + json_mapper.emplace("pubkey", func_table); + item_list.push_back("pubkey"); + func_table = { + PsbtSignatureData::GetSignatureString, + PsbtSignatureData::SetSignatureString, + PsbtSignatureData::GetSignatureFieldType, + }; + json_mapper.emplace("signature", func_table); + item_list.push_back("signature"); +} + +void PsbtSignatureData::ConvertFromStruct( + const PsbtSignatureDataStruct& data) { + pubkey_ = data.pubkey; + signature_ = data.signature; + ignore_items = data.ignore_items; +} + +PsbtSignatureDataStruct PsbtSignatureData::ConvertToStruct() const { // NOLINT + PsbtSignatureDataStruct result; + result.pubkey = pubkey_; + result.signature = signature_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// XpubData +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + XpubData::json_mapper; +std::vector XpubData::item_list; + +void XpubData::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + XpubData::GetBase58String, + XpubData::SetBase58String, + XpubData::GetBase58FieldType, + }; + json_mapper.emplace("base58", func_table); + item_list.push_back("base58"); + func_table = { + XpubData::GetHexString, + XpubData::SetHexString, + XpubData::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); +} + +void XpubData::ConvertFromStruct( + const XpubDataStruct& data) { + base58_ = data.base58; + hex_ = data.hex; + ignore_items = data.ignore_items; +} + +XpubDataStruct XpubData::ConvertToStruct() const { // NOLINT + XpubDataStruct result; + result.base58 = base58_; + result.hex = hex_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// DecodePsbtInput +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtInput::json_mapper; +std::vector DecodePsbtInput::item_list; + +void DecodePsbtInput::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtInput::GetNon_witness_utxo_hexString, + DecodePsbtInput::SetNon_witness_utxo_hexString, + DecodePsbtInput::GetNon_witness_utxo_hexFieldType, + }; + json_mapper.emplace("non_witness_utxo_hex", func_table); + item_list.push_back("non_witness_utxo_hex"); + func_table = { + DecodePsbtInput::GetNon_witness_utxoString, + DecodePsbtInput::SetNon_witness_utxoString, + DecodePsbtInput::GetNon_witness_utxoFieldType, + }; + json_mapper.emplace("non_witness_utxo", func_table); + item_list.push_back("non_witness_utxo"); + func_table = { + DecodePsbtInput::GetWitness_utxoString, + DecodePsbtInput::SetWitness_utxoString, + DecodePsbtInput::GetWitness_utxoFieldType, + }; + json_mapper.emplace("witness_utxo", func_table); + item_list.push_back("witness_utxo"); + func_table = { + DecodePsbtInput::GetPartial_signaturesString, + DecodePsbtInput::SetPartial_signaturesString, + DecodePsbtInput::GetPartial_signaturesFieldType, + }; + json_mapper.emplace("partial_signatures", func_table); + item_list.push_back("partial_signatures"); + func_table = { + DecodePsbtInput::GetSighashString, + DecodePsbtInput::SetSighashString, + DecodePsbtInput::GetSighashFieldType, + }; + json_mapper.emplace("sighash", func_table); + item_list.push_back("sighash"); + func_table = { + DecodePsbtInput::GetRedeem_scriptString, + DecodePsbtInput::SetRedeem_scriptString, + DecodePsbtInput::GetRedeem_scriptFieldType, + }; + json_mapper.emplace("redeem_script", func_table); + item_list.push_back("redeem_script"); + func_table = { + DecodePsbtInput::GetWitness_scriptString, + DecodePsbtInput::SetWitness_scriptString, + DecodePsbtInput::GetWitness_scriptFieldType, + }; + json_mapper.emplace("witness_script", func_table); + item_list.push_back("witness_script"); + func_table = { + DecodePsbtInput::GetBip32_derivsString, + DecodePsbtInput::SetBip32_derivsString, + DecodePsbtInput::GetBip32_derivsFieldType, + }; + json_mapper.emplace("bip32_derivs", func_table); + item_list.push_back("bip32_derivs"); + func_table = { + DecodePsbtInput::GetFinal_scriptsigString, + DecodePsbtInput::SetFinal_scriptsigString, + DecodePsbtInput::GetFinal_scriptsigFieldType, + }; + json_mapper.emplace("final_scriptsig", func_table); + item_list.push_back("final_scriptsig"); + func_table = { + DecodePsbtInput::GetFinal_scriptwitnessString, + DecodePsbtInput::SetFinal_scriptwitnessString, + DecodePsbtInput::GetFinal_scriptwitnessFieldType, + }; + json_mapper.emplace("final_scriptwitness", func_table); + item_list.push_back("final_scriptwitness"); + func_table = { + DecodePsbtInput::GetUnknownString, + DecodePsbtInput::SetUnknownString, + DecodePsbtInput::GetUnknownFieldType, + }; + json_mapper.emplace("unknown", func_table); + item_list.push_back("unknown"); +} + +void DecodePsbtInput::ConvertFromStruct( + const DecodePsbtInputStruct& data) { + non_witness_utxo_hex_ = data.non_witness_utxo_hex; + non_witness_utxo_.ConvertFromStruct(data.non_witness_utxo); + witness_utxo_.ConvertFromStruct(data.witness_utxo); + partial_signatures_.ConvertFromStruct(data.partial_signatures); + sighash_ = data.sighash; + redeem_script_.ConvertFromStruct(data.redeem_script); + witness_script_.ConvertFromStruct(data.witness_script); + bip32_derivs_.ConvertFromStruct(data.bip32_derivs); + final_scriptsig_.ConvertFromStruct(data.final_scriptsig); + final_scriptwitness_.ConvertFromStruct(data.final_scriptwitness); + unknown_.ConvertFromStruct(data.unknown); + ignore_items = data.ignore_items; +} + +DecodePsbtInputStruct DecodePsbtInput::ConvertToStruct() const { // NOLINT + DecodePsbtInputStruct result; + result.non_witness_utxo_hex = non_witness_utxo_hex_; + result.non_witness_utxo = non_witness_utxo_.ConvertToStruct(); + result.witness_utxo = witness_utxo_.ConvertToStruct(); + result.partial_signatures = partial_signatures_.ConvertToStruct(); + result.sighash = sighash_; + result.redeem_script = redeem_script_.ConvertToStruct(); + result.witness_script = witness_script_.ConvertToStruct(); + result.bip32_derivs = bip32_derivs_.ConvertToStruct(); + result.final_scriptsig = final_scriptsig_.ConvertToStruct(); + result.final_scriptwitness = final_scriptwitness_.ConvertToStruct(); + result.unknown = unknown_.ConvertToStruct(); + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// DecodePsbtOutput +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtOutput::json_mapper; +std::vector DecodePsbtOutput::item_list; + +void DecodePsbtOutput::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtOutput::GetRedeem_scriptString, + DecodePsbtOutput::SetRedeem_scriptString, + DecodePsbtOutput::GetRedeem_scriptFieldType, + }; + json_mapper.emplace("redeem_script", func_table); + item_list.push_back("redeem_script"); + func_table = { + DecodePsbtOutput::GetWitness_scriptString, + DecodePsbtOutput::SetWitness_scriptString, + DecodePsbtOutput::GetWitness_scriptFieldType, + }; + json_mapper.emplace("witness_script", func_table); + item_list.push_back("witness_script"); + func_table = { + DecodePsbtOutput::GetBip32_derivsString, + DecodePsbtOutput::SetBip32_derivsString, + DecodePsbtOutput::GetBip32_derivsFieldType, + }; + json_mapper.emplace("bip32_derivs", func_table); + item_list.push_back("bip32_derivs"); + func_table = { + DecodePsbtOutput::GetUnknownString, + DecodePsbtOutput::SetUnknownString, + DecodePsbtOutput::GetUnknownFieldType, + }; + json_mapper.emplace("unknown", func_table); + item_list.push_back("unknown"); +} + +void DecodePsbtOutput::ConvertFromStruct( + const DecodePsbtOutputStruct& data) { + redeem_script_.ConvertFromStruct(data.redeem_script); + witness_script_.ConvertFromStruct(data.witness_script); + bip32_derivs_.ConvertFromStruct(data.bip32_derivs); + unknown_.ConvertFromStruct(data.unknown); + ignore_items = data.ignore_items; +} + +DecodePsbtOutputStruct DecodePsbtOutput::ConvertToStruct() const { // NOLINT + DecodePsbtOutputStruct result; + result.redeem_script = redeem_script_.ConvertToStruct(); + result.witness_script = witness_script_.ConvertToStruct(); + result.bip32_derivs = bip32_derivs_.ConvertToStruct(); + result.unknown = unknown_.ConvertToStruct(); + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionTxIn +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + ElementsDecodeRawTransactionTxIn::json_mapper; +std::vector ElementsDecodeRawTransactionTxIn::item_list; + +void ElementsDecodeRawTransactionTxIn::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + ElementsDecodeRawTransactionTxIn::GetCoinbaseString, + ElementsDecodeRawTransactionTxIn::SetCoinbaseString, + ElementsDecodeRawTransactionTxIn::GetCoinbaseFieldType, + }; + json_mapper.emplace("coinbase", func_table); + item_list.push_back("coinbase"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetTxidString, + ElementsDecodeRawTransactionTxIn::SetTxidString, + ElementsDecodeRawTransactionTxIn::GetTxidFieldType, + }; + json_mapper.emplace("txid", func_table); + item_list.push_back("txid"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetVoutString, + ElementsDecodeRawTransactionTxIn::SetVoutString, + ElementsDecodeRawTransactionTxIn::GetVoutFieldType, + }; + json_mapper.emplace("vout", func_table); + item_list.push_back("vout"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetScriptSigString, + ElementsDecodeRawTransactionTxIn::SetScriptSigString, + ElementsDecodeRawTransactionTxIn::GetScriptSigFieldType, + }; + json_mapper.emplace("scriptSig", func_table); + item_list.push_back("scriptSig"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetIs_peginString, + ElementsDecodeRawTransactionTxIn::SetIs_peginString, + ElementsDecodeRawTransactionTxIn::GetIs_peginFieldType, + }; + json_mapper.emplace("is_pegin", func_table); + item_list.push_back("is_pegin"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetSequenceString, + ElementsDecodeRawTransactionTxIn::SetSequenceString, + ElementsDecodeRawTransactionTxIn::GetSequenceFieldType, + }; + json_mapper.emplace("sequence", func_table); + item_list.push_back("sequence"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetTxinwitnessString, + ElementsDecodeRawTransactionTxIn::SetTxinwitnessString, + ElementsDecodeRawTransactionTxIn::GetTxinwitnessFieldType, + }; + json_mapper.emplace("txinwitness", func_table); + item_list.push_back("txinwitness"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetPegin_witnessString, + ElementsDecodeRawTransactionTxIn::SetPegin_witnessString, + ElementsDecodeRawTransactionTxIn::GetPegin_witnessFieldType, + }; + json_mapper.emplace("pegin_witness", func_table); + item_list.push_back("pegin_witness"); + func_table = { + ElementsDecodeRawTransactionTxIn::GetIssuanceString, + ElementsDecodeRawTransactionTxIn::SetIssuanceString, + ElementsDecodeRawTransactionTxIn::GetIssuanceFieldType, + }; + json_mapper.emplace("issuance", func_table); + item_list.push_back("issuance"); +} + +void ElementsDecodeRawTransactionTxIn::ConvertFromStruct( + const ElementsDecodeRawTransactionTxInStruct& data) { + coinbase_ = data.coinbase; + txid_ = data.txid; + vout_ = data.vout; + script_sig_.ConvertFromStruct(data.script_sig); + is_pegin_ = data.is_pegin; + sequence_ = data.sequence; + txinwitness_.ConvertFromStruct(data.txinwitness); + pegin_witness_.ConvertFromStruct(data.pegin_witness); + issuance_.ConvertFromStruct(data.issuance); + ignore_items = data.ignore_items; +} + +ElementsDecodeRawTransactionTxInStruct ElementsDecodeRawTransactionTxIn::ConvertToStruct() const { // NOLINT + ElementsDecodeRawTransactionTxInStruct result; + result.coinbase = coinbase_; + result.txid = txid_; + result.vout = vout_; + result.script_sig = script_sig_.ConvertToStruct(); + result.is_pegin = is_pegin_; + result.sequence = sequence_; + result.txinwitness = txinwitness_.ConvertToStruct(); + result.pegin_witness = pegin_witness_.ConvertToStruct(); + result.issuance = issuance_.ConvertToStruct(); + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionTxOut +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + ElementsDecodeRawTransactionTxOut::json_mapper; +std::vector ElementsDecodeRawTransactionTxOut::item_list; + +void ElementsDecodeRawTransactionTxOut::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + ElementsDecodeRawTransactionTxOut::GetValueString, + ElementsDecodeRawTransactionTxOut::SetValueString, + ElementsDecodeRawTransactionTxOut::GetValueFieldType, + }; + json_mapper.emplace("value", func_table); + item_list.push_back("value"); + func_table = { + ElementsDecodeRawTransactionTxOut::GetValue_minimumString, + ElementsDecodeRawTransactionTxOut::SetValue_minimumString, + ElementsDecodeRawTransactionTxOut::GetValue_minimumFieldType, + }; + json_mapper.emplace("value-minimum", func_table); + item_list.push_back("value-minimum"); + func_table = { + ElementsDecodeRawTransactionTxOut::GetValue_maximumString, + ElementsDecodeRawTransactionTxOut::SetValue_maximumString, + ElementsDecodeRawTransactionTxOut::GetValue_maximumFieldType, }; json_mapper.emplace("value-maximum", func_table); item_list.push_back("value-maximum"); @@ -1062,6 +1478,352 @@ ElementsDecodeRawTransactionTxOutStruct ElementsDecodeRawTransactionTxOut::Conve return result; } +// ------------------------------------------------------------------------ +// PsbtGlobalXpub +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + PsbtGlobalXpub::json_mapper; +std::vector PsbtGlobalXpub::item_list; + +void PsbtGlobalXpub::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + PsbtGlobalXpub::GetXpubString, + PsbtGlobalXpub::SetXpubString, + PsbtGlobalXpub::GetXpubFieldType, + }; + json_mapper.emplace("xpub", func_table); + item_list.push_back("xpub"); + func_table = { + PsbtGlobalXpub::GetMaster_fingerprintString, + PsbtGlobalXpub::SetMaster_fingerprintString, + PsbtGlobalXpub::GetMaster_fingerprintFieldType, + }; + json_mapper.emplace("master_fingerprint", func_table); + item_list.push_back("master_fingerprint"); + func_table = { + PsbtGlobalXpub::GetPathString, + PsbtGlobalXpub::SetPathString, + PsbtGlobalXpub::GetPathFieldType, + }; + json_mapper.emplace("path", func_table); + item_list.push_back("path"); + func_table = { + PsbtGlobalXpub::GetDescriptorXpubString, + PsbtGlobalXpub::SetDescriptorXpubString, + PsbtGlobalXpub::GetDescriptorXpubFieldType, + }; + json_mapper.emplace("descriptorXpub", func_table); + item_list.push_back("descriptorXpub"); +} + +void PsbtGlobalXpub::ConvertFromStruct( + const PsbtGlobalXpubStruct& data) { + xpub_.ConvertFromStruct(data.xpub); + master_fingerprint_ = data.master_fingerprint; + path_ = data.path; + descriptor_xpub_ = data.descriptor_xpub; + ignore_items = data.ignore_items; +} + +PsbtGlobalXpubStruct PsbtGlobalXpub::ConvertToStruct() const { // NOLINT + PsbtGlobalXpubStruct result; + result.xpub = xpub_.ConvertToStruct(); + result.master_fingerprint = master_fingerprint_; + result.path = path_; + result.descriptor_xpub = descriptor_xpub_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// DecodePsbtRequest +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtRequest::json_mapper; +std::vector DecodePsbtRequest::item_list; + +void DecodePsbtRequest::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtRequest::GetPsbtString, + DecodePsbtRequest::SetPsbtString, + DecodePsbtRequest::GetPsbtFieldType, + }; + json_mapper.emplace("psbt", func_table); + item_list.push_back("psbt"); + func_table = { + DecodePsbtRequest::GetNetworkString, + DecodePsbtRequest::SetNetworkString, + DecodePsbtRequest::GetNetworkFieldType, + }; + json_mapper.emplace("network", func_table); + item_list.push_back("network"); + func_table = { + DecodePsbtRequest::GetHasDetailString, + DecodePsbtRequest::SetHasDetailString, + DecodePsbtRequest::GetHasDetailFieldType, + }; + json_mapper.emplace("hasDetail", func_table); + item_list.push_back("hasDetail"); + func_table = { + DecodePsbtRequest::GetHasSimpleString, + DecodePsbtRequest::SetHasSimpleString, + DecodePsbtRequest::GetHasSimpleFieldType, + }; + json_mapper.emplace("hasSimple", func_table); + item_list.push_back("hasSimple"); +} + +void DecodePsbtRequest::ConvertFromStruct( + const DecodePsbtRequestStruct& data) { + psbt_ = data.psbt; + network_ = data.network; + has_detail_ = data.has_detail; + has_simple_ = data.has_simple; + ignore_items = data.ignore_items; +} + +DecodePsbtRequestStruct DecodePsbtRequest::ConvertToStruct() const { // NOLINT + DecodePsbtRequestStruct result; + result.psbt = psbt_; + result.network = network_; + result.has_detail = has_detail_; + result.has_simple = has_simple_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// DecodePsbtResponse +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodePsbtResponse::json_mapper; +std::vector DecodePsbtResponse::item_list; + +void DecodePsbtResponse::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodePsbtResponse::GetTxString, + DecodePsbtResponse::SetTxString, + DecodePsbtResponse::GetTxFieldType, + }; + json_mapper.emplace("tx", func_table); + item_list.push_back("tx"); + func_table = { + DecodePsbtResponse::GetTx_hexString, + DecodePsbtResponse::SetTx_hexString, + DecodePsbtResponse::GetTx_hexFieldType, + }; + json_mapper.emplace("tx_hex", func_table); + item_list.push_back("tx_hex"); + func_table = { + DecodePsbtResponse::GetXpubsString, + DecodePsbtResponse::SetXpubsString, + DecodePsbtResponse::GetXpubsFieldType, + }; + json_mapper.emplace("xpubs", func_table); + item_list.push_back("xpubs"); + func_table = { + DecodePsbtResponse::GetVersionString, + DecodePsbtResponse::SetVersionString, + DecodePsbtResponse::GetVersionFieldType, + }; + json_mapper.emplace("version", func_table); + item_list.push_back("version"); + func_table = { + DecodePsbtResponse::GetUnknownString, + DecodePsbtResponse::SetUnknownString, + DecodePsbtResponse::GetUnknownFieldType, + }; + json_mapper.emplace("unknown", func_table); + item_list.push_back("unknown"); + func_table = { + DecodePsbtResponse::GetInputsString, + DecodePsbtResponse::SetInputsString, + DecodePsbtResponse::GetInputsFieldType, + }; + json_mapper.emplace("inputs", func_table); + item_list.push_back("inputs"); + func_table = { + DecodePsbtResponse::GetOutputsString, + DecodePsbtResponse::SetOutputsString, + DecodePsbtResponse::GetOutputsFieldType, + }; + json_mapper.emplace("outputs", func_table); + item_list.push_back("outputs"); + func_table = { + DecodePsbtResponse::GetFeeString, + DecodePsbtResponse::SetFeeString, + DecodePsbtResponse::GetFeeFieldType, + }; + json_mapper.emplace("fee", func_table); + item_list.push_back("fee"); +} + +void DecodePsbtResponse::ConvertFromStruct( + const DecodePsbtResponseStruct& data) { + tx_.ConvertFromStruct(data.tx); + tx_hex_ = data.tx_hex; + xpubs_.ConvertFromStruct(data.xpubs); + version_ = data.version; + unknown_.ConvertFromStruct(data.unknown); + inputs_.ConvertFromStruct(data.inputs); + outputs_.ConvertFromStruct(data.outputs); + fee_ = data.fee; + ignore_items = data.ignore_items; +} + +DecodePsbtResponseStruct DecodePsbtResponse::ConvertToStruct() const { // NOLINT + DecodePsbtResponseStruct result; + result.tx = tx_.ConvertToStruct(); + result.tx_hex = tx_hex_; + result.xpubs = xpubs_.ConvertToStruct(); + result.version = version_; + result.unknown = unknown_.ConvertToStruct(); + result.inputs = inputs_.ConvertToStruct(); + result.outputs = outputs_.ConvertToStruct(); + result.fee = fee_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// DecodeRawTransactionRequest +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + DecodeRawTransactionRequest::json_mapper; +std::vector DecodeRawTransactionRequest::item_list; + +void DecodeRawTransactionRequest::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + DecodeRawTransactionRequest::GetHexString, + DecodeRawTransactionRequest::SetHexString, + DecodeRawTransactionRequest::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); + func_table = { + DecodeRawTransactionRequest::GetNetworkString, + DecodeRawTransactionRequest::SetNetworkString, + DecodeRawTransactionRequest::GetNetworkFieldType, + }; + json_mapper.emplace("network", func_table); + item_list.push_back("network"); + func_table = { + DecodeRawTransactionRequest::GetIswitnessString, + DecodeRawTransactionRequest::SetIswitnessString, + DecodeRawTransactionRequest::GetIswitnessFieldType, + }; + json_mapper.emplace("iswitness", func_table); + item_list.push_back("iswitness"); +} + +void DecodeRawTransactionRequest::ConvertFromStruct( + const DecodeRawTransactionRequestStruct& data) { + hex_ = data.hex; + network_ = data.network; + iswitness_ = data.iswitness; + ignore_items = data.ignore_items; +} + +DecodeRawTransactionRequestStruct DecodeRawTransactionRequest::ConvertToStruct() const { // NOLINT + DecodeRawTransactionRequestStruct result; + result.hex = hex_; + result.network = network_; + result.iswitness = iswitness_; + result.ignore_items = ignore_items; + return result; +} + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionRequest +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap + ElementsDecodeRawTransactionRequest::json_mapper; +std::vector ElementsDecodeRawTransactionRequest::item_list; + +void ElementsDecodeRawTransactionRequest::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE func_table; // NOLINT + + func_table = { + ElementsDecodeRawTransactionRequest::GetHexString, + ElementsDecodeRawTransactionRequest::SetHexString, + ElementsDecodeRawTransactionRequest::GetHexFieldType, + }; + json_mapper.emplace("hex", func_table); + item_list.push_back("hex"); + func_table = { + ElementsDecodeRawTransactionRequest::GetNetworkString, + ElementsDecodeRawTransactionRequest::SetNetworkString, + ElementsDecodeRawTransactionRequest::GetNetworkFieldType, + }; + json_mapper.emplace("network", func_table); + item_list.push_back("network"); + func_table = { + ElementsDecodeRawTransactionRequest::GetMainchainNetworkString, + ElementsDecodeRawTransactionRequest::SetMainchainNetworkString, + ElementsDecodeRawTransactionRequest::GetMainchainNetworkFieldType, + }; + json_mapper.emplace("mainchainNetwork", func_table); + item_list.push_back("mainchainNetwork"); + func_table = { + ElementsDecodeRawTransactionRequest::GetIswitnessString, + ElementsDecodeRawTransactionRequest::SetIswitnessString, + ElementsDecodeRawTransactionRequest::GetIswitnessFieldType, + }; + json_mapper.emplace("iswitness", func_table); + item_list.push_back("iswitness"); + func_table = { + ElementsDecodeRawTransactionRequest::GetFullDumpString, + ElementsDecodeRawTransactionRequest::SetFullDumpString, + ElementsDecodeRawTransactionRequest::GetFullDumpFieldType, + }; + json_mapper.emplace("fullDump", func_table); + item_list.push_back("fullDump"); +} + +void ElementsDecodeRawTransactionRequest::ConvertFromStruct( + const ElementsDecodeRawTransactionRequestStruct& data) { + hex_ = data.hex; + network_ = data.network; + mainchain_network_ = data.mainchain_network; + iswitness_ = data.iswitness; + full_dump_ = data.full_dump; + ignore_items = data.ignore_items; +} + +ElementsDecodeRawTransactionRequestStruct ElementsDecodeRawTransactionRequest::ConvertToStruct() const { // NOLINT + ElementsDecodeRawTransactionRequestStruct result; + result.hex = hex_; + result.network = network_; + result.mainchain_network = mainchain_network_; + result.iswitness = iswitness_; + result.full_dump = full_dump_; + result.ignore_items = ignore_items; + return result; +} + // ------------------------------------------------------------------------ // ElementsDecodeRawTransactionResponse // ------------------------------------------------------------------------ diff --git a/src/jsonapi/autogen/cfd_api_json_autogen.h b/src/jsonapi/autogen/cfd_api_json_autogen.h index eecb07f3..b2a9518d 100644 --- a/src/jsonapi/autogen/cfd_api_json_autogen.h +++ b/src/jsonapi/autogen/cfd_api_json_autogen.h @@ -1,4 +1,4 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfd_api_json_autogen.h * @@ -27,18 +27,18 @@ using cfd::core::JsonVector; // @formatter:off // ------------------------------------------------------------------------ -// DecodeRawTransactionRequest +// DecodeLockingScript // ------------------------------------------------------------------------ /** - * @brief JSON-API (DecodeRawTransactionRequest) class + * @brief JSON-API (DecodeLockingScript) class */ -class DecodeRawTransactionRequest - : public cfd::core::JsonClassBase { +class DecodeLockingScript + : public cfd::core::JsonClassBase { public: - DecodeRawTransactionRequest() { + DecodeLockingScript() { CollectFieldName(); } - virtual ~DecodeRawTransactionRequest() { + virtual ~DecodeLockingScript() { // do nothing } /** @@ -46,6 +46,49 @@ class DecodeRawTransactionRequest */ static void CollectFieldName(); + /** + * @brief Get of asm + * @return asm + */ + std::string GetAsm() const { + return asm__; + } + /** + * @brief Set to asm + * @param[in] asm_ setting value. + */ + void SetAsm( // line separate + const std::string& asm_) { // NOLINT + this->asm__ = asm_; + } + /** + * @brief Get data type of asm + * @return Data type of asm + */ + static std::string GetAsmFieldType() { + return "std::string"; + } + /** + * @brief Get json string of asm field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetAsmString( // line separate + const DecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asm__); + } + /** + * @brief Set json object to asm field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetAsmString( // line separate + DecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asm__, json_value); + } + /** * @brief Get of hex * @return hex @@ -74,7 +117,7 @@ class DecodeRawTransactionRequest * @return JSON string */ static std::string GetHexString( // line separate - const DecodeRawTransactionRequest& obj) { // NOLINT + const DecodeLockingScript& obj) { // NOLINT return cfd::core::ConvertToString(obj.hex_); } /** @@ -83,96 +126,140 @@ class DecodeRawTransactionRequest * @param[in] json_value JSON object. */ static void SetHexString( // line separate - DecodeRawTransactionRequest& obj, // NOLINT + DecodeLockingScript& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate obj.hex_, json_value); } /** - * @brief Get of network - * @return network + * @brief Get of reqSigs + * @return reqSigs */ - std::string GetNetwork() const { - return network_; + int GetReqSigs() const { + return req_sigs_; } /** - * @brief Set to network - * @param[in] network setting value. + * @brief Set to reqSigs + * @param[in] req_sigs setting value. */ - void SetNetwork( // line separate - const std::string& network) { // NOLINT - this->network_ = network; + void SetReqSigs( // line separate + const int& req_sigs) { // NOLINT + this->req_sigs_ = req_sigs; } /** - * @brief Get data type of network - * @return Data type of network + * @brief Get data type of reqSigs + * @return Data type of reqSigs */ - static std::string GetNetworkFieldType() { - return "std::string"; + static std::string GetReqSigsFieldType() { + return "int"; } /** - * @brief Get json string of network field. + * @brief Get json string of reqSigs field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetNetworkString( // line separate - const DecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.network_); + static std::string GetReqSigsString( // line separate + const DecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.req_sigs_); } /** - * @brief Set json object to network field. + * @brief Set json object to reqSigs field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetNetworkString( // line separate - DecodeRawTransactionRequest& obj, // NOLINT + static void SetReqSigsString( // line separate + DecodeLockingScript& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.network_, json_value); + obj.req_sigs_, json_value); } /** - * @brief Get of iswitness - * @return iswitness + * @brief Get of type + * @return type */ - bool GetIswitness() const { - return iswitness_; + std::string GetType() const { + return type_; } /** - * @brief Set to iswitness - * @param[in] iswitness setting value. + * @brief Set to type + * @param[in] type setting value. */ - void SetIswitness( // line separate - const bool& iswitness) { // NOLINT - this->iswitness_ = iswitness; + void SetType( // line separate + const std::string& type) { // NOLINT + this->type_ = type; } /** - * @brief Get data type of iswitness - * @return Data type of iswitness + * @brief Get data type of type + * @return Data type of type */ - static std::string GetIswitnessFieldType() { - return "bool"; + static std::string GetTypeFieldType() { + return "std::string"; } /** - * @brief Get json string of iswitness field. + * @brief Get json string of type field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetIswitnessString( // line separate - const DecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.iswitness_); + static std::string GetTypeString( // line separate + const DecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.type_); } /** - * @brief Set json object to iswitness field. + * @brief Set json object to type field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetIswitnessString( // line separate - DecodeRawTransactionRequest& obj, // NOLINT + static void SetTypeString( // line separate + DecodeLockingScript& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.iswitness_, json_value); + obj.type_, json_value); + } + + /** + * @brief Get of addresses. + * @return addresses + */ + JsonValueVector& GetAddresses() { // NOLINT + return addresses_; + } + /** + * @brief Set to addresses. + * @param[in] addresses setting value. + */ + void SetAddresses( // line separate + const JsonValueVector& addresses) { // NOLINT + this->addresses_ = addresses; + } + /** + * @brief Get data type of addresses. + * @return Data type of addresses. + */ + static std::string GetAddressesFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of addresses field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetAddressesString( // line separate + const DecodeLockingScript& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.addresses_.Serialize(); + } + /** + * @brief Set json object to addresses field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetAddressesString( // line separate + DecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + obj.addresses_.DeserializeUniValue(json_value); } /** @@ -188,27 +275,27 @@ class DecodeRawTransactionRequest * @param[in] data struct data. */ void ConvertFromStruct( - const DecodeRawTransactionRequestStruct& data); + const DecodeLockingScriptStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - DecodeRawTransactionRequestStruct ConvertToStruct() const; + DecodeLockingScriptStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using DecodeRawTransactionRequestMapTable = - cfd::core::JsonTableMap; + using DecodeLockingScriptMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const DecodeRawTransactionRequestMapTable& GetJsonMapper() const { // NOLINT + virtual const DecodeLockingScriptMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -221,7 +308,7 @@ class DecodeRawTransactionRequest return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -234,7 +321,7 @@ class DecodeRawTransactionRequest /** * @brief JsonFunctionMap table */ - static DecodeRawTransactionRequestMapTable json_mapper; + static DecodeLockingScriptMapTable json_mapper; /** * @brief field name list. */ @@ -244,18 +331,26 @@ class DecodeRawTransactionRequest */ std::set ignore_items; + /** + * @brief JsonAPI(asm) value + */ + std::string asm__ = ""; /** * @brief JsonAPI(hex) value */ std::string hex_ = ""; /** - * @brief JsonAPI(network) value + * @brief JsonAPI(reqSigs) value */ - std::string network_ = "mainnet"; + int req_sigs_ = 0; /** - * @brief JsonAPI(iswitness) value + * @brief JsonAPI(type) value */ - bool iswitness_ = true; + std::string type_ = ""; + /** + * @brief JsonAPI(addresses) value + */ + JsonValueVector addresses_; // NOLINT }; // ------------------------------------------------------------------------ @@ -410,7 +505,7 @@ class DecodeUnlockingScript return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -444,18 +539,18 @@ class DecodeUnlockingScript }; // ------------------------------------------------------------------------ -// DecodeRawTransactionTxIn +// DecodePsbtLockingScript // ------------------------------------------------------------------------ /** - * @brief JSON-API (DecodeRawTransactionTxIn) class + * @brief JSON-API (DecodePsbtLockingScript) class */ -class DecodeRawTransactionTxIn - : public cfd::core::JsonClassBase { +class DecodePsbtLockingScript + : public cfd::core::JsonClassBase { public: - DecodeRawTransactionTxIn() { + DecodePsbtLockingScript() { CollectFieldName(); } - virtual ~DecodeRawTransactionTxIn() { + virtual ~DecodePsbtLockingScript() { // do nothing } /** @@ -464,43 +559,322 @@ class DecodeRawTransactionTxIn static void CollectFieldName(); /** - * @brief Get of coinbase - * @return coinbase + * @brief Get of asm + * @return asm */ - std::string GetCoinbase() const { - return coinbase_; + std::string GetAsm() const { + return asm__; } /** - * @brief Set to coinbase - * @param[in] coinbase setting value. + * @brief Set to asm + * @param[in] asm_ setting value. */ - void SetCoinbase( // line separate - const std::string& coinbase) { // NOLINT - this->coinbase_ = coinbase; + void SetAsm( // line separate + const std::string& asm_) { // NOLINT + this->asm__ = asm_; } /** - * @brief Get data type of coinbase - * @return Data type of coinbase + * @brief Get data type of asm + * @return Data type of asm */ - static std::string GetCoinbaseFieldType() { + static std::string GetAsmFieldType() { return "std::string"; } /** - * @brief Get json string of coinbase field. + * @brief Get json string of asm field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetCoinbaseString( // line separate - const DecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.coinbase_); + static std::string GetAsmString( // line separate + const DecodePsbtLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asm__); } /** - * @brief Set json object to coinbase field. + * @brief Set json object to asm field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetCoinbaseString( // line separate - DecodeRawTransactionTxIn& obj, // NOLINT + static void SetAsmString( // line separate + DecodePsbtLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asm__, json_value); + } + + /** + * @brief Get of hex + * @return hex + */ + std::string GetHex() const { + return hex_; + } + /** + * @brief Set to hex + * @param[in] hex setting value. + */ + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; + } + /** + * @brief Get data type of hex + * @return Data type of hex + */ + static std::string GetHexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHexString( // line separate + const DecodePsbtLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); + } + /** + * @brief Set json object to hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHexString( // line separate + DecodePsbtLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.hex_, json_value); + } + + /** + * @brief Get of type + * @return type + */ + std::string GetType() const { + return type_; + } + /** + * @brief Set to type + * @param[in] type setting value. + */ + void SetType( // line separate + const std::string& type) { // NOLINT + this->type_ = type; + } + /** + * @brief Get data type of type + * @return Data type of type + */ + static std::string GetTypeFieldType() { + return "std::string"; + } + /** + * @brief Get json string of type field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetTypeString( // line separate + const DecodePsbtLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.type_); + } + /** + * @brief Set json object to type field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetTypeString( // line separate + DecodePsbtLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.type_, json_value); + } + + /** + * @brief Get of address + * @return address + */ + std::string GetAddress() const { + return address_; + } + /** + * @brief Set to address + * @param[in] address setting value. + */ + void SetAddress( // line separate + const std::string& address) { // NOLINT + this->address_ = address; + } + /** + * @brief Get data type of address + * @return Data type of address + */ + static std::string GetAddressFieldType() { + return "std::string"; + } + /** + * @brief Get json string of address field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetAddressString( // line separate + const DecodePsbtLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.address_); + } + /** + * @brief Set json object to address field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetAddressString( // line separate + DecodePsbtLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.address_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const DecodePsbtLockingScriptStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + DecodePsbtLockingScriptStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using DecodePsbtLockingScriptMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const DecodePsbtLockingScriptMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static DecodePsbtLockingScriptMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(asm) value + */ + std::string asm__ = ""; + /** + * @brief JsonAPI(hex) value + */ + std::string hex_ = ""; + /** + * @brief JsonAPI(type) value + */ + std::string type_ = ""; + /** + * @brief JsonAPI(address) value + */ + std::string address_ = ""; +}; + +// ------------------------------------------------------------------------ +// DecodeRawTransactionTxIn +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (DecodeRawTransactionTxIn) class + */ +class DecodeRawTransactionTxIn + : public cfd::core::JsonClassBase { + public: + DecodeRawTransactionTxIn() { + CollectFieldName(); + } + virtual ~DecodeRawTransactionTxIn() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of coinbase + * @return coinbase + */ + std::string GetCoinbase() const { + return coinbase_; + } + /** + * @brief Set to coinbase + * @param[in] coinbase setting value. + */ + void SetCoinbase( // line separate + const std::string& coinbase) { // NOLINT + this->coinbase_ = coinbase; + } + /** + * @brief Get data type of coinbase + * @return Data type of coinbase + */ + static std::string GetCoinbaseFieldType() { + return "std::string"; + } + /** + * @brief Get json string of coinbase field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetCoinbaseString( // line separate + const DecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.coinbase_); + } + /** + * @brief Set json object to coinbase field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetCoinbaseString( // line separate + DecodeRawTransactionTxIn& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate obj.coinbase_, json_value); @@ -553,7 +927,7 @@ class DecodeRawTransactionTxIn * @brief Get of vout * @return vout */ - int64_t GetVout() const { + uint32_t GetVout() const { return vout_; } /** @@ -561,7 +935,7 @@ class DecodeRawTransactionTxIn * @param[in] vout setting value. */ void SetVout( // line separate - const int64_t& vout) { // NOLINT + const uint32_t& vout) { // NOLINT this->vout_ = vout; } /** @@ -569,7 +943,7 @@ class DecodeRawTransactionTxIn * @return Data type of vout */ static std::string GetVoutFieldType() { - return "int64_t"; + return "uint32_t"; } /** * @brief Get json string of vout field. @@ -684,7 +1058,7 @@ class DecodeRawTransactionTxIn * @brief Get of sequence * @return sequence */ - int64_t GetSequence() const { + uint32_t GetSequence() const { return sequence_; } /** @@ -692,7 +1066,7 @@ class DecodeRawTransactionTxIn * @param[in] sequence setting value. */ void SetSequence( // line separate - const int64_t& sequence) { // NOLINT + const uint32_t& sequence) { // NOLINT this->sequence_ = sequence; } /** @@ -700,7 +1074,7 @@ class DecodeRawTransactionTxIn * @return Data type of sequence */ static std::string GetSequenceFieldType() { - return "int64_t"; + return "uint32_t"; } /** * @brief Get json string of sequence field. @@ -769,7 +1143,7 @@ class DecodeRawTransactionTxIn return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -803,7 +1177,7 @@ class DecodeRawTransactionTxIn /** * @brief JsonAPI(vout) value */ - int64_t vout_ = 0; + uint32_t vout_ = 0; /** * @brief JsonAPI(scriptSig) value */ @@ -815,22 +1189,22 @@ class DecodeRawTransactionTxIn /** * @brief JsonAPI(sequence) value */ - int64_t sequence_ = 0; + uint32_t sequence_ = 0; }; // ------------------------------------------------------------------------ -// DecodeLockingScript +// DecodeRawTransactionTxOut // ------------------------------------------------------------------------ /** - * @brief JSON-API (DecodeLockingScript) class + * @brief JSON-API (DecodeRawTransactionTxOut) class */ -class DecodeLockingScript - : public cfd::core::JsonClassBase { +class DecodeRawTransactionTxOut + : public cfd::core::JsonClassBase { public: - DecodeLockingScript() { + DecodeRawTransactionTxOut() { CollectFieldName(); } - virtual ~DecodeLockingScript() { + virtual ~DecodeRawTransactionTxOut() { // do nothing } /** @@ -839,219 +1213,133 @@ class DecodeLockingScript static void CollectFieldName(); /** - * @brief Get of asm - * @return asm - */ - std::string GetAsm() const { - return asm__; - } - /** - * @brief Set to asm - * @param[in] asm_ setting value. - */ - void SetAsm( // line separate - const std::string& asm_) { // NOLINT - this->asm__ = asm_; - } - /** - * @brief Get data type of asm - * @return Data type of asm - */ - static std::string GetAsmFieldType() { - return "std::string"; - } - /** - * @brief Get json string of asm field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetAsmString( // line separate - const DecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asm__); - } - /** - * @brief Set json object to asm field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetAsmString( // line separate - DecodeLockingScript& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.asm__, json_value); - } - - /** - * @brief Get of hex - * @return hex - */ - std::string GetHex() const { - return hex_; - } - /** - * @brief Set to hex - * @param[in] hex setting value. - */ - void SetHex( // line separate - const std::string& hex) { // NOLINT - this->hex_ = hex; - } - /** - * @brief Get data type of hex - * @return Data type of hex - */ - static std::string GetHexFieldType() { - return "std::string"; - } - /** - * @brief Get json string of hex field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetHexString( // line separate - const DecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.hex_); - } - /** - * @brief Set json object to hex field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetHexString( // line separate - DecodeLockingScript& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.hex_, json_value); - } - - /** - * @brief Get of reqSigs - * @return reqSigs + * @brief Get of value + * @return value */ - int64_t GetReqSigs() const { - return req_sigs_; + int64_t GetValue() const { + return value_; } /** - * @brief Set to reqSigs - * @param[in] req_sigs setting value. + * @brief Set to value + * @param[in] value setting value. */ - void SetReqSigs( // line separate - const int64_t& req_sigs) { // NOLINT - this->req_sigs_ = req_sigs; + void SetValue( // line separate + const int64_t& value) { // NOLINT + this->value_ = value; } /** - * @brief Get data type of reqSigs - * @return Data type of reqSigs + * @brief Get data type of value + * @return Data type of value */ - static std::string GetReqSigsFieldType() { + static std::string GetValueFieldType() { return "int64_t"; } /** - * @brief Get json string of reqSigs field. + * @brief Get json string of value field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetReqSigsString( // line separate - const DecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.req_sigs_); + static std::string GetValueString( // line separate + const DecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.value_); } /** - * @brief Set json object to reqSigs field. + * @brief Set json object to value field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetReqSigsString( // line separate - DecodeLockingScript& obj, // NOLINT + static void SetValueString( // line separate + DecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.req_sigs_, json_value); + obj.value_, json_value); } /** - * @brief Get of type - * @return type + * @brief Get of n + * @return n */ - std::string GetType() const { - return type_; + uint32_t GetN() const { + return n_; } /** - * @brief Set to type - * @param[in] type setting value. + * @brief Set to n + * @param[in] n setting value. */ - void SetType( // line separate - const std::string& type) { // NOLINT - this->type_ = type; + void SetN( // line separate + const uint32_t& n) { // NOLINT + this->n_ = n; } /** - * @brief Get data type of type - * @return Data type of type + * @brief Get data type of n + * @return Data type of n */ - static std::string GetTypeFieldType() { - return "std::string"; + static std::string GetNFieldType() { + return "uint32_t"; } /** - * @brief Get json string of type field. + * @brief Get json string of n field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetTypeString( // line separate - const DecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.type_); + static std::string GetNString( // line separate + const DecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.n_); } /** - * @brief Set json object to type field. + * @brief Set json object to n field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetTypeString( // line separate - DecodeLockingScript& obj, // NOLINT + static void SetNString( // line separate + DecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.type_, json_value); + obj.n_, json_value); } /** - * @brief Get of addresses. - * @return addresses + * @brief Get of scriptPubKey. + * @return scriptPubKey */ - JsonValueVector& GetAddresses() { // NOLINT - return addresses_; + DecodeLockingScript& GetScriptPubKey() { // NOLINT + return script_pub_key_; } /** - * @brief Set to addresses. - * @param[in] addresses setting value. + * @brief Set to scriptPubKey. + * @param[in] script_pub_key setting value. */ - void SetAddresses( // line separate - const JsonValueVector& addresses) { // NOLINT - this->addresses_ = addresses; + void SetScriptPubKey( // line separate + const DecodeLockingScript& script_pub_key) { // NOLINT + this->script_pub_key_ = script_pub_key; } /** - * @brief Get data type of addresses. - * @return Data type of addresses. + * @brief Get data type of scriptPubKey. + * @return Data type of scriptPubKey. */ - static std::string GetAddressesFieldType() { - return "JsonValueVector"; // NOLINT + static std::string GetScriptPubKeyFieldType() { + return "DecodeLockingScript"; // NOLINT } /** - * @brief Get json string of addresses field. + * @brief Get json string of scriptPubKey field. * @param[in,out] obj class object * @return JSON string. */ - static std::string GetAddressesString( // line separate - const DecodeLockingScript& obj) { // NOLINT + static std::string GetScriptPubKeyString( // line separate + const DecodeRawTransactionTxOut& obj) { // NOLINT // Do not set to const, because substitution of member variables // may occur in pre / post processing inside Serialize - return obj.addresses_.Serialize(); + return obj.script_pub_key_.Serialize(); } /** - * @brief Set json object to addresses field. + * @brief Set json object to scriptPubKey field. * @param[in,out] obj class object * @param[in] json_value JSON object */ - static void SetAddressesString( // line separate - DecodeLockingScript& obj, // NOLINT + static void SetScriptPubKeyString( // line separate + DecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { - obj.addresses_.DeserializeUniValue(json_value); + obj.script_pub_key_.DeserializeUniValue(json_value); } /** @@ -1067,27 +1355,27 @@ class DecodeLockingScript * @param[in] data struct data. */ void ConvertFromStruct( - const DecodeLockingScriptStruct& data); + const DecodeRawTransactionTxOutStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - DecodeLockingScriptStruct ConvertToStruct() const; + DecodeRawTransactionTxOutStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using DecodeLockingScriptMapTable = - cfd::core::JsonTableMap; + using DecodeRawTransactionTxOutMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const DecodeLockingScriptMapTable& GetJsonMapper() const { // NOLINT + virtual const DecodeRawTransactionTxOutMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -1100,7 +1388,7 @@ class DecodeLockingScript return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -1113,7 +1401,7 @@ class DecodeLockingScript /** * @brief JsonFunctionMap table */ - static DecodeLockingScriptMapTable json_mapper; + static DecodeRawTransactionTxOutMapTable json_mapper; /** * @brief field name list. */ @@ -1124,40 +1412,32 @@ class DecodeLockingScript std::set ignore_items; /** - * @brief JsonAPI(asm) value - */ - std::string asm__ = ""; - /** - * @brief JsonAPI(hex) value - */ - std::string hex_ = ""; - /** - * @brief JsonAPI(reqSigs) value + * @brief JsonAPI(value) value */ - int64_t req_sigs_ = 0; + int64_t value_ = 0; /** - * @brief JsonAPI(type) value + * @brief JsonAPI(n) value */ - std::string type_ = ""; + uint32_t n_ = 0; /** - * @brief JsonAPI(addresses) value + * @brief JsonAPI(scriptPubKey) value */ - JsonValueVector addresses_; // NOLINT + DecodeLockingScript script_pub_key_; // NOLINT }; // ------------------------------------------------------------------------ -// DecodeRawTransactionTxOut +// DecodePsbtUtxo // ------------------------------------------------------------------------ /** - * @brief JSON-API (DecodeRawTransactionTxOut) class + * @brief JSON-API (DecodePsbtUtxo) class */ -class DecodeRawTransactionTxOut - : public cfd::core::JsonClassBase { +class DecodePsbtUtxo + : public cfd::core::JsonClassBase { public: - DecodeRawTransactionTxOut() { + DecodePsbtUtxo() { CollectFieldName(); } - virtual ~DecodeRawTransactionTxOut() { + virtual ~DecodePsbtUtxo() { // do nothing } /** @@ -1166,96 +1446,53 @@ class DecodeRawTransactionTxOut static void CollectFieldName(); /** - * @brief Get of value - * @return value - */ - double GetValue() const { - return value_; - } - /** - * @brief Set to value - * @param[in] value setting value. - */ - void SetValue( // line separate - const double& value) { // NOLINT - this->value_ = value; - } - /** - * @brief Get data type of value - * @return Data type of value - */ - static std::string GetValueFieldType() { - return "double"; - } - /** - * @brief Get json string of value field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetValueString( // line separate - const DecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.value_); - } - /** - * @brief Set json object to value field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetValueString( // line separate - DecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.value_, json_value); - } - - /** - * @brief Get of n - * @return n + * @brief Get of amount + * @return amount */ - int64_t GetN() const { - return n_; + int64_t GetAmount() const { + return amount_; } /** - * @brief Set to n - * @param[in] n setting value. + * @brief Set to amount + * @param[in] amount setting value. */ - void SetN( // line separate - const int64_t& n) { // NOLINT - this->n_ = n; + void SetAmount( // line separate + const int64_t& amount) { // NOLINT + this->amount_ = amount; } /** - * @brief Get data type of n - * @return Data type of n + * @brief Get data type of amount + * @return Data type of amount */ - static std::string GetNFieldType() { + static std::string GetAmountFieldType() { return "int64_t"; } /** - * @brief Get json string of n field. + * @brief Get json string of amount field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetNString( // line separate - const DecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.n_); + static std::string GetAmountString( // line separate + const DecodePsbtUtxo& obj) { // NOLINT + return cfd::core::ConvertToString(obj.amount_); } /** - * @brief Set json object to n field. + * @brief Set json object to amount field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetNString( // line separate - DecodeRawTransactionTxOut& obj, // NOLINT + static void SetAmountString( // line separate + DecodePsbtUtxo& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.n_, json_value); + obj.amount_, json_value); } /** * @brief Get of scriptPubKey. * @return scriptPubKey */ - DecodeLockingScript& GetScriptPubKey() { // NOLINT + DecodePsbtLockingScript& GetScriptPubKey() { // NOLINT return script_pub_key_; } /** @@ -1263,7 +1500,7 @@ class DecodeRawTransactionTxOut * @param[in] script_pub_key setting value. */ void SetScriptPubKey( // line separate - const DecodeLockingScript& script_pub_key) { // NOLINT + const DecodePsbtLockingScript& script_pub_key) { // NOLINT this->script_pub_key_ = script_pub_key; } /** @@ -1271,7 +1508,7 @@ class DecodeRawTransactionTxOut * @return Data type of scriptPubKey. */ static std::string GetScriptPubKeyFieldType() { - return "DecodeLockingScript"; // NOLINT + return "DecodePsbtLockingScript"; // NOLINT } /** * @brief Get json string of scriptPubKey field. @@ -1279,7 +1516,7 @@ class DecodeRawTransactionTxOut * @return JSON string. */ static std::string GetScriptPubKeyString( // line separate - const DecodeRawTransactionTxOut& obj) { // NOLINT + const DecodePsbtUtxo& obj) { // NOLINT // Do not set to const, because substitution of member variables // may occur in pre / post processing inside Serialize return obj.script_pub_key_.Serialize(); @@ -1290,7 +1527,7 @@ class DecodeRawTransactionTxOut * @param[in] json_value JSON object */ static void SetScriptPubKeyString( // line separate - DecodeRawTransactionTxOut& obj, // NOLINT + DecodePsbtUtxo& obj, // NOLINT const UniValue& json_value) { obj.script_pub_key_.DeserializeUniValue(json_value); } @@ -1308,27 +1545,27 @@ class DecodeRawTransactionTxOut * @param[in] data struct data. */ void ConvertFromStruct( - const DecodeRawTransactionTxOutStruct& data); + const DecodePsbtUtxoStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - DecodeRawTransactionTxOutStruct ConvertToStruct() const; + DecodePsbtUtxoStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using DecodeRawTransactionTxOutMapTable = - cfd::core::JsonTableMap; + using DecodePsbtUtxoMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const DecodeRawTransactionTxOutMapTable& GetJsonMapper() const { // NOLINT + virtual const DecodePsbtUtxoMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -1341,7 +1578,7 @@ class DecodeRawTransactionTxOut return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -1354,7 +1591,7 @@ class DecodeRawTransactionTxOut /** * @brief JsonFunctionMap table */ - static DecodeRawTransactionTxOutMapTable json_mapper; + static DecodePsbtUtxoMapTable json_mapper; /** * @brief field name list. */ @@ -1365,17 +1602,13 @@ class DecodeRawTransactionTxOut std::set ignore_items; /** - * @brief JsonAPI(value) value - */ - double value_ = 0; - /** - * @brief JsonAPI(n) value + * @brief JsonAPI(amount) value */ - int64_t n_ = 0; + int64_t amount_ = 0; /** * @brief JsonAPI(scriptPubKey) value */ - DecodeLockingScript script_pub_key_; // NOLINT + DecodePsbtLockingScript script_pub_key_; // NOLINT }; // ------------------------------------------------------------------------ @@ -1531,7 +1764,7 @@ class DecodeRawTransactionResponse * @brief Get of size * @return size */ - int64_t GetSize() const { + uint32_t GetSize() const { return size_; } /** @@ -1539,7 +1772,7 @@ class DecodeRawTransactionResponse * @param[in] size setting value. */ void SetSize( // line separate - const int64_t& size) { // NOLINT + const uint32_t& size) { // NOLINT this->size_ = size; } /** @@ -1547,7 +1780,7 @@ class DecodeRawTransactionResponse * @return Data type of size */ static std::string GetSizeFieldType() { - return "int64_t"; + return "uint32_t"; } /** * @brief Get json string of size field. @@ -1574,7 +1807,7 @@ class DecodeRawTransactionResponse * @brief Get of vsize * @return vsize */ - int64_t GetVsize() const { + uint32_t GetVsize() const { return vsize_; } /** @@ -1582,7 +1815,7 @@ class DecodeRawTransactionResponse * @param[in] vsize setting value. */ void SetVsize( // line separate - const int64_t& vsize) { // NOLINT + const uint32_t& vsize) { // NOLINT this->vsize_ = vsize; } /** @@ -1590,7 +1823,7 @@ class DecodeRawTransactionResponse * @return Data type of vsize */ static std::string GetVsizeFieldType() { - return "int64_t"; + return "uint32_t"; } /** * @brief Get json string of vsize field. @@ -1617,7 +1850,7 @@ class DecodeRawTransactionResponse * @brief Get of weight * @return weight */ - int64_t GetWeight() const { + uint32_t GetWeight() const { return weight_; } /** @@ -1625,7 +1858,7 @@ class DecodeRawTransactionResponse * @param[in] weight setting value. */ void SetWeight( // line separate - const int64_t& weight) { // NOLINT + const uint32_t& weight) { // NOLINT this->weight_ = weight; } /** @@ -1633,7 +1866,7 @@ class DecodeRawTransactionResponse * @return Data type of weight */ static std::string GetWeightFieldType() { - return "int64_t"; + return "uint32_t"; } /** * @brief Get json string of weight field. @@ -1833,7 +2066,7 @@ class DecodeRawTransactionResponse return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -1871,15 +2104,15 @@ class DecodeRawTransactionResponse /** * @brief JsonAPI(size) value */ - int64_t size_ = 0; + uint32_t size_ = 0; /** * @brief JsonAPI(vsize) value */ - int64_t vsize_ = 0; + uint32_t vsize_ = 0; /** * @brief JsonAPI(weight) value */ - int64_t weight_ = 0; + uint32_t weight_ = 0; /** * @brief JsonAPI(locktime) value */ @@ -1895,18 +2128,18 @@ class DecodeRawTransactionResponse }; // ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionRequest +// ElementsDecodeIssuance // ------------------------------------------------------------------------ /** - * @brief JSON-API (ElementsDecodeRawTransactionRequest) class + * @brief JSON-API (ElementsDecodeIssuance) class */ -class ElementsDecodeRawTransactionRequest - : public cfd::core::JsonClassBase { +class ElementsDecodeIssuance + : public cfd::core::JsonClassBase { public: - ElementsDecodeRawTransactionRequest() { + ElementsDecodeIssuance() { CollectFieldName(); } - virtual ~ElementsDecodeRawTransactionRequest() { + virtual ~ElementsDecodeIssuance() { // do nothing } /** @@ -1915,729 +2148,4238 @@ class ElementsDecodeRawTransactionRequest static void CollectFieldName(); /** - * @brief Get of hex - * @return hex + * @brief Get of assetBlindingNonce + * @return assetBlindingNonce */ - std::string GetHex() const { - return hex_; + std::string GetAssetBlindingNonce() const { + return asset_blinding_nonce_; } /** - * @brief Set to hex - * @param[in] hex setting value. + * @brief Set to assetBlindingNonce + * @param[in] asset_blinding_nonce setting value. */ - void SetHex( // line separate - const std::string& hex) { // NOLINT - this->hex_ = hex; + void SetAssetBlindingNonce( // line separate + const std::string& asset_blinding_nonce) { // NOLINT + this->asset_blinding_nonce_ = asset_blinding_nonce; } /** - * @brief Get data type of hex - * @return Data type of hex + * @brief Get data type of assetBlindingNonce + * @return Data type of assetBlindingNonce */ - static std::string GetHexFieldType() { + static std::string GetAssetBlindingNonceFieldType() { return "std::string"; } /** - * @brief Get json string of hex field. + * @brief Get json string of assetBlindingNonce field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetHexString( // line separate - const ElementsDecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.hex_); + static std::string GetAssetBlindingNonceString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asset_blinding_nonce_); } /** - * @brief Set json object to hex field. + * @brief Set json object to assetBlindingNonce field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetHexString( // line separate - ElementsDecodeRawTransactionRequest& obj, // NOLINT + static void SetAssetBlindingNonceString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.hex_, json_value); + obj.asset_blinding_nonce_, json_value); } /** - * @brief Get of network - * @return network + * @brief Get of assetEntropy + * @return assetEntropy */ - std::string GetNetwork() const { - return network_; + std::string GetAssetEntropy() const { + return asset_entropy_; } /** - * @brief Set to network - * @param[in] network setting value. + * @brief Set to assetEntropy + * @param[in] asset_entropy setting value. */ - void SetNetwork( // line separate - const std::string& network) { // NOLINT - this->network_ = network; + void SetAssetEntropy( // line separate + const std::string& asset_entropy) { // NOLINT + this->asset_entropy_ = asset_entropy; } /** - * @brief Get data type of network - * @return Data type of network + * @brief Get data type of assetEntropy + * @return Data type of assetEntropy */ - static std::string GetNetworkFieldType() { + static std::string GetAssetEntropyFieldType() { return "std::string"; } /** - * @brief Get json string of network field. + * @brief Get json string of assetEntropy field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetNetworkString( // line separate - const ElementsDecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.network_); + static std::string GetAssetEntropyString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asset_entropy_); } /** - * @brief Set json object to network field. + * @brief Set json object to assetEntropy field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetNetworkString( // line separate - ElementsDecodeRawTransactionRequest& obj, // NOLINT + static void SetAssetEntropyString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.network_, json_value); + obj.asset_entropy_, json_value); } /** - * @brief Get of mainchainNetwork - * @return mainchainNetwork + * @brief Get of contractHash + * @return contractHash */ - std::string GetMainchainNetwork() const { - return mainchain_network_; + std::string GetContractHash() const { + return contract_hash_; } /** - * @brief Set to mainchainNetwork - * @param[in] mainchain_network setting value. + * @brief Set to contractHash + * @param[in] contract_hash setting value. */ - void SetMainchainNetwork( // line separate - const std::string& mainchain_network) { // NOLINT - this->mainchain_network_ = mainchain_network; + void SetContractHash( // line separate + const std::string& contract_hash) { // NOLINT + this->contract_hash_ = contract_hash; } /** - * @brief Get data type of mainchainNetwork - * @return Data type of mainchainNetwork + * @brief Get data type of contractHash + * @return Data type of contractHash */ - static std::string GetMainchainNetworkFieldType() { + static std::string GetContractHashFieldType() { return "std::string"; } /** - * @brief Get json string of mainchainNetwork field. + * @brief Get json string of contractHash field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetMainchainNetworkString( // line separate - const ElementsDecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.mainchain_network_); + static std::string GetContractHashString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.contract_hash_); } /** - * @brief Set json object to mainchainNetwork field. + * @brief Set json object to contractHash field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetMainchainNetworkString( // line separate - ElementsDecodeRawTransactionRequest& obj, // NOLINT + static void SetContractHashString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.mainchain_network_, json_value); + obj.contract_hash_, json_value); } /** - * @brief Get of iswitness - * @return iswitness + * @brief Get of isreissuance + * @return isreissuance */ - bool GetIswitness() const { - return iswitness_; + bool GetIsreissuance() const { + return isreissuance_; } /** - * @brief Set to iswitness - * @param[in] iswitness setting value. + * @brief Set to isreissuance + * @param[in] isreissuance setting value. */ - void SetIswitness( // line separate - const bool& iswitness) { // NOLINT - this->iswitness_ = iswitness; + void SetIsreissuance( // line separate + const bool& isreissuance) { // NOLINT + this->isreissuance_ = isreissuance; } /** - * @brief Get data type of iswitness - * @return Data type of iswitness + * @brief Get data type of isreissuance + * @return Data type of isreissuance */ - static std::string GetIswitnessFieldType() { + static std::string GetIsreissuanceFieldType() { return "bool"; } /** - * @brief Get json string of iswitness field. + * @brief Get json string of isreissuance field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetIswitnessString( // line separate - const ElementsDecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.iswitness_); + static std::string GetIsreissuanceString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.isreissuance_); } /** - * @brief Set json object to iswitness field. + * @brief Set json object to isreissuance field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetIswitnessString( // line separate - ElementsDecodeRawTransactionRequest& obj, // NOLINT + static void SetIsreissuanceString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.iswitness_, json_value); + obj.isreissuance_, json_value); } /** - * @brief Get of fullDump - * @return fullDump + * @brief Get of token + * @return token */ - bool GetFullDump() const { - return full_dump_; + std::string GetToken() const { + return token_; } /** - * @brief Set to fullDump - * @param[in] full_dump setting value. + * @brief Set to token + * @param[in] token setting value. */ - void SetFullDump( // line separate - const bool& full_dump) { // NOLINT - this->full_dump_ = full_dump; + void SetToken( // line separate + const std::string& token) { // NOLINT + this->token_ = token; } /** - * @brief Get data type of fullDump - * @return Data type of fullDump + * @brief Get data type of token + * @return Data type of token */ - static std::string GetFullDumpFieldType() { - return "bool"; + static std::string GetTokenFieldType() { + return "std::string"; } /** - * @brief Get json string of fullDump field. + * @brief Get json string of token field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetFullDumpString( // line separate - const ElementsDecodeRawTransactionRequest& obj) { // NOLINT - return cfd::core::ConvertToString(obj.full_dump_); + static std::string GetTokenString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.token_); } /** - * @brief Set json object to fullDump field. + * @brief Set json object to token field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetFullDumpString( // line separate - ElementsDecodeRawTransactionRequest& obj, // NOLINT + static void SetTokenString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.full_dump_, json_value); + obj.token_, json_value); } /** - * @brief Set ignore item. - * @param[in] key ignore target key name. + * @brief Get of asset + * @return asset */ - void SetIgnoreItem(const std::string& key) { - ignore_items.insert(key); + std::string GetAsset() const { + return asset_; } - - /** - * @brief Convert struct to class. - * @param[in] data struct data. - */ - void ConvertFromStruct( - const ElementsDecodeRawTransactionRequestStruct& data); - - /** - * @brief Convert class to struct. - * @return struct data. - */ - ElementsDecodeRawTransactionRequestStruct ConvertToStruct() const; - - protected: /** - * @brief definition type of Map table. - */ - using ElementsDecodeRawTransactionRequestMapTable = - cfd::core::JsonTableMap; - - /** - * @brief Get JSON mapping object. - * @return JSON mapping object. - * @see cfd::core::JsonClassBase::GetJsonMapper() + * @brief Set to asset + * @param[in] asset setting value. */ - virtual const ElementsDecodeRawTransactionRequestMapTable& GetJsonMapper() const { // NOLINT - return json_mapper; + void SetAsset( // line separate + const std::string& asset) { // NOLINT + this->asset_ = asset; } /** - * @brief Get item lists of JSON mapping. - * Fetch a list of target variable names in the order of definition. - * @return Item lists of JSON mapping. - * @see cfd::core::JsonClassBase::GetJsonItemList() + * @brief Get data type of asset + * @return Data type of asset */ - virtual const std::vector& GetJsonItemList() const { - return item_list; + static std::string GetAssetFieldType() { + return "std::string"; } /** - * @brief Get ignore item lists of JSON mnapping. - * Ignore the target variable at Serialize. - * @return Item list of JSON mapping. - * @see cfd::core::JsonClassBase::GetIgnoreItem() + * @brief Get json string of asset field. + * @param[in,out] obj class object. + * @return JSON string */ - virtual const std::set& GetIgnoreItem() const { - return ignore_items; + static std::string GetAssetString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asset_); } - - private: - /** - * @brief JsonFunctionMap table - */ - static ElementsDecodeRawTransactionRequestMapTable json_mapper; - /** - * @brief field name list. - */ - static std::vector item_list; - /** - * @brief ignore item list. - */ - std::set ignore_items; - - /** - * @brief JsonAPI(hex) value - */ - std::string hex_ = ""; /** - * @brief JsonAPI(network) value - */ - std::string network_ = "liquidv1"; - /** - * @brief JsonAPI(mainchainNetwork) value - */ - std::string mainchain_network_ = ""; - /** - * @brief JsonAPI(iswitness) value - */ - bool iswitness_ = true; - /** - * @brief JsonAPI(fullDump) value + * @brief Set json object to asset field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. */ - bool full_dump_ = false; -}; - -// ------------------------------------------------------------------------ -// ElementsDecodeUnlockingScript -// ------------------------------------------------------------------------ -/** - * @brief JSON-API (ElementsDecodeUnlockingScript) class - */ -class ElementsDecodeUnlockingScript - : public cfd::core::JsonClassBase { - public: - ElementsDecodeUnlockingScript() { - CollectFieldName(); - } - virtual ~ElementsDecodeUnlockingScript() { - // do nothing + static void SetAssetString( // line separate + ElementsDecodeIssuance& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asset_, json_value); } - /** - * @brief collect field name. - */ - static void CollectFieldName(); /** - * @brief Get of asm - * @return asm + * @brief Get of assetamount + * @return assetamount */ - std::string GetAsm() const { - return asm__; + int64_t GetAssetamount() const { + return assetamount_; } /** - * @brief Set to asm - * @param[in] asm_ setting value. + * @brief Set to assetamount + * @param[in] assetamount setting value. */ - void SetAsm( // line separate - const std::string& asm_) { // NOLINT - this->asm__ = asm_; + void SetAssetamount( // line separate + const int64_t& assetamount) { // NOLINT + this->assetamount_ = assetamount; } /** - * @brief Get data type of asm - * @return Data type of asm + * @brief Get data type of assetamount + * @return Data type of assetamount */ - static std::string GetAsmFieldType() { - return "std::string"; + static std::string GetAssetamountFieldType() { + return "int64_t"; } /** - * @brief Get json string of asm field. + * @brief Get json string of assetamount field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAsmString( // line separate - const ElementsDecodeUnlockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asm__); + static std::string GetAssetamountString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.assetamount_); } /** - * @brief Set json object to asm field. + * @brief Set json object to assetamount field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAsmString( // line separate - ElementsDecodeUnlockingScript& obj, // NOLINT + static void SetAssetamountString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.asm__, json_value); + obj.assetamount_, json_value); } /** - * @brief Get of hex - * @return hex + * @brief Get of assetamountcommitment + * @return assetamountcommitment */ - std::string GetHex() const { - return hex_; + std::string GetAssetamountcommitment() const { + return assetamountcommitment_; } /** - * @brief Set to hex - * @param[in] hex setting value. + * @brief Set to assetamountcommitment + * @param[in] assetamountcommitment setting value. */ - void SetHex( // line separate - const std::string& hex) { // NOLINT - this->hex_ = hex; + void SetAssetamountcommitment( // line separate + const std::string& assetamountcommitment) { // NOLINT + this->assetamountcommitment_ = assetamountcommitment; } /** - * @brief Get data type of hex - * @return Data type of hex + * @brief Get data type of assetamountcommitment + * @return Data type of assetamountcommitment */ - static std::string GetHexFieldType() { + static std::string GetAssetamountcommitmentFieldType() { return "std::string"; } /** - * @brief Get json string of hex field. + * @brief Get json string of assetamountcommitment field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetHexString( // line separate - const ElementsDecodeUnlockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.hex_); + static std::string GetAssetamountcommitmentString( // line separate + const ElementsDecodeIssuance& obj) { // NOLINT + return cfd::core::ConvertToString(obj.assetamountcommitment_); } /** - * @brief Set json object to hex field. + * @brief Set json object to assetamountcommitment field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetHexString( // line separate - ElementsDecodeUnlockingScript& obj, // NOLINT + static void SetAssetamountcommitmentString( // line separate + ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.hex_, json_value); + obj.assetamountcommitment_, json_value); } /** - * @brief Set ignore item. - * @param[in] key ignore target key name. + * @brief Get of tokenamount + * @return tokenamount */ - void SetIgnoreItem(const std::string& key) { - ignore_items.insert(key); - } - - /** - * @brief Convert struct to class. - * @param[in] data struct data. - */ - void ConvertFromStruct( - const ElementsDecodeUnlockingScriptStruct& data); - - /** - * @brief Convert class to struct. - * @return struct data. - */ - ElementsDecodeUnlockingScriptStruct ConvertToStruct() const; - - protected: - /** - * @brief definition type of Map table. - */ - using ElementsDecodeUnlockingScriptMapTable = - cfd::core::JsonTableMap; - - /** - * @brief Get JSON mapping object. - * @return JSON mapping object. - * @see cfd::core::JsonClassBase::GetJsonMapper() - */ - virtual const ElementsDecodeUnlockingScriptMapTable& GetJsonMapper() const { // NOLINT - return json_mapper; - } - /** - * @brief Get item lists of JSON mapping. - * Fetch a list of target variable names in the order of definition. - * @return Item lists of JSON mapping. - * @see cfd::core::JsonClassBase::GetJsonItemList() - */ - virtual const std::vector& GetJsonItemList() const { - return item_list; - } - /** - * @brief Get ignore item lists of JSON mnapping. - * Ignore the target variable at Serialize. - * @return Item list of JSON mapping. - * @see cfd::core::JsonClassBase::GetIgnoreItem() - */ - virtual const std::set& GetIgnoreItem() const { - return ignore_items; - } - - private: - /** - * @brief JsonFunctionMap table - */ - static ElementsDecodeUnlockingScriptMapTable json_mapper; - /** - * @brief field name list. - */ - static std::vector item_list; - /** - * @brief ignore item list. - */ - std::set ignore_items; - - /** - * @brief JsonAPI(asm) value - */ - std::string asm__ = ""; - /** - * @brief JsonAPI(hex) value - */ - std::string hex_ = ""; -}; - -// ------------------------------------------------------------------------ -// ElementsDecodeIssuance -// ------------------------------------------------------------------------ -/** - * @brief JSON-API (ElementsDecodeIssuance) class - */ -class ElementsDecodeIssuance - : public cfd::core::JsonClassBase { - public: - ElementsDecodeIssuance() { - CollectFieldName(); - } - virtual ~ElementsDecodeIssuance() { - // do nothing - } - /** - * @brief collect field name. - */ - static void CollectFieldName(); - - /** - * @brief Get of assetBlindingNonce - * @return assetBlindingNonce - */ - std::string GetAssetBlindingNonce() const { - return asset_blinding_nonce_; + int64_t GetTokenamount() const { + return tokenamount_; } /** - * @brief Set to assetBlindingNonce - * @param[in] asset_blinding_nonce setting value. + * @brief Set to tokenamount + * @param[in] tokenamount setting value. */ - void SetAssetBlindingNonce( // line separate - const std::string& asset_blinding_nonce) { // NOLINT - this->asset_blinding_nonce_ = asset_blinding_nonce; + void SetTokenamount( // line separate + const int64_t& tokenamount) { // NOLINT + this->tokenamount_ = tokenamount; } /** - * @brief Get data type of assetBlindingNonce - * @return Data type of assetBlindingNonce + * @brief Get data type of tokenamount + * @return Data type of tokenamount */ - static std::string GetAssetBlindingNonceFieldType() { - return "std::string"; + static std::string GetTokenamountFieldType() { + return "int64_t"; } /** - * @brief Get json string of assetBlindingNonce field. + * @brief Get json string of tokenamount field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAssetBlindingNonceString( // line separate + static std::string GetTokenamountString( // line separate const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asset_blinding_nonce_); + return cfd::core::ConvertToString(obj.tokenamount_); } /** - * @brief Set json object to assetBlindingNonce field. + * @brief Set json object to tokenamount field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAssetBlindingNonceString( // line separate + static void SetTokenamountString( // line separate ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.asset_blinding_nonce_, json_value); + obj.tokenamount_, json_value); } /** - * @brief Get of assetEntropy - * @return assetEntropy + * @brief Get of tokenamountcommitment + * @return tokenamountcommitment */ - std::string GetAssetEntropy() const { - return asset_entropy_; + std::string GetTokenamountcommitment() const { + return tokenamountcommitment_; } /** - * @brief Set to assetEntropy - * @param[in] asset_entropy setting value. + * @brief Set to tokenamountcommitment + * @param[in] tokenamountcommitment setting value. */ - void SetAssetEntropy( // line separate - const std::string& asset_entropy) { // NOLINT - this->asset_entropy_ = asset_entropy; + void SetTokenamountcommitment( // line separate + const std::string& tokenamountcommitment) { // NOLINT + this->tokenamountcommitment_ = tokenamountcommitment; } /** - * @brief Get data type of assetEntropy - * @return Data type of assetEntropy + * @brief Get data type of tokenamountcommitment + * @return Data type of tokenamountcommitment */ - static std::string GetAssetEntropyFieldType() { + static std::string GetTokenamountcommitmentFieldType() { return "std::string"; } /** - * @brief Get json string of assetEntropy field. + * @brief Get json string of tokenamountcommitment field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAssetEntropyString( // line separate + static std::string GetTokenamountcommitmentString( // line separate const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asset_entropy_); + return cfd::core::ConvertToString(obj.tokenamountcommitment_); } /** - * @brief Set json object to assetEntropy field. + * @brief Set json object to tokenamountcommitment field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAssetEntropyString( // line separate + static void SetTokenamountcommitmentString( // line separate ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.asset_entropy_, json_value); + obj.tokenamountcommitment_, json_value); } /** - * @brief Get of contractHash - * @return contractHash + * @brief Get of assetRangeproof + * @return assetRangeproof */ - std::string GetContractHash() const { - return contract_hash_; + std::string GetAssetRangeproof() const { + return asset_rangeproof_; } /** - * @brief Set to contractHash - * @param[in] contract_hash setting value. + * @brief Set to assetRangeproof + * @param[in] asset_rangeproof setting value. */ - void SetContractHash( // line separate - const std::string& contract_hash) { // NOLINT - this->contract_hash_ = contract_hash; + void SetAssetRangeproof( // line separate + const std::string& asset_rangeproof) { // NOLINT + this->asset_rangeproof_ = asset_rangeproof; } /** - * @brief Get data type of contractHash - * @return Data type of contractHash + * @brief Get data type of assetRangeproof + * @return Data type of assetRangeproof */ - static std::string GetContractHashFieldType() { + static std::string GetAssetRangeproofFieldType() { return "std::string"; } /** - * @brief Get json string of contractHash field. + * @brief Get json string of assetRangeproof field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetContractHashString( // line separate + static std::string GetAssetRangeproofString( // line separate const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.contract_hash_); + return cfd::core::ConvertToString(obj.asset_rangeproof_); } /** - * @brief Set json object to contractHash field. + * @brief Set json object to assetRangeproof field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetContractHashString( // line separate + static void SetAssetRangeproofString( // line separate ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.contract_hash_, json_value); + obj.asset_rangeproof_, json_value); } /** - * @brief Get of isreissuance - * @return isreissuance + * @brief Get of tokenRangeproof + * @return tokenRangeproof */ - bool GetIsreissuance() const { - return isreissuance_; + std::string GetTokenRangeproof() const { + return token_rangeproof_; } /** - * @brief Set to isreissuance - * @param[in] isreissuance setting value. + * @brief Set to tokenRangeproof + * @param[in] token_rangeproof setting value. */ - void SetIsreissuance( // line separate - const bool& isreissuance) { // NOLINT - this->isreissuance_ = isreissuance; + void SetTokenRangeproof( // line separate + const std::string& token_rangeproof) { // NOLINT + this->token_rangeproof_ = token_rangeproof; } /** - * @brief Get data type of isreissuance - * @return Data type of isreissuance + * @brief Get data type of tokenRangeproof + * @return Data type of tokenRangeproof */ - static std::string GetIsreissuanceFieldType() { - return "bool"; + static std::string GetTokenRangeproofFieldType() { + return "std::string"; } /** - * @brief Get json string of isreissuance field. + * @brief Get json string of tokenRangeproof field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetIsreissuanceString( // line separate + static std::string GetTokenRangeproofString( // line separate const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.isreissuance_); + return cfd::core::ConvertToString(obj.token_rangeproof_); } /** - * @brief Set json object to isreissuance field. + * @brief Set json object to tokenRangeproof field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetIsreissuanceString( // line separate + static void SetTokenRangeproofString( // line separate ElementsDecodeIssuance& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.isreissuance_, json_value); + obj.token_rangeproof_, json_value); } /** - * @brief Get of token - * @return token + * @brief Set ignore item. + * @param[in] key ignore target key name. */ - std::string GetToken() const { - return token_; + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); } + /** - * @brief Set to token - * @param[in] token setting value. + * @brief Convert struct to class. + * @param[in] data struct data. */ - void SetToken( // line separate - const std::string& token) { // NOLINT - this->token_ = token; + void ConvertFromStruct( + const ElementsDecodeIssuanceStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + ElementsDecodeIssuanceStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using ElementsDecodeIssuanceMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const ElementsDecodeIssuanceMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static ElementsDecodeIssuanceMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(assetBlindingNonce) value + */ + std::string asset_blinding_nonce_ = ""; + /** + * @brief JsonAPI(assetEntropy) value + */ + std::string asset_entropy_ = ""; + /** + * @brief JsonAPI(contractHash) value + */ + std::string contract_hash_ = ""; + /** + * @brief JsonAPI(isreissuance) value + */ + bool isreissuance_ = false; + /** + * @brief JsonAPI(token) value + */ + std::string token_ = ""; + /** + * @brief JsonAPI(asset) value + */ + std::string asset_ = ""; + /** + * @brief JsonAPI(assetamount) value + */ + int64_t assetamount_ = 0; + /** + * @brief JsonAPI(assetamountcommitment) value + */ + std::string assetamountcommitment_ = ""; + /** + * @brief JsonAPI(tokenamount) value + */ + int64_t tokenamount_ = 0; + /** + * @brief JsonAPI(tokenamountcommitment) value + */ + std::string tokenamountcommitment_ = ""; + /** + * @brief JsonAPI(assetRangeproof) value + */ + std::string asset_rangeproof_ = ""; + /** + * @brief JsonAPI(tokenRangeproof) value + */ + std::string token_rangeproof_ = ""; +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeLockingScript +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (ElementsDecodeLockingScript) class + */ +class ElementsDecodeLockingScript + : public cfd::core::JsonClassBase { + public: + ElementsDecodeLockingScript() { + CollectFieldName(); + } + virtual ~ElementsDecodeLockingScript() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of asm + * @return asm + */ + std::string GetAsm() const { + return asm__; + } + /** + * @brief Set to asm + * @param[in] asm_ setting value. + */ + void SetAsm( // line separate + const std::string& asm_) { // NOLINT + this->asm__ = asm_; + } + /** + * @brief Get data type of asm + * @return Data type of asm + */ + static std::string GetAsmFieldType() { + return "std::string"; + } + /** + * @brief Get json string of asm field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetAsmString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asm__); + } + /** + * @brief Set json object to asm field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetAsmString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asm__, json_value); + } + + /** + * @brief Get of hex + * @return hex + */ + std::string GetHex() const { + return hex_; + } + /** + * @brief Set to hex + * @param[in] hex setting value. + */ + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; + } + /** + * @brief Get data type of hex + * @return Data type of hex + */ + static std::string GetHexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHexString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); + } + /** + * @brief Set json object to hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHexString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.hex_, json_value); + } + + /** + * @brief Get of reqSigs + * @return reqSigs + */ + int GetReqSigs() const { + return req_sigs_; + } + /** + * @brief Set to reqSigs + * @param[in] req_sigs setting value. + */ + void SetReqSigs( // line separate + const int& req_sigs) { // NOLINT + this->req_sigs_ = req_sigs; + } + /** + * @brief Get data type of reqSigs + * @return Data type of reqSigs + */ + static std::string GetReqSigsFieldType() { + return "int"; + } + /** + * @brief Get json string of reqSigs field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetReqSigsString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.req_sigs_); + } + /** + * @brief Set json object to reqSigs field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetReqSigsString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.req_sigs_, json_value); + } + + /** + * @brief Get of type + * @return type + */ + std::string GetType() const { + return type_; + } + /** + * @brief Set to type + * @param[in] type setting value. + */ + void SetType( // line separate + const std::string& type) { // NOLINT + this->type_ = type; + } + /** + * @brief Get data type of type + * @return Data type of type + */ + static std::string GetTypeFieldType() { + return "std::string"; + } + /** + * @brief Get json string of type field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetTypeString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.type_); + } + /** + * @brief Set json object to type field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetTypeString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.type_, json_value); + } + + /** + * @brief Get of addresses. + * @return addresses + */ + JsonValueVector& GetAddresses() { // NOLINT + return addresses_; + } + /** + * @brief Set to addresses. + * @param[in] addresses setting value. + */ + void SetAddresses( // line separate + const JsonValueVector& addresses) { // NOLINT + this->addresses_ = addresses; + } + /** + * @brief Get data type of addresses. + * @return Data type of addresses. + */ + static std::string GetAddressesFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of addresses field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetAddressesString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.addresses_.Serialize(); + } + /** + * @brief Set json object to addresses field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetAddressesString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + obj.addresses_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of pegout_chain + * @return pegout_chain + */ + std::string GetPegout_chain() const { + return pegout_chain_; + } + /** + * @brief Set to pegout_chain + * @param[in] pegout_chain setting value. + */ + void SetPegout_chain( // line separate + const std::string& pegout_chain) { // NOLINT + this->pegout_chain_ = pegout_chain; + } + /** + * @brief Get data type of pegout_chain + * @return Data type of pegout_chain + */ + static std::string GetPegout_chainFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pegout_chain field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPegout_chainString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pegout_chain_); + } + /** + * @brief Set json object to pegout_chain field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPegout_chainString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pegout_chain_, json_value); + } + + /** + * @brief Get of pegout_asm + * @return pegout_asm + */ + std::string GetPegout_asm() const { + return pegout_asm_; + } + /** + * @brief Set to pegout_asm + * @param[in] pegout_asm setting value. + */ + void SetPegout_asm( // line separate + const std::string& pegout_asm) { // NOLINT + this->pegout_asm_ = pegout_asm; + } + /** + * @brief Get data type of pegout_asm + * @return Data type of pegout_asm + */ + static std::string GetPegout_asmFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pegout_asm field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPegout_asmString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pegout_asm_); + } + /** + * @brief Set json object to pegout_asm field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPegout_asmString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pegout_asm_, json_value); + } + + /** + * @brief Get of pegout_hex + * @return pegout_hex + */ + std::string GetPegout_hex() const { + return pegout_hex_; + } + /** + * @brief Set to pegout_hex + * @param[in] pegout_hex setting value. + */ + void SetPegout_hex( // line separate + const std::string& pegout_hex) { // NOLINT + this->pegout_hex_ = pegout_hex; + } + /** + * @brief Get data type of pegout_hex + * @return Data type of pegout_hex + */ + static std::string GetPegout_hexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pegout_hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPegout_hexString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pegout_hex_); + } + /** + * @brief Set json object to pegout_hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPegout_hexString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pegout_hex_, json_value); + } + + /** + * @brief Get of pegout_reqSigs + * @return pegout_reqSigs + */ + int GetPegout_reqSigs() const { + return pegout_req_sigs_; + } + /** + * @brief Set to pegout_reqSigs + * @param[in] pegout_req_sigs setting value. + */ + void SetPegout_reqSigs( // line separate + const int& pegout_req_sigs) { // NOLINT + this->pegout_req_sigs_ = pegout_req_sigs; + } + /** + * @brief Get data type of pegout_reqSigs + * @return Data type of pegout_reqSigs + */ + static std::string GetPegout_reqSigsFieldType() { + return "int"; + } + /** + * @brief Get json string of pegout_reqSigs field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPegout_reqSigsString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pegout_req_sigs_); + } + /** + * @brief Set json object to pegout_reqSigs field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPegout_reqSigsString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pegout_req_sigs_, json_value); + } + + /** + * @brief Get of pegout_type + * @return pegout_type + */ + std::string GetPegout_type() const { + return pegout_type_; + } + /** + * @brief Set to pegout_type + * @param[in] pegout_type setting value. + */ + void SetPegout_type( // line separate + const std::string& pegout_type) { // NOLINT + this->pegout_type_ = pegout_type; + } + /** + * @brief Get data type of pegout_type + * @return Data type of pegout_type + */ + static std::string GetPegout_typeFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pegout_type field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPegout_typeString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pegout_type_); + } + /** + * @brief Set json object to pegout_type field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPegout_typeString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pegout_type_, json_value); + } + + /** + * @brief Get of pegout_addresses. + * @return pegout_addresses + */ + JsonValueVector& GetPegout_addresses() { // NOLINT + return pegout_addresses_; + } + /** + * @brief Set to pegout_addresses. + * @param[in] pegout_addresses setting value. + */ + void SetPegout_addresses( // line separate + const JsonValueVector& pegout_addresses) { // NOLINT + this->pegout_addresses_ = pegout_addresses; + } + /** + * @brief Get data type of pegout_addresses. + * @return Data type of pegout_addresses. + */ + static std::string GetPegout_addressesFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of pegout_addresses field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetPegout_addressesString( // line separate + const ElementsDecodeLockingScript& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.pegout_addresses_.Serialize(); + } + /** + * @brief Set json object to pegout_addresses field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetPegout_addressesString( // line separate + ElementsDecodeLockingScript& obj, // NOLINT + const UniValue& json_value) { + obj.pegout_addresses_.DeserializeUniValue(json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const ElementsDecodeLockingScriptStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + ElementsDecodeLockingScriptStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using ElementsDecodeLockingScriptMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const ElementsDecodeLockingScriptMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static ElementsDecodeLockingScriptMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(asm) value + */ + std::string asm__ = ""; + /** + * @brief JsonAPI(hex) value + */ + std::string hex_ = ""; + /** + * @brief JsonAPI(reqSigs) value + */ + int req_sigs_ = 0; + /** + * @brief JsonAPI(type) value + */ + std::string type_ = ""; + /** + * @brief JsonAPI(addresses) value + */ + JsonValueVector addresses_; // NOLINT + /** + * @brief JsonAPI(pegout_chain) value + */ + std::string pegout_chain_ = ""; + /** + * @brief JsonAPI(pegout_asm) value + */ + std::string pegout_asm_ = ""; + /** + * @brief JsonAPI(pegout_hex) value + */ + std::string pegout_hex_ = ""; + /** + * @brief JsonAPI(pegout_reqSigs) value + */ + int pegout_req_sigs_ = 0; + /** + * @brief JsonAPI(pegout_type) value + */ + std::string pegout_type_ = ""; + /** + * @brief JsonAPI(pegout_addresses) value + */ + JsonValueVector pegout_addresses_; // NOLINT +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeUnlockingScript +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (ElementsDecodeUnlockingScript) class + */ +class ElementsDecodeUnlockingScript + : public cfd::core::JsonClassBase { + public: + ElementsDecodeUnlockingScript() { + CollectFieldName(); + } + virtual ~ElementsDecodeUnlockingScript() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of asm + * @return asm + */ + std::string GetAsm() const { + return asm__; + } + /** + * @brief Set to asm + * @param[in] asm_ setting value. + */ + void SetAsm( // line separate + const std::string& asm_) { // NOLINT + this->asm__ = asm_; + } + /** + * @brief Get data type of asm + * @return Data type of asm + */ + static std::string GetAsmFieldType() { + return "std::string"; + } + /** + * @brief Get json string of asm field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetAsmString( // line separate + const ElementsDecodeUnlockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asm__); + } + /** + * @brief Set json object to asm field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetAsmString( // line separate + ElementsDecodeUnlockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asm__, json_value); + } + + /** + * @brief Get of hex + * @return hex + */ + std::string GetHex() const { + return hex_; + } + /** + * @brief Set to hex + * @param[in] hex setting value. + */ + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; + } + /** + * @brief Get data type of hex + * @return Data type of hex + */ + static std::string GetHexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHexString( // line separate + const ElementsDecodeUnlockingScript& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); + } + /** + * @brief Set json object to hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHexString( // line separate + ElementsDecodeUnlockingScript& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.hex_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const ElementsDecodeUnlockingScriptStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + ElementsDecodeUnlockingScriptStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using ElementsDecodeUnlockingScriptMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const ElementsDecodeUnlockingScriptMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static ElementsDecodeUnlockingScriptMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(asm) value + */ + std::string asm__ = ""; + /** + * @brief JsonAPI(hex) value + */ + std::string hex_ = ""; +}; + +// ------------------------------------------------------------------------ +// PsbtBip32Data +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (PsbtBip32Data) class + */ +class PsbtBip32Data + : public cfd::core::JsonClassBase { + public: + PsbtBip32Data() { + CollectFieldName(); + } + virtual ~PsbtBip32Data() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of pubkey + * @return pubkey + */ + std::string GetPubkey() const { + return pubkey_; + } + /** + * @brief Set to pubkey + * @param[in] pubkey setting value. + */ + void SetPubkey( // line separate + const std::string& pubkey) { // NOLINT + this->pubkey_ = pubkey; + } + /** + * @brief Get data type of pubkey + * @return Data type of pubkey + */ + static std::string GetPubkeyFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pubkey field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPubkeyString( // line separate + const PsbtBip32Data& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pubkey_); + } + /** + * @brief Set json object to pubkey field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPubkeyString( // line separate + PsbtBip32Data& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pubkey_, json_value); + } + + /** + * @brief Get of master_fingerprint + * @return master_fingerprint + */ + std::string GetMaster_fingerprint() const { + return master_fingerprint_; + } + /** + * @brief Set to master_fingerprint + * @param[in] master_fingerprint setting value. + */ + void SetMaster_fingerprint( // line separate + const std::string& master_fingerprint) { // NOLINT + this->master_fingerprint_ = master_fingerprint; + } + /** + * @brief Get data type of master_fingerprint + * @return Data type of master_fingerprint + */ + static std::string GetMaster_fingerprintFieldType() { + return "std::string"; + } + /** + * @brief Get json string of master_fingerprint field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetMaster_fingerprintString( // line separate + const PsbtBip32Data& obj) { // NOLINT + return cfd::core::ConvertToString(obj.master_fingerprint_); + } + /** + * @brief Set json object to master_fingerprint field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetMaster_fingerprintString( // line separate + PsbtBip32Data& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.master_fingerprint_, json_value); + } + + /** + * @brief Get of path + * @return path + */ + std::string GetPath() const { + return path_; + } + /** + * @brief Set to path + * @param[in] path setting value. + */ + void SetPath( // line separate + const std::string& path) { // NOLINT + this->path_ = path; + } + /** + * @brief Get data type of path + * @return Data type of path + */ + static std::string GetPathFieldType() { + return "std::string"; + } + /** + * @brief Get json string of path field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPathString( // line separate + const PsbtBip32Data& obj) { // NOLINT + return cfd::core::ConvertToString(obj.path_); + } + /** + * @brief Set json object to path field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPathString( // line separate + PsbtBip32Data& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.path_, json_value); + } + + /** + * @brief Get of descriptor + * @return descriptor + */ + std::string GetDescriptor() const { + return descriptor_; + } + /** + * @brief Set to descriptor + * @param[in] descriptor setting value. + */ + void SetDescriptor( // line separate + const std::string& descriptor) { // NOLINT + this->descriptor_ = descriptor; + } + /** + * @brief Get data type of descriptor + * @return Data type of descriptor + */ + static std::string GetDescriptorFieldType() { + return "std::string"; + } + /** + * @brief Get json string of descriptor field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetDescriptorString( // line separate + const PsbtBip32Data& obj) { // NOLINT + return cfd::core::ConvertToString(obj.descriptor_); + } + /** + * @brief Set json object to descriptor field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetDescriptorString( // line separate + PsbtBip32Data& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.descriptor_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const PsbtBip32DataStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + PsbtBip32DataStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using PsbtBip32DataMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const PsbtBip32DataMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static PsbtBip32DataMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(pubkey) value + */ + std::string pubkey_ = ""; + /** + * @brief JsonAPI(master_fingerprint) value + */ + std::string master_fingerprint_ = ""; + /** + * @brief JsonAPI(path) value + */ + std::string path_ = ""; + /** + * @brief JsonAPI(descriptor) value + */ + std::string descriptor_ = ""; +}; + +// ------------------------------------------------------------------------ +// PsbtMapData +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (PsbtMapData) class + */ +class PsbtMapData + : public cfd::core::JsonClassBase { + public: + PsbtMapData() { + CollectFieldName(); + } + virtual ~PsbtMapData() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of key + * @return key + */ + std::string GetKey() const { + return key_; + } + /** + * @brief Set to key + * @param[in] key setting value. + */ + void SetKey( // line separate + const std::string& key) { // NOLINT + this->key_ = key; + } + /** + * @brief Get data type of key + * @return Data type of key + */ + static std::string GetKeyFieldType() { + return "std::string"; + } + /** + * @brief Get json string of key field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetKeyString( // line separate + const PsbtMapData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.key_); + } + /** + * @brief Set json object to key field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetKeyString( // line separate + PsbtMapData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.key_, json_value); + } + + /** + * @brief Get of value + * @return value + */ + std::string GetValue() const { + return value_; + } + /** + * @brief Set to value + * @param[in] value setting value. + */ + void SetValue( // line separate + const std::string& value) { // NOLINT + this->value_ = value; + } + /** + * @brief Get data type of value + * @return Data type of value + */ + static std::string GetValueFieldType() { + return "std::string"; + } + /** + * @brief Get json string of value field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetValueString( // line separate + const PsbtMapData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.value_); + } + /** + * @brief Set json object to value field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetValueString( // line separate + PsbtMapData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.value_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const PsbtMapDataStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + PsbtMapDataStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using PsbtMapDataMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const PsbtMapDataMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static PsbtMapDataMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(key) value + */ + std::string key_ = ""; + /** + * @brief JsonAPI(value) value + */ + std::string value_ = ""; +}; + +// ------------------------------------------------------------------------ +// PsbtScriptData +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (PsbtScriptData) class + */ +class PsbtScriptData + : public cfd::core::JsonClassBase { + public: + PsbtScriptData() { + CollectFieldName(); + } + virtual ~PsbtScriptData() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of asm + * @return asm + */ + std::string GetAsm() const { + return asm__; + } + /** + * @brief Set to asm + * @param[in] asm_ setting value. + */ + void SetAsm( // line separate + const std::string& asm_) { // NOLINT + this->asm__ = asm_; + } + /** + * @brief Get data type of asm + * @return Data type of asm + */ + static std::string GetAsmFieldType() { + return "std::string"; + } + /** + * @brief Get json string of asm field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetAsmString( // line separate + const PsbtScriptData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.asm__); + } + /** + * @brief Set json object to asm field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetAsmString( // line separate + PsbtScriptData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.asm__, json_value); + } + + /** + * @brief Get of hex + * @return hex + */ + std::string GetHex() const { + return hex_; + } + /** + * @brief Set to hex + * @param[in] hex setting value. + */ + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; + } + /** + * @brief Get data type of hex + * @return Data type of hex + */ + static std::string GetHexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHexString( // line separate + const PsbtScriptData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); + } + /** + * @brief Set json object to hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHexString( // line separate + PsbtScriptData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.hex_, json_value); + } + + /** + * @brief Get of type + * @return type + */ + std::string GetType() const { + return type_; + } + /** + * @brief Set to type + * @param[in] type setting value. + */ + void SetType( // line separate + const std::string& type) { // NOLINT + this->type_ = type; + } + /** + * @brief Get data type of type + * @return Data type of type + */ + static std::string GetTypeFieldType() { + return "std::string"; + } + /** + * @brief Get json string of type field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetTypeString( // line separate + const PsbtScriptData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.type_); + } + /** + * @brief Set json object to type field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetTypeString( // line separate + PsbtScriptData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.type_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const PsbtScriptDataStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + PsbtScriptDataStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using PsbtScriptDataMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const PsbtScriptDataMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static PsbtScriptDataMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(asm) value + */ + std::string asm__ = ""; + /** + * @brief JsonAPI(hex) value + */ + std::string hex_ = ""; + /** + * @brief JsonAPI(type) value + */ + std::string type_ = ""; +}; + +// ------------------------------------------------------------------------ +// PsbtSignatureData +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (PsbtSignatureData) class + */ +class PsbtSignatureData + : public cfd::core::JsonClassBase { + public: + PsbtSignatureData() { + CollectFieldName(); + } + virtual ~PsbtSignatureData() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of pubkey + * @return pubkey + */ + std::string GetPubkey() const { + return pubkey_; + } + /** + * @brief Set to pubkey + * @param[in] pubkey setting value. + */ + void SetPubkey( // line separate + const std::string& pubkey) { // NOLINT + this->pubkey_ = pubkey; + } + /** + * @brief Get data type of pubkey + * @return Data type of pubkey + */ + static std::string GetPubkeyFieldType() { + return "std::string"; + } + /** + * @brief Get json string of pubkey field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPubkeyString( // line separate + const PsbtSignatureData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.pubkey_); + } + /** + * @brief Set json object to pubkey field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetPubkeyString( // line separate + PsbtSignatureData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.pubkey_, json_value); + } + + /** + * @brief Get of signature + * @return signature + */ + std::string GetSignature() const { + return signature_; + } + /** + * @brief Set to signature + * @param[in] signature setting value. + */ + void SetSignature( // line separate + const std::string& signature) { // NOLINT + this->signature_ = signature; + } + /** + * @brief Get data type of signature + * @return Data type of signature + */ + static std::string GetSignatureFieldType() { + return "std::string"; + } + /** + * @brief Get json string of signature field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetSignatureString( // line separate + const PsbtSignatureData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.signature_); + } + /** + * @brief Set json object to signature field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetSignatureString( // line separate + PsbtSignatureData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.signature_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const PsbtSignatureDataStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + PsbtSignatureDataStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using PsbtSignatureDataMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const PsbtSignatureDataMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static PsbtSignatureDataMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(pubkey) value + */ + std::string pubkey_ = ""; + /** + * @brief JsonAPI(signature) value + */ + std::string signature_ = ""; +}; + +// ------------------------------------------------------------------------ +// XpubData +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (XpubData) class + */ +class XpubData + : public cfd::core::JsonClassBase { + public: + XpubData() { + CollectFieldName(); + } + virtual ~XpubData() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of base58 + * @return base58 + */ + std::string GetBase58() const { + return base58_; + } + /** + * @brief Set to base58 + * @param[in] base58 setting value. + */ + void SetBase58( // line separate + const std::string& base58) { // NOLINT + this->base58_ = base58; + } + /** + * @brief Get data type of base58 + * @return Data type of base58 + */ + static std::string GetBase58FieldType() { + return "std::string"; + } + /** + * @brief Get json string of base58 field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetBase58String( // line separate + const XpubData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.base58_); + } + /** + * @brief Set json object to base58 field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetBase58String( // line separate + XpubData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.base58_, json_value); + } + + /** + * @brief Get of hex + * @return hex + */ + std::string GetHex() const { + return hex_; + } + /** + * @brief Set to hex + * @param[in] hex setting value. + */ + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; + } + /** + * @brief Get data type of hex + * @return Data type of hex + */ + static std::string GetHexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHexString( // line separate + const XpubData& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); + } + /** + * @brief Set json object to hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHexString( // line separate + XpubData& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.hex_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const XpubDataStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + XpubDataStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using XpubDataMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const XpubDataMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static XpubDataMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(base58) value + */ + std::string base58_ = ""; + /** + * @brief JsonAPI(hex) value + */ + std::string hex_ = ""; +}; + +// ------------------------------------------------------------------------ +// DecodePsbtInput +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (DecodePsbtInput) class + */ +class DecodePsbtInput + : public cfd::core::JsonClassBase { + public: + DecodePsbtInput() { + CollectFieldName(); + } + virtual ~DecodePsbtInput() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of non_witness_utxo_hex + * @return non_witness_utxo_hex + */ + std::string GetNon_witness_utxo_hex() const { + return non_witness_utxo_hex_; + } + /** + * @brief Set to non_witness_utxo_hex + * @param[in] non_witness_utxo_hex setting value. + */ + void SetNon_witness_utxo_hex( // line separate + const std::string& non_witness_utxo_hex) { // NOLINT + this->non_witness_utxo_hex_ = non_witness_utxo_hex; + } + /** + * @brief Get data type of non_witness_utxo_hex + * @return Data type of non_witness_utxo_hex + */ + static std::string GetNon_witness_utxo_hexFieldType() { + return "std::string"; + } + /** + * @brief Get json string of non_witness_utxo_hex field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetNon_witness_utxo_hexString( // line separate + const DecodePsbtInput& obj) { // NOLINT + return cfd::core::ConvertToString(obj.non_witness_utxo_hex_); + } + /** + * @brief Set json object to non_witness_utxo_hex field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetNon_witness_utxo_hexString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.non_witness_utxo_hex_, json_value); + } + + /** + * @brief Get of non_witness_utxo. + * @return non_witness_utxo + */ + DecodeRawTransactionResponse& GetNon_witness_utxo() { // NOLINT + return non_witness_utxo_; + } + /** + * @brief Set to non_witness_utxo. + * @param[in] non_witness_utxo setting value. + */ + void SetNon_witness_utxo( // line separate + const DecodeRawTransactionResponse& non_witness_utxo) { // NOLINT + this->non_witness_utxo_ = non_witness_utxo; + } + /** + * @brief Get data type of non_witness_utxo. + * @return Data type of non_witness_utxo. + */ + static std::string GetNon_witness_utxoFieldType() { + return "DecodeRawTransactionResponse"; // NOLINT + } + /** + * @brief Get json string of non_witness_utxo field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetNon_witness_utxoString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.non_witness_utxo_.Serialize(); + } + /** + * @brief Set json object to non_witness_utxo field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetNon_witness_utxoString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.non_witness_utxo_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of witness_utxo. + * @return witness_utxo + */ + DecodePsbtUtxo& GetWitness_utxo() { // NOLINT + return witness_utxo_; + } + /** + * @brief Set to witness_utxo. + * @param[in] witness_utxo setting value. + */ + void SetWitness_utxo( // line separate + const DecodePsbtUtxo& witness_utxo) { // NOLINT + this->witness_utxo_ = witness_utxo; + } + /** + * @brief Get data type of witness_utxo. + * @return Data type of witness_utxo. + */ + static std::string GetWitness_utxoFieldType() { + return "DecodePsbtUtxo"; // NOLINT + } + /** + * @brief Get json string of witness_utxo field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetWitness_utxoString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.witness_utxo_.Serialize(); + } + /** + * @brief Set json object to witness_utxo field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetWitness_utxoString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.witness_utxo_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of partial_signatures. + * @return partial_signatures + */ + JsonObjectVector& GetPartial_signatures() { // NOLINT + return partial_signatures_; + } + /** + * @brief Set to partial_signatures. + * @param[in] partial_signatures setting value. + */ + void SetPartial_signatures( // line separate + const JsonObjectVector& partial_signatures) { // NOLINT + this->partial_signatures_ = partial_signatures; + } + /** + * @brief Get data type of partial_signatures. + * @return Data type of partial_signatures. + */ + static std::string GetPartial_signaturesFieldType() { + return "JsonObjectVector"; // NOLINT + } + /** + * @brief Get json string of partial_signatures field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetPartial_signaturesString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.partial_signatures_.Serialize(); + } + /** + * @brief Set json object to partial_signatures field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetPartial_signaturesString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.partial_signatures_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of sighash + * @return sighash + */ + std::string GetSighash() const { + return sighash_; + } + /** + * @brief Set to sighash + * @param[in] sighash setting value. + */ + void SetSighash( // line separate + const std::string& sighash) { // NOLINT + this->sighash_ = sighash; + } + /** + * @brief Get data type of sighash + * @return Data type of sighash + */ + static std::string GetSighashFieldType() { + return "std::string"; + } + /** + * @brief Get json string of sighash field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetSighashString( // line separate + const DecodePsbtInput& obj) { // NOLINT + return cfd::core::ConvertToString(obj.sighash_); + } + /** + * @brief Set json object to sighash field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetSighashString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.sighash_, json_value); + } + + /** + * @brief Get of redeem_script. + * @return redeem_script + */ + PsbtScriptData& GetRedeem_script() { // NOLINT + return redeem_script_; + } + /** + * @brief Set to redeem_script. + * @param[in] redeem_script setting value. + */ + void SetRedeem_script( // line separate + const PsbtScriptData& redeem_script) { // NOLINT + this->redeem_script_ = redeem_script; + } + /** + * @brief Get data type of redeem_script. + * @return Data type of redeem_script. + */ + static std::string GetRedeem_scriptFieldType() { + return "PsbtScriptData"; // NOLINT + } + /** + * @brief Get json string of redeem_script field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetRedeem_scriptString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.redeem_script_.Serialize(); + } + /** + * @brief Set json object to redeem_script field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetRedeem_scriptString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.redeem_script_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of witness_script. + * @return witness_script + */ + PsbtScriptData& GetWitness_script() { // NOLINT + return witness_script_; + } + /** + * @brief Set to witness_script. + * @param[in] witness_script setting value. + */ + void SetWitness_script( // line separate + const PsbtScriptData& witness_script) { // NOLINT + this->witness_script_ = witness_script; + } + /** + * @brief Get data type of witness_script. + * @return Data type of witness_script. + */ + static std::string GetWitness_scriptFieldType() { + return "PsbtScriptData"; // NOLINT + } + /** + * @brief Get json string of witness_script field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetWitness_scriptString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.witness_script_.Serialize(); + } + /** + * @brief Set json object to witness_script field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetWitness_scriptString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.witness_script_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of bip32_derivs. + * @return bip32_derivs + */ + JsonObjectVector& GetBip32_derivs() { // NOLINT + return bip32_derivs_; + } + /** + * @brief Set to bip32_derivs. + * @param[in] bip32_derivs setting value. + */ + void SetBip32_derivs( // line separate + const JsonObjectVector& bip32_derivs) { // NOLINT + this->bip32_derivs_ = bip32_derivs; + } + /** + * @brief Get data type of bip32_derivs. + * @return Data type of bip32_derivs. + */ + static std::string GetBip32_derivsFieldType() { + return "JsonObjectVector"; // NOLINT + } + /** + * @brief Get json string of bip32_derivs field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetBip32_derivsString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.bip32_derivs_.Serialize(); + } + /** + * @brief Set json object to bip32_derivs field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetBip32_derivsString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.bip32_derivs_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of final_scriptsig. + * @return final_scriptsig + */ + DecodeUnlockingScript& GetFinal_scriptsig() { // NOLINT + return final_scriptsig_; + } + /** + * @brief Set to final_scriptsig. + * @param[in] final_scriptsig setting value. + */ + void SetFinal_scriptsig( // line separate + const DecodeUnlockingScript& final_scriptsig) { // NOLINT + this->final_scriptsig_ = final_scriptsig; + } + /** + * @brief Get data type of final_scriptsig. + * @return Data type of final_scriptsig. + */ + static std::string GetFinal_scriptsigFieldType() { + return "DecodeUnlockingScript"; // NOLINT + } + /** + * @brief Get json string of final_scriptsig field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetFinal_scriptsigString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.final_scriptsig_.Serialize(); + } + /** + * @brief Set json object to final_scriptsig field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetFinal_scriptsigString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.final_scriptsig_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of final_scriptwitness. + * @return final_scriptwitness + */ + JsonValueVector& GetFinal_scriptwitness() { // NOLINT + return final_scriptwitness_; + } + /** + * @brief Set to final_scriptwitness. + * @param[in] final_scriptwitness setting value. + */ + void SetFinal_scriptwitness( // line separate + const JsonValueVector& final_scriptwitness) { // NOLINT + this->final_scriptwitness_ = final_scriptwitness; + } + /** + * @brief Get data type of final_scriptwitness. + * @return Data type of final_scriptwitness. + */ + static std::string GetFinal_scriptwitnessFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of final_scriptwitness field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetFinal_scriptwitnessString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.final_scriptwitness_.Serialize(); + } + /** + * @brief Set json object to final_scriptwitness field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetFinal_scriptwitnessString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.final_scriptwitness_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of unknown. + * @return unknown + */ + JsonObjectVector& GetUnknown() { // NOLINT + return unknown_; + } + /** + * @brief Set to unknown. + * @param[in] unknown setting value. + */ + void SetUnknown( // line separate + const JsonObjectVector& unknown) { // NOLINT + this->unknown_ = unknown; + } + /** + * @brief Get data type of unknown. + * @return Data type of unknown. + */ + static std::string GetUnknownFieldType() { + return "JsonObjectVector"; // NOLINT + } + /** + * @brief Get json string of unknown field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetUnknownString( // line separate + const DecodePsbtInput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.unknown_.Serialize(); + } + /** + * @brief Set json object to unknown field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetUnknownString( // line separate + DecodePsbtInput& obj, // NOLINT + const UniValue& json_value) { + obj.unknown_.DeserializeUniValue(json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const DecodePsbtInputStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + DecodePsbtInputStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using DecodePsbtInputMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const DecodePsbtInputMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static DecodePsbtInputMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(non_witness_utxo_hex) value + */ + std::string non_witness_utxo_hex_ = ""; + /** + * @brief JsonAPI(non_witness_utxo) value + */ + DecodeRawTransactionResponse non_witness_utxo_; // NOLINT + /** + * @brief JsonAPI(witness_utxo) value + */ + DecodePsbtUtxo witness_utxo_; // NOLINT + /** + * @brief JsonAPI(partial_signatures) value + */ + JsonObjectVector partial_signatures_; // NOLINT + /** + * @brief JsonAPI(sighash) value + */ + std::string sighash_ = ""; + /** + * @brief JsonAPI(redeem_script) value + */ + PsbtScriptData redeem_script_; // NOLINT + /** + * @brief JsonAPI(witness_script) value + */ + PsbtScriptData witness_script_; // NOLINT + /** + * @brief JsonAPI(bip32_derivs) value + */ + JsonObjectVector bip32_derivs_; // NOLINT + /** + * @brief JsonAPI(final_scriptsig) value + */ + DecodeUnlockingScript final_scriptsig_; // NOLINT + /** + * @brief JsonAPI(final_scriptwitness) value + */ + JsonValueVector final_scriptwitness_; // NOLINT + /** + * @brief JsonAPI(unknown) value + */ + JsonObjectVector unknown_; // NOLINT +}; + +// ------------------------------------------------------------------------ +// DecodePsbtOutput +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (DecodePsbtOutput) class + */ +class DecodePsbtOutput + : public cfd::core::JsonClassBase { + public: + DecodePsbtOutput() { + CollectFieldName(); + } + virtual ~DecodePsbtOutput() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of redeem_script. + * @return redeem_script + */ + PsbtScriptData& GetRedeem_script() { // NOLINT + return redeem_script_; + } + /** + * @brief Set to redeem_script. + * @param[in] redeem_script setting value. + */ + void SetRedeem_script( // line separate + const PsbtScriptData& redeem_script) { // NOLINT + this->redeem_script_ = redeem_script; + } + /** + * @brief Get data type of redeem_script. + * @return Data type of redeem_script. + */ + static std::string GetRedeem_scriptFieldType() { + return "PsbtScriptData"; // NOLINT + } + /** + * @brief Get json string of redeem_script field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetRedeem_scriptString( // line separate + const DecodePsbtOutput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.redeem_script_.Serialize(); + } + /** + * @brief Set json object to redeem_script field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetRedeem_scriptString( // line separate + DecodePsbtOutput& obj, // NOLINT + const UniValue& json_value) { + obj.redeem_script_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of witness_script. + * @return witness_script + */ + PsbtScriptData& GetWitness_script() { // NOLINT + return witness_script_; + } + /** + * @brief Set to witness_script. + * @param[in] witness_script setting value. + */ + void SetWitness_script( // line separate + const PsbtScriptData& witness_script) { // NOLINT + this->witness_script_ = witness_script; + } + /** + * @brief Get data type of witness_script. + * @return Data type of witness_script. + */ + static std::string GetWitness_scriptFieldType() { + return "PsbtScriptData"; // NOLINT + } + /** + * @brief Get json string of witness_script field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetWitness_scriptString( // line separate + const DecodePsbtOutput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.witness_script_.Serialize(); + } + /** + * @brief Set json object to witness_script field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetWitness_scriptString( // line separate + DecodePsbtOutput& obj, // NOLINT + const UniValue& json_value) { + obj.witness_script_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of bip32_derivs. + * @return bip32_derivs + */ + JsonObjectVector& GetBip32_derivs() { // NOLINT + return bip32_derivs_; + } + /** + * @brief Set to bip32_derivs. + * @param[in] bip32_derivs setting value. + */ + void SetBip32_derivs( // line separate + const JsonObjectVector& bip32_derivs) { // NOLINT + this->bip32_derivs_ = bip32_derivs; + } + /** + * @brief Get data type of bip32_derivs. + * @return Data type of bip32_derivs. + */ + static std::string GetBip32_derivsFieldType() { + return "JsonObjectVector"; // NOLINT + } + /** + * @brief Get json string of bip32_derivs field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetBip32_derivsString( // line separate + const DecodePsbtOutput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.bip32_derivs_.Serialize(); + } + /** + * @brief Set json object to bip32_derivs field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetBip32_derivsString( // line separate + DecodePsbtOutput& obj, // NOLINT + const UniValue& json_value) { + obj.bip32_derivs_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of unknown. + * @return unknown + */ + JsonObjectVector& GetUnknown() { // NOLINT + return unknown_; + } + /** + * @brief Set to unknown. + * @param[in] unknown setting value. + */ + void SetUnknown( // line separate + const JsonObjectVector& unknown) { // NOLINT + this->unknown_ = unknown; + } + /** + * @brief Get data type of unknown. + * @return Data type of unknown. + */ + static std::string GetUnknownFieldType() { + return "JsonObjectVector"; // NOLINT + } + /** + * @brief Get json string of unknown field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetUnknownString( // line separate + const DecodePsbtOutput& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.unknown_.Serialize(); + } + /** + * @brief Set json object to unknown field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetUnknownString( // line separate + DecodePsbtOutput& obj, // NOLINT + const UniValue& json_value) { + obj.unknown_.DeserializeUniValue(json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const DecodePsbtOutputStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + DecodePsbtOutputStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using DecodePsbtOutputMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const DecodePsbtOutputMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static DecodePsbtOutputMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(redeem_script) value + */ + PsbtScriptData redeem_script_; // NOLINT + /** + * @brief JsonAPI(witness_script) value + */ + PsbtScriptData witness_script_; // NOLINT + /** + * @brief JsonAPI(bip32_derivs) value + */ + JsonObjectVector bip32_derivs_; // NOLINT + /** + * @brief JsonAPI(unknown) value + */ + JsonObjectVector unknown_; // NOLINT +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionTxIn +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (ElementsDecodeRawTransactionTxIn) class + */ +class ElementsDecodeRawTransactionTxIn + : public cfd::core::JsonClassBase { + public: + ElementsDecodeRawTransactionTxIn() { + CollectFieldName(); + } + virtual ~ElementsDecodeRawTransactionTxIn() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of coinbase + * @return coinbase + */ + std::string GetCoinbase() const { + return coinbase_; + } + /** + * @brief Set to coinbase + * @param[in] coinbase setting value. + */ + void SetCoinbase( // line separate + const std::string& coinbase) { // NOLINT + this->coinbase_ = coinbase; + } + /** + * @brief Get data type of coinbase + * @return Data type of coinbase + */ + static std::string GetCoinbaseFieldType() { + return "std::string"; + } + /** + * @brief Get json string of coinbase field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetCoinbaseString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.coinbase_); + } + /** + * @brief Set json object to coinbase field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetCoinbaseString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.coinbase_, json_value); + } + + /** + * @brief Get of txid + * @return txid + */ + std::string GetTxid() const { + return txid_; + } + /** + * @brief Set to txid + * @param[in] txid setting value. + */ + void SetTxid( // line separate + const std::string& txid) { // NOLINT + this->txid_ = txid; + } + /** + * @brief Get data type of txid + * @return Data type of txid + */ + static std::string GetTxidFieldType() { + return "std::string"; + } + /** + * @brief Get json string of txid field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetTxidString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.txid_); + } + /** + * @brief Set json object to txid field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetTxidString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.txid_, json_value); + } + + /** + * @brief Get of vout + * @return vout + */ + uint32_t GetVout() const { + return vout_; + } + /** + * @brief Set to vout + * @param[in] vout setting value. + */ + void SetVout( // line separate + const uint32_t& vout) { // NOLINT + this->vout_ = vout; + } + /** + * @brief Get data type of vout + * @return Data type of vout + */ + static std::string GetVoutFieldType() { + return "uint32_t"; + } + /** + * @brief Get json string of vout field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetVoutString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.vout_); + } + /** + * @brief Set json object to vout field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetVoutString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.vout_, json_value); + } + + /** + * @brief Get of scriptSig. + * @return scriptSig + */ + ElementsDecodeUnlockingScript& GetScriptSig() { // NOLINT + return script_sig_; + } + /** + * @brief Set to scriptSig. + * @param[in] script_sig setting value. + */ + void SetScriptSig( // line separate + const ElementsDecodeUnlockingScript& script_sig) { // NOLINT + this->script_sig_ = script_sig; + } + /** + * @brief Get data type of scriptSig. + * @return Data type of scriptSig. + */ + static std::string GetScriptSigFieldType() { + return "ElementsDecodeUnlockingScript"; // NOLINT + } + /** + * @brief Get json string of scriptSig field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetScriptSigString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.script_sig_.Serialize(); + } + /** + * @brief Set json object to scriptSig field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetScriptSigString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + obj.script_sig_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of is_pegin + * @return is_pegin + */ + bool GetIs_pegin() const { + return is_pegin_; + } + /** + * @brief Set to is_pegin + * @param[in] is_pegin setting value. + */ + void SetIs_pegin( // line separate + const bool& is_pegin) { // NOLINT + this->is_pegin_ = is_pegin; + } + /** + * @brief Get data type of is_pegin + * @return Data type of is_pegin + */ + static std::string GetIs_peginFieldType() { + return "bool"; + } + /** + * @brief Get json string of is_pegin field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetIs_peginString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.is_pegin_); + } + /** + * @brief Set json object to is_pegin field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetIs_peginString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.is_pegin_, json_value); + } + + /** + * @brief Get of sequence + * @return sequence + */ + int64_t GetSequence() const { + return sequence_; + } + /** + * @brief Set to sequence + * @param[in] sequence setting value. + */ + void SetSequence( // line separate + const int64_t& sequence) { // NOLINT + this->sequence_ = sequence; + } + /** + * @brief Get data type of sequence + * @return Data type of sequence + */ + static std::string GetSequenceFieldType() { + return "int64_t"; + } + /** + * @brief Get json string of sequence field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetSequenceString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + return cfd::core::ConvertToString(obj.sequence_); + } + /** + * @brief Set json object to sequence field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetSequenceString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.sequence_, json_value); + } + + /** + * @brief Get of txinwitness. + * @return txinwitness + */ + JsonValueVector& GetTxinwitness() { // NOLINT + return txinwitness_; + } + /** + * @brief Set to txinwitness. + * @param[in] txinwitness setting value. + */ + void SetTxinwitness( // line separate + const JsonValueVector& txinwitness) { // NOLINT + this->txinwitness_ = txinwitness; + } + /** + * @brief Get data type of txinwitness. + * @return Data type of txinwitness. + */ + static std::string GetTxinwitnessFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of txinwitness field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetTxinwitnessString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.txinwitness_.Serialize(); + } + /** + * @brief Set json object to txinwitness field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetTxinwitnessString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + obj.txinwitness_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of pegin_witness. + * @return pegin_witness + */ + JsonValueVector& GetPegin_witness() { // NOLINT + return pegin_witness_; + } + /** + * @brief Set to pegin_witness. + * @param[in] pegin_witness setting value. + */ + void SetPegin_witness( // line separate + const JsonValueVector& pegin_witness) { // NOLINT + this->pegin_witness_ = pegin_witness; + } + /** + * @brief Get data type of pegin_witness. + * @return Data type of pegin_witness. + */ + static std::string GetPegin_witnessFieldType() { + return "JsonValueVector"; // NOLINT + } + /** + * @brief Get json string of pegin_witness field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetPegin_witnessString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.pegin_witness_.Serialize(); + } + /** + * @brief Set json object to pegin_witness field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetPegin_witnessString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + obj.pegin_witness_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of issuance. + * @return issuance + */ + ElementsDecodeIssuance& GetIssuance() { // NOLINT + return issuance_; + } + /** + * @brief Set to issuance. + * @param[in] issuance setting value. + */ + void SetIssuance( // line separate + const ElementsDecodeIssuance& issuance) { // NOLINT + this->issuance_ = issuance; + } + /** + * @brief Get data type of issuance. + * @return Data type of issuance. + */ + static std::string GetIssuanceFieldType() { + return "ElementsDecodeIssuance"; // NOLINT + } + /** + * @brief Get json string of issuance field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string GetIssuanceString( // line separate + const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.issuance_.Serialize(); + } + /** + * @brief Set json object to issuance field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void SetIssuanceString( // line separate + ElementsDecodeRawTransactionTxIn& obj, // NOLINT + const UniValue& json_value) { + obj.issuance_.DeserializeUniValue(json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const ElementsDecodeRawTransactionTxInStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + ElementsDecodeRawTransactionTxInStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using ElementsDecodeRawTransactionTxInMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const ElementsDecodeRawTransactionTxInMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static ElementsDecodeRawTransactionTxInMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(coinbase) value + */ + std::string coinbase_ = ""; + /** + * @brief JsonAPI(txid) value + */ + std::string txid_ = ""; + /** + * @brief JsonAPI(vout) value + */ + uint32_t vout_ = 0; + /** + * @brief JsonAPI(scriptSig) value + */ + ElementsDecodeUnlockingScript script_sig_; // NOLINT + /** + * @brief JsonAPI(is_pegin) value + */ + bool is_pegin_ = false; + /** + * @brief JsonAPI(sequence) value + */ + int64_t sequence_ = 0; + /** + * @brief JsonAPI(txinwitness) value + */ + JsonValueVector txinwitness_; // NOLINT + /** + * @brief JsonAPI(pegin_witness) value + */ + JsonValueVector pegin_witness_; // NOLINT + /** + * @brief JsonAPI(issuance) value + */ + ElementsDecodeIssuance issuance_; // NOLINT +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionTxOut +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (ElementsDecodeRawTransactionTxOut) class + */ +class ElementsDecodeRawTransactionTxOut + : public cfd::core::JsonClassBase { + public: + ElementsDecodeRawTransactionTxOut() { + CollectFieldName(); + } + virtual ~ElementsDecodeRawTransactionTxOut() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); + + /** + * @brief Get of value + * @return value + */ + int64_t GetValue() const { + return value_; + } + /** + * @brief Set to value + * @param[in] value setting value. + */ + void SetValue( // line separate + const int64_t& value) { // NOLINT + this->value_ = value; + } + /** + * @brief Get data type of value + * @return Data type of value + */ + static std::string GetValueFieldType() { + return "int64_t"; + } + /** + * @brief Get json string of value field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetValueString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.value_); + } + /** + * @brief Set json object to value field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetValueString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.value_, json_value); + } + + /** + * @brief Get of value-minimum + * @return value-minimum + */ + int64_t GetValue_minimum() const { + return value_minimum_; + } + /** + * @brief Set to value-minimum + * @param[in] value_minimum setting value. + */ + void SetValue_minimum( // line separate + const int64_t& value_minimum) { // NOLINT + this->value_minimum_ = value_minimum; + } + /** + * @brief Get data type of value-minimum + * @return Data type of value-minimum + */ + static std::string GetValue_minimumFieldType() { + return "int64_t"; + } + /** + * @brief Get json string of value-minimum field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetValue_minimumString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.value_minimum_); + } + /** + * @brief Set json object to value-minimum field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetValue_minimumString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.value_minimum_, json_value); + } + + /** + * @brief Get of value-maximum + * @return value-maximum + */ + int64_t GetValue_maximum() const { + return value_maximum_; + } + /** + * @brief Set to value-maximum + * @param[in] value_maximum setting value. + */ + void SetValue_maximum( // line separate + const int64_t& value_maximum) { // NOLINT + this->value_maximum_ = value_maximum; + } + /** + * @brief Get data type of value-maximum + * @return Data type of value-maximum + */ + static std::string GetValue_maximumFieldType() { + return "int64_t"; + } + /** + * @brief Get json string of value-maximum field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetValue_maximumString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.value_maximum_); + } + /** + * @brief Set json object to value-maximum field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetValue_maximumString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.value_maximum_, json_value); + } + + /** + * @brief Get of ct-exponent + * @return ct-exponent + */ + int GetCt_exponent() const { + return ct_exponent_; + } + /** + * @brief Set to ct-exponent + * @param[in] ct_exponent setting value. + */ + void SetCt_exponent( // line separate + const int& ct_exponent) { // NOLINT + this->ct_exponent_ = ct_exponent; + } + /** + * @brief Get data type of ct-exponent + * @return Data type of ct-exponent + */ + static std::string GetCt_exponentFieldType() { + return "int"; + } + /** + * @brief Get json string of ct-exponent field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetCt_exponentString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.ct_exponent_); + } + /** + * @brief Set json object to ct-exponent field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetCt_exponentString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.ct_exponent_, json_value); + } + + /** + * @brief Get of ct-bits + * @return ct-bits + */ + int GetCt_bits() const { + return ct_bits_; + } + /** + * @brief Set to ct-bits + * @param[in] ct_bits setting value. + */ + void SetCt_bits( // line separate + const int& ct_bits) { // NOLINT + this->ct_bits_ = ct_bits; + } + /** + * @brief Get data type of ct-bits + * @return Data type of ct-bits + */ + static std::string GetCt_bitsFieldType() { + return "int"; + } + /** + * @brief Get json string of ct-bits field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetCt_bitsString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.ct_bits_); + } + /** + * @brief Set json object to ct-bits field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetCt_bitsString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.ct_bits_, json_value); } + /** - * @brief Get data type of token - * @return Data type of token + * @brief Get of surjectionproof + * @return surjectionproof */ - static std::string GetTokenFieldType() { + std::string GetSurjectionproof() const { + return surjectionproof_; + } + /** + * @brief Set to surjectionproof + * @param[in] surjectionproof setting value. + */ + void SetSurjectionproof( // line separate + const std::string& surjectionproof) { // NOLINT + this->surjectionproof_ = surjectionproof; + } + /** + * @brief Get data type of surjectionproof + * @return Data type of surjectionproof + */ + static std::string GetSurjectionproofFieldType() { return "std::string"; } /** - * @brief Get json string of token field. + * @brief Get json string of surjectionproof field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetTokenString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.token_); + static std::string GetSurjectionproofString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.surjectionproof_); } /** - * @brief Set json object to token field. + * @brief Set json object to surjectionproof field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetTokenString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetSurjectionproofString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.token_, json_value); + obj.surjectionproof_, json_value); + } + + /** + * @brief Get of valuecommitment + * @return valuecommitment + */ + std::string GetValuecommitment() const { + return valuecommitment_; + } + /** + * @brief Set to valuecommitment + * @param[in] valuecommitment setting value. + */ + void SetValuecommitment( // line separate + const std::string& valuecommitment) { // NOLINT + this->valuecommitment_ = valuecommitment; + } + /** + * @brief Get data type of valuecommitment + * @return Data type of valuecommitment + */ + static std::string GetValuecommitmentFieldType() { + return "std::string"; + } + /** + * @brief Get json string of valuecommitment field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetValuecommitmentString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.valuecommitment_); + } + /** + * @brief Set json object to valuecommitment field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetValuecommitmentString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.valuecommitment_, json_value); } /** @@ -2668,7 +6410,7 @@ class ElementsDecodeIssuance * @return JSON string */ static std::string GetAssetString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT return cfd::core::ConvertToString(obj.asset_); } /** @@ -2677,268 +6419,269 @@ class ElementsDecodeIssuance * @param[in] json_value JSON object. */ static void SetAssetString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate obj.asset_, json_value); } /** - * @brief Get of assetamount - * @return assetamount + * @brief Get of assetcommitment + * @return assetcommitment */ - int64_t GetAssetamount() const { - return assetamount_; + std::string GetAssetcommitment() const { + return assetcommitment_; } /** - * @brief Set to assetamount - * @param[in] assetamount setting value. + * @brief Set to assetcommitment + * @param[in] assetcommitment setting value. */ - void SetAssetamount( // line separate - const int64_t& assetamount) { // NOLINT - this->assetamount_ = assetamount; + void SetAssetcommitment( // line separate + const std::string& assetcommitment) { // NOLINT + this->assetcommitment_ = assetcommitment; } /** - * @brief Get data type of assetamount - * @return Data type of assetamount + * @brief Get data type of assetcommitment + * @return Data type of assetcommitment */ - static std::string GetAssetamountFieldType() { - return "int64_t"; + static std::string GetAssetcommitmentFieldType() { + return "std::string"; } /** - * @brief Get json string of assetamount field. + * @brief Get json string of assetcommitment field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAssetamountString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.assetamount_); + static std::string GetAssetcommitmentString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.assetcommitment_); } /** - * @brief Set json object to assetamount field. + * @brief Set json object to assetcommitment field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAssetamountString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetAssetcommitmentString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.assetamount_, json_value); + obj.assetcommitment_, json_value); } /** - * @brief Get of assetamountcommitment - * @return assetamountcommitment + * @brief Get of commitmentnonce + * @return commitmentnonce */ - std::string GetAssetamountcommitment() const { - return assetamountcommitment_; + std::string GetCommitmentnonce() const { + return commitmentnonce_; } /** - * @brief Set to assetamountcommitment - * @param[in] assetamountcommitment setting value. + * @brief Set to commitmentnonce + * @param[in] commitmentnonce setting value. */ - void SetAssetamountcommitment( // line separate - const std::string& assetamountcommitment) { // NOLINT - this->assetamountcommitment_ = assetamountcommitment; + void SetCommitmentnonce( // line separate + const std::string& commitmentnonce) { // NOLINT + this->commitmentnonce_ = commitmentnonce; } /** - * @brief Get data type of assetamountcommitment - * @return Data type of assetamountcommitment + * @brief Get data type of commitmentnonce + * @return Data type of commitmentnonce */ - static std::string GetAssetamountcommitmentFieldType() { + static std::string GetCommitmentnonceFieldType() { return "std::string"; } /** - * @brief Get json string of assetamountcommitment field. + * @brief Get json string of commitmentnonce field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAssetamountcommitmentString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.assetamountcommitment_); + static std::string GetCommitmentnonceString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.commitmentnonce_); } /** - * @brief Set json object to assetamountcommitment field. + * @brief Set json object to commitmentnonce field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAssetamountcommitmentString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetCommitmentnonceString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.assetamountcommitment_, json_value); + obj.commitmentnonce_, json_value); } /** - * @brief Get of tokenamount - * @return tokenamount + * @brief Get of commitmentnonce_fully_valid + * @return commitmentnonce_fully_valid */ - int64_t GetTokenamount() const { - return tokenamount_; + bool GetCommitmentnonce_fully_valid() const { + return commitmentnonce_fully_valid_; } /** - * @brief Set to tokenamount - * @param[in] tokenamount setting value. + * @brief Set to commitmentnonce_fully_valid + * @param[in] commitmentnonce_fully_valid setting value. */ - void SetTokenamount( // line separate - const int64_t& tokenamount) { // NOLINT - this->tokenamount_ = tokenamount; + void SetCommitmentnonce_fully_valid( // line separate + const bool& commitmentnonce_fully_valid) { // NOLINT + this->commitmentnonce_fully_valid_ = commitmentnonce_fully_valid; } /** - * @brief Get data type of tokenamount - * @return Data type of tokenamount + * @brief Get data type of commitmentnonce_fully_valid + * @return Data type of commitmentnonce_fully_valid */ - static std::string GetTokenamountFieldType() { - return "int64_t"; + static std::string GetCommitmentnonce_fully_validFieldType() { + return "bool"; } /** - * @brief Get json string of tokenamount field. + * @brief Get json string of commitmentnonce_fully_valid field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetTokenamountString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.tokenamount_); + static std::string GetCommitmentnonce_fully_validString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.commitmentnonce_fully_valid_); } /** - * @brief Set json object to tokenamount field. + * @brief Set json object to commitmentnonce_fully_valid field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetTokenamountString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetCommitmentnonce_fully_validString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.tokenamount_, json_value); + obj.commitmentnonce_fully_valid_, json_value); } /** - * @brief Get of tokenamountcommitment - * @return tokenamountcommitment + * @brief Get of n + * @return n */ - std::string GetTokenamountcommitment() const { - return tokenamountcommitment_; + uint32_t GetN() const { + return n_; } /** - * @brief Set to tokenamountcommitment - * @param[in] tokenamountcommitment setting value. + * @brief Set to n + * @param[in] n setting value. */ - void SetTokenamountcommitment( // line separate - const std::string& tokenamountcommitment) { // NOLINT - this->tokenamountcommitment_ = tokenamountcommitment; + void SetN( // line separate + const uint32_t& n) { // NOLINT + this->n_ = n; } /** - * @brief Get data type of tokenamountcommitment - * @return Data type of tokenamountcommitment + * @brief Get data type of n + * @return Data type of n */ - static std::string GetTokenamountcommitmentFieldType() { - return "std::string"; + static std::string GetNFieldType() { + return "uint32_t"; } /** - * @brief Get json string of tokenamountcommitment field. + * @brief Get json string of n field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetTokenamountcommitmentString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.tokenamountcommitment_); + static std::string GetNString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.n_); } /** - * @brief Set json object to tokenamountcommitment field. + * @brief Set json object to n field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetTokenamountcommitmentString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetNString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.tokenamountcommitment_, json_value); + obj.n_, json_value); } /** - * @brief Get of assetRangeproof - * @return assetRangeproof + * @brief Get of scriptPubKey. + * @return scriptPubKey */ - std::string GetAssetRangeproof() const { - return asset_rangeproof_; + ElementsDecodeLockingScript& GetScriptPubKey() { // NOLINT + return script_pub_key_; } /** - * @brief Set to assetRangeproof - * @param[in] asset_rangeproof setting value. + * @brief Set to scriptPubKey. + * @param[in] script_pub_key setting value. */ - void SetAssetRangeproof( // line separate - const std::string& asset_rangeproof) { // NOLINT - this->asset_rangeproof_ = asset_rangeproof; + void SetScriptPubKey( // line separate + const ElementsDecodeLockingScript& script_pub_key) { // NOLINT + this->script_pub_key_ = script_pub_key; } /** - * @brief Get data type of assetRangeproof - * @return Data type of assetRangeproof + * @brief Get data type of scriptPubKey. + * @return Data type of scriptPubKey. */ - static std::string GetAssetRangeproofFieldType() { - return "std::string"; + static std::string GetScriptPubKeyFieldType() { + return "ElementsDecodeLockingScript"; // NOLINT } /** - * @brief Get json string of assetRangeproof field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get json string of scriptPubKey field. + * @param[in,out] obj class object + * @return JSON string. */ - static std::string GetAssetRangeproofString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asset_rangeproof_); + static std::string GetScriptPubKeyString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.script_pub_key_.Serialize(); } /** - * @brief Set json object to assetRangeproof field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Set json object to scriptPubKey field. + * @param[in,out] obj class object + * @param[in] json_value JSON object */ - static void SetAssetRangeproofString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetScriptPubKeyString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.asset_rangeproof_, json_value); + obj.script_pub_key_.DeserializeUniValue(json_value); } /** - * @brief Get of tokenRangeproof - * @return tokenRangeproof + * @brief Get of rangeproof + * @return rangeproof */ - std::string GetTokenRangeproof() const { - return token_rangeproof_; + std::string GetRangeproof() const { + return rangeproof_; } /** - * @brief Set to tokenRangeproof - * @param[in] token_rangeproof setting value. + * @brief Set to rangeproof + * @param[in] rangeproof setting value. */ - void SetTokenRangeproof( // line separate - const std::string& token_rangeproof) { // NOLINT - this->token_rangeproof_ = token_rangeproof; + void SetRangeproof( // line separate + const std::string& rangeproof) { // NOLINT + this->rangeproof_ = rangeproof; } /** - * @brief Get data type of tokenRangeproof - * @return Data type of tokenRangeproof + * @brief Get data type of rangeproof + * @return Data type of rangeproof */ - static std::string GetTokenRangeproofFieldType() { + static std::string GetRangeproofFieldType() { return "std::string"; } /** - * @brief Get json string of tokenRangeproof field. + * @brief Get json string of rangeproof field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetTokenRangeproofString( // line separate - const ElementsDecodeIssuance& obj) { // NOLINT - return cfd::core::ConvertToString(obj.token_rangeproof_); + static std::string GetRangeproofString( // line separate + const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT + return cfd::core::ConvertToString(obj.rangeproof_); } /** - * @brief Set json object to tokenRangeproof field. + * @brief Set json object to rangeproof field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetTokenRangeproofString( // line separate - ElementsDecodeIssuance& obj, // NOLINT + static void SetRangeproofString( // line separate + ElementsDecodeRawTransactionTxOut& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.token_rangeproof_, json_value); + obj.rangeproof_, json_value); } /** @@ -2954,27 +6697,27 @@ class ElementsDecodeIssuance * @param[in] data struct data. */ void ConvertFromStruct( - const ElementsDecodeIssuanceStruct& data); + const ElementsDecodeRawTransactionTxOutStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - ElementsDecodeIssuanceStruct ConvertToStruct() const; + ElementsDecodeRawTransactionTxOutStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using ElementsDecodeIssuanceMapTable = - cfd::core::JsonTableMap; + using ElementsDecodeRawTransactionTxOutMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const ElementsDecodeIssuanceMapTable& GetJsonMapper() const { // NOLINT + virtual const ElementsDecodeRawTransactionTxOutMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -2987,488 +6730,278 @@ class ElementsDecodeIssuance return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() - */ - virtual const std::set& GetIgnoreItem() const { - return ignore_items; - } - - private: - /** - * @brief JsonFunctionMap table - */ - static ElementsDecodeIssuanceMapTable json_mapper; - /** - * @brief field name list. - */ - static std::vector item_list; - /** - * @brief ignore item list. - */ - std::set ignore_items; - - /** - * @brief JsonAPI(assetBlindingNonce) value - */ - std::string asset_blinding_nonce_ = ""; - /** - * @brief JsonAPI(assetEntropy) value - */ - std::string asset_entropy_ = ""; - /** - * @brief JsonAPI(contractHash) value - */ - std::string contract_hash_ = ""; - /** - * @brief JsonAPI(isreissuance) value - */ - bool isreissuance_ = false; - /** - * @brief JsonAPI(token) value - */ - std::string token_ = ""; - /** - * @brief JsonAPI(asset) value - */ - std::string asset_ = ""; - /** - * @brief JsonAPI(assetamount) value - */ - int64_t assetamount_ = 0; - /** - * @brief JsonAPI(assetamountcommitment) value - */ - std::string assetamountcommitment_ = ""; - /** - * @brief JsonAPI(tokenamount) value - */ - int64_t tokenamount_ = 0; - /** - * @brief JsonAPI(tokenamountcommitment) value - */ - std::string tokenamountcommitment_ = ""; - /** - * @brief JsonAPI(assetRangeproof) value - */ - std::string asset_rangeproof_ = ""; - /** - * @brief JsonAPI(tokenRangeproof) value - */ - std::string token_rangeproof_ = ""; -}; - -// ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionTxIn -// ------------------------------------------------------------------------ -/** - * @brief JSON-API (ElementsDecodeRawTransactionTxIn) class - */ -class ElementsDecodeRawTransactionTxIn - : public cfd::core::JsonClassBase { - public: - ElementsDecodeRawTransactionTxIn() { - CollectFieldName(); - } - virtual ~ElementsDecodeRawTransactionTxIn() { - // do nothing - } - /** - * @brief collect field name. - */ - static void CollectFieldName(); - - /** - * @brief Get of coinbase - * @return coinbase - */ - std::string GetCoinbase() const { - return coinbase_; - } - /** - * @brief Set to coinbase - * @param[in] coinbase setting value. - */ - void SetCoinbase( // line separate - const std::string& coinbase) { // NOLINT - this->coinbase_ = coinbase; - } - /** - * @brief Get data type of coinbase - * @return Data type of coinbase - */ - static std::string GetCoinbaseFieldType() { - return "std::string"; - } - /** - * @brief Get json string of coinbase field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetCoinbaseString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.coinbase_); - } - /** - * @brief Set json object to coinbase field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetCoinbaseString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.coinbase_, json_value); - } - - /** - * @brief Get of txid - * @return txid - */ - std::string GetTxid() const { - return txid_; - } - /** - * @brief Set to txid - * @param[in] txid setting value. - */ - void SetTxid( // line separate - const std::string& txid) { // NOLINT - this->txid_ = txid; - } - /** - * @brief Get data type of txid - * @return Data type of txid - */ - static std::string GetTxidFieldType() { - return "std::string"; + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; } + + private: + /** + * @brief JsonFunctionMap table + */ + static ElementsDecodeRawTransactionTxOutMapTable json_mapper; /** - * @brief Get json string of txid field. - * @param[in,out] obj class object. - * @return JSON string + * @brief field name list. */ - static std::string GetTxidString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.txid_); - } + static std::vector item_list; /** - * @brief Set json object to txid field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief ignore item list. */ - static void SetTxidString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.txid_, json_value); - } + std::set ignore_items; /** - * @brief Get of vout - * @return vout + * @brief JsonAPI(value) value */ - uint32_t GetVout() const { - return vout_; - } + int64_t value_ = 0; /** - * @brief Set to vout - * @param[in] vout setting value. + * @brief JsonAPI(value-minimum) value */ - void SetVout( // line separate - const uint32_t& vout) { // NOLINT - this->vout_ = vout; - } + int64_t value_minimum_ = 0; /** - * @brief Get data type of vout - * @return Data type of vout + * @brief JsonAPI(value-maximum) value */ - static std::string GetVoutFieldType() { - return "uint32_t"; - } + int64_t value_maximum_ = 0; /** - * @brief Get json string of vout field. - * @param[in,out] obj class object. - * @return JSON string + * @brief JsonAPI(ct-exponent) value */ - static std::string GetVoutString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.vout_); - } + int ct_exponent_ = 0; /** - * @brief Set json object to vout field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief JsonAPI(ct-bits) value */ - static void SetVoutString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.vout_, json_value); - } - + int ct_bits_ = 0; /** - * @brief Get of scriptSig. - * @return scriptSig + * @brief JsonAPI(surjectionproof) value */ - ElementsDecodeUnlockingScript& GetScriptSig() { // NOLINT - return script_sig_; - } + std::string surjectionproof_ = ""; /** - * @brief Set to scriptSig. - * @param[in] script_sig setting value. + * @brief JsonAPI(valuecommitment) value */ - void SetScriptSig( // line separate - const ElementsDecodeUnlockingScript& script_sig) { // NOLINT - this->script_sig_ = script_sig; - } + std::string valuecommitment_ = ""; /** - * @brief Get data type of scriptSig. - * @return Data type of scriptSig. + * @brief JsonAPI(asset) value */ - static std::string GetScriptSigFieldType() { - return "ElementsDecodeUnlockingScript"; // NOLINT - } + std::string asset_ = ""; /** - * @brief Get json string of scriptSig field. - * @param[in,out] obj class object - * @return JSON string. + * @brief JsonAPI(assetcommitment) value */ - static std::string GetScriptSigString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - // Do not set to const, because substitution of member variables - // may occur in pre / post processing inside Serialize - return obj.script_sig_.Serialize(); - } + std::string assetcommitment_ = ""; /** - * @brief Set json object to scriptSig field. - * @param[in,out] obj class object - * @param[in] json_value JSON object + * @brief JsonAPI(commitmentnonce) value */ - static void SetScriptSigString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT - const UniValue& json_value) { - obj.script_sig_.DeserializeUniValue(json_value); - } - + std::string commitmentnonce_ = ""; /** - * @brief Get of is_pegin - * @return is_pegin + * @brief JsonAPI(commitmentnonce_fully_valid) value */ - bool GetIs_pegin() const { - return is_pegin_; - } + bool commitmentnonce_fully_valid_ = false; /** - * @brief Set to is_pegin - * @param[in] is_pegin setting value. + * @brief JsonAPI(n) value */ - void SetIs_pegin( // line separate - const bool& is_pegin) { // NOLINT - this->is_pegin_ = is_pegin; - } + uint32_t n_ = 0; /** - * @brief Get data type of is_pegin - * @return Data type of is_pegin + * @brief JsonAPI(scriptPubKey) value */ - static std::string GetIs_peginFieldType() { - return "bool"; - } + ElementsDecodeLockingScript script_pub_key_; // NOLINT /** - * @brief Get json string of is_pegin field. - * @param[in,out] obj class object. - * @return JSON string + * @brief JsonAPI(rangeproof) value */ - static std::string GetIs_peginString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.is_pegin_); + std::string rangeproof_ = ""; +}; + +// ------------------------------------------------------------------------ +// PsbtGlobalXpub +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (PsbtGlobalXpub) class + */ +class PsbtGlobalXpub + : public cfd::core::JsonClassBase { + public: + PsbtGlobalXpub() { + CollectFieldName(); + } + virtual ~PsbtGlobalXpub() { + // do nothing } /** - * @brief Set json object to is_pegin field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief collect field name. */ - static void SetIs_peginString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.is_pegin_, json_value); - } + static void CollectFieldName(); /** - * @brief Get of sequence - * @return sequence + * @brief Get of xpub. + * @return xpub */ - int64_t GetSequence() const { - return sequence_; + XpubData& GetXpub() { // NOLINT + return xpub_; } /** - * @brief Set to sequence - * @param[in] sequence setting value. + * @brief Set to xpub. + * @param[in] xpub setting value. */ - void SetSequence( // line separate - const int64_t& sequence) { // NOLINT - this->sequence_ = sequence; + void SetXpub( // line separate + const XpubData& xpub) { // NOLINT + this->xpub_ = xpub; } /** - * @brief Get data type of sequence - * @return Data type of sequence + * @brief Get data type of xpub. + * @return Data type of xpub. */ - static std::string GetSequenceFieldType() { - return "int64_t"; + static std::string GetXpubFieldType() { + return "XpubData"; // NOLINT } /** - * @brief Get json string of sequence field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get json string of xpub field. + * @param[in,out] obj class object + * @return JSON string. */ - static std::string GetSequenceString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - return cfd::core::ConvertToString(obj.sequence_); + static std::string GetXpubString( // line separate + const PsbtGlobalXpub& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.xpub_.Serialize(); } /** - * @brief Set json object to sequence field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Set json object to xpub field. + * @param[in,out] obj class object + * @param[in] json_value JSON object */ - static void SetSequenceString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT + static void SetXpubString( // line separate + PsbtGlobalXpub& obj, // NOLINT const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.sequence_, json_value); + obj.xpub_.DeserializeUniValue(json_value); } /** - * @brief Get of txinwitness. - * @return txinwitness + * @brief Get of master_fingerprint + * @return master_fingerprint */ - JsonValueVector& GetTxinwitness() { // NOLINT - return txinwitness_; + std::string GetMaster_fingerprint() const { + return master_fingerprint_; } /** - * @brief Set to txinwitness. - * @param[in] txinwitness setting value. + * @brief Set to master_fingerprint + * @param[in] master_fingerprint setting value. */ - void SetTxinwitness( // line separate - const JsonValueVector& txinwitness) { // NOLINT - this->txinwitness_ = txinwitness; + void SetMaster_fingerprint( // line separate + const std::string& master_fingerprint) { // NOLINT + this->master_fingerprint_ = master_fingerprint; } /** - * @brief Get data type of txinwitness. - * @return Data type of txinwitness. + * @brief Get data type of master_fingerprint + * @return Data type of master_fingerprint */ - static std::string GetTxinwitnessFieldType() { - return "JsonValueVector"; // NOLINT + static std::string GetMaster_fingerprintFieldType() { + return "std::string"; } /** - * @brief Get json string of txinwitness field. - * @param[in,out] obj class object - * @return JSON string. + * @brief Get json string of master_fingerprint field. + * @param[in,out] obj class object. + * @return JSON string */ - static std::string GetTxinwitnessString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - // Do not set to const, because substitution of member variables - // may occur in pre / post processing inside Serialize - return obj.txinwitness_.Serialize(); + static std::string GetMaster_fingerprintString( // line separate + const PsbtGlobalXpub& obj) { // NOLINT + return cfd::core::ConvertToString(obj.master_fingerprint_); } /** - * @brief Set json object to txinwitness field. - * @param[in,out] obj class object - * @param[in] json_value JSON object + * @brief Set json object to master_fingerprint field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. */ - static void SetTxinwitnessString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT + static void SetMaster_fingerprintString( // line separate + PsbtGlobalXpub& obj, // NOLINT const UniValue& json_value) { - obj.txinwitness_.DeserializeUniValue(json_value); + cfd::core::ConvertFromUniValue( // line separate + obj.master_fingerprint_, json_value); } /** - * @brief Get of pegin_witness. - * @return pegin_witness + * @brief Get of path + * @return path */ - JsonValueVector& GetPegin_witness() { // NOLINT - return pegin_witness_; + std::string GetPath() const { + return path_; } /** - * @brief Set to pegin_witness. - * @param[in] pegin_witness setting value. + * @brief Set to path + * @param[in] path setting value. */ - void SetPegin_witness( // line separate - const JsonValueVector& pegin_witness) { // NOLINT - this->pegin_witness_ = pegin_witness; + void SetPath( // line separate + const std::string& path) { // NOLINT + this->path_ = path; } /** - * @brief Get data type of pegin_witness. - * @return Data type of pegin_witness. + * @brief Get data type of path + * @return Data type of path */ - static std::string GetPegin_witnessFieldType() { - return "JsonValueVector"; // NOLINT + static std::string GetPathFieldType() { + return "std::string"; } - /** - * @brief Get json string of pegin_witness field. - * @param[in,out] obj class object - * @return JSON string. - */ - static std::string GetPegin_witnessString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - // Do not set to const, because substitution of member variables - // may occur in pre / post processing inside Serialize - return obj.pegin_witness_.Serialize(); + /** + * @brief Get json string of path field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetPathString( // line separate + const PsbtGlobalXpub& obj) { // NOLINT + return cfd::core::ConvertToString(obj.path_); } /** - * @brief Set json object to pegin_witness field. - * @param[in,out] obj class object - * @param[in] json_value JSON object + * @brief Set json object to path field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. */ - static void SetPegin_witnessString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT + static void SetPathString( // line separate + PsbtGlobalXpub& obj, // NOLINT const UniValue& json_value) { - obj.pegin_witness_.DeserializeUniValue(json_value); + cfd::core::ConvertFromUniValue( // line separate + obj.path_, json_value); } /** - * @brief Get of issuance. - * @return issuance + * @brief Get of descriptorXpub + * @return descriptorXpub */ - ElementsDecodeIssuance& GetIssuance() { // NOLINT - return issuance_; + std::string GetDescriptorXpub() const { + return descriptor_xpub_; } /** - * @brief Set to issuance. - * @param[in] issuance setting value. + * @brief Set to descriptorXpub + * @param[in] descriptor_xpub setting value. */ - void SetIssuance( // line separate - const ElementsDecodeIssuance& issuance) { // NOLINT - this->issuance_ = issuance; + void SetDescriptorXpub( // line separate + const std::string& descriptor_xpub) { // NOLINT + this->descriptor_xpub_ = descriptor_xpub; } /** - * @brief Get data type of issuance. - * @return Data type of issuance. + * @brief Get data type of descriptorXpub + * @return Data type of descriptorXpub */ - static std::string GetIssuanceFieldType() { - return "ElementsDecodeIssuance"; // NOLINT + static std::string GetDescriptorXpubFieldType() { + return "std::string"; } /** - * @brief Get json string of issuance field. - * @param[in,out] obj class object - * @return JSON string. + * @brief Get json string of descriptorXpub field. + * @param[in,out] obj class object. + * @return JSON string */ - static std::string GetIssuanceString( // line separate - const ElementsDecodeRawTransactionTxIn& obj) { // NOLINT - // Do not set to const, because substitution of member variables - // may occur in pre / post processing inside Serialize - return obj.issuance_.Serialize(); + static std::string GetDescriptorXpubString( // line separate + const PsbtGlobalXpub& obj) { // NOLINT + return cfd::core::ConvertToString(obj.descriptor_xpub_); } /** - * @brief Set json object to issuance field. - * @param[in,out] obj class object - * @param[in] json_value JSON object + * @brief Set json object to descriptorXpub field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. */ - static void SetIssuanceString( // line separate - ElementsDecodeRawTransactionTxIn& obj, // NOLINT + static void SetDescriptorXpubString( // line separate + PsbtGlobalXpub& obj, // NOLINT const UniValue& json_value) { - obj.issuance_.DeserializeUniValue(json_value); + cfd::core::ConvertFromUniValue( // line separate + obj.descriptor_xpub_, json_value); } /** @@ -3484,27 +7017,27 @@ class ElementsDecodeRawTransactionTxIn * @param[in] data struct data. */ void ConvertFromStruct( - const ElementsDecodeRawTransactionTxInStruct& data); + const PsbtGlobalXpubStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - ElementsDecodeRawTransactionTxInStruct ConvertToStruct() const; + PsbtGlobalXpubStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using ElementsDecodeRawTransactionTxInMapTable = - cfd::core::JsonTableMap; + using PsbtGlobalXpubMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const ElementsDecodeRawTransactionTxInMapTable& GetJsonMapper() const { // NOLINT + virtual const PsbtGlobalXpubMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -3517,7 +7050,7 @@ class ElementsDecodeRawTransactionTxIn return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -3530,7 +7063,7 @@ class ElementsDecodeRawTransactionTxIn /** * @brief JsonFunctionMap table */ - static ElementsDecodeRawTransactionTxInMapTable json_mapper; + static PsbtGlobalXpubMapTable json_mapper; /** * @brief field name list. */ @@ -3541,56 +7074,36 @@ class ElementsDecodeRawTransactionTxIn std::set ignore_items; /** - * @brief JsonAPI(coinbase) value - */ - std::string coinbase_ = ""; - /** - * @brief JsonAPI(txid) value - */ - std::string txid_ = ""; - /** - * @brief JsonAPI(vout) value - */ - uint32_t vout_ = 0; - /** - * @brief JsonAPI(scriptSig) value - */ - ElementsDecodeUnlockingScript script_sig_; // NOLINT - /** - * @brief JsonAPI(is_pegin) value - */ - bool is_pegin_ = false; - /** - * @brief JsonAPI(sequence) value + * @brief JsonAPI(xpub) value */ - int64_t sequence_ = 0; + XpubData xpub_; // NOLINT /** - * @brief JsonAPI(txinwitness) value + * @brief JsonAPI(master_fingerprint) value */ - JsonValueVector txinwitness_; // NOLINT + std::string master_fingerprint_ = ""; /** - * @brief JsonAPI(pegin_witness) value + * @brief JsonAPI(path) value */ - JsonValueVector pegin_witness_; // NOLINT + std::string path_ = ""; /** - * @brief JsonAPI(issuance) value + * @brief JsonAPI(descriptorXpub) value */ - ElementsDecodeIssuance issuance_; // NOLINT + std::string descriptor_xpub_ = ""; }; // ------------------------------------------------------------------------ -// ElementsDecodeLockingScript +// DecodePsbtRequest // ------------------------------------------------------------------------ /** - * @brief JSON-API (ElementsDecodeLockingScript) class + * @brief JSON-API (DecodePsbtRequest) class */ -class ElementsDecodeLockingScript - : public cfd::core::JsonClassBase { +class DecodePsbtRequest + : public cfd::core::JsonClassBase { public: - ElementsDecodeLockingScript() { + DecodePsbtRequest() { CollectFieldName(); } - virtual ~ElementsDecodeLockingScript() { + virtual ~DecodePsbtRequest() { // do nothing } /** @@ -3599,478 +7112,631 @@ class ElementsDecodeLockingScript static void CollectFieldName(); /** - * @brief Get of asm - * @return asm + * @brief Get of psbt + * @return psbt */ - std::string GetAsm() const { - return asm__; + std::string GetPsbt() const { + return psbt_; } /** - * @brief Set to asm - * @param[in] asm_ setting value. + * @brief Set to psbt + * @param[in] psbt setting value. */ - void SetAsm( // line separate - const std::string& asm_) { // NOLINT - this->asm__ = asm_; + void SetPsbt( // line separate + const std::string& psbt) { // NOLINT + this->psbt_ = psbt; } /** - * @brief Get data type of asm - * @return Data type of asm + * @brief Get data type of psbt + * @return Data type of psbt */ - static std::string GetAsmFieldType() { + static std::string GetPsbtFieldType() { return "std::string"; } /** - * @brief Get json string of asm field. + * @brief Get json string of psbt field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetAsmString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asm__); + static std::string GetPsbtString( // line separate + const DecodePsbtRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.psbt_); } /** - * @brief Set json object to asm field. + * @brief Set json object to psbt field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetAsmString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetPsbtString( // line separate + DecodePsbtRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.asm__, json_value); + obj.psbt_, json_value); } /** - * @brief Get of hex - * @return hex + * @brief Get of network + * @return network */ - std::string GetHex() const { - return hex_; + std::string GetNetwork() const { + return network_; } /** - * @brief Set to hex - * @param[in] hex setting value. + * @brief Set to network + * @param[in] network setting value. */ - void SetHex( // line separate - const std::string& hex) { // NOLINT - this->hex_ = hex; + void SetNetwork( // line separate + const std::string& network) { // NOLINT + this->network_ = network; } /** - * @brief Get data type of hex - * @return Data type of hex + * @brief Get data type of network + * @return Data type of network */ - static std::string GetHexFieldType() { + static std::string GetNetworkFieldType() { return "std::string"; } /** - * @brief Get json string of hex field. + * @brief Get json string of network field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetHexString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.hex_); + static std::string GetNetworkString( // line separate + const DecodePsbtRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.network_); } /** - * @brief Set json object to hex field. + * @brief Set json object to network field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetHexString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetNetworkString( // line separate + DecodePsbtRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.hex_, json_value); + obj.network_, json_value); } /** - * @brief Get of reqSigs - * @return reqSigs + * @brief Get of hasDetail + * @return hasDetail */ - int GetReqSigs() const { - return req_sigs_; + bool GetHasDetail() const { + return has_detail_; } /** - * @brief Set to reqSigs - * @param[in] req_sigs setting value. + * @brief Set to hasDetail + * @param[in] has_detail setting value. */ - void SetReqSigs( // line separate - const int& req_sigs) { // NOLINT - this->req_sigs_ = req_sigs; + void SetHasDetail( // line separate + const bool& has_detail) { // NOLINT + this->has_detail_ = has_detail; } /** - * @brief Get data type of reqSigs - * @return Data type of reqSigs + * @brief Get data type of hasDetail + * @return Data type of hasDetail */ - static std::string GetReqSigsFieldType() { - return "int"; + static std::string GetHasDetailFieldType() { + return "bool"; } /** - * @brief Get json string of reqSigs field. + * @brief Get json string of hasDetail field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetReqSigsString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.req_sigs_); + static std::string GetHasDetailString( // line separate + const DecodePsbtRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.has_detail_); } /** - * @brief Set json object to reqSigs field. + * @brief Set json object to hasDetail field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetReqSigsString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetHasDetailString( // line separate + DecodePsbtRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.req_sigs_, json_value); + obj.has_detail_, json_value); } /** - * @brief Get of type - * @return type + * @brief Get of hasSimple + * @return hasSimple */ - std::string GetType() const { - return type_; + bool GetHasSimple() const { + return has_simple_; } /** - * @brief Set to type - * @param[in] type setting value. + * @brief Set to hasSimple + * @param[in] has_simple setting value. + */ + void SetHasSimple( // line separate + const bool& has_simple) { // NOLINT + this->has_simple_ = has_simple; + } + /** + * @brief Get data type of hasSimple + * @return Data type of hasSimple + */ + static std::string GetHasSimpleFieldType() { + return "bool"; + } + /** + * @brief Get json string of hasSimple field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetHasSimpleString( // line separate + const DecodePsbtRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.has_simple_); + } + /** + * @brief Set json object to hasSimple field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetHasSimpleString( // line separate + DecodePsbtRequest& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.has_simple_, json_value); + } + + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const DecodePsbtRequestStruct& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + DecodePsbtRequestStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using DecodePsbtRequestMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const DecodePsbtRequestMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static DecodePsbtRequestMapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; + + /** + * @brief JsonAPI(psbt) value + */ + std::string psbt_ = ""; + /** + * @brief JsonAPI(network) value */ - void SetType( // line separate - const std::string& type) { // NOLINT - this->type_ = type; - } + std::string network_ = "mainnet"; /** - * @brief Get data type of type - * @return Data type of type + * @brief JsonAPI(hasDetail) value */ - static std::string GetTypeFieldType() { - return "std::string"; - } + bool has_detail_ = false; /** - * @brief Get json string of type field. - * @param[in,out] obj class object. - * @return JSON string + * @brief JsonAPI(hasSimple) value */ - static std::string GetTypeString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.type_); + bool has_simple_ = false; +}; + +// ------------------------------------------------------------------------ +// DecodePsbtResponse +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (DecodePsbtResponse) class + */ +class DecodePsbtResponse + : public cfd::core::JsonClassBase { + public: + DecodePsbtResponse() { + CollectFieldName(); + } + virtual ~DecodePsbtResponse() { + // do nothing } /** - * @brief Set json object to type field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief collect field name. */ - static void SetTypeString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.type_, json_value); - } + static void CollectFieldName(); /** - * @brief Get of addresses. - * @return addresses + * @brief Get of tx. + * @return tx */ - JsonValueVector& GetAddresses() { // NOLINT - return addresses_; + DecodeRawTransactionResponse& GetTx() { // NOLINT + return tx_; } /** - * @brief Set to addresses. - * @param[in] addresses setting value. + * @brief Set to tx. + * @param[in] tx setting value. */ - void SetAddresses( // line separate - const JsonValueVector& addresses) { // NOLINT - this->addresses_ = addresses; + void SetTx( // line separate + const DecodeRawTransactionResponse& tx) { // NOLINT + this->tx_ = tx; } /** - * @brief Get data type of addresses. - * @return Data type of addresses. + * @brief Get data type of tx. + * @return Data type of tx. */ - static std::string GetAddressesFieldType() { - return "JsonValueVector"; // NOLINT + static std::string GetTxFieldType() { + return "DecodeRawTransactionResponse"; // NOLINT } /** - * @brief Get json string of addresses field. + * @brief Get json string of tx field. * @param[in,out] obj class object * @return JSON string. */ - static std::string GetAddressesString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT + static std::string GetTxString( // line separate + const DecodePsbtResponse& obj) { // NOLINT // Do not set to const, because substitution of member variables // may occur in pre / post processing inside Serialize - return obj.addresses_.Serialize(); + return obj.tx_.Serialize(); } /** - * @brief Set json object to addresses field. + * @brief Set json object to tx field. * @param[in,out] obj class object * @param[in] json_value JSON object */ - static void SetAddressesString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetTxString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { - obj.addresses_.DeserializeUniValue(json_value); + obj.tx_.DeserializeUniValue(json_value); } /** - * @brief Get of pegout_chain - * @return pegout_chain + * @brief Get of tx_hex + * @return tx_hex */ - std::string GetPegout_chain() const { - return pegout_chain_; + std::string GetTx_hex() const { + return tx_hex_; } /** - * @brief Set to pegout_chain - * @param[in] pegout_chain setting value. + * @brief Set to tx_hex + * @param[in] tx_hex setting value. */ - void SetPegout_chain( // line separate - const std::string& pegout_chain) { // NOLINT - this->pegout_chain_ = pegout_chain; + void SetTx_hex( // line separate + const std::string& tx_hex) { // NOLINT + this->tx_hex_ = tx_hex; } /** - * @brief Get data type of pegout_chain - * @return Data type of pegout_chain + * @brief Get data type of tx_hex + * @return Data type of tx_hex */ - static std::string GetPegout_chainFieldType() { + static std::string GetTx_hexFieldType() { return "std::string"; } /** - * @brief Get json string of pegout_chain field. + * @brief Get json string of tx_hex field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetPegout_chainString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.pegout_chain_); + static std::string GetTx_hexString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + return cfd::core::ConvertToString(obj.tx_hex_); } /** - * @brief Set json object to pegout_chain field. + * @brief Set json object to tx_hex field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetPegout_chainString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetTx_hexString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.pegout_chain_, json_value); + obj.tx_hex_, json_value); } /** - * @brief Get of pegout_asm - * @return pegout_asm + * @brief Get of xpubs. + * @return xpubs */ - std::string GetPegout_asm() const { - return pegout_asm_; + JsonObjectVector& GetXpubs() { // NOLINT + return xpubs_; } /** - * @brief Set to pegout_asm - * @param[in] pegout_asm setting value. + * @brief Set to xpubs. + * @param[in] xpubs setting value. */ - void SetPegout_asm( // line separate - const std::string& pegout_asm) { // NOLINT - this->pegout_asm_ = pegout_asm; + void SetXpubs( // line separate + const JsonObjectVector& xpubs) { // NOLINT + this->xpubs_ = xpubs; } /** - * @brief Get data type of pegout_asm - * @return Data type of pegout_asm + * @brief Get data type of xpubs. + * @return Data type of xpubs. */ - static std::string GetPegout_asmFieldType() { - return "std::string"; + static std::string GetXpubsFieldType() { + return "JsonObjectVector"; // NOLINT } /** - * @brief Get json string of pegout_asm field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get json string of xpubs field. + * @param[in,out] obj class object + * @return JSON string. */ - static std::string GetPegout_asmString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.pegout_asm_); + static std::string GetXpubsString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.xpubs_.Serialize(); } /** - * @brief Set json object to pegout_asm field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Set json object to xpubs field. + * @param[in,out] obj class object + * @param[in] json_value JSON object */ - static void SetPegout_asmString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetXpubsString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.pegout_asm_, json_value); + obj.xpubs_.DeserializeUniValue(json_value); } /** - * @brief Get of pegout_hex - * @return pegout_hex + * @brief Get of version + * @return version */ - std::string GetPegout_hex() const { - return pegout_hex_; + uint32_t GetVersion() const { + return version_; } /** - * @brief Set to pegout_hex - * @param[in] pegout_hex setting value. + * @brief Set to version + * @param[in] version setting value. */ - void SetPegout_hex( // line separate - const std::string& pegout_hex) { // NOLINT - this->pegout_hex_ = pegout_hex; + void SetVersion( // line separate + const uint32_t& version) { // NOLINT + this->version_ = version; } /** - * @brief Get data type of pegout_hex - * @return Data type of pegout_hex + * @brief Get data type of version + * @return Data type of version */ - static std::string GetPegout_hexFieldType() { - return "std::string"; + static std::string GetVersionFieldType() { + return "uint32_t"; } /** - * @brief Get json string of pegout_hex field. + * @brief Get json string of version field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetPegout_hexString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.pegout_hex_); + static std::string GetVersionString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + return cfd::core::ConvertToString(obj.version_); } /** - * @brief Set json object to pegout_hex field. + * @brief Set json object to version field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetPegout_hexString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetVersionString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.pegout_hex_, json_value); + obj.version_, json_value); } /** - * @brief Get of pegout_reqSigs - * @return pegout_reqSigs + * @brief Get of unknown. + * @return unknown */ - int GetPegout_reqSigs() const { - return pegout_req_sigs_; + JsonObjectVector& GetUnknown() { // NOLINT + return unknown_; } /** - * @brief Set to pegout_reqSigs - * @param[in] pegout_req_sigs setting value. + * @brief Set to unknown. + * @param[in] unknown setting value. */ - void SetPegout_reqSigs( // line separate - const int& pegout_req_sigs) { // NOLINT - this->pegout_req_sigs_ = pegout_req_sigs; + void SetUnknown( // line separate + const JsonObjectVector& unknown) { // NOLINT + this->unknown_ = unknown; } /** - * @brief Get data type of pegout_reqSigs - * @return Data type of pegout_reqSigs + * @brief Get data type of unknown. + * @return Data type of unknown. */ - static std::string GetPegout_reqSigsFieldType() { - return "int"; + static std::string GetUnknownFieldType() { + return "JsonObjectVector"; // NOLINT } /** - * @brief Get json string of pegout_reqSigs field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get json string of unknown field. + * @param[in,out] obj class object + * @return JSON string. */ - static std::string GetPegout_reqSigsString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.pegout_req_sigs_); + static std::string GetUnknownString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.unknown_.Serialize(); } /** - * @brief Set json object to pegout_reqSigs field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Set json object to unknown field. + * @param[in,out] obj class object + * @param[in] json_value JSON object */ - static void SetPegout_reqSigsString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetUnknownString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.pegout_req_sigs_, json_value); + obj.unknown_.DeserializeUniValue(json_value); } /** - * @brief Get of pegout_type - * @return pegout_type + * @brief Get of inputs. + * @return inputs */ - std::string GetPegout_type() const { - return pegout_type_; + JsonObjectVector& GetInputs() { // NOLINT + return inputs_; } /** - * @brief Set to pegout_type - * @param[in] pegout_type setting value. + * @brief Set to inputs. + * @param[in] inputs setting value. */ - void SetPegout_type( // line separate - const std::string& pegout_type) { // NOLINT - this->pegout_type_ = pegout_type; + void SetInputs( // line separate + const JsonObjectVector& inputs) { // NOLINT + this->inputs_ = inputs; } /** - * @brief Get data type of pegout_type - * @return Data type of pegout_type + * @brief Get data type of inputs. + * @return Data type of inputs. */ - static std::string GetPegout_typeFieldType() { - return "std::string"; + static std::string GetInputsFieldType() { + return "JsonObjectVector"; // NOLINT } /** - * @brief Get json string of pegout_type field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get json string of inputs field. + * @param[in,out] obj class object + * @return JSON string. */ - static std::string GetPegout_typeString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT - return cfd::core::ConvertToString(obj.pegout_type_); + static std::string GetInputsString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.inputs_.Serialize(); } /** - * @brief Set json object to pegout_type field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Set json object to inputs field. + * @param[in,out] obj class object + * @param[in] json_value JSON object */ - static void SetPegout_typeString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetInputsString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.pegout_type_, json_value); + obj.inputs_.DeserializeUniValue(json_value); } /** - * @brief Get of pegout_addresses. - * @return pegout_addresses + * @brief Get of outputs. + * @return outputs */ - JsonValueVector& GetPegout_addresses() { // NOLINT - return pegout_addresses_; + JsonObjectVector& GetOutputs() { // NOLINT + return outputs_; } /** - * @brief Set to pegout_addresses. - * @param[in] pegout_addresses setting value. + * @brief Set to outputs. + * @param[in] outputs setting value. */ - void SetPegout_addresses( // line separate - const JsonValueVector& pegout_addresses) { // NOLINT - this->pegout_addresses_ = pegout_addresses; + void SetOutputs( // line separate + const JsonObjectVector& outputs) { // NOLINT + this->outputs_ = outputs; } /** - * @brief Get data type of pegout_addresses. - * @return Data type of pegout_addresses. + * @brief Get data type of outputs. + * @return Data type of outputs. */ - static std::string GetPegout_addressesFieldType() { - return "JsonValueVector"; // NOLINT + static std::string GetOutputsFieldType() { + return "JsonObjectVector"; // NOLINT } /** - * @brief Get json string of pegout_addresses field. + * @brief Get json string of outputs field. * @param[in,out] obj class object * @return JSON string. */ - static std::string GetPegout_addressesString( // line separate - const ElementsDecodeLockingScript& obj) { // NOLINT + static std::string GetOutputsString( // line separate + const DecodePsbtResponse& obj) { // NOLINT // Do not set to const, because substitution of member variables // may occur in pre / post processing inside Serialize - return obj.pegout_addresses_.Serialize(); + return obj.outputs_.Serialize(); } /** - * @brief Set json object to pegout_addresses field. + * @brief Set json object to outputs field. * @param[in,out] obj class object * @param[in] json_value JSON object */ - static void SetPegout_addressesString( // line separate - ElementsDecodeLockingScript& obj, // NOLINT + static void SetOutputsString( // line separate + DecodePsbtResponse& obj, // NOLINT + const UniValue& json_value) { + obj.outputs_.DeserializeUniValue(json_value); + } + + /** + * @brief Get of fee + * @return fee + */ + int64_t GetFee() const { + return fee_; + } + /** + * @brief Set to fee + * @param[in] fee setting value. + */ + void SetFee( // line separate + const int64_t& fee) { // NOLINT + this->fee_ = fee; + } + /** + * @brief Get data type of fee + * @return Data type of fee + */ + static std::string GetFeeFieldType() { + return "int64_t"; + } + /** + * @brief Get json string of fee field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string GetFeeString( // line separate + const DecodePsbtResponse& obj) { // NOLINT + return cfd::core::ConvertToString(obj.fee_); + } + /** + * @brief Set json object to fee field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void SetFeeString( // line separate + DecodePsbtResponse& obj, // NOLINT const UniValue& json_value) { - obj.pegout_addresses_.DeserializeUniValue(json_value); + cfd::core::ConvertFromUniValue( // line separate + obj.fee_, json_value); } /** @@ -4086,27 +7752,27 @@ class ElementsDecodeLockingScript * @param[in] data struct data. */ void ConvertFromStruct( - const ElementsDecodeLockingScriptStruct& data); + const DecodePsbtResponseStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - ElementsDecodeLockingScriptStruct ConvertToStruct() const; + DecodePsbtResponseStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using ElementsDecodeLockingScriptMapTable = - cfd::core::JsonTableMap; + using DecodePsbtResponseMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const ElementsDecodeLockingScriptMapTable& GetJsonMapper() const { // NOLINT + virtual const DecodePsbtResponseMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -4119,7 +7785,7 @@ class ElementsDecodeLockingScript return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -4132,7 +7798,7 @@ class ElementsDecodeLockingScript /** * @brief JsonFunctionMap table */ - static ElementsDecodeLockingScriptMapTable json_mapper; + static DecodePsbtResponseMapTable json_mapper; /** * @brief field name list. */ @@ -4143,64 +7809,52 @@ class ElementsDecodeLockingScript std::set ignore_items; /** - * @brief JsonAPI(asm) value - */ - std::string asm__ = ""; - /** - * @brief JsonAPI(hex) value - */ - std::string hex_ = ""; - /** - * @brief JsonAPI(reqSigs) value - */ - int req_sigs_ = 0; - /** - * @brief JsonAPI(type) value + * @brief JsonAPI(tx) value */ - std::string type_ = ""; + DecodeRawTransactionResponse tx_; // NOLINT /** - * @brief JsonAPI(addresses) value + * @brief JsonAPI(tx_hex) value */ - JsonValueVector addresses_; // NOLINT + std::string tx_hex_ = ""; /** - * @brief JsonAPI(pegout_chain) value + * @brief JsonAPI(xpubs) value */ - std::string pegout_chain_ = ""; + JsonObjectVector xpubs_; // NOLINT /** - * @brief JsonAPI(pegout_asm) value + * @brief JsonAPI(version) value */ - std::string pegout_asm_ = ""; + uint32_t version_ = 0; /** - * @brief JsonAPI(pegout_hex) value + * @brief JsonAPI(unknown) value */ - std::string pegout_hex_ = ""; + JsonObjectVector unknown_; // NOLINT /** - * @brief JsonAPI(pegout_reqSigs) value + * @brief JsonAPI(inputs) value */ - int pegout_req_sigs_ = 0; + JsonObjectVector inputs_; // NOLINT /** - * @brief JsonAPI(pegout_type) value + * @brief JsonAPI(outputs) value */ - std::string pegout_type_ = ""; + JsonObjectVector outputs_; // NOLINT /** - * @brief JsonAPI(pegout_addresses) value + * @brief JsonAPI(fee) value */ - JsonValueVector pegout_addresses_; // NOLINT + int64_t fee_ = 0; }; // ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionTxOut +// DecodeRawTransactionRequest // ------------------------------------------------------------------------ /** - * @brief JSON-API (ElementsDecodeRawTransactionTxOut) class + * @brief JSON-API (DecodeRawTransactionRequest) class */ -class ElementsDecodeRawTransactionTxOut - : public cfd::core::JsonClassBase { +class DecodeRawTransactionRequest + : public cfd::core::JsonClassBase { public: - ElementsDecodeRawTransactionTxOut() { + DecodeRawTransactionRequest() { CollectFieldName(); } - virtual ~ElementsDecodeRawTransactionTxOut() { + virtual ~DecodeRawTransactionRequest() { // do nothing } /** @@ -4209,606 +7863,450 @@ class ElementsDecodeRawTransactionTxOut static void CollectFieldName(); /** - * @brief Get of value - * @return value - */ - int64_t GetValue() const { - return value_; - } - /** - * @brief Set to value - * @param[in] value setting value. - */ - void SetValue( // line separate - const int64_t& value) { // NOLINT - this->value_ = value; - } - /** - * @brief Get data type of value - * @return Data type of value - */ - static std::string GetValueFieldType() { - return "int64_t"; - } - /** - * @brief Get json string of value field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetValueString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.value_); - } - /** - * @brief Set json object to value field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetValueString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.value_, json_value); - } - - /** - * @brief Get of value-minimum - * @return value-minimum - */ - int64_t GetValue_minimum() const { - return value_minimum_; - } - /** - * @brief Set to value-minimum - * @param[in] value_minimum setting value. - */ - void SetValue_minimum( // line separate - const int64_t& value_minimum) { // NOLINT - this->value_minimum_ = value_minimum; - } - /** - * @brief Get data type of value-minimum - * @return Data type of value-minimum - */ - static std::string GetValue_minimumFieldType() { - return "int64_t"; - } - /** - * @brief Get json string of value-minimum field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetValue_minimumString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.value_minimum_); - } - /** - * @brief Set json object to value-minimum field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetValue_minimumString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.value_minimum_, json_value); - } - - /** - * @brief Get of value-maximum - * @return value-maximum - */ - int64_t GetValue_maximum() const { - return value_maximum_; - } - /** - * @brief Set to value-maximum - * @param[in] value_maximum setting value. - */ - void SetValue_maximum( // line separate - const int64_t& value_maximum) { // NOLINT - this->value_maximum_ = value_maximum; - } - /** - * @brief Get data type of value-maximum - * @return Data type of value-maximum - */ - static std::string GetValue_maximumFieldType() { - return "int64_t"; - } - /** - * @brief Get json string of value-maximum field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetValue_maximumString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.value_maximum_); - } - /** - * @brief Set json object to value-maximum field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetValue_maximumString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.value_maximum_, json_value); - } - - /** - * @brief Get of ct-exponent - * @return ct-exponent - */ - int GetCt_exponent() const { - return ct_exponent_; - } - /** - * @brief Set to ct-exponent - * @param[in] ct_exponent setting value. - */ - void SetCt_exponent( // line separate - const int& ct_exponent) { // NOLINT - this->ct_exponent_ = ct_exponent; - } - /** - * @brief Get data type of ct-exponent - * @return Data type of ct-exponent - */ - static std::string GetCt_exponentFieldType() { - return "int"; - } - /** - * @brief Get json string of ct-exponent field. - * @param[in,out] obj class object. - * @return JSON string - */ - static std::string GetCt_exponentString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.ct_exponent_); - } - /** - * @brief Set json object to ct-exponent field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. - */ - static void SetCt_exponentString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.ct_exponent_, json_value); - } - - /** - * @brief Get of ct-bits - * @return ct-bits + * @brief Get of hex + * @return hex */ - int GetCt_bits() const { - return ct_bits_; + std::string GetHex() const { + return hex_; } /** - * @brief Set to ct-bits - * @param[in] ct_bits setting value. + * @brief Set to hex + * @param[in] hex setting value. */ - void SetCt_bits( // line separate - const int& ct_bits) { // NOLINT - this->ct_bits_ = ct_bits; + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; } /** - * @brief Get data type of ct-bits - * @return Data type of ct-bits + * @brief Get data type of hex + * @return Data type of hex */ - static std::string GetCt_bitsFieldType() { - return "int"; + static std::string GetHexFieldType() { + return "std::string"; } /** - * @brief Get json string of ct-bits field. + * @brief Get json string of hex field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetCt_bitsString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.ct_bits_); + static std::string GetHexString( // line separate + const DecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); } /** - * @brief Set json object to ct-bits field. + * @brief Set json object to hex field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetCt_bitsString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetHexString( // line separate + DecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.ct_bits_, json_value); + obj.hex_, json_value); } /** - * @brief Get of surjectionproof - * @return surjectionproof + * @brief Get of network + * @return network */ - std::string GetSurjectionproof() const { - return surjectionproof_; + std::string GetNetwork() const { + return network_; } /** - * @brief Set to surjectionproof - * @param[in] surjectionproof setting value. + * @brief Set to network + * @param[in] network setting value. */ - void SetSurjectionproof( // line separate - const std::string& surjectionproof) { // NOLINT - this->surjectionproof_ = surjectionproof; + void SetNetwork( // line separate + const std::string& network) { // NOLINT + this->network_ = network; } /** - * @brief Get data type of surjectionproof - * @return Data type of surjectionproof + * @brief Get data type of network + * @return Data type of network */ - static std::string GetSurjectionproofFieldType() { + static std::string GetNetworkFieldType() { return "std::string"; } /** - * @brief Get json string of surjectionproof field. + * @brief Get json string of network field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetSurjectionproofString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.surjectionproof_); + static std::string GetNetworkString( // line separate + const DecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.network_); } /** - * @brief Set json object to surjectionproof field. + * @brief Set json object to network field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetSurjectionproofString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetNetworkString( // line separate + DecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.surjectionproof_, json_value); + obj.network_, json_value); } /** - * @brief Get of valuecommitment - * @return valuecommitment + * @brief Get of iswitness + * @return iswitness */ - std::string GetValuecommitment() const { - return valuecommitment_; + bool GetIswitness() const { + return iswitness_; } /** - * @brief Set to valuecommitment - * @param[in] valuecommitment setting value. + * @brief Set to iswitness + * @param[in] iswitness setting value. */ - void SetValuecommitment( // line separate - const std::string& valuecommitment) { // NOLINT - this->valuecommitment_ = valuecommitment; + void SetIswitness( // line separate + const bool& iswitness) { // NOLINT + this->iswitness_ = iswitness; } /** - * @brief Get data type of valuecommitment - * @return Data type of valuecommitment + * @brief Get data type of iswitness + * @return Data type of iswitness */ - static std::string GetValuecommitmentFieldType() { - return "std::string"; + static std::string GetIswitnessFieldType() { + return "bool"; } /** - * @brief Get json string of valuecommitment field. + * @brief Get json string of iswitness field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetValuecommitmentString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.valuecommitment_); + static std::string GetIswitnessString( // line separate + const DecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.iswitness_); } /** - * @brief Set json object to valuecommitment field. + * @brief Set json object to iswitness field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetValuecommitmentString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetIswitnessString( // line separate + DecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.valuecommitment_, json_value); + obj.iswitness_, json_value); } /** - * @brief Get of asset - * @return asset + * @brief Set ignore item. + * @param[in] key ignore target key name. */ - std::string GetAsset() const { - return asset_; + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); } + /** - * @brief Set to asset - * @param[in] asset setting value. + * @brief Convert struct to class. + * @param[in] data struct data. */ - void SetAsset( // line separate - const std::string& asset) { // NOLINT - this->asset_ = asset; - } + void ConvertFromStruct( + const DecodeRawTransactionRequestStruct& data); + /** - * @brief Get data type of asset - * @return Data type of asset + * @brief Convert class to struct. + * @return struct data. */ - static std::string GetAssetFieldType() { - return "std::string"; + DecodeRawTransactionRequestStruct ConvertToStruct() const; + + protected: + /** + * @brief definition type of Map table. + */ + using DecodeRawTransactionRequestMapTable = + cfd::core::JsonTableMap; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const DecodeRawTransactionRequestMapTable& GetJsonMapper() const { // NOLINT + return json_mapper; } /** - * @brief Get json string of asset field. - * @param[in,out] obj class object. - * @return JSON string + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() */ - static std::string GetAssetString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.asset_); + virtual const std::vector& GetJsonItemList() const { + return item_list; } /** - * @brief Set json object to asset field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() */ - static void SetAssetString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.asset_, json_value); + virtual const std::set& GetIgnoreItem() const { + return ignore_items; } + private: + /** + * @brief JsonFunctionMap table + */ + static DecodeRawTransactionRequestMapTable json_mapper; /** - * @brief Get of assetcommitment - * @return assetcommitment + * @brief field name list. */ - std::string GetAssetcommitment() const { - return assetcommitment_; - } + static std::vector item_list; /** - * @brief Set to assetcommitment - * @param[in] assetcommitment setting value. + * @brief ignore item list. */ - void SetAssetcommitment( // line separate - const std::string& assetcommitment) { // NOLINT - this->assetcommitment_ = assetcommitment; - } + std::set ignore_items; + /** - * @brief Get data type of assetcommitment - * @return Data type of assetcommitment + * @brief JsonAPI(hex) value */ - static std::string GetAssetcommitmentFieldType() { - return "std::string"; - } + std::string hex_ = ""; /** - * @brief Get json string of assetcommitment field. - * @param[in,out] obj class object. - * @return JSON string + * @brief JsonAPI(network) value */ - static std::string GetAssetcommitmentString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.assetcommitment_); - } + std::string network_ = "mainnet"; /** - * @brief Set json object to assetcommitment field. - * @param[in,out] obj class object. - * @param[in] json_value JSON object. + * @brief JsonAPI(iswitness) value */ - static void SetAssetcommitmentString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT - const UniValue& json_value) { - cfd::core::ConvertFromUniValue( // line separate - obj.assetcommitment_, json_value); + bool iswitness_ = true; +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionRequest +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (ElementsDecodeRawTransactionRequest) class + */ +class ElementsDecodeRawTransactionRequest + : public cfd::core::JsonClassBase { + public: + ElementsDecodeRawTransactionRequest() { + CollectFieldName(); + } + virtual ~ElementsDecodeRawTransactionRequest() { + // do nothing } + /** + * @brief collect field name. + */ + static void CollectFieldName(); /** - * @brief Get of commitmentnonce - * @return commitmentnonce + * @brief Get of hex + * @return hex */ - std::string GetCommitmentnonce() const { - return commitmentnonce_; + std::string GetHex() const { + return hex_; } /** - * @brief Set to commitmentnonce - * @param[in] commitmentnonce setting value. + * @brief Set to hex + * @param[in] hex setting value. */ - void SetCommitmentnonce( // line separate - const std::string& commitmentnonce) { // NOLINT - this->commitmentnonce_ = commitmentnonce; + void SetHex( // line separate + const std::string& hex) { // NOLINT + this->hex_ = hex; } /** - * @brief Get data type of commitmentnonce - * @return Data type of commitmentnonce + * @brief Get data type of hex + * @return Data type of hex */ - static std::string GetCommitmentnonceFieldType() { + static std::string GetHexFieldType() { return "std::string"; } /** - * @brief Get json string of commitmentnonce field. + * @brief Get json string of hex field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetCommitmentnonceString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.commitmentnonce_); + static std::string GetHexString( // line separate + const ElementsDecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.hex_); } /** - * @brief Set json object to commitmentnonce field. + * @brief Set json object to hex field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetCommitmentnonceString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetHexString( // line separate + ElementsDecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.commitmentnonce_, json_value); + obj.hex_, json_value); } /** - * @brief Get of commitmentnonce_fully_valid - * @return commitmentnonce_fully_valid + * @brief Get of network + * @return network */ - bool GetCommitmentnonce_fully_valid() const { - return commitmentnonce_fully_valid_; + std::string GetNetwork() const { + return network_; } /** - * @brief Set to commitmentnonce_fully_valid - * @param[in] commitmentnonce_fully_valid setting value. + * @brief Set to network + * @param[in] network setting value. */ - void SetCommitmentnonce_fully_valid( // line separate - const bool& commitmentnonce_fully_valid) { // NOLINT - this->commitmentnonce_fully_valid_ = commitmentnonce_fully_valid; + void SetNetwork( // line separate + const std::string& network) { // NOLINT + this->network_ = network; } /** - * @brief Get data type of commitmentnonce_fully_valid - * @return Data type of commitmentnonce_fully_valid + * @brief Get data type of network + * @return Data type of network */ - static std::string GetCommitmentnonce_fully_validFieldType() { - return "bool"; + static std::string GetNetworkFieldType() { + return "std::string"; } /** - * @brief Get json string of commitmentnonce_fully_valid field. + * @brief Get json string of network field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetCommitmentnonce_fully_validString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.commitmentnonce_fully_valid_); + static std::string GetNetworkString( // line separate + const ElementsDecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.network_); } /** - * @brief Set json object to commitmentnonce_fully_valid field. + * @brief Set json object to network field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetCommitmentnonce_fully_validString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetNetworkString( // line separate + ElementsDecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.commitmentnonce_fully_valid_, json_value); + obj.network_, json_value); } /** - * @brief Get of n - * @return n + * @brief Get of mainchainNetwork + * @return mainchainNetwork */ - uint32_t GetN() const { - return n_; + std::string GetMainchainNetwork() const { + return mainchain_network_; } /** - * @brief Set to n - * @param[in] n setting value. + * @brief Set to mainchainNetwork + * @param[in] mainchain_network setting value. */ - void SetN( // line separate - const uint32_t& n) { // NOLINT - this->n_ = n; + void SetMainchainNetwork( // line separate + const std::string& mainchain_network) { // NOLINT + this->mainchain_network_ = mainchain_network; } /** - * @brief Get data type of n - * @return Data type of n + * @brief Get data type of mainchainNetwork + * @return Data type of mainchainNetwork */ - static std::string GetNFieldType() { - return "uint32_t"; + static std::string GetMainchainNetworkFieldType() { + return "std::string"; } /** - * @brief Get json string of n field. + * @brief Get json string of mainchainNetwork field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetNString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.n_); + static std::string GetMainchainNetworkString( // line separate + const ElementsDecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.mainchain_network_); } /** - * @brief Set json object to n field. + * @brief Set json object to mainchainNetwork field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetNString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetMainchainNetworkString( // line separate + ElementsDecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.n_, json_value); + obj.mainchain_network_, json_value); } /** - * @brief Get of scriptPubKey. - * @return scriptPubKey + * @brief Get of iswitness + * @return iswitness */ - ElementsDecodeLockingScript& GetScriptPubKey() { // NOLINT - return script_pub_key_; + bool GetIswitness() const { + return iswitness_; } /** - * @brief Set to scriptPubKey. - * @param[in] script_pub_key setting value. + * @brief Set to iswitness + * @param[in] iswitness setting value. */ - void SetScriptPubKey( // line separate - const ElementsDecodeLockingScript& script_pub_key) { // NOLINT - this->script_pub_key_ = script_pub_key; + void SetIswitness( // line separate + const bool& iswitness) { // NOLINT + this->iswitness_ = iswitness; } /** - * @brief Get data type of scriptPubKey. - * @return Data type of scriptPubKey. + * @brief Get data type of iswitness + * @return Data type of iswitness */ - static std::string GetScriptPubKeyFieldType() { - return "ElementsDecodeLockingScript"; // NOLINT + static std::string GetIswitnessFieldType() { + return "bool"; } /** - * @brief Get json string of scriptPubKey field. - * @param[in,out] obj class object - * @return JSON string. + * @brief Get json string of iswitness field. + * @param[in,out] obj class object. + * @return JSON string */ - static std::string GetScriptPubKeyString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - // Do not set to const, because substitution of member variables - // may occur in pre / post processing inside Serialize - return obj.script_pub_key_.Serialize(); + static std::string GetIswitnessString( // line separate + const ElementsDecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.iswitness_); } /** - * @brief Set json object to scriptPubKey field. - * @param[in,out] obj class object - * @param[in] json_value JSON object + * @brief Set json object to iswitness field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. */ - static void SetScriptPubKeyString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetIswitnessString( // line separate + ElementsDecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { - obj.script_pub_key_.DeserializeUniValue(json_value); + cfd::core::ConvertFromUniValue( // line separate + obj.iswitness_, json_value); } /** - * @brief Get of rangeproof - * @return rangeproof + * @brief Get of fullDump + * @return fullDump */ - std::string GetRangeproof() const { - return rangeproof_; + bool GetFullDump() const { + return full_dump_; } /** - * @brief Set to rangeproof - * @param[in] rangeproof setting value. + * @brief Set to fullDump + * @param[in] full_dump setting value. */ - void SetRangeproof( // line separate - const std::string& rangeproof) { // NOLINT - this->rangeproof_ = rangeproof; + void SetFullDump( // line separate + const bool& full_dump) { // NOLINT + this->full_dump_ = full_dump; } /** - * @brief Get data type of rangeproof - * @return Data type of rangeproof + * @brief Get data type of fullDump + * @return Data type of fullDump */ - static std::string GetRangeproofFieldType() { - return "std::string"; + static std::string GetFullDumpFieldType() { + return "bool"; } /** - * @brief Get json string of rangeproof field. + * @brief Get json string of fullDump field. * @param[in,out] obj class object. * @return JSON string */ - static std::string GetRangeproofString( // line separate - const ElementsDecodeRawTransactionTxOut& obj) { // NOLINT - return cfd::core::ConvertToString(obj.rangeproof_); + static std::string GetFullDumpString( // line separate + const ElementsDecodeRawTransactionRequest& obj) { // NOLINT + return cfd::core::ConvertToString(obj.full_dump_); } /** - * @brief Set json object to rangeproof field. + * @brief Set json object to fullDump field. * @param[in,out] obj class object. * @param[in] json_value JSON object. */ - static void SetRangeproofString( // line separate - ElementsDecodeRawTransactionTxOut& obj, // NOLINT + static void SetFullDumpString( // line separate + ElementsDecodeRawTransactionRequest& obj, // NOLINT const UniValue& json_value) { cfd::core::ConvertFromUniValue( // line separate - obj.rangeproof_, json_value); + obj.full_dump_, json_value); } /** @@ -4824,27 +8322,27 @@ class ElementsDecodeRawTransactionTxOut * @param[in] data struct data. */ void ConvertFromStruct( - const ElementsDecodeRawTransactionTxOutStruct& data); + const ElementsDecodeRawTransactionRequestStruct& data); /** * @brief Convert class to struct. * @return struct data. */ - ElementsDecodeRawTransactionTxOutStruct ConvertToStruct() const; + ElementsDecodeRawTransactionRequestStruct ConvertToStruct() const; protected: /** * @brief definition type of Map table. */ - using ElementsDecodeRawTransactionTxOutMapTable = - cfd::core::JsonTableMap; + using ElementsDecodeRawTransactionRequestMapTable = + cfd::core::JsonTableMap; /** * @brief Get JSON mapping object. * @return JSON mapping object. * @see cfd::core::JsonClassBase::GetJsonMapper() */ - virtual const ElementsDecodeRawTransactionTxOutMapTable& GetJsonMapper() const { // NOLINT + virtual const ElementsDecodeRawTransactionRequestMapTable& GetJsonMapper() const { // NOLINT return json_mapper; } /** @@ -4857,7 +8355,7 @@ class ElementsDecodeRawTransactionTxOut return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() @@ -4870,7 +8368,7 @@ class ElementsDecodeRawTransactionTxOut /** * @brief JsonFunctionMap table */ - static ElementsDecodeRawTransactionTxOutMapTable json_mapper; + static ElementsDecodeRawTransactionRequestMapTable json_mapper; /** * @brief field name list. */ @@ -4881,61 +8379,25 @@ class ElementsDecodeRawTransactionTxOut std::set ignore_items; /** - * @brief JsonAPI(value) value - */ - int64_t value_ = 0; - /** - * @brief JsonAPI(value-minimum) value - */ - int64_t value_minimum_ = 0; - /** - * @brief JsonAPI(value-maximum) value - */ - int64_t value_maximum_ = 0; - /** - * @brief JsonAPI(ct-exponent) value - */ - int ct_exponent_ = 0; - /** - * @brief JsonAPI(ct-bits) value - */ - int ct_bits_ = 0; - /** - * @brief JsonAPI(surjectionproof) value - */ - std::string surjectionproof_ = ""; - /** - * @brief JsonAPI(valuecommitment) value - */ - std::string valuecommitment_ = ""; - /** - * @brief JsonAPI(asset) value - */ - std::string asset_ = ""; - /** - * @brief JsonAPI(assetcommitment) value - */ - std::string assetcommitment_ = ""; - /** - * @brief JsonAPI(commitmentnonce) value + * @brief JsonAPI(hex) value */ - std::string commitmentnonce_ = ""; + std::string hex_ = ""; /** - * @brief JsonAPI(commitmentnonce_fully_valid) value + * @brief JsonAPI(network) value */ - bool commitmentnonce_fully_valid_ = false; + std::string network_ = "liquidv1"; /** - * @brief JsonAPI(n) value + * @brief JsonAPI(mainchainNetwork) value */ - uint32_t n_ = 0; + std::string mainchain_network_ = ""; /** - * @brief JsonAPI(scriptPubKey) value + * @brief JsonAPI(iswitness) value */ - ElementsDecodeLockingScript script_pub_key_; // NOLINT + bool iswitness_ = true; /** - * @brief JsonAPI(rangeproof) value + * @brief JsonAPI(fullDump) value */ - std::string rangeproof_ = ""; + bool full_dump_ = false; }; // ------------------------------------------------------------------------ @@ -5479,7 +8941,7 @@ class ElementsDecodeRawTransactionResponse return item_list; } /** - * @brief Get ignore item lists of JSON mnapping. + * @brief Get ignore item lists of JSON mapping. * Ignore the target variable at Serialize. * @return Item list of JSON mapping. * @see cfd::core::JsonClassBase::GetIgnoreItem() diff --git a/src/jsonapi/cfd_json_mapping_api.cpp b/src/jsonapi/cfd_json_mapping_api.cpp index e2dd47e3..7362aa02 100644 --- a/src/jsonapi/cfd_json_mapping_api.cpp +++ b/src/jsonapi/cfd_json_mapping_api.cpp @@ -10,6 +10,7 @@ #include "cfd/cfd_json_mapping_api.h" #include "jsonapi/autogen/cfd_api_json_autogen.h" // NOLINT #include "jsonapi/cfd_json_transaction.h" // NOLINT +#include "jsonapi/cfd_json_psbt.h" // NOLINT #include "jsonapi/cfd_json_elements_transaction.h" // NOLINT // using @@ -107,6 +108,12 @@ std::string JsonMappingApi::DecodeRawTransaction( request_message, TransactionJsonApi::DecodeRawTransaction); } +std::string JsonMappingApi::DecodePsbt( + const std::string &request_message) { + return ExecuteDirectApi( + request_message, PsbtJsonApi::DecodePsbt); +} + #ifndef CFD_DISABLE_ELEMENTS std::string JsonMappingApi::ElementsDecodeRawTransaction( diff --git a/src/jsonapi/cfd_json_psbt.cpp b/src/jsonapi/cfd_json_psbt.cpp new file mode 100644 index 00000000..13621a95 --- /dev/null +++ b/src/jsonapi/cfd_json_psbt.cpp @@ -0,0 +1,366 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfd_json_psbt.cpp + * + * @brief This file is defines Partially Signed Bitcoin Transaction by json. + */ +#include +#include +#include +#include +#include +#include + +#include "cfd/cfd_address.h" +#include "cfd/cfd_psbt.h" +#include "cfdcore/cfdcore_psbt.h" +#include "cfdcore/cfdcore_util.h" +#include "jsonapi/autogen/cfd_api_json_autogen.h" // NOLINT +#include "jsonapi/cfd_json_psbt.h" +#include "jsonapi/cfd_json_transaction.h" + +namespace cfd { +namespace api { +namespace json { + +using cfd::AddressFactory; +using cfd::core::Address; +using cfd::core::AddressType; +using cfd::core::Amount; +using cfd::core::BlockHash; +using cfd::core::ByteData; +using cfd::core::ByteData160; +using cfd::core::ByteData256; +using cfd::core::CfdError; +using cfd::core::CfdException; +using cfd::core::HashType; +using cfd::core::NetType; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::Psbt; +using cfd::core::Script; +using cfd::core::ScriptElement; +using cfd::core::ScriptUtil; +using cfd::core::SigHashType; +using cfd::core::SigHashAlgorithm; +using cfd::core::Transaction; +using cfd::core::Txid; +using cfd::core::logger::info; +using cfd::core::logger::warn; + +// ----------------------------------------------------------------------------- +// PsbtJsonApiクラス +// ----------------------------------------------------------------------------- +void PsbtJsonApi::DecodePsbt( + DecodePsbtRequest* request, DecodePsbtResponse* response) { + if (request == nullptr) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "request is null."); + } + if (response == nullptr) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, "response is null."); + } + // validate input hex + const std::string& hex_string = request->GetPsbt(); + if (hex_string.empty()) { + warn( + CFD_LOG_SOURCE, + "Failed to DecodePsbt. empty psbt."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid hex string. empty data."); + } + + auto net_type = TransactionJsonApi::ConvertNetType(request->GetNetwork()); + AddressFactory addr_factory(net_type); + + // Decode transaction hex + Psbt psbt(hex_string); + Transaction tx = psbt.GetTransaction(); + + DecodeRawTransactionRequest tx_req; + DecodeRawTransactionResponse tx_res; + PsbtMapData item; + + // global + auto tx_hex = tx.GetHex(); + tx_req.SetHex(tx_hex); + tx_req.SetNetwork(request->GetNetwork()); + TransactionJsonApi::DecodeRawTransaction(&tx_req, &tx_res); + response->SetTx(tx_res); + if (request->GetHasDetail()) { + response->SetTx_hex(tx_hex); + if (request->GetHasSimple()) response->SetIgnoreItem("tx"); + } else { + response->SetIgnoreItem("tx_hex"); + } + + auto key_list = psbt.GetGlobalRecordKeyList(); + if (request->GetHasDetail()) { + response->SetVersion(psbt.GetPsbtVersion()); + auto xpub_list = psbt.GetGlobalXpubkeyDataList(); + auto& xpubs = response->GetXpubs(); + for (const auto& xpub : xpub_list) { + const auto& extkey = xpub.GetExtPubkey(); + PsbtGlobalXpub xpub_obj; + auto& xpub_data = xpub_obj.GetXpub(); + xpub_data.SetBase58(extkey.ToString()); + xpub_data.SetHex(extkey.GetData().GetHex()); + xpub_obj.SetPath(xpub.GetBip32Path()); + xpub_obj.SetMaster_fingerprint(xpub.GetFingerprint().GetHex()); + xpub_obj.SetDescriptorXpub(xpub.ToString()); + xpubs.emplace_back(xpub_obj); + } + if (xpubs.empty()) response->SetIgnoreItem("xpubs"); + } else { + response->SetIgnoreItem("version"); + response->SetIgnoreItem("xpubs"); + if (psbt.GetPsbtVersion() > 0) key_list.push_back(ByteData("fb")); + } + + if (psbt.GetPsbtVersion() > 0) { + key_list.push_back(ByteData("fb")); + } + auto& unknown_list = response->GetUnknown(); + for (const auto& key : key_list) { + if (request->GetHasDetail() && (key.GetHeadData() == Psbt::kPsbtGlobalXpub)) { + continue; + } + auto data = psbt.GetGlobalRecord(key); + item.SetKey(key.GetHex()); + item.SetValue(data.GetHex()); + unknown_list.emplace_back(item); + } + if (request->GetHasSimple() && unknown_list.empty()) { + response->SetIgnoreItem("unknown"); + } + + Amount total_input; + bool is_unset_utxo = false; + uint32_t max = static_cast(tx.GetTxInCount()); + for (uint32_t index=0; indexGetNetwork()); + TransactionJsonApi::DecodeRawTransaction(&tx_req, &tx_res); + input.SetNon_witness_utxo(tx_res); + + if (request->GetHasDetail()) { + input.SetNon_witness_utxo_hex(utxo_tx_hex); + if (utxo_tx_hex.empty()) { + input.SetIgnoreItem("non_witness_utxo_hex"); + } + if (request->GetHasSimple()) { + input.SetIgnoreItem("non_witness_utxo"); + } + } else { + input.SetIgnoreItem("non_witness_utxo_hex"); + } + if (has_amount) { + // do nothing + } else if (full_utxo.GetTxOutCount() > tx_input.GetVout()) { + auto txout = full_utxo.GetTxOut(tx_input.GetVout()); + total_input += txout.GetValue(); + has_amount = true; + } else { + input.SetIgnoreItem("non_witness_utxo"); + is_unset_utxo = true; + } + } + + auto sig_pubkey_list = psbt.GetTxInSignaturePubkeyList(index); + if (sig_pubkey_list.empty()) { + input.SetIgnoreItem("partial_signatures"); + } else { + auto& sig_list = input.GetPartial_signatures(); + for (auto pubkey : sig_pubkey_list) { + auto sig = psbt.GetTxInSignature(index, pubkey); + PsbtSignatureData sig_data; + sig_data.SetPubkey(pubkey.GetHex()); + sig_data.SetSignature(sig.GetHex()); + sig_list.emplace_back(sig_data); + } + } + + if (!psbt.IsFindTxInSighashType(index)) { + input.SetIgnoreItem("sighash"); + } else { + auto sighashtype = psbt.GetTxInSighashType(index); + input.SetSighash(sighashtype.ToString()); + } + + Script redeem_script = psbt.GetTxInRedeemScriptDirect(index, true, false); + if (redeem_script.IsEmpty()) { + input.SetIgnoreItem("redeem_script"); + } else { + std::string script_type; + auto addr_list = TransactionJsonApi::ConvertFromLockingScript( + addr_factory, redeem_script, &script_type, nullptr); + input.GetRedeem_script().SetHex(redeem_script.GetHex()); + input.GetRedeem_script().SetAsm(redeem_script.ToString()); + input.GetRedeem_script().SetType(script_type); + } + + Script witness_script = psbt.GetTxInRedeemScriptDirect(index, true, true); + if (witness_script.IsEmpty()) { + input.SetIgnoreItem("witness_script"); + } else { + std::string script_type; + auto addr_list = TransactionJsonApi::ConvertFromLockingScript( + addr_factory, witness_script, &script_type, nullptr); + input.GetWitness_script().SetHex(witness_script.GetHex()); + input.GetWitness_script().SetAsm(witness_script.ToString()); + input.GetWitness_script().SetType(script_type); + } + + auto bip32_pubkey_list = psbt.GetTxInKeyDataList(index); + if (bip32_pubkey_list.empty()) { + input.SetIgnoreItem("bip32_derivs"); + } else { + auto& bip32_list = input.GetBip32_derivs(); + for (auto key_data : bip32_pubkey_list) { + PsbtBip32Data bip32_data; + bip32_data.SetPubkey(key_data.GetPubkey().GetHex()); + bip32_data.SetMaster_fingerprint(key_data.GetFingerprint().GetHex()); + bip32_data.SetPath(key_data.GetBip32Path()); + + if (request->GetHasDetail()) { + bip32_data.SetDescriptor(key_data.ToString()); + } else { + bip32_data.SetIgnoreItem("descriptor"); + } + bip32_list.emplace_back(bip32_data); + } + } + + auto scriptsig_arr = psbt.GetTxInFinalScript(index, false); + if ((!scriptsig_arr.empty()) && (!scriptsig_arr[0].IsEmpty())) { + input.GetFinal_scriptsig().SetHex(scriptsig_arr[0].GetHex()); + input.GetFinal_scriptsig().SetAsm(Script(scriptsig_arr[0]).ToString()); + } else { + input.SetIgnoreItem("final_scriptsig"); + } + + auto witness_stack = psbt.GetTxInFinalScript(index, true); + if (witness_stack.empty()) { + input.SetIgnoreItem("final_scriptwitness"); + } else { + auto& witness = input.GetFinal_scriptwitness(); + for (const auto& stack : witness_stack) { + witness.emplace_back(stack.GetHex()); + } + } + + key_list = psbt.GetTxInRecordKeyList(index); + for (const auto& key : key_list) { + auto data = psbt.GetTxInRecord(index, key); + item.SetKey(key.GetHex()); + item.SetValue(data.GetHex()); + input.GetUnknown().emplace_back(item); + } + if (input.GetUnknown().empty()) input.SetIgnoreItem("unknown"); + + response->GetInputs().push_back(input); + } + + Amount total_output; + max = static_cast(tx.GetTxOutCount()); + for (uint32_t index=0; indexGetHasDetail()) { + bip32_data.SetDescriptor(key_data.ToString()); + } else { + bip32_data.SetIgnoreItem("descriptor"); + } + bip32_list.emplace_back(bip32_data); + } + } + + key_list = psbt.GetTxOutRecordKeyList(index); + for (const auto& key : key_list) { + auto data = psbt.GetTxOutRecord(index, key); + item.SetKey(key.GetHex()); + item.SetValue(data.GetHex()); + output.GetUnknown().emplace_back(item); + } + if (output.GetUnknown().empty()) output.SetIgnoreItem("unknown"); + + response->GetOutputs().push_back(output); + } + + if (is_unset_utxo || (total_output > total_input)) { + response->SetIgnoreItem("fee"); + } else { + response->SetFee((total_input - total_output).GetSatoshiValue()); + } +} + +} // namespace json +} // namespace api +} // namespace cfd diff --git a/src/jsonapi/cfd_json_psbt.h b/src/jsonapi/cfd_json_psbt.h new file mode 100644 index 00000000..23471aad --- /dev/null +++ b/src/jsonapi/cfd_json_psbt.h @@ -0,0 +1,37 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfd_json_psbt.h + * + * @brief This file is defines Partially Signed Bitcoin Transaction by json. + */ +#ifndef CFD_SRC_JSONAPI_CFD_JSON_PSBT_H_ +#define CFD_SRC_JSONAPI_CFD_JSON_PSBT_H_ + +#include "jsonapi/autogen/cfd_api_json_autogen.h" // NOLINT + +namespace cfd { +namespace api { +namespace json { + +/** + * @brief The class of PSBT Json API. + */ +class PsbtJsonApi { + public: + /** + * @brief Decode PSBT using JSON parameter. + * @param[in] request request data + * @param[out] response response data + */ + static void DecodePsbt(DecodePsbtRequest* request, + DecodePsbtResponse* response); + + private: + PsbtJsonApi(); +}; + +} // namespace json +} // namespace api +} // namespace cfd + +#endif // CFD_SRC_JSONAPI_CFD_JSON_PSBT_H_ diff --git a/src/jsonapi/cfd_json_transaction.cpp b/src/jsonapi/cfd_json_transaction.cpp index f03e673f..21d7f015 100644 --- a/src/jsonapi/cfd_json_transaction.cpp +++ b/src/jsonapi/cfd_json_transaction.cpp @@ -137,52 +137,20 @@ void TransactionJsonApi::DecodeRawTransaction( script_pub_key_res.SetHex(locking_script.GetHex()); script_pub_key_res.SetAsm(locking_script.ToString()); - ExtractScriptData extract_data = - TransactionJsonApi::ExtractLockingScript(locking_script); - LockingScriptType type = extract_data.script_type; - script_pub_key_res.SetType("nonstandard"); - if (type != LockingScriptType::kFee) { - script_pub_key_res.SetType( - TransactionJsonApi::ConvertLockingScriptTypeString(type)); - } - script_pub_key_res.SetReqSigs(extract_data.pushed_datas.size()); - AddressFactory addr_factory(net_type); - Address address; - if (type == LockingScriptType::kMultisig) { - script_pub_key_res.SetReqSigs(extract_data.req_sigs); - for (ByteData pubkey_bytes : extract_data.pushed_datas) { - Pubkey pubkey = Pubkey(pubkey_bytes); - address = addr_factory.CreateP2pkhAddress(pubkey); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } - } else if (type == LockingScriptType::kPayToPubkey) { - Pubkey pubkey = Pubkey(extract_data.pushed_datas[0]); - address = addr_factory.CreateP2pkhAddress(pubkey); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } else if (type == LockingScriptType::kPayToPubkeyHash) { - ByteData160 hash = - ByteData160(extract_data.pushed_datas[0].GetBytes()); - address = addr_factory.GetAddressByHash( - AddressType::kP2pkhAddress, hash); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } else if (type == LockingScriptType::kPayToScriptHash) { - ByteData160 hash = - ByteData160(extract_data.pushed_datas[0].GetBytes()); - address = addr_factory.GetAddressByHash( - AddressType::kP2shAddress, hash); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } else if (type == LockingScriptType::kWitnessV0KeyHash) { - address = - addr_factory.GetSegwitAddressByHash(extract_data.pushed_datas[0]); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } else if (type == LockingScriptType::kWitnessV0ScriptHash) { - address = - addr_factory.GetSegwitAddressByHash(extract_data.pushed_datas[0]); - script_pub_key_res.GetAddresses().push_back(address.GetAddress()); - } else { + int64_t require_num = 0; + std::string script_type; + auto addr_list = TransactionJsonApi::ConvertFromLockingScript( + addr_factory, locking_script, &script_type, &require_num); + script_pub_key_res.SetType(script_type); + script_pub_key_res.SetReqSigs(require_num); + if (addr_list.empty()) { script_pub_key_res.SetIgnoreItem("reqSigs"); script_pub_key_res.SetIgnoreItem("addresses"); + } else { + for (auto addr : addr_list) { + script_pub_key_res.GetAddresses().push_back(addr.GetAddress()); + } } res_txout.SetScriptPubKey(script_pub_key_res); @@ -265,6 +233,8 @@ ExtractScriptData TransactionJsonApi::ExtractLockingScript( if (locking_script.IsWitnessProgram()) { uint8_t witness_version = static_cast(elems[0].GetNumber()); ByteData program = elems[1].GetBinaryData(); + extract_data.witness_version = static_cast( + witness_version); if (locking_script.IsP2wpkhScript()) { // tx witness keyhash extract_data.script_type = LockingScriptType::kWitnessV0KeyHash; @@ -273,19 +243,17 @@ ExtractScriptData TransactionJsonApi::ExtractLockingScript( // tx witness scripthash extract_data.script_type = LockingScriptType::kWitnessV0ScriptHash; extract_data.pushed_datas.push_back(program); + } else if (locking_script.IsTaprootScript()) { + extract_data.script_type = LockingScriptType::kWitnessV1Taproot; + extract_data.pushed_datas.push_back(program); } else if (witness_version != 0) { // tx witness unknown extract_data.script_type = LockingScriptType::kWitnessUnknown; - std::vector data; - data.reserve(program.GetDataSize() + 1); - data.push_back(witness_version); - std::copy( - program.GetBytes().begin(), program.GetBytes().end(), - std::back_inserter(data)); - extract_data.pushed_datas.push_back(ByteData(data)); + extract_data.pushed_datas.push_back(program); } else { // non standard extract_data.script_type = LockingScriptType::kNonStandard; + extract_data.witness_version = WitnessVersion::kVersionNone; } return extract_data; } @@ -344,6 +312,8 @@ std::string TransactionJsonApi::ConvertLockingScriptTypeString( return "witness_v0_scripthash"; case LockingScriptType::kWitnessV0KeyHash: return "witness_v0_keyhash"; + case LockingScriptType::kWitnessV1Taproot: + return "witness_v1_taproot"; case LockingScriptType::kWitnessUnknown: return "witness_unknown"; case LockingScriptType::kTrue: @@ -380,6 +350,70 @@ NetType TransactionJsonApi::ConvertNetType(const std::string& network_type) { return net_type; } +std::vector
TransactionJsonApi::ConvertFromLockingScript( + const AddressFactory& factory, const Script& script, + std::string* script_type, int64_t* require_num) { + ExtractScriptData extract_data = + TransactionJsonApi::ExtractLockingScript(script); + LockingScriptType type = extract_data.script_type; + if (script_type != nullptr) { + std::string type_str = "nonstandard"; + if (type != LockingScriptType::kFee) { + type_str = TransactionJsonApi::ConvertLockingScriptTypeString(type); + } + *script_type = type_str; + } + if (require_num != nullptr) { + *require_num = static_cast(extract_data.pushed_datas.size()); + } + + std::vector
addr_list; + Address address; + if (type == LockingScriptType::kMultisig) { + if (require_num != nullptr) { + *require_num = extract_data.req_sigs; + } + for (ByteData pubkey_bytes : extract_data.pushed_datas) { + Pubkey pubkey = Pubkey(pubkey_bytes); + address = factory.CreateP2pkhAddress(pubkey); + addr_list.push_back(address); + } + } else if (type == LockingScriptType::kPayToPubkey) { + Pubkey pubkey = Pubkey(extract_data.pushed_datas[0]); + address = factory.CreateP2pkhAddress(pubkey); + addr_list.push_back(address); + } else if (type == LockingScriptType::kPayToPubkeyHash) { + ByteData160 hash = + ByteData160(extract_data.pushed_datas[0].GetBytes()); + address = factory.GetAddressByHash( + AddressType::kP2pkhAddress, hash); + addr_list.push_back(address); + } else if (type == LockingScriptType::kPayToScriptHash) { + ByteData160 hash = + ByteData160(extract_data.pushed_datas[0].GetBytes()); + address = factory.GetAddressByHash( + AddressType::kP2shAddress, hash); + addr_list.push_back(address); + } else if ((type == LockingScriptType::kWitnessV0KeyHash) || + (type == LockingScriptType::kWitnessV0ScriptHash) || + (type == LockingScriptType::kWitnessV1Taproot)) { + address = factory.GetSegwitAddressByHash( + extract_data.pushed_datas[0], extract_data.witness_version); + addr_list.push_back(address); + } else if (type == LockingScriptType::kWitnessUnknown) { + try { + address = factory.GetSegwitAddressByHash( + extract_data.pushed_datas[0], extract_data.witness_version); + addr_list.push_back(address); + } catch (const CfdException&) { + // If the data is invalid, it will not be output. + } + } else { + // do nothing + } + return addr_list; +} + } // namespace json } // namespace api } // namespace cfd diff --git a/src/jsonapi/cfd_json_transaction.h b/src/jsonapi/cfd_json_transaction.h index 7cadf6ff..8c5f76a6 100644 --- a/src/jsonapi/cfd_json_transaction.h +++ b/src/jsonapi/cfd_json_transaction.h @@ -9,6 +9,11 @@ #ifndef CFD_SRC_JSONAPI_CFD_JSON_TRANSACTION_H_ #define CFD_SRC_JSONAPI_CFD_JSON_TRANSACTION_H_ +#include +#include + +#include "cfd/cfd_address.h" +#include "cfdcore/cfdcore_address.h" #include "cfdcore/cfdcore_bytedata.h" #include "cfdcore/cfdcore_key.h" #include "cfdcore/cfdcore_script.h" @@ -19,6 +24,8 @@ namespace cfd { namespace api { namespace json { +using cfd::AddressFactory; +using cfd::core::Address; using cfd::core::ByteData; using cfd::core::Script; using cfd::core::SigHashType; @@ -37,18 +44,21 @@ enum LockingScriptType { kNullData, //!< null data of locking script kWitnessV0ScriptHash, //!< p2wsh locking script kWitnessV0KeyHash, //!< p2wpkh locking script + kWitnessV1Taproot, //!< taproot locking script kWitnessUnknown, //!< invalid witness ver locking script kTrue, //!< can spend anyone script kFee, //!< type fee (elements only) }; /** - * @brief LockingScriptの解析情報 + * @brief LockingScript extract data */ struct ExtractScriptData { - LockingScriptType script_type; //!< LockingScript種別 - std::vector pushed_datas; //!< LockingScriptに含まれるhashデータ - int64_t req_sigs; //!< Unlockingに必要なSignature数 + LockingScriptType script_type; //!< LockingScript type + std::vector pushed_datas; //!< hashed data by locking script + int64_t req_sigs; //!< multisig unlocking signature num + //! Witness version + WitnessVersion witness_version = WitnessVersion::kVersionNone; }; /** @@ -111,6 +121,18 @@ class TransactionJsonApi { */ static NetType ConvertNetType(const std::string& network_type); + /** + * @brief Convert from locking script. + * @param[in] factory address factory + * @param[in] script locking script + * @param[out] script_type script type + * @param[out] require_num multisig require num + * @return address list + */ + static std::vector
ConvertFromLockingScript( + const AddressFactory& factory, const Script& script, + std::string* script_type, int64_t* require_num); + private: TransactionJsonApi(); }; diff --git a/src/jsonapi/cfd_struct.h b/src/jsonapi/cfd_struct.h index d8052781..a408e17d 100644 --- a/src/jsonapi/cfd_struct.h +++ b/src/jsonapi/cfd_struct.h @@ -1,4 +1,4 @@ -// Copyright 2019 CryptoGarage +// Copyright 2020 CryptoGarage /** * @file cfd_struct.h * @@ -14,19 +14,19 @@ // clang-format off // @formatter:off -namespace cfd { -namespace api { // ------------------------------------------------------------------------ -// DecodeRawTransactionRequestStruct +// DecodeLockingScriptStruct // ------------------------------------------------------------------------ /** - * @brief DecodeRawTransactionRequestStruct struct + * @brief DecodeLockingScriptStruct struct */ -struct DecodeRawTransactionRequestStruct { - std::string hex = ""; //!< hex // NOLINT - std::string network = "mainnet"; //!< network // NOLINT - bool iswitness = true; //!< iswitness // NOLINT +struct DecodeLockingScriptStruct { + std::string asm_ = ""; //!< asm_ // NOLINT + std::string hex = ""; //!< hex // NOLINT + int req_sigs = 0; //!< req_sigs // NOLINT + std::string type = ""; //!< type // NOLINT + std::vector addresses; //!< addresses // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; @@ -42,6 +42,20 @@ struct DecodeUnlockingScriptStruct { std::set ignore_items; //!< using on JSON mapping convert. }; +// ------------------------------------------------------------------------ +// DecodePsbtLockingScriptStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodePsbtLockingScriptStruct struct + */ +struct DecodePsbtLockingScriptStruct { + std::string asm_ = ""; //!< asm_ // NOLINT + std::string hex = ""; //!< hex // NOLINT + std::string type = ""; //!< type // NOLINT + std::string address = ""; //!< address // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + // ------------------------------------------------------------------------ // DecodeRawTransactionTxInStruct // ------------------------------------------------------------------------ @@ -51,38 +65,35 @@ struct DecodeUnlockingScriptStruct { struct DecodeRawTransactionTxInStruct { std::string coinbase = ""; //!< coinbase // NOLINT std::string txid = ""; //!< txid // NOLINT - int64_t vout = 0; //!< vout // NOLINT + uint32_t vout = 0; //!< vout // NOLINT DecodeUnlockingScriptStruct script_sig; //!< script_sig // NOLINT std::vector txinwitness; //!< txinwitness // NOLINT - int64_t sequence = 0; //!< sequence // NOLINT + uint32_t sequence = 0; //!< sequence // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; // ------------------------------------------------------------------------ -// DecodeLockingScriptStruct +// DecodeRawTransactionTxOutStruct // ------------------------------------------------------------------------ /** - * @brief DecodeLockingScriptStruct struct + * @brief DecodeRawTransactionTxOutStruct struct */ -struct DecodeLockingScriptStruct { - std::string asm_ = ""; //!< asm_ // NOLINT - std::string hex = ""; //!< hex // NOLINT - int64_t req_sigs = 0; //!< req_sigs // NOLINT - std::string type = ""; //!< type // NOLINT - std::vector addresses; //!< addresses // NOLINT +struct DecodeRawTransactionTxOutStruct { + int64_t value = 0; //!< value // NOLINT + uint32_t n = 0; //!< n // NOLINT + DecodeLockingScriptStruct script_pub_key; //!< script_pub_key // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; // ------------------------------------------------------------------------ -// DecodeRawTransactionTxOutStruct +// DecodePsbtUtxoStruct // ------------------------------------------------------------------------ /** - * @brief DecodeRawTransactionTxOutStruct struct + * @brief DecodePsbtUtxoStruct struct */ -struct DecodeRawTransactionTxOutStruct { - double value = 0; //!< value // NOLINT - int64_t n = 0; //!< n // NOLINT - DecodeLockingScriptStruct script_pub_key; //!< script_pub_key // NOLINT +struct DecodePsbtUtxoStruct { + int64_t amount = 0; //!< amount // NOLINT + DecodePsbtLockingScriptStruct script_pub_key; //!< script_pub_key // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; @@ -96,9 +107,9 @@ struct DecodeRawTransactionResponseStruct { std::string txid = ""; //!< txid // NOLINT std::string hash = ""; //!< hash // NOLINT uint32_t version = 0; //!< version // NOLINT - int64_t size = 0; //!< size // NOLINT - int64_t vsize = 0; //!< vsize // NOLINT - int64_t weight = 0; //!< weight // NOLINT + uint32_t size = 0; //!< size // NOLINT + uint32_t vsize = 0; //!< vsize // NOLINT + uint32_t weight = 0; //!< weight // NOLINT uint32_t locktime = 0; //!< locktime // NOLINT std::vector vin; //!< vin // NOLINT std::vector vout; //!< vout // NOLINT @@ -106,17 +117,45 @@ struct DecodeRawTransactionResponseStruct { }; // ------------------------------------------------------------------------ -// ElementsDecodeRawTransactionRequestStruct +// ElementsDecodeIssuanceStruct // ------------------------------------------------------------------------ /** - * @brief ElementsDecodeRawTransactionRequestStruct struct + * @brief ElementsDecodeIssuanceStruct struct */ -struct ElementsDecodeRawTransactionRequestStruct { - std::string hex = ""; //!< hex // NOLINT - std::string network = "liquidv1"; //!< network // NOLINT - std::string mainchain_network = ""; //!< mainchain_network // NOLINT - bool iswitness = true; //!< iswitness // NOLINT - bool full_dump = false; //!< full_dump // NOLINT +struct ElementsDecodeIssuanceStruct { + std::string asset_blinding_nonce = ""; //!< asset_blinding_nonce // NOLINT + std::string asset_entropy = ""; //!< asset_entropy // NOLINT + std::string contract_hash = ""; //!< contract_hash // NOLINT + bool isreissuance = false; //!< isreissuance // NOLINT + std::string token = ""; //!< token // NOLINT + std::string asset = ""; //!< asset // NOLINT + int64_t assetamount = 0; //!< assetamount // NOLINT + std::string assetamountcommitment = ""; //!< assetamountcommitment // NOLINT + int64_t tokenamount = 0; //!< tokenamount // NOLINT + std::string tokenamountcommitment = ""; //!< tokenamountcommitment // NOLINT + std::string asset_rangeproof = ""; //!< asset_rangeproof // NOLINT + std::string token_rangeproof = ""; //!< token_rangeproof // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeLockingScriptStruct +// ------------------------------------------------------------------------ +/** + * @brief ElementsDecodeLockingScriptStruct struct + */ +struct ElementsDecodeLockingScriptStruct { + std::string asm_ = ""; //!< asm_ // NOLINT + std::string hex = ""; //!< hex // NOLINT + int req_sigs = 0; //!< req_sigs // NOLINT + std::string type = ""; //!< type // NOLINT + std::vector addresses; //!< addresses // NOLINT + std::string pegout_chain = ""; //!< pegout_chain // NOLINT + std::string pegout_asm = ""; //!< pegout_asm // NOLINT + std::string pegout_hex = ""; //!< pegout_hex // NOLINT + int pegout_req_sigs = 0; //!< pegout_req_sigs // NOLINT + std::string pegout_type = ""; //!< pegout_type // NOLINT + std::vector pegout_addresses; //!< pegout_addresses // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; @@ -133,24 +172,100 @@ struct ElementsDecodeUnlockingScriptStruct { }; // ------------------------------------------------------------------------ -// ElementsDecodeIssuanceStruct +// PsbtBip32DataStruct // ------------------------------------------------------------------------ /** - * @brief ElementsDecodeIssuanceStruct struct + * @brief PsbtBip32DataStruct struct */ -struct ElementsDecodeIssuanceStruct { - std::string asset_blinding_nonce = ""; //!< asset_blinding_nonce // NOLINT - std::string asset_entropy = ""; //!< asset_entropy // NOLINT - std::string contract_hash = ""; //!< contract_hash // NOLINT - bool isreissuance = false; //!< isreissuance // NOLINT - std::string token = ""; //!< token // NOLINT - std::string asset = ""; //!< asset // NOLINT - int64_t assetamount = 0; //!< assetamount // NOLINT - std::string assetamountcommitment = ""; //!< assetamountcommitment // NOLINT - int64_t tokenamount = 0; //!< tokenamount // NOLINT - std::string tokenamountcommitment = ""; //!< tokenamountcommitment // NOLINT - std::string asset_rangeproof = ""; //!< asset_rangeproof // NOLINT - std::string token_rangeproof = ""; //!< token_rangeproof // NOLINT +struct PsbtBip32DataStruct { + std::string pubkey = ""; //!< pubkey // NOLINT + std::string master_fingerprint = ""; //!< master_fingerprint // NOLINT + std::string path = ""; //!< path // NOLINT + std::string descriptor = ""; //!< descriptor // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// PsbtMapDataStruct +// ------------------------------------------------------------------------ +/** + * @brief PsbtMapDataStruct struct + */ +struct PsbtMapDataStruct { + std::string key = ""; //!< key // NOLINT + std::string value = ""; //!< value // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// PsbtScriptDataStruct +// ------------------------------------------------------------------------ +/** + * @brief PsbtScriptDataStruct struct + */ +struct PsbtScriptDataStruct { + std::string asm_ = ""; //!< asm_ // NOLINT + std::string hex = ""; //!< hex // NOLINT + std::string type = ""; //!< type // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// PsbtSignatureDataStruct +// ------------------------------------------------------------------------ +/** + * @brief PsbtSignatureDataStruct struct + */ +struct PsbtSignatureDataStruct { + std::string pubkey = ""; //!< pubkey // NOLINT + std::string signature = ""; //!< signature // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// XpubDataStruct +// ------------------------------------------------------------------------ +/** + * @brief XpubDataStruct struct + */ +struct XpubDataStruct { + std::string base58 = ""; //!< base58 // NOLINT + std::string hex = ""; //!< hex // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// DecodePsbtInputStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodePsbtInputStruct struct + */ +struct DecodePsbtInputStruct { + std::string non_witness_utxo_hex = ""; //!< non_witness_utxo_hex // NOLINT + DecodeRawTransactionResponseStruct non_witness_utxo; //!< non_witness_utxo // NOLINT + DecodePsbtUtxoStruct witness_utxo; //!< witness_utxo // NOLINT + std::vector partial_signatures; //!< partial_signatures // NOLINT + std::string sighash = ""; //!< sighash // NOLINT + PsbtScriptDataStruct redeem_script; //!< redeem_script // NOLINT + PsbtScriptDataStruct witness_script; //!< witness_script // NOLINT + std::vector bip32_derivs; //!< bip32_derivs // NOLINT + DecodeUnlockingScriptStruct final_scriptsig; //!< final_scriptsig // NOLINT + std::vector final_scriptwitness; //!< final_scriptwitness // NOLINT + std::vector unknown; //!< unknown // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// DecodePsbtOutputStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodePsbtOutputStruct struct + */ +struct DecodePsbtOutputStruct { + PsbtScriptDataStruct redeem_script; //!< redeem_script // NOLINT + PsbtScriptDataStruct witness_script; //!< witness_script // NOLINT + std::vector bip32_derivs; //!< bip32_derivs // NOLINT + std::vector unknown; //!< unknown // NOLINT std::set ignore_items; //!< using on JSON mapping convert. }; @@ -173,27 +288,6 @@ struct ElementsDecodeRawTransactionTxInStruct { std::set ignore_items; //!< using on JSON mapping convert. }; -// ------------------------------------------------------------------------ -// ElementsDecodeLockingScriptStruct -// ------------------------------------------------------------------------ -/** - * @brief ElementsDecodeLockingScriptStruct struct - */ -struct ElementsDecodeLockingScriptStruct { - std::string asm_ = ""; //!< asm_ // NOLINT - std::string hex = ""; //!< hex // NOLINT - int req_sigs = 0; //!< req_sigs // NOLINT - std::string type = ""; //!< type // NOLINT - std::vector addresses; //!< addresses // NOLINT - std::string pegout_chain = ""; //!< pegout_chain // NOLINT - std::string pegout_asm = ""; //!< pegout_asm // NOLINT - std::string pegout_hex = ""; //!< pegout_hex // NOLINT - int pegout_req_sigs = 0; //!< pegout_req_sigs // NOLINT - std::string pegout_type = ""; //!< pegout_type // NOLINT - std::vector pegout_addresses; //!< pegout_addresses // NOLINT - std::set ignore_items; //!< using on JSON mapping convert. -}; - // ------------------------------------------------------------------------ // ElementsDecodeRawTransactionTxOutStruct // ------------------------------------------------------------------------ @@ -218,6 +312,82 @@ struct ElementsDecodeRawTransactionTxOutStruct { std::set ignore_items; //!< using on JSON mapping convert. }; +// ------------------------------------------------------------------------ +// PsbtGlobalXpubStruct +// ------------------------------------------------------------------------ +/** + * @brief PsbtGlobalXpubStruct struct + */ +struct PsbtGlobalXpubStruct { + XpubDataStruct xpub; //!< xpub // NOLINT + std::string master_fingerprint = ""; //!< master_fingerprint // NOLINT + std::string path = ""; //!< path // NOLINT + std::string descriptor_xpub = ""; //!< descriptor_xpub // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; +namespace cfd { +namespace api { + +// ------------------------------------------------------------------------ +// DecodePsbtRequestStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodePsbtRequestStruct struct + */ +struct DecodePsbtRequestStruct { + std::string psbt = ""; //!< psbt // NOLINT + std::string network = "mainnet"; //!< network // NOLINT + bool has_detail = false; //!< has_detail // NOLINT + bool has_simple = false; //!< has_simple // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// DecodePsbtResponseStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodePsbtResponseStruct struct + */ +struct DecodePsbtResponseStruct { + DecodeRawTransactionResponseStruct tx; //!< tx // NOLINT + std::string tx_hex = ""; //!< tx_hex // NOLINT + std::vector xpubs; //!< xpubs // NOLINT + uint32_t version = 0; //!< version // NOLINT + std::vector unknown; //!< unknown // NOLINT + std::vector inputs; //!< inputs // NOLINT + std::vector outputs; //!< outputs // NOLINT + int64_t fee = 0; //!< fee // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// DecodeRawTransactionRequestStruct +// ------------------------------------------------------------------------ +/** + * @brief DecodeRawTransactionRequestStruct struct + */ +struct DecodeRawTransactionRequestStruct { + std::string hex = ""; //!< hex // NOLINT + std::string network = "mainnet"; //!< network // NOLINT + bool iswitness = true; //!< iswitness // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + +// ------------------------------------------------------------------------ +// ElementsDecodeRawTransactionRequestStruct +// ------------------------------------------------------------------------ +/** + * @brief ElementsDecodeRawTransactionRequestStruct struct + */ +struct ElementsDecodeRawTransactionRequestStruct { + std::string hex = ""; //!< hex // NOLINT + std::string network = "liquidv1"; //!< network // NOLINT + std::string mainchain_network = ""; //!< mainchain_network // NOLINT + bool iswitness = true; //!< iswitness // NOLINT + bool full_dump = false; //!< full_dump // NOLINT + std::set ignore_items; //!< using on JSON mapping convert. +}; + // ------------------------------------------------------------------------ // ElementsDecodeRawTransactionResponseStruct // ------------------------------------------------------------------------ diff --git a/src/jsonapi/input_json_format/cfdapi_decode_psbt.json b/src/jsonapi/input_json_format/cfdapi_decode_psbt.json new file mode 100644 index 00000000..b9664e97 --- /dev/null +++ b/src/jsonapi/input_json_format/cfdapi_decode_psbt.json @@ -0,0 +1,502 @@ +{ + "namespace": ["cfd","api","json"], + "functionName": "DecodePsbt", + "request": { + ":class": "DecodePsbtRequest", + ":class:comment": "request for decode psbt.", + "psbt": "", + "psbt:require": "require", + "psbt:comment": "psbt data (hex or base64)", + "network": "mainnet", + "network:require": "optional", + "network:comment": "network type", + "network:hint": "mainnet, testnet, regtest", + "hasDetail": false, + "hasDetail:require": "optional", + "hasDetail:comment": "detail dump option.", + "hasSimple": false, + "hasSimple:require": "optional", + "hasSimple:comment": "simple dump option." + }, + "response": { + ":class": "DecodePsbtResponse", + ":class:comment": "response data of decode psbt.", + "tx:require": "optional", + "tx:comment": "transaction data. If hasDetail and hasSimple are true, this field is disabled.", + "tx": { + ":class": "DecodeRawTransactionResponse", + "txid": "", + "txid:require": "require", + "txid:comment": "txid", + "hash": "", + "hash:require": "require", + "hash:comment": "transaction hash", + "hash:hint": "txid or wtxid", + "version": 0, + "version:type": "uint32_t", + "version:require": "require", + "version:comment": "transaction version", + "size": 0, + "size:type": "uint32_t", + "size:require": "require", + "size:comment": "transaction size", + "vsize": 0, + "vsize:type": "uint32_t", + "vsize:require": "require", + "vsize:comment": "transaction vsize", + "weight": 0, + "weight:type": "uint32_t", + "weight:require": "require", + "weight:comment": "weight", + "locktime": 0, + "locktime:type": "uint32_t", + "locktime:require": "require", + "locktime:comment": "locktime", + "vin:require": "optional", + "vin:comment": "txin list", + "vin": [ + { + ":class": "DecodeRawTransactionTxIn", + ":class:comment": "decode txin data", + "coinbase": "", + "coinbase:require": "optional", + "coinbase:comment": "coinbase flag", + "coinbase:hint": "coinbase is only", + "txid": "", + "txid:require": "optional", + "txid:comment": "utxo txid", + "vout": 0, + "vout:type": "uint32_t", + "vout:require": "optional", + "vout:comment": "utxo vout", + "scriptSig:require": "optional", + "scriptSig:comment": "scriptsig", + "scriptSig": { + ":class": "DecodeUnlockingScript", + ":class:comment": "script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex" + }, + "txinwitness:require": "optional", + "txinwitness:comment": "txin witness stack", + "txinwitness": [ + "" + ], + "sequence": 0, + "sequence:type": "uint32_t", + "sequence:require": "optional", + "sequence:comment": "sequence number" + } + ], + "vout:require": "optional", + "vout:comment": "txout list", + "vout": [ + { + ":class": "DecodeRawTransactionTxOut", + ":class:comment": "txout data", + "value": 0, + "value:type": "int64_t", + "value:require": "optional", + "value:comment": "satoshi amount", + "n": 0, + "n:require": "require", + "n:type": "uint32_t", + "n:comment": "vout number", + "scriptPubKey:require": "optional", + "scriptPubKey:comment": "locking script", + "scriptPubKey": { + ":class": "DecodeLockingScript", + ":class:comment": "locking script data", + "asm": "", + "asm:require": "optional", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "optional", + "hex:comment": "script hex", + "reqSigs": 0, + "reqSigs:type": "int", + "reqSigs:require": "optional", + "reqSigs:comment": "require signature num", + "type": "", + "type:require": "optional", + "type:comment": "script type", + "addresses:require": "optional", + "addresses:comment": "address list", + "addresses": [ + "" + ] + } + } + ] + }, + "tx_hex": "", + "tx_hex:require": "optional", + "tx_hex:comment": "If hasDetail is true, tx hex set.", + "xpubs:require": "optional", + "xpubs:comment": "If hasDetail is true, this field is set. (remove from global unknown)", + "xpubs": [ + { + ":class": "PsbtGlobalXpub", + ":class:comment": "psbt global xpub data", + "xpub:require": "require", + "xpub:comment": "xpub data", + "xpub": { + ":class": "XpubData", + ":class:comment": "xpub data", + "base58": "", + "base58:require": "require", + "base58:comment": "xpub base58 string", + "hex": "", + "hex:require": "require", + "hex:comment": "xpub hex string" + }, + "master_fingerprint": "", + "master_fingerprint:require": "require", + "master_fingerprint:comment": "master pubkey fingerprint.", + "path": "", + "path:require": "require", + "path:comment": "bip32 path.", + "descriptorXpub": "", + "descriptorXpub:require": "require", + "descriptorXpub:comment": "the descriptor xpub string." + } + ], + "version": 0, + "version:type": "uint32_t", + "version:require": "optional", + "version:comment": "If hasDetail is true, psbt version set. (remove from global unknown)", + "unknown:require": "optional", + "unknown:comment": "PSBT global unknowns. If list is empty and hasSimple is true, exclude list.", + "unknown": [{ + ":class": "PsbtMapData", + ":class:comment": "psbt map data.", + "key": "", + "key:require": "require", + "key:comment": "key hex", + "value": "", + "value:require": "require", + "value:comment": "value hex" + }], + "inputs:require": "require", + "inputs:comment": "PSBT inputs", + "inputs": [ + { + ":class": "DecodePsbtInput", + ":class:comment": "psbt input data", + "non_witness_utxo_hex": "", + "non_witness_utxo_hex:require": "optional", + "non_witness_utxo_hex:comment": "If hasDetail is true, tx hex for not witness set.", + "non_witness_utxo:require": "optional", + "non_witness_utxo:comment": "utxo for not witness. If hasDetail and hasSimple are true, this field is disabled.", + "non_witness_utxo": { + ":class": "DecodeRawTransactionResponse", + "txid": "", + "txid:require": "require", + "txid:comment": "txid", + "hash": "", + "hash:require": "require", + "hash:comment": "transaction hash", + "hash:hint": "txid or wtxid", + "version": 0, + "version:type": "uint32_t", + "version:require": "require", + "version:comment": "transaction version", + "size": 0, + "size:type": "uint32_t", + "size:require": "require", + "size:comment": "transaction size", + "vsize": 0, + "vsize:type": "uint32_t", + "vsize:require": "require", + "vsize:comment": "transaction vsize", + "weight": 0, + "weight:type": "uint32_t", + "weight:require": "require", + "weight:comment": "weight", + "locktime": 0, + "locktime:type": "uint32_t", + "locktime:require": "require", + "locktime:comment": "locktime", + "vin:require": "optional", + "vin:comment": "txin list", + "vin": [ + { + ":class": "DecodeRawTransactionTxIn", + ":class:comment": "decode txin data", + "coinbase": "", + "coinbase:require": "optional", + "coinbase:comment": "coinbase flag", + "coinbase:hint": "coinbase is only", + "txid": "", + "txid:require": "optional", + "txid:comment": "utxo txid", + "vout": 0, + "vout:type": "uint32_t", + "vout:require": "optional", + "vout:comment": "utxo vout", + "scriptSig:require": "optional", + "scriptSig:comment": "scriptsig", + "scriptSig": { + ":class": "DecodeUnlockingScript", + ":class:comment": "script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex" + }, + "txinwitness:require": "optional", + "txinwitness:comment": "txin witness stack", + "txinwitness": [ + "" + ], + "sequence": 0, + "sequence:type": "uint32_t", + "sequence:require": "optional", + "sequence:comment": "sequence number" + } + ], + "vout:require": "optional", + "vout:comment": "txout list", + "vout": [ + { + ":class": "DecodeRawTransactionTxOut", + ":class:comment": "txout data", + "value": 0, + "value:type": "int64_t", + "value:require": "optional", + "value:comment": "satoshi amount", + "n": 0, + "n:require": "require", + "n:type": "uint32_t", + "n:comment": "vout number", + "scriptPubKey:require": "optional", + "scriptPubKey:comment": "locking script", + "scriptPubKey": { + ":class": "DecodeLockingScript", + ":class:comment": "locking script data", + "asm": "", + "asm:require": "optional", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "optional", + "hex:comment": "script hex", + "reqSigs": 0, + "reqSigs:type": "int", + "reqSigs:require": "optional", + "reqSigs:comment": "require signature num", + "type": "", + "type:require": "optional", + "type:comment": "script type", + "addresses:require": "optional", + "addresses:comment": "address list", + "addresses": [ + "" + ] + } + } + ] + }, + "witness_utxo:require": "optional", + "witness_utxo:comment": "utxo for witness", + "witness_utxo": { + ":class": "DecodePsbtUtxo", + ":class:comment": "psbt witness utxo", + "amount": 0, + "amount:require": "require", + "amount:comment": "psbt witness utxo", + "scriptPubKey": { + ":class": "DecodePsbtLockingScript", + "asm": "", + "asm:require": "optional", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "optional", + "hex:comment": "script hex", + "type": "", + "type:require": "optional", + "type:comment": "script type", + "address": "", + "address:require": "optional", + "address:comment": "address" + } + }, + "partial_signatures:require": "optional", + "partial_signatures:comment": "signature", + "partial_signatures": [{ + ":class": "PsbtSignatureData", + ":class:comment": "psbt signature data.", + "pubkey": "", + "pubkey:require": "optional", + "pubkey:comment": "pubkey hex", + "signature": "", + "signature:require": "optional", + "signature:comment": "signature hex" + }], + "sighash": "", + "sighash:require": "optional", + "sighash:comment": "sighash type", + "sighash:hint": "ALL, SINGLE, NONE", + "redeem_script:require": "optional", + "redeem_script:comment": "redeem script", + "redeem_script": { + ":class": "PsbtScriptData", + ":class:comment": "psbt script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex", + "type": "", + "type:require": "optional", + "type:comment": "script type" + }, + "witness_script:require": "optional", + "witness_script:comment": "witness script", + "witness_script": { + ":class": "PsbtScriptData", + ":class:comment": "psbt script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex", + "type": "", + "type:require": "optional", + "type:comment": "script type" + }, + "bip32_derivs:require": "optional", + "bip32_derivs:comment": "bip32 derive pubkey", + "bip32_derivs": [ + { + ":class": "PsbtBip32Data", + ":class:comment": "psbt script data", + "pubkey": "", + "pubkey:require": "require", + "pubkey:comment": "pubkey hex", + "master_fingerprint": "", + "master_fingerprint:require": "require", + "master_fingerprint:comment": "master pubkey fingerprint.", + "path": "", + "path:require": "require", + "path:comment": "bip32 path.", + "descriptor": "", + "descriptor:require": "optional", + "descriptor:comment": "If hasDetail is true, the descriptor pubkey string is set." + } + ], + "final_scriptsig:require": "optional", + "final_scriptsig:comment": "final scriptsig", + "final_scriptsig": { + ":class": "DecodeUnlockingScript", + ":class:comment": "script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex" + }, + "final_scriptwitness": [""], + "final_scriptwitness:require": "optional", + "final_scriptwitness:comment": "final witness stack", + "unknown:require": "optional", + "unknown:comment": "unknown record", + "unknown": [ + { + ":class": "PsbtMapData", + ":class:comment": "psbt map data.", + "key": "", + "key:require": "require", + "key:comment": "key hex", + "value": "", + "value:require": "require", + "value:comment": "value hex" + } + ] + } + ], + "outputs:require": "require", + "outputs:comment": "PSBT outputs", + "outputs": [ + { + ":class": "DecodePsbtOutput", + ":class:comment": "psbt output data", + "redeem_script:require": "optional", + "redeem_script:comment": "redeem script", + "redeem_script": { + ":class": "PsbtScriptData", + ":class:comment": "psbt script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex", + "type": "", + "type:require": "optional", + "type:comment": "script type" + }, + "witness_script:require": "optional", + "witness_script:comment": "witness script", + "witness_script": { + ":class": "PsbtScriptData", + ":class:comment": "psbt script data", + "asm": "", + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex", + "type": "", + "type:require": "optional", + "type:comment": "script type" + }, + "bip32_derivs:require": "optional", + "bip32_derivs:comment": "bip32 derive pubkey", + "bip32_derivs": [ + { + ":class": "PsbtBip32Data", + ":class:comment": "psbt script data", + "pubkey": "", + "pubkey:require": "require", + "pubkey:comment": "pubkey hex", + "master_fingerprint": "", + "master_fingerprint:require": "require", + "master_fingerprint:comment": "master pubkey fingerprint.", + "path": "", + "path:require": "require", + "path:comment": "bip32 path.", + "descriptor": "", + "descriptor:require": "optional", + "descriptor:comment": "If hasDetail is true, the descriptor pubkey string is set." + } + ], + "unknown:require": "optional", + "unknown:comment": "unknown record", + "unknown": [ + { + ":class": "PsbtMapData", + ":class:comment": "psbt map data.", + "key": "", + "key:require": "require", + "key:comment": "key hex", + "value": "", + "value:require": "require", + "value:comment": "value hex" + } + ] + } + ], + "fee": 0, + "fee:require": "optional", + "fee:comment": "If all utxos filled, this field has set fee amount." + } +} \ No newline at end of file diff --git a/src/jsonapi/input_json_format/cfdapi_decode_transaction.json b/src/jsonapi/input_json_format/cfdapi_decode_transaction.json index 6d034cae..3b9a5cc5 100644 --- a/src/jsonapi/input_json_format/cfdapi_decode_transaction.json +++ b/src/jsonapi/input_json_format/cfdapi_decode_transaction.json @@ -9,44 +9,106 @@ "response": { ":class": "DecodeRawTransactionResponse", "txid": "", + "txid:require": "require", + "txid:comment": "txid", "hash": "", + "hash:require": "require", + "hash:comment": "transaction hash", + "hash:hint": "txid or wtxid", "version": 0, "version:type": "uint32_t", + "version:require": "require", + "version:comment": "transaction version", "size": 0, + "size:type": "uint32_t", + "size:require": "require", + "size:comment": "transaction size", "vsize": 0, + "vsize:type": "uint32_t", + "vsize:require": "require", + "vsize:comment": "transaction vsize", "weight": 0, + "weight:type": "uint32_t", + "weight:require": "require", + "weight:comment": "weight", "locktime": 0, "locktime:type": "uint32_t", + "locktime:require": "require", + "locktime:comment": "locktime", + "vin:require": "optional", + "vin:comment": "txin list", "vin": [ { ":class": "DecodeRawTransactionTxIn", + ":class:comment": "decode txin data", "coinbase": "", - "coinbase:hint": "coinbase is only ", + "coinbase:require": "optional", + "coinbase:comment": "coinbase flag", + "coinbase:hint": "coinbase is only", "txid": "", + "txid:require": "optional", + "txid:comment": "utxo txid", "vout": 0, + "vout:type": "uint32_t", + "vout:require": "optional", + "vout:comment": "utxo vout", + "scriptSig:require": "optional", + "scriptSig:comment": "scriptsig", "scriptSig": { ":class": "DecodeUnlockingScript", + ":class:comment": "script data", "asm": "", - "hex": "" + "asm:require": "require", + "asm:comment": "script asm string", + "hex": "", + "hex:require": "require", + "hex:comment": "script hex" }, + "txinwitness:require": "optional", + "txinwitness:comment": "txin witness stack", "txinwitness": [ "" ], - "sequence": 0 + "sequence": 0, + "sequence:type": "uint32_t", + "sequence:require": "optional", + "sequence:comment": "sequence number" } ], + "vout:require": "optional", + "vout:comment": "txout list", "vout": [ { ":class": "DecodeRawTransactionTxOut", + ":class:comment": "txout data", "value": 0, - "value:type": "double", + "value:type": "int64_t", + "value:require": "optional", + "value:comment": "satoshi amount", "n": 0, + "n:require": "require", + "n:type": "uint32_t", + "n:comment": "vout number", + "scriptPubKey:require": "optional", + "scriptPubKey:comment": "locking script", "scriptPubKey": { ":class": "DecodeLockingScript", + ":class:comment": "locking script data", "asm": "", + "asm:require": "optional", + "asm:comment": "script asm string", "hex": "", + "hex:require": "optional", + "hex:comment": "script hex", "reqSigs": 0, + "reqSigs:type": "int", + "reqSigs:require": "optional", + "reqSigs:comment": "require signature num", "type": "", + "type:require": "optional", + "type:comment": "script type", + "addresses:require": "optional", + "addresses:comment": "address list", "addresses": [ "" ] diff --git a/test/Makefile.srclist b/test/Makefile.srclist index d7a7801e..3af5ff56 100644 --- a/test/Makefile.srclist +++ b/test/Makefile.srclist @@ -10,6 +10,7 @@ TEST_CFD_SOURCES= \ test_cfd_transaction_context.cpp \ test_cfd_coin_selection.cpp \ test_cfd_utxoutil.cpp \ + test_cfd_psbt.cpp \ test_cfdapi_address.cpp \ test_cfdapi_elements_transaction.cpp \ test_cfdapi_transaction.cpp \ @@ -25,6 +26,7 @@ TEST_CFD_CAPI_SOURCES= \ capi/test_cfdcapi_elements_transaction.cpp \ capi/test_cfdcapi_key.cpp \ capi/test_cfdcapi_ledger \ + capi/test_cfdcapi_psbt.cpp \ capi/test_cfdcapi_script.cpp \ capi/test_cfdcapi_transaction.cpp diff --git a/test/capi/test_cfdcapi_address.cpp b/test/capi/test_cfdcapi_address.cpp index e1e29239..92cb6f8c 100644 --- a/test/capi/test_cfdcapi_address.cpp +++ b/test/capi/test_cfdcapi_address.cpp @@ -391,6 +391,14 @@ TEST(cfdcapi_address, CfdGetAddressInfoTest) { kCfdP2wsh, kCfdWitnessVersion0, }, + { // HashType(Taproot) mainnet + "bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4", + "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + kCfdNetworkMainnet, + kCfdTaproot, + kCfdWitnessVersion1, + }, #ifndef CFD_DISABLE_ELEMENTS { // Elements HashType(P2PKH) liquidv1 "QKXGAM4Cvd1fvLEz5tbq4YwNRzTjdMWi2q", @@ -459,4 +467,66 @@ TEST(cfdcapi_address, CfdGetAddressInfoTest) { ret = CfdFreeHandle(handle); EXPECT_EQ(kCfdSuccess, ret); -} \ No newline at end of file +} + +TEST(cfdcapi_address, CfdCreateAddressTest) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + struct CfdCreateAddressExpectData { + const char* pubkey; + const char* script; + int hash_type; + int network; + const char* address; + const char* locking_script; + }; + + const struct CfdCreateAddressExpectData exp_datas[] = { + { // taproot testnet + "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "", + kCfdTaproot, + kCfdNetworkTestnet, + "tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6", + "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + }, + { // taproot regtest + "88de1a59b38939f58fb4f8c5ffc3d56390d43e9e91c7b1d67f91e070f3108799", + "", + kCfdTaproot, + kCfdNetworkRegtest, + "bcrt1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vsn6c9jz", + "512088de1a59b38939f58fb4f8c5ffc3d56390d43e9e91c7b1d67f91e070f3108799" + }, + }; + size_t list_size = sizeof(exp_datas) / sizeof(struct CfdCreateAddressExpectData); + + for (size_t idx = 0; idx < list_size; ++idx) { + char* locking_script = nullptr; + char* address = nullptr; + ret = CfdCreateAddress( + handle, exp_datas[idx].hash_type, exp_datas[idx].pubkey, + exp_datas[idx].script, exp_datas[idx].network, + &address, &locking_script, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_datas[idx].address, address); + EXPECT_STREQ(exp_datas[idx].locking_script, locking_script); + CfdFreeStringBuffer(locking_script); + CfdFreeStringBuffer(address); + } else { + char* message = nullptr; + ret = CfdGetLastErrorMessage(handle, &message); + if (ret == kCfdSuccess) { + EXPECT_STREQ("", message); + CfdFreeStringBuffer(message); + } + } + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} diff --git a/test/capi/test_cfdcapi_coin.cpp b/test/capi/test_cfdcapi_coin.cpp index 15fba10d..552637ef 100644 --- a/test/capi/test_cfdcapi_coin.cpp +++ b/test/capi/test_cfdcapi_coin.cpp @@ -38,6 +38,7 @@ using cfd::core::ByteData256; using cfd::core::CfdError; using cfd::core::CfdException; using cfd::core::Script; +using cfd::core::ScriptBuilder; using cfd::core::Txid; #ifndef CFD_DISABLE_ELEMENTS @@ -62,25 +63,23 @@ TEST(cfdcapi_coin, EstimateFeeTest) { AddressFactory factory; std::vector utxos = { UtxoData{ - 12, + static_cast(12), BlockHash("590ba2283fbe5fb9363c74417bc1cb3f76d4df9d31890f9124b9e1706136b4f4"), Txid("ffe2f9aa4e2c2adb1676008aa07aec5f64149470abb5d89d91b26dfba14e9318"), - 0, + static_cast(0), Script("a914155801788fcd51f57e097a0b6c30f1b69057290d87"), Script("001477567d12cf82a3ef6e9bb0ab3a1aba7f65410cbd"), factory.GetAddress("33dsYKS5E5Vp3LhAw3krvhzNaWtqmrH29k"), "sh(wpkh([ef735203/0\'/0\'/5\']03948c01f159b4204b682668d6e850440564b6610c0e5bf30da684b2131f77c449))#2u75feqc", Amount::CreateBySatoshiAmount(600000000), static_cast(0), -#ifndef CFD_DISABLE_ELEMENTS nullptr, +#ifndef CFD_DISABLE_ELEMENTS ConfidentialAssetId(), ElementsConfidentialAddress(), BlindFactor(), BlindFactor(), ConfidentialValue(), -#else - nullptr, #endif // CFD_DISABLE_ELEMENTS Script() }, @@ -134,8 +133,8 @@ TEST(cfdcapi_coin, EstimateFeeTest) { ret = CfdFinalizeEstimateFee( handle, fee_handle, kTxData, nullptr, &tx_fee, &utxo_fee, true, 20.0); EXPECT_EQ(kCfdSuccess, ret); - EXPECT_EQ(static_cast(2780), tx_fee); - EXPECT_EQ(static_cast(3600), utxo_fee); + EXPECT_EQ(static_cast(2820), tx_fee); + EXPECT_EQ(static_cast(3660), utxo_fee); ret = CfdFreeEstimateFeeHandle(handle, fee_handle); EXPECT_EQ(kCfdSuccess, ret); @@ -155,6 +154,169 @@ TEST(cfdcapi_coin, EstimateFeeTest) { EXPECT_EQ(kCfdSuccess, ret); } +TEST(cfdcapi_coin, EstimateFeeBySchnorr) { + // FIXME 実装する + AddressFactory factory; + std::vector utxos = { + UtxoData{ + 12, + BlockHash("590ba2283fbe5fb9363c74417bc1cb3f76d4df9d31890f9124b9e1706136b4f4"), + Txid("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"), + 0, + Script("51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"), + Script(""), + factory.GetAddress("bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4"), + "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)", + Amount::CreateBySatoshiAmount(2499999000), + AddressType::kTaprootAddress, +#ifndef CFD_DISABLE_ELEMENTS + nullptr, + ConfidentialAssetId(), + ElementsConfidentialAddress(), + BlindFactor(), + BlindFactor(), + ConfidentialValue(), +#else + nullptr, +#endif // CFD_DISABLE_ELEMENTS + Script() + } + }; + + // 1 output data + static const char* const kTxData = "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50000000000"; + + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + void* fee_handle = nullptr; + ret = CfdInitializeEstimateFee(handle, &fee_handle, false); + EXPECT_EQ(kCfdSuccess, ret); + + if (ret == kCfdSuccess) { + for (auto& utxo : utxos) { + ret = CfdAddTxInForEstimateFee( + handle, fee_handle, utxo.txid.GetHex().c_str(), utxo.vout, + utxo.descriptor.c_str(), nullptr, + false, false, false, 0, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + } + + int64_t tx_fee, utxo_fee; + ret = CfdFinalizeEstimateFee( + handle, fee_handle, kTxData, nullptr, &tx_fee, &utxo_fee, true, 2.0); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(static_cast(86), tx_fee); + EXPECT_EQ(static_cast(116), utxo_fee); + + ret = CfdFreeEstimateFeeHandle(handle, fee_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + // exp: 0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000 + // vsize: 100 + + ret = CfdGetLastErrorCode(handle); + if (ret != kCfdSuccess) { + char* str_buffer = NULL; + ret = CfdGetLastErrorMessage(handle, &str_buffer); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_STREQ("", str_buffer); + CfdFreeStringBuffer(str_buffer); + str_buffer = NULL; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_coin, EstimateFeeByTapscript) { + ScriptBuilder builder; + builder.AppendData("f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee901"); + builder.AppendData("201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac"); + builder.AppendData("c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54"); + Script template_script = builder.Build(); + EXPECT_EQ("41f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac4c61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", template_script.GetHex()); + + AddressFactory factory(NetType::kRegtest); + std::vector utxos = { + UtxoData{ + 12, + BlockHash("590ba2283fbe5fb9363c74417bc1cb3f76d4df9d31890f9124b9e1706136b4f4"), + Txid("195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b"), + 0, + Script("51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2"), + Script(""), + factory.GetAddress("bcrt1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fq50z666"), + "raw(51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2)", + Amount::CreateBySatoshiAmount(2499999000), + AddressType::kTaprootAddress, +#ifndef CFD_DISABLE_ELEMENTS + nullptr, + ConfidentialAssetId(), + ElementsConfidentialAddress(), + BlindFactor(), + BlindFactor(), + ConfidentialValue(), +#else + nullptr, +#endif // CFD_DISABLE_ELEMENTS + template_script + } + }; + + // 1 output data + static const char* const kTxData = "02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000"; + + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + void* fee_handle = nullptr; + ret = CfdInitializeEstimateFee(handle, &fee_handle, false); + EXPECT_EQ(kCfdSuccess, ret); + + if (ret == kCfdSuccess) { + for (auto& utxo : utxos) { + ret = CfdAddTxInTemplateForEstimateFee( + handle, fee_handle, utxo.txid.GetHex().c_str(), utxo.vout, + utxo.descriptor.c_str(), nullptr, + false, false, false, 0, nullptr, + utxo.scriptsig_template.GetHex().c_str()); + EXPECT_EQ(kCfdSuccess, ret); + } + + int64_t tx_fee, utxo_fee; + ret = CfdFinalizeEstimateFee( + handle, fee_handle, kTxData, nullptr, &tx_fee, &utxo_fee, true, 2.0); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(static_cast(86), tx_fee); + EXPECT_EQ(static_cast(184), utxo_fee); + + ret = CfdFreeEstimateFeeHandle(handle, fee_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + // exp: 020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000 + // vsize: 133 + + ret = CfdGetLastErrorCode(handle); + if (ret != kCfdSuccess) { + char* str_buffer = NULL; + ret = CfdGetLastErrorMessage(handle, &str_buffer); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_STREQ("", str_buffer); + CfdFreeStringBuffer(str_buffer); + str_buffer = NULL; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + #ifndef CFD_DISABLE_ELEMENTS static ConfidentialAssetId exp_dummy_asset_ca("aa00000000000000000000000000000000000000000000000000000000000000"); static ConfidentialAssetId exp_dummy_asset_cb("bb00000000000000000000000000000000000000000000000000000000000000"); @@ -386,7 +548,7 @@ TEST(cfdcapi_coin, CfCoinSelection_BTC1) { int64_t utxo_fee_amount = 0; ret = CfdFinalizeCoinSelection(handle, coin_select_handle, &utxo_fee_amount); EXPECT_EQ(kCfdSuccess, ret); - EXPECT_EQ(7200, utxo_fee_amount); + EXPECT_EQ(7360, utxo_fee_amount); int32_t utxo_index = 0; std::vector indexes; @@ -490,7 +652,7 @@ TEST(cfdcapi_coin, CfCoinSelection_Asset1) { int64_t utxo_fee_amount = 0; ret = CfdFinalizeCoinSelection(handle, coin_select_handle, &utxo_fee_amount); EXPECT_EQ(kCfdSuccess, ret); - EXPECT_EQ(9100, utxo_fee_amount); + EXPECT_EQ(9200, utxo_fee_amount); int32_t utxo_index = 0; std::vector indexes; diff --git a/test/capi/test_cfdcapi_common.cpp b/test/capi/test_cfdcapi_common.cpp index 966adc25..c8eb0248 100644 --- a/test/capi/test_cfdcapi_common.cpp +++ b/test/capi/test_cfdcapi_common.cpp @@ -215,6 +215,127 @@ TEST(cfdcapi_common, CfdRequestExecuteJson_decodetx) { } respons_json = NULL; + // DecodeRawTransaction taproot send + const char* request_json4 = "{\"hex\":\"02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000\",\"network\":\"regtest\"}"; + const char* exp_json4 = "{\"txid\":\"2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916\",\"hash\":\"d95071dc758c623d7f2d91178f8b8ce1ee8272131ffedf510595fc4abe2960ad\",\"version\":2,\"size\":203,\"vsize\":122,\"weight\":485,\"locktime\":0,\"vin\":[{\"txid\":\"1f9866dc0a19c427347c2db0b5910bdc2c20b78fa9f74f8756b21db890dba8ff\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"txinwitness\":[\"3044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e8175925301\",\"023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44\"],\"sequence\":4294967295}],\"vout\":[{\"value\":2499999000,\"n\":0,\"scriptPubKey\":{\"asm\":\"1 1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb\",\"hex\":\"51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb\",\"reqSigs\":1,\"type\":\"witness_v1_taproot\",\"addresses\":[\"bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq\"]}}]}"; + ret = CfdRequestExecuteJson(handle, "DecodeRawTransaction", request_json4, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json4, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodeRawTransaction tapscript + const char* request_json5 = "{\"hex\":\"020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000\",\"network\":\"regtest\"}"; + const char* exp_json5 = "{\"txid\":\"e4810312b04fcd7178f04153c08e9f065dbc3a2fcc27db77e76e4347d243f725\",\"hash\":\"dc8767a20b972967c1652436258500772fe93f5e0eae108fbb0a38c30e731c36\",\"version\":2,\"size\":284,\"vsize\":133,\"weight\":530,\"locktime\":0,\"vin\":[{\"txid\":\"195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"txinwitness\":[\"f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee901\",\"201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac\",\"c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54\"],\"sequence\":4294967295}],\"vout\":[{\"value\":2499998000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 164e985d0fc92c927a66c0cbaf78e6ea389629d5\",\"hex\":\"0014164e985d0fc92c927a66c0cbaf78e6ea389629d5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40\"]}}]}"; + ret = CfdRequestExecuteJson(handle, "DecodeRawTransaction", request_json5, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json5, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdRequestExecuteJson_decodepsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + char* respons_json = nullptr; + + // DecodePsbt multiple input and output. + const char* request_json1 = "{\"psbt\":\"cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQcXFgAUlixOCPM206+8NBXJ01muEEBHBSABCGsCRzBEAiApmGKmeptFTWzaX+40pS2Qib+gJERNiAMcNKmOKYvbygIgS0f89Qe4CVQQjgN1Zz/b3QhMX7uZT2yVFyDy6y9ruGYBIQJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/gABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAAAEHakcwRAIgZuyVbpZ4PNYSLJwHcyo66ZMbaava/x82LCjtX6lnKNMCIGoW1K5G/tSNPQSSc3YTvZtR/j4kU7REbkR8Zb8sln4QASEC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=\"}"; + const char* exp_json1 = "{\"tx\":{\"txid\":\"f2bd9cccedc37e91a8b10e8034eefc49b901afa6220833097a31ed91772d81a5\",\"hash\":\"f2bd9cccedc37e91a8b10e8034eefc49b901afa6220833097a31ed91772d81a5\",\"version\":2,\"size\":154,\"vsize\":154,\"weight\":616,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[{\"value\":100000000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 b322bddce633b851ac7370ab454f0b367a0654e5\",\"hex\":\"0014b322bddce633b851ac7370ab454f0b367a0654e5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qkv3tmh8xxwu9rtrnwz452nctxeaqv489dcscvw\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"0 cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"hex\":\"0014cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qe2uv2wnw3lqzjmgu6wg45vra28zfrf24f5qa8p\"]}}]},\"unknown\":[],\"inputs\":[{\"non_witness_utxo\":{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"hash\":\"9cb67d0ba945c8da2342429e19b12e11852e3a032144ca908996ff46f05dd906\",\"version\":2,\"size\":246,\"vsize\":165,\"weight\":657,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1\",\"vout\":0,\"scriptSig\":{\"asm\":\"0014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\",\"hex\":\"160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\"},\"txinwitness\":[\"304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a01\",\"024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a28306\"],\"sequence\":4294967295}],\"vout\":[{\"value\":100000000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 b322bddce633b851ac7370ab454f0b367a0654e5\",\"hex\":\"0014b322bddce633b851ac7370ab454f0b367a0654e5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qkv3tmh8xxwu9rtrnwz452nctxeaqv489dcscvw\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"0 cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"hex\":\"0014cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qe2uv2wnw3lqzjmgu6wg45vra28zfrf24f5qa8p\"]}},{\"value\":4899996680,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 09de2a0431cbb3444fc22cad9d9a0fd096397210\",\"hex\":\"001409de2a0431cbb3444fc22cad9d9a0fd096397210\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qp80z5pp3ewe5gn7z9jkemxs06ztrjusscl4mx0\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"reqSigs\":1,\"type\":\"scripthash\",\"addresses\":[\"393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm\"]}}]},\"witness_utxo\":{\"amount\":100000000,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"type\":\"scripthash\",\"address\":\"393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm\"}},\"final_scriptsig\":{\"asm\":\"0014962c4e08f336d3afbc3415c9d359ae1040470520\",\"hex\":\"160014962c4e08f336d3afbc3415c9d359ae1040470520\"},\"final_scriptwitness\":[\"30440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601\",\"02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe\"]},{\"non_witness_utxo\":{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"hash\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"version\":2,\"size\":191,\"vsize\":191,\"weight\":764,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1\",\"vout\":0,\"scriptSig\":{\"asm\":\"0014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\",\"hex\":\"160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\"},\"txinwitness\":[\"304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a01\",\"024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a28306\"],\"sequence\":4294967295},{\"txid\":\"405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6\",\"vout\":0,\"scriptSig\":{\"asm\":\"3044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c01 03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec\",\"hex\":\"473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec\"},\"sequence\":4294967295}],\"vout\":[{\"value\":100000000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 b322bddce633b851ac7370ab454f0b367a0654e5\",\"hex\":\"0014b322bddce633b851ac7370ab454f0b367a0654e5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qkv3tmh8xxwu9rtrnwz452nctxeaqv489dcscvw\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"0 cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"hex\":\"0014cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qe2uv2wnw3lqzjmgu6wg45vra28zfrf24f5qa8p\"]}},{\"value\":4899996680,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 09de2a0431cbb3444fc22cad9d9a0fd096397210\",\"hex\":\"001409de2a0431cbb3444fc22cad9d9a0fd096397210\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"bc1qp80z5pp3ewe5gn7z9jkemxs06ztrjusscl4mx0\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"reqSigs\":1,\"type\":\"scripthash\",\"addresses\":[\"393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm\"]}},{\"value\":499995000,\"n\":0,\"scriptPubKey\":{\"asm\":\"OP_DUP OP_HASH160 8d20443a91969e3bca0e240cd0ffe4dc98c63de2 OP_EQUALVERIFY OP_CHECKSIG\",\"hex\":\"76a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac\",\"reqSigs\":1,\"type\":\"pubkeyhash\",\"addresses\":[\"1DsCvxydk2JqbEj1EqL6mXcEvStsKQvKbx\"]}}]},\"final_scriptsig\":{\"asm\":\"3044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001 02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7\",\"hex\":\"473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7\"}}],\"outputs\":[{\"bip32_derivs\":[{\"pubkey\":\"03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81\",\"master_fingerprint\":\"2a704760\",\"path\":\"44'/0'/0'/0/2\"}]},{\"bip32_derivs\":[{\"pubkey\":\"036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c\",\"master_fingerprint\":\"9d6b6d86\",\"path\":\"44'/0'/0'/0/2\"}]}],\"fee\":399995000}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json1, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json1, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt2 multiple input and output. + const char* request_json2 = "{\"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==\",\"network\":\"testnet\"}"; + const char* exp_json2 = "{\"tx\":{\"txid\":\"4fca1d22793c84025ac994fe144b6840db376fcc50fe87ed98f60cd5717143df\",\"hash\":\"4fca1d22793c84025ac994fe144b6840db376fcc50fe87ed98f60cd5717143df\",\"version\":2,\"size\":94,\"vsize\":94,\"weight\":376,\"locktime\":0,\"vin\":[{\"txid\":\"544d545a1e53becbf9dd9b0e424e1189b8f6e46d6bc36b191816d341c2d32f69\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[{\"value\":499993360,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 3cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc\",\"hex\":\"00203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc\",\"reqSigs\":1,\"type\":\"witness_v0_scripthash\",\"addresses\":[\"tb1q8jksvxw7vlnzg7nk5ypgzd34cpf52lrt5n77ftqllkq534cwf0xqjsj24p\"]}}]},\"unknown\":[],\"inputs\":[{\"witness_utxo\":{\"amount\":499996680,\"scriptPubKey\":{\"asm\":\"OP_HASH160 945fb50391a70637c1ffc5ab7fb65308c2f23175 OP_EQUAL\",\"hex\":\"a914945fb50391a70637c1ffc5ab7fb65308c2f2317587\",\"type\":\"scripthash\",\"address\":\"2N6mkeX6gvq65nySsWmQUqacjfnH9HV2ufY\"}},\"partial_signatures\":[{\"pubkey\":\"03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3\",\"signature\":\"3044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b8401\"},{\"pubkey\":\"03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47\",\"signature\":\"304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc01\"}],\"sighash\":\"ALL\",\"redeem_script\":{\"asm\":\"0 9c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9\",\"hex\":\"00209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9\",\"type\":\"witness_v0_scripthash\"},\"witness_script\":{\"asm\":\"2 03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3 03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47 2 OP_CHECKMULTISIG\",\"hex\":\"522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae\",\"type\":\"multisig\"},\"bip32_derivs\":[{\"pubkey\":\"03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3\",\"master_fingerprint\":\"2a704760\",\"path\":\"44'/0'/0'/0/11\"},{\"pubkey\":\"03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47\",\"master_fingerprint\":\"9d6b6d86\",\"path\":\"44'/0'/0'/0/11\"}]}],\"outputs\":[{\"witness_script\":{\"asm\":\"2 02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b 02622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242 2 OP_CHECKMULTISIG\",\"hex\":\"522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae\",\"type\":\"\"},\"bip32_derivs\":[{\"pubkey\":\"02622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242\",\"master_fingerprint\":\"9d6b6d86\",\"path\":\"44'/0'/0'/0/12\"},{\"pubkey\":\"02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b\",\"master_fingerprint\":\"2a704760\",\"path\":\"44'/0'/0'/0/12\"}]}],\"fee\":3320}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json2, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json2, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt3 + const char* request_json3 = "{\"psbt\":\"cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwA=\",\"network\":\"testnet\"}"; + const char* exp_json3 = "{\"tx\":{\"txid\":\"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115\",\"hash\":\"75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115\",\"version\":2,\"size\":63,\"vsize\":63,\"weight\":252,\"locktime\":0,\"vin\":[{\"txid\":\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[{\"value\":0,\"n\":0,\"scriptPubKey\":{\"asm\":\"OP_RETURN 0\",\"hex\":\"6a0100\",\"type\":\"nulldata\"}}]},\"unknown\":[{\"key\":\"0f010203040506070809\",\"value\":\"0102030405060708090a0b0c0d0e0f\"}],\"inputs\":[{\"unknown\":[{\"key\":\"0f010203040506070809\",\"value\":\"0102030405060708090a0b0c0d0e0f\"}]}],\"outputs\":[{\"unknown\":[{\"key\":\"0f010203040506070809\",\"value\":\"0102030405060708090a0b0c0d0e0f\"}]}]}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json3, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json3, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt4 + const char* request_json4 = "{\"psbt\":\"cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=\",\"network\":\"testnet\"}"; + const char* exp_json4 = "{\"tx\":{\"txid\":\"b4ca8f48572bf08354f8302adfbd9e5c2fc2a52731de5401a39aa048f68c9c21\",\"hash\":\"b4ca8f48572bf08354f8302adfbd9e5c2fc2a52731de5401a39aa048f68c9c21\",\"version\":2,\"size\":85,\"vsize\":85,\"weight\":340,\"locktime\":0,\"vin\":[{\"txid\":\"39bc5c3b33d66ce3d7852a7942331e3ec10f8ba50f225fc41fb5dfa523239a27\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[{\"value\":199908000,\"n\":0,\"scriptPubKey\":{\"asm\":\"OP_DUP OP_HASH160 ffe9c0061097cc3b636f2cb0460fa4fc427d2b45 OP_EQUALVERIFY OP_CHECKSIG\",\"hex\":\"76a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac\",\"reqSigs\":1,\"type\":\"pubkeyhash\",\"addresses\":[\"n4r6dDFCuGSZ1yQwxTJW3be3QFsoziJp3z\"]}}]},\"unknown\":[],\"inputs\":[{\"witness_utxo\":{\"amount\":199909013,\"scriptPubKey\":{\"asm\":\"OP_HASH160 6345200f68d189e1adc0df1c4d16ea8f14c0dbeb OP_EQUAL\",\"hex\":\"a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87\",\"type\":\"scripthash\",\"address\":\"2N2J7hBS8e4eLE4sNVjJGF1eHuZoYdELe8g\"}},\"partial_signatures\":[{\"pubkey\":\"03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46\",\"signature\":\"304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01\"}],\"redeem_script\":{\"asm\":\"0 771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681\",\"hex\":\"0020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681\",\"type\":\"witness_v0_scripthash\"},\"witness_script\":{\"asm\":\"2 03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46 03de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd 2 OP_CHECKMULTISIG\",\"hex\":\"522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae\",\"type\":\"multisig\"},\"bip32_derivs\":[{\"pubkey\":\"03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46\",\"master_fingerprint\":\"b4a6ba67\",\"path\":\"0'/0'/4'\"},{\"pubkey\":\"03de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd\",\"master_fingerprint\":\"b4a6ba67\",\"path\":\"0'/0'/5'\"}]}],\"outputs\":[{}],\"fee\":1013}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json4, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json4, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt5 + const char* request_json5 = "{\"psbt\":\"cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAAAAAA==\",\"network\":\"testnet\"}"; + const char* exp_json5 = "{\"tx\":{\"txid\":\"f2bd9cccedc37e91a8b10e8034eefc49b901afa6220833097a31ed91772d81a5\",\"hash\":\"f2bd9cccedc37e91a8b10e8034eefc49b901afa6220833097a31ed91772d81a5\",\"version\":2,\"size\":154,\"vsize\":154,\"weight\":616,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[{\"value\":100000000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 b322bddce633b851ac7370ab454f0b367a0654e5\",\"hex\":\"0014b322bddce633b851ac7370ab454f0b367a0654e5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qkv3tmh8xxwu9rtrnwz452nctxeaqv48987ttha\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"0 cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"hex\":\"0014cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qe2uv2wnw3lqzjmgu6wg45vra28zfrf24rjmwuj\"]}}]},\"unknown\":[{\"key\":\"fc03636664000664756d6d7931\",\"value\":\"01020304\"},{\"key\":\"fc03636664000664756d6d7932\",\"value\":\"00\"}],\"inputs\":[{},{}],\"outputs\":[{},{}]}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json5, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json5, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt6 + const char* request_json6 = "{\"psbt\":\"cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==\",\"network\":\"testnet\"}"; + const char* exp_json6 = "{\"tx\":{\"txid\":\"772e587ed2406fa46800082714af39c6a0393eaab8b4c977aff22aa052dbdac0\",\"hash\":\"772e587ed2406fa46800082714af39c6a0393eaab8b4c977aff22aa052dbdac0\",\"version\":2,\"size\":72,\"vsize\":72,\"weight\":288,\"locktime\":0,\"vin\":[],\"vout\":[{\"value\":100000000,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 b322bddce633b851ac7370ab454f0b367a0654e5\",\"hex\":\"0014b322bddce633b851ac7370ab454f0b367a0654e5\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qkv3tmh8xxwu9rtrnwz452nctxeaqv48987ttha\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"0 cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"hex\":\"0014cab8c53a6e8fc0296d1cd3915a307d51c491a555\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qe2uv2wnw3lqzjmgu6wg45vra28zfrf24rjmwuj\"]}}]},\"unknown\":[],\"inputs\":[],\"outputs\":[{\"bip32_derivs\":[{\"pubkey\":\"03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81\",\"master_fingerprint\":\"2a704760\",\"path\":\"44'/0'/0'/0/2\"}],\"unknown\":[{\"key\":\"fc03636664000664756d6d7931\",\"value\":\"01020304\"},{\"key\":\"fc03636664000664756d6d7932\",\"value\":\"00\"}]},{\"bip32_derivs\":[{\"pubkey\":\"036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c\",\"master_fingerprint\":\"9d6b6d86\",\"path\":\"44'/0'/0'/0/2\"}]}]}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json6, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json6, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + // DecodePsbt7 + const char* request_json7 = "{\"psbt\":\"cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAA\",\"network\":\"testnet\"}"; + const char* exp_json7 = "{\"tx\":{\"txid\":\"6014abba7b6cef826488bdd44ce3269616cbfa37bf8e832d9e7703cf144ce646\",\"hash\":\"6014abba7b6cef826488bdd44ce3269616cbfa37bf8e832d9e7703cf144ce646\",\"version\":2,\"size\":92,\"vsize\":92,\"weight\":368,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295}],\"vout\":[]},\"unknown\":[],\"inputs\":[{\"non_witness_utxo\":{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"hash\":\"9cb67d0ba945c8da2342429e19b12e11852e3a032144ca908996ff46f05dd906\",\"version\":2,\"size\":246,\"vsize\":165,\"weight\":657,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1\",\"vout\":0,\"scriptSig\":{\"asm\":\"0014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\",\"hex\":\"160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\"},\"txinwitness\":[\"304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a01\",\"024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a28306\"],\"sequence\":4294967295}],\"vout\":[{\"value\":4899996680,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 09de2a0431cbb3444fc22cad9d9a0fd096397210\",\"hex\":\"001409de2a0431cbb3444fc22cad9d9a0fd096397210\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qp80z5pp3ewe5gn7z9jkemxs06ztrjussjewgau\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"reqSigs\":1,\"type\":\"scripthash\",\"addresses\":[\"2MzbWwSa1GsmzGLhYbsYRACNhgx4RjqGtTi\"]}}]},\"witness_utxo\":{\"amount\":100000000,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"type\":\"scripthash\",\"address\":\"2MzbWwSa1GsmzGLhYbsYRACNhgx4RjqGtTi\"}},\"sighash\":\"ALL\",\"redeem_script\":{\"asm\":\"0 962c4e08f336d3afbc3415c9d359ae1040470520\",\"hex\":\"0014962c4e08f336d3afbc3415c9d359ae1040470520\",\"type\":\"witness_v0_keyhash\"},\"bip32_derivs\":[{\"pubkey\":\"02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe\",\"master_fingerprint\":\"2a704760\",\"path\":\"44'/0'/0'/1/1\"}],\"unknown\":[{\"key\":\"fc03636664000664756d6d7931\",\"value\":\"01020304\"},{\"key\":\"fc03636664000664756d6d7932\",\"value\":\"00\"}]},{\"non_witness_utxo\":{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"hash\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"version\":2,\"size\":191,\"vsize\":191,\"weight\":764,\"locktime\":0,\"vin\":[{\"txid\":\"c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26\",\"vout\":1,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d\",\"vout\":0,\"scriptSig\":{\"asm\":\"\",\"hex\":\"\"},\"sequence\":4294967295},{\"txid\":\"8b84fd7266e1ec09cb5a27cd032729be0102178e250645ee429518e7e83f99f1\",\"vout\":0,\"scriptSig\":{\"asm\":\"0014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\",\"hex\":\"160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5\"},\"txinwitness\":[\"304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a01\",\"024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a28306\"],\"sequence\":4294967295},{\"txid\":\"405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6\",\"vout\":0,\"scriptSig\":{\"asm\":\"3044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c01 03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec\",\"hex\":\"473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec\"},\"sequence\":4294967295}],\"vout\":[{\"value\":4899996680,\"n\":0,\"scriptPubKey\":{\"asm\":\"0 09de2a0431cbb3444fc22cad9d9a0fd096397210\",\"hex\":\"001409de2a0431cbb3444fc22cad9d9a0fd096397210\",\"reqSigs\":1,\"type\":\"witness_v0_keyhash\",\"addresses\":[\"tb1qp80z5pp3ewe5gn7z9jkemxs06ztrjussjewgau\"]}},{\"value\":100000000,\"n\":1,\"scriptPubKey\":{\"asm\":\"OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL\",\"hex\":\"a914509f5985f4e90a14fb90e39316fdb4f3ac97530787\",\"reqSigs\":1,\"type\":\"scripthash\",\"addresses\":[\"2MzbWwSa1GsmzGLhYbsYRACNhgx4RjqGtTi\"]}},{\"value\":499995000,\"n\":0,\"scriptPubKey\":{\"asm\":\"OP_DUP OP_HASH160 8d20443a91969e3bca0e240cd0ffe4dc98c63de2 OP_EQUALVERIFY OP_CHECKSIG\",\"hex\":\"76a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac\",\"reqSigs\":1,\"type\":\"pubkeyhash\",\"addresses\":[\"mtPAE24cZ3k6NMCcxQJUbSpZnSVaCu96Wj\"]}}]},\"bip32_derivs\":[{\"pubkey\":\"02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7\",\"master_fingerprint\":\"9d6b6d86\",\"path\":\"44'/0'/0'/0/1\"}]}],\"outputs\":[],\"fee\":599995000}"; + ret = CfdRequestExecuteJson(handle, "DecodePsbt", request_json7, &respons_json); + EXPECT_EQ(kCfdSuccess, ret); + if (respons_json != nullptr) { + EXPECT_STREQ(exp_json7, respons_json); + CfdFreeStringBuffer(respons_json); + } + respons_json = NULL; + + if (ret != kCfdSuccess) { + char* str_buffer = nullptr; + ret = CfdGetLastErrorMessage(handle, &str_buffer); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("dummy_except", str_buffer); + CfdFreeStringBuffer(str_buffer); + str_buffer = nullptr; + } + } + ret = CfdFreeHandle(handle); EXPECT_EQ(kCfdSuccess, ret); } @@ -363,6 +484,257 @@ TEST(cfdcapi_common, CfdSerializeByteData) { EXPECT_EQ(kCfdSuccess, ret); } +TEST(cfdcapi_common, CfdEncryptDecryptAES) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* target_data = "74657374207465737420746573742074657374"; + const char* exp_aes = "752fe203af4a4d427997e5d2c8b246530e0546b66d2982a49e333e77295dccea"; + ret = CfdEncryptAES(handle, + "616975656F616975656F616975656F616975656F616975656F616975656F6169", + "", + target_data, + &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_aes, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdDecryptAES(handle, + "616975656F616975656F616975656F616975656F616975656F616975656F6169", + "", + exp_aes, + &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("7465737420746573742074657374207465737400000000000000000000000000", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdEncryptDecryptAES_CBC) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* target_data = "74657374207465737420746573742074657374"; + const char* exp_aes = "2ef199bb7d160f94fc17fa5f01b220c630d6b19a5973f4b313868c921fc10d22"; + ret = CfdEncryptAES(handle, + "616975656F616975656F616975656F616975656F616975656F616975656F6169", + "33343536373839303132333435363738", + target_data, + &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_aes, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdDecryptAES(handle, + "616975656F616975656F616975656F616975656F616975656F616975656F6169", + "33343536373839303132333435363738", + exp_aes, + &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(target_data, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdEncodeDecodeBase64) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* target_data = "54686520717569636b2062726f776e20666f78206a756d7073206f766572203133206c617a7920646f67732e"; + const char* exp = "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4="; + ret = CfdEncodeBase64(handle, target_data, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdDecodeBase64(handle, exp, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(target_data, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdEncodeDecodeBase58) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* target_data = "0488b21e051431616f00000000e6ba4088246b104837c62bd01fd8ba1cf2931ad1a5376c2360a1f112f2cfc63c02acf89ab4e3daa79bceef2ebecee2af92712e6bf5e4b0d10c74bbecc27ac13da8"; + const char* exp = "9XpNiCWvYUYz78YLbbNYoBMUef5GNJooCQ9i2nf9AH95Njpp4AbuEcmL5iVAwxa6LdR6FyRPeGFEmkFDr3KPGww6peFFtqtabW75Ush4TR"; + ret = CfdEncodeBase58(handle, target_data, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdDecodeBase58(handle, exp, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(target_data, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdEncodeDecodeBase58Check) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* target_data = "0488b21e051431616f00000000e6ba4088246b104837c62bd01fd8ba1cf2931ad1a5376c2360a1f112f2cfc63c02acf89ab4e3daa79bceef2ebecee2af92712e6bf5e4b0d10c74bbecc27ac13da8"; + const char* exp = "xpub6FZeZ5vwcYiT6r7ZYKJhyUqBxMBvzSmb6SpPQCsSenGPrVjKk5SGW4JJpc7cKERN8w9KnJZcMgJA4B2cHnpGq5TahYrDvZSBY2EMLKPRMTT"; + ret = CfdEncodeBase58(handle, target_data, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdDecodeBase58(handle, exp, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(target_data, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdHashMessageByHex) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* message_hex = "001412012222880A"; + + ret = CfdRipemd160(handle, message_hex, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("5f4e6799b6d87fbf4cee7820b8a168b13225dbc8", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdSha256(handle, message_hex, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("7c90e7f25d3c26b098d43ae18cfc67e2d6c200f8acf8b16737c3ec6143e8ba8b", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdHash160(handle, message_hex, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("1097f123808affe262cc5b7cb6acfa84a7a61bb6", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdHash256(handle, message_hex, false, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("c6a3953d698f9ed23812c40bcf7aba724d66fbd9f771ffed8f5d6d2b4b267bcf", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_common, CfdHashMessageByText) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + const char* message = "The quick brown fox jumps over the lazy dog"; + + ret = CfdRipemd160(handle, message, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("37f332f68db77bd9d7edd4969571ad671cf9dd3b", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdSha256(handle, message, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdHash160(handle, message, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("0e3397b4abc7a382b3ea2365883c3c7ca5f07600", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdHash256(handle, message, true, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("6d37795021e544d82b41850edf7aabab9a0ebe274e54a519840c4666f35b3937", output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + // last test. /* comment out. TEST(cfdcapi_common, CfdFinalize) { diff --git a/test/capi/test_cfdcapi_key.cpp b/test/capi/test_cfdcapi_key.cpp index 91b2a03f..33f6da6a 100644 --- a/test/capi/test_cfdcapi_key.cpp +++ b/test/capi/test_cfdcapi_key.cpp @@ -305,6 +305,35 @@ TEST(cfdcapi_key, CompressUncompressTest) { EXPECT_EQ(kCfdSuccess, ret); } +TEST(cfdcapi_key, GetPubkeyFingerprint) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + char* output = nullptr; + ret = CfdGetPubkeyFingerprint(handle, + "03662a01c232918c9deb3b330272483c3e4ec0c6b5da86df59252835afeb4ab5f9", &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("7df5646a", output); + CfdFreeStringBuffer(output); + } + + ret = CfdGetLastErrorCode(handle); + if (ret != kCfdSuccess) { + char* str_buffer = NULL; + ret = CfdGetLastErrorMessage(handle, &str_buffer); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_STREQ("7df5646a", str_buffer); + CfdFreeStringBuffer(str_buffer); + str_buffer = NULL; + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + TEST(cfdcapi_key, CombinePubkeysTest1) { void* handle = NULL; int ret = CfdCreateHandle(&handle); @@ -974,3 +1003,85 @@ TEST(cfdcapi_key, SchnorrKeyTest) { ret = CfdFreeHandle(handle); EXPECT_EQ(kCfdSuccess, ret); } + +TEST(cfdcapi_key, SchnorrSignatureTest) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + struct CfdSchnorrSignatureTestData { + const char* signature; + const char* sighash_signature; + int sighash_type; + bool anyone_can_pay; + }; + const struct CfdSchnorrSignatureTestData exp_datas[] = { + { // sighash all + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce801", + kCfdSigHashAll, + false + }, + { // sighash default + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + kCfdSigHashDefault, + false + }, + { // sighash none + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce802", + kCfdSigHashNone, + false + }, + { // sighash single + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce803", + kCfdSigHashSingle, + false + }, + { // sighash single + anyone can pay + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8", + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce883", + kCfdSigHashSingle, + true + }, + }; + size_t list_size = sizeof(exp_datas) / sizeof(struct CfdSchnorrSignatureTestData); + + for (size_t idx = 0; idx < list_size; ++idx) { + char* sig = nullptr; + int sighash_type = 0; + bool anyone_can_pay = false; + ret = CfdAddSighashTypeInSchnorrSignature( + handle, exp_datas[idx].signature, exp_datas[idx].sighash_type, + exp_datas[idx].anyone_can_pay, &sig); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_datas[idx].sighash_signature, sig); + CfdFreeStringBuffer(sig); + sig = nullptr; + + ret = CfdGetSighashTypeFromSchnorrSignature( + handle, exp_datas[idx].sighash_signature, &sighash_type, &anyone_can_pay); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_EQ(exp_datas[idx].sighash_type, sighash_type); + EXPECT_EQ(exp_datas[idx].anyone_can_pay, anyone_can_pay); + } + } + + if (ret != kCfdSuccess) { + char* message = nullptr; + ret = CfdGetLastErrorMessage(handle, &message); + if (ret == kCfdSuccess) { + EXPECT_STREQ("", message); + CfdFreeStringBuffer(message); + } + } + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} diff --git a/test/capi/test_cfdcapi_psbt.cpp b/test/capi/test_cfdcapi_psbt.cpp new file mode 100644 index 00000000..3a13613d --- /dev/null +++ b/test/capi/test_cfdcapi_psbt.cpp @@ -0,0 +1,1469 @@ +#include "gtest/gtest.h" + +#include +#include + +#include "cfd/cfd_utxo.h" +#include "cfdc/cfdcapi_common.h" +#include "cfdc/cfdcapi_key.h" +#include "cfdc/cfdcapi_psbt.h" +#include "cfdc/cfdcapi_address.h" +#include "cfdc/cfdcapi_transaction.h" +#include "capi/cfdc_internal.h" +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_util.h" + +using cfd::core::HashUtil; +using cfd::core::StringUtil; +using cfd::core::Privkey; +using cfd::core::CryptoUtil; +using cfd::core::SigHashType; + +/** + * @brief testing class. + */ +class cfdcapi_psbt : public ::testing::Test { + protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + +struct CfdUtxo { + const char* txid; + uint32_t vout; + int64_t amount; + const char* descriptor; + const char* full_utxo_tx; +}; + +static void CfdcapiPsbtDumpLog(int result, void* handle) { + if (result != kCfdSuccess) { + char* output = nullptr; + int ret = CfdGetLastErrorMessage(handle, &output); + if (ret == kCfdSuccess) { + EXPECT_STREQ("", output); + } + } +} + +TEST(cfdcapi_psbt, CreatePsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAP0+AQIAAAAG+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////AoDw+gIAAAAAFgAUsyK93OYzuFGsc3CrRU8LNnoGVOWA8PoCAAAAABYAFMq4xTpuj8ApbRzTkVowfVHEkaVVAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEBH4CWmAAAAAAAFgAUi/CdYLfjTzSCfY28scdjkKkWyoIiBgInRM+yQ24VYEDsHI/oQtnvmhjLQK1B8xJyU5DDX3vTaxgqcEdgLAAAgAAAAIAAAACAAQAAAAIAAAAAAQEfgJaYAAAAAAAWABTx0+5ngpIl64ksyrAeIvand+fiHiIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgYDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20QYKnBHYCwAAIAAAACAAAAAgAEAAAAEAAAAAAEBH4CWmAAAAAAAFgAUEreVSnXvwqIOhuMtwteGR9ZwB3kiBgL7Bhcw2948gGtKF6mfRUyBKCrs7m1G9Kl1qUzf06BlBBgqcEdgLAAAgAAAAIAAAACAAQAAAAUAAAAAAQEfgPD6AgAAAAAWABSXipBGDkRnGlL0mgm7WcxnlLY8iSIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA=="; + const char* exp_psbt = "70736274ff0100fd3e010200000006f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff0280f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca822206022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b182a7047602c000080000000800000008001000000020000000001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e220602e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855182a7047602c000080000000800000008001000000030000000001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb0752206033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44182a7047602c000080000000800000008001000000040000000001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d6700779220602fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504182a7047602c000080000000800000008001000000050000000001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c89220603e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec189d6b6d862c0000800000008000000080010000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000"; + char* output_base64 = nullptr; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "", "", 2, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + int64_t amount1 = 0x989680; + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", 1, + 0xffffffff, amount1, "", + "wpkh([2a704760/44h/0h/0h/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", 2, + 0xffffffff, amount1, "", + "wpkh([2a704760/44h/0h/0h/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", 3, + 0xffffffff, amount1, "", + "wpkh([2a704760/44h/0h/0h/1/3]02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", 4, + 0xffffffff, amount1, "", + "wpkh([2a704760/44h/0h/0h/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "b98f570401eb46bded2d3efb41aba9090d014f083a0ad2d232c8229a4d1abea0", 5, + 0xffffffff, amount1, "", + "wpkh([2a704760/44h/0h/0h/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", 1, + 0xffffffff, 0x2faf080, "", + "wpkh([9d6b6d86/44h/0h/0h/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec)", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + uint32_t index = 0; + ret = CfdAddPsbtTxOutWithPubkey(handle, psbt_handle, + 50000000, "", + "wpkh([2a704760/44h/0h/0h/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81)", + &index); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(0, index); + + ret = CfdAddPsbtTxOutWithPubkey(handle, psbt_handle, + 50000000, "", + "wpkh([9d6b6d86/44h/0h/0h/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c)", + &index); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(1, index); + + ret = CfdGetPsbtData(handle, psbt_handle, &output_base64, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output_base64); + EXPECT_STREQ(exp_psbt, output); + CfdFreeStringBuffer(output_base64); + output_base64 = nullptr; + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, ParsePsbtBase64) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQMEAQAAAAEEIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBBUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSriIGA6US9fWcDnkB/EeO1jU+73b0T5yywYW/vPfwubt2cWvzGCpwR2AsAACAAAAAgAAAAIAAAAAACwAAACIGA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHGJ1rbYYsAACAAAAAgAAAAIAAAAAACwAAAAABACIAIDytBhneZ+YkenahAoE2NcBTRXxrpP3krB/9gUjXDkvMAQFHUiECkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnshAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCUq4iAgJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQhida22GLAAAgAAAAIAAAACAAAAAAAwAAAAiAgKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGexgqcEdgLAAAgAAAAIAAAACAAAAAAAwAAAAA"; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, exp_psbt_base64, "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, ParsePsbtHex) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt = "70736274ff0100fd3e010200000006f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff0280f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca822206022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b182a7047602c000080000000800000008001000000020000000001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e220602e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855182a7047602c000080000000800000008001000000030000000001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb0752206033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44182a7047602c000080000000800000008001000000040000000001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d6700779220602fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504182a7047602c000080000000800000008001000000050000000001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c89220603e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec189d6b6d862c0000800000008000000080010000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000"; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, exp_psbt, "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdGetPsbtData(handle, psbt_handle, nullptr, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, ConvertPsbtFromTx) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_tx = "0200000006f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff0280f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000"; + const char* exp_psbt_base64 = "cHNidP8BAP0+AQIAAAAG+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////AoDw+gIAAAAAFgAUsyK93OYzuFGsc3CrRU8LNnoGVOWA8PoCAAAAABYAFMq4xTpuj8ApbRzTkVowfVHEkaVVAAAAAAAAAAAAAAAAAA=="; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "", exp_psbt_tx, 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + uint32_t psbt_version = 0; + uint32_t txin_count = 0; + uint32_t txout_count = 0; + ret = CfdGetPsbtGlobalData(handle, psbt_handle, &psbt_version, + &output, &txin_count, &txout_count); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_EQ(0, psbt_version); + EXPECT_EQ(6, txin_count); + EXPECT_EQ(2, txout_count); + EXPECT_STREQ(exp_psbt_tx, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, JoinPsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdJoinPsbt(handle, psbt_handle, "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, SignPsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA=="; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdSignPsbt(handle, psbt_handle, "KwNwembMPPQpgFfbhb5WPCgENwhnTaNQjf3a6cBuBiZ993Gu5gaR", true); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, CombinePsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "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"; + char* output = nullptr; + // tx: 0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000 + ret = CfdCreatePsbtHandle( + handle, net_type, "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdCombinePsbt(handle, psbt_handle, "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiAgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jx0cwRAIgZuyVbpZ4PNYSLJwHcyo66ZMbaava/x82LCjtX6lnKNMCIGoW1K5G/tSNPQSSc3YTvZtR/j4kU7REbkR8Zb8sln4QASIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA=="); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, FinalizePsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQcXFgAUlixOCPM206+8NBXJ01muEEBHBSABCGsCRzBEAiApmGKmeptFTWzaX+40pS2Qib+gJERNiAMcNKmOKYvbygIgS0f89Qe4CVQQjgN1Zz/b3QhMX7uZT2yVFyDy6y9ruGYBIQJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/gABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAAAEHakcwRAIgZuyVbpZ4PNYSLJwHcyo66ZMbaava/x82LCjtX6lnKNMCIGoW1K5G/tSNPQSSc3YTvZtR/j4kU7REbkR8Zb8sln4QASEC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA="; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "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", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdIsFinalizedPsbt(handle, psbt_handle); + EXPECT_EQ(kCfdSignVerificationError, ret); + + ret = CfdIsFinalizedPsbtInput(handle, psbt_handle, "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", 1); + EXPECT_EQ(kCfdSignVerificationError, ret); + + ret = CfdIsFinalizedPsbtInput(handle, psbt_handle, "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", 0); + EXPECT_EQ(kCfdSignVerificationError, ret); + + ret = CfdVerifyPsbtTxIn(handle, psbt_handle, "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", 1); + EXPECT_EQ(kCfdSignVerificationError, ret); + + ret = CfdFinalizePsbt(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdIsFinalizedPsbt(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdIsFinalizedPsbtInput(handle, psbt_handle, "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", 1); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdIsFinalizedPsbtInput(handle, psbt_handle, "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", 0); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdVerifyPsbtTxIn(handle, psbt_handle, "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", 1); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, ExtractPsbtTransaction) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_tx = "02000000000102267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000017160014962c4e08f336d3afbc3415c9d359ae1040470520ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc0000000006a473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555024730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb866012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0000000000"; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQcXFgAUlixOCPM206+8NBXJ01muEEBHBSABCGsCRzBEAiApmGKmeptFTWzaX+40pS2Qib+gJERNiAMcNKmOKYvbygIgS0f89Qe4CVQQjgN1Zz/b3QhMX7uZT2yVFyDy6y9ruGYBIQJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/gABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAAAEHakcwRAIgZuyVbpZ4PNYSLJwHcyo66ZMbaava/x82LCjtX6lnKNMCIGoW1K5G/tSNPQSSc3YTvZtR/j4kU7REbkR8Zb8sln4QASEC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdExtractPsbtTransaction(handle, psbt_handle, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_tx, output); + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, UsecaseMultisig) { + // const char* psbt_utxo_sh_wsh_multi = "02000000000102f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0100000017160014c1768dd714526732f54de6c8581a78e385d0900affffffffc6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e4001000000171600141305fd9c86bb2cd2d671928388eccd2b1140aa5affffffff010858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f2317587024730440220032350deae8e8fc60a45800508b09f475ba6043b40ce87f8020a6cc532e401ab02205979d8ad394e19ef9881bb987f77dab465f1112a746ca17a509215c2dbfea93201210206d743464ae738be5ba6d07dd434bed3f24af32f9fad805ffb9a241ec56ea24802473044022051f8776ba287c7424b2d783ebde964e25bde5fec4728735b8b455c806daae16602207988cabc3475109e264a7d378873d0fcee78959c46facdafb099af4ca6eace94012103e241f36e5e17a599c465ec82f3520e6d7a0506cc862de06ff4ba26b255a2f62c00000000"; + + auto path1 = "44h/0h/0h/0/12"; + auto key1 = "02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b"; + auto key2 = "02622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242"; + auto out_multisig = "522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae"; + // auto addr = "tb1q8jksvxw7vlnzg7nk5ypgzd34cpf52lrt5n77ftqllkq534cwf0xqjsj24p"; + + auto in_path = "44h/0h/0h/0/11"; + auto key_in1 = "ac9aa506601b23ea7362dfcb9350ff3c2edeb67b334258d6489889e67dfa417f"; + auto pubkey_in1 = "03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3"; + auto key_in2 = "973e1d45dca188b0c0ac67bcc5de3ccdbe479d7505bdc43c57231c75f8db55e3"; + auto pubkey_in2 = "03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47"; + auto multisig = "522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae"; + + const char* txid = "544d545a1e53becbf9dd9b0e424e1189b8f6e46d6bc36b191816d341c2d32f69"; + uint32_t vout = 0; + + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "", "", 2, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + // psbt_utxo_sh_wsh_multi + ret = CfdAddPsbtTxInWithScript(handle, psbt_handle, + txid, vout, + 0xffffffff, 499996680, + "a914945fb50391a70637c1ffc5ab7fb65308c2f2317587", + multisig, "", ""); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdSetPsbtSighashType(handle, psbt_handle, + txid, vout, + 1); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + char* psbt1 = nullptr; + char* psbt2 = nullptr; + char* base64 = nullptr; + if (ret == kCfdSuccess) { + EXPECT_STREQ("cHNidP8BADMCAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////AAAAAAAAAQEgCFjNHQAAAAAXqRSUX7UDkacGN8H/xat/tlMIwvIxdYcBAwQBAAAAAQQiACCcTayyXruK2ou7Gt24ad6k2BcMyVHx2WlLIVThWDJ2yQEFR1IhA6US9fWcDnkB/EeO1jU+73b0T5yywYW/vPfwubt2cWvzIQP01HNhTpVKxPVRjm98szB7T4R0PhN+1O7LX0+2Q8I7R1KuAA==", output); + + void* psbt_handle1 = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, output, "", 0, 0, &psbt_handle1); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + if (ret == kCfdSuccess) { + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle1, + txid, vout, + pubkey_in1, "2a704760", in_path); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle1, &psbt1, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreePsbtHandle(handle, psbt_handle1); + EXPECT_EQ(kCfdSuccess, ret); + } + + void* psbt_handle2 = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, output, "", 0, 0, &psbt_handle2); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle2, + txid, vout, + pubkey_in2, "9d6b6d86", in_path); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle2, &psbt2, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreePsbtHandle(handle, psbt_handle2); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeStringBuffer(output); + EXPECT_EQ(kCfdSuccess, ret); + output = nullptr; + } + + ret = CfdAddPsbtTxOutWithScript(handle, psbt_handle, 499993360, + "00203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc", + out_multisig, "", nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxOutBip32Pubkey(handle, psbt_handle, 0, + key1, "2a704760", path1); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxOutBip32Pubkey(handle, psbt_handle, 0, + key2, "9d6b6d86", path1); + EXPECT_EQ(kCfdSuccess, ret); + + if ((psbt1 != nullptr) && (psbt2 != nullptr)) { + ret = CfdJoinPsbt(handle, psbt_handle, psbt1); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdJoinPsbt(handle, psbt_handle, psbt2); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &base64, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("70736274ff01005e0200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc00000000000101200858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f23175870103040100000001042200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9010547522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae220603a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3182a7047602c0000800000008000000080000000000b000000220603f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47189d6b6d862c0000800000008000000080000000000b00000000010147522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae220202622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242189d6b6d862c0000800000008000000080000000000c000000220202906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b182a7047602c0000800000008000000080000000000c00000000", output); + EXPECT_STREQ("cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQMEAQAAAAEEIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBBUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSriIGA6US9fWcDnkB/EeO1jU+73b0T5yywYW/vPfwubt2cWvzGCpwR2AsAACAAAAAgAAAAIAAAAAACwAAACIGA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHGJ1rbYYsAACAAAAAgAAAAIAAAAAACwAAAAABAUdSIQKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGeyECYix5dOwy3nWvo3M7CabDPQ3qUUsp+qzPsJkXdPRiIkJSriICAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCGJ1rbYYsAACAAAAAgAAAAIAAAAAADAAAACICApBtOZ9tu+zImNS43j9JdHTIakHyzzbXH5XlygawdIZ7GCpwR2AsAACAAAAAgAAAAIAAAAAADAAAAAA=", base64); + + ret = CfdFreeStringBuffer(output); + EXPECT_EQ(kCfdSuccess, ret); + output = nullptr; + ret = CfdFreeStringBuffer(base64); + EXPECT_EQ(kCfdSuccess, ret); + base64 = nullptr; + } + } + if (psbt1 != nullptr) { + ret = CfdFreeStringBuffer(psbt1); + EXPECT_EQ(kCfdSuccess, ret); + psbt1 = nullptr; + } + if (psbt2 != nullptr) { + ret = CfdFreeStringBuffer(psbt2); + EXPECT_EQ(kCfdSuccess, ret); + psbt2 = nullptr; + } + + ret = CfdGetPsbtData(handle, psbt_handle, &output, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + // sign psbt + void* psbt_handle1 = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, output, "", 0, 0, &psbt_handle1); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdSignPsbt(handle, psbt_handle1, key_in1, true); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle1, &psbt1, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreePsbtHandle(handle, psbt_handle1); + EXPECT_EQ(kCfdSuccess, ret); + } + + void* psbt_handle2 = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, output, "", 0, 0, &psbt_handle2); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + // ret = CfdSignPsbt(handle, psbt_handle2, key_in2, true); + // EXPECT_EQ(kCfdSuccess, ret); + // CfdcapiPsbtDumpLog(ret, handle); + // Same processing. + int sighash_type = 0; + ret = CfdGetPsbtSighashType(handle, psbt_handle2, txid, vout, &sighash_type); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(1, sighash_type); + + int64_t amount = 0; + char* script = nullptr; + char* tx = nullptr; + ret = CfdGetPsbtUtxoData(handle, psbt_handle2, txid, vout, &amount, nullptr, &script, nullptr, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + if (ret == kCfdSuccess) { + ret = CfdGetPsbtGlobalData(handle, psbt_handle2, nullptr, &tx, nullptr, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + char* sighash = nullptr; + ret = CfdCreateSighash(handle, net_type, tx, txid, vout, kCfdP2shP2wsh, + pubkey_in2, script, amount, sighash_type, false, &sighash); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + char* sig = nullptr; + ret = CfdCalculateEcSignature(handle, sighash, key_in2, nullptr, net_type, true, &sig); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + char* der_sig = nullptr; + ret = CfdEncodeSignatureByDer(handle, sig, sighash_type, false, &der_sig); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + ret = CfdSetPsbtSignature(handle, psbt_handle2, txid, vout, pubkey_in2, der_sig); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreeStringBuffer(der_sig); + EXPECT_EQ(kCfdSuccess, ret); + der_sig = nullptr; + } + ret = CfdFreeStringBuffer(sig); + EXPECT_EQ(kCfdSuccess, ret); + sig = nullptr; + } + ret = CfdFreeStringBuffer(sighash); + EXPECT_EQ(kCfdSuccess, ret); + sighash = nullptr; + } + ret = CfdFreeStringBuffer(tx); + EXPECT_EQ(kCfdSuccess, ret); + tx = nullptr; + } + ret = CfdFreeStringBuffer(script); + EXPECT_EQ(kCfdSuccess, ret); + script = nullptr; + } + + ret = CfdGetPsbtData(handle, psbt_handle2, &psbt2, nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdFreePsbtHandle(handle, psbt_handle2); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeStringBuffer(output); + EXPECT_EQ(kCfdSuccess, ret); + output = nullptr; + } + + if ((psbt1 != nullptr) && (psbt2 != nullptr)) { + ret = CfdCombinePsbt(handle, psbt_handle, psbt1); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdCombinePsbt(handle, psbt_handle, psbt2); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &base64, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("70736274ff01005e0200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc00000000000101200858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f2317587220203a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3473044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b8401220203f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4747304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc010103040100000001042200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9010547522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae220603a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3182a7047602c0000800000008000000080000000000b000000220603f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47189d6b6d862c0000800000008000000080000000000b00000000010147522102906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b2102622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f462224252ae220202622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242189d6b6d862c0000800000008000000080000000000c000000220202906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b182a7047602c0000800000008000000080000000000c00000000", output); + 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", base64); + + ret = CfdFreeStringBuffer(output); + EXPECT_EQ(kCfdSuccess, ret); + output = nullptr; + ret = CfdFreeStringBuffer(base64); + EXPECT_EQ(kCfdSuccess, ret); + base64 = nullptr; + } + } + if (psbt1 != nullptr) { + ret = CfdFreeStringBuffer(psbt1); + EXPECT_EQ(kCfdSuccess, ret); + psbt1 = nullptr; + } + if (psbt2 != nullptr) { + ret = CfdFreeStringBuffer(psbt2); + EXPECT_EQ(kCfdSuccess, ret); + psbt2 = nullptr; + } + + ret = CfdIsFinalizedPsbt(handle, psbt_handle); + EXPECT_EQ(kCfdSignVerificationError, ret); + + ret = CfdSetPsbtFinalizeScript(handle, psbt_handle, + txid, vout, + "00473044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b840147304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc0147522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae"); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdIsFinalizedPsbt(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdClearPsbtSignData(handle, psbt_handle, + txid, vout); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdExtractPsbtTransaction(handle, psbt_handle, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ("02000000000101692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d5400000000232200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc0400473044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b840147304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc0147522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae00000000", output); + ret = CfdFreeStringBuffer(output); + EXPECT_EQ(kCfdSuccess, ret); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + + +TEST(cfdcapi_psbt, CreatePsbtNotUseDescriptor) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAP0+AQIAAAAG+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////AoDw+gIAAAAAFgAUsyK93OYzuFGsc3CrRU8LNnoGVOWA8PoCAAAAABYAFMq4xTpuj8ApbRzTkVowfVHEkaVVAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEBH4CWmAAAAAAAFgAUi/CdYLfjTzSCfY28scdjkKkWyoIiBgInRM+yQ24VYEDsHI/oQtnvmhjLQK1B8xJyU5DDX3vTaxgqcEdgLAAAgAAAAIAAAACAAQAAAAIAAAAAAQEfgJaYAAAAAAAWABTx0+5ngpIl64ksyrAeIvand+fiHiIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgYDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20QYKnBHYCwAAIAAAACAAAAAgAEAAAAEAAAAAAEBH4CWmAAAAAAAFgAUEreVSnXvwqIOhuMtwteGR9ZwB3kiBgL7Bhcw2948gGtKF6mfRUyBKCrs7m1G9Kl1qUzf06BlBBgqcEdgLAAAgAAAAIAAAACAAQAAAAUAAAAAAQEfgPD6AgAAAAAWABSXipBGDkRnGlL0mgm7WcxnlLY8iSIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA=="; + const char* exp_psbt = "70736274ff0100fd3e010200000006f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff0280f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca822206022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b182a7047602c000080000000800000008001000000020000000001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e220602e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855182a7047602c000080000000800000008001000000030000000001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb0752206033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44182a7047602c000080000000800000008001000000040000000001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d6700779220602fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504182a7047602c000080000000800000008001000000050000000001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c89220603e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec189d6b6d862c0000800000008000000080010000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000"; + char* output_base64 = nullptr; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "", "", 2, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + int64_t amount1 = 0x989680; + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", 1, + 0xffffffff, amount1, "0014962c4e08f336d3afbc3415c9d359ae1040470520", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + CfdcapiPsbtDumpLog(ret, handle); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", 1, + "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", "2a704760", "44h/0h/0h/1/1"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", 2, + 0xffffffff, amount1, "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", 2, + "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", "2a704760", "44h/0h/0h/1/2"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", 3, + 0xffffffff, amount1, "0014f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", 3, + "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", "2a704760", "44h/0h/0h/1/3"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", 4, + 0xffffffff, amount1, "001419d65f8328b2206d9970785660ec0d34808fb075", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", 4, + "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", "2a704760", "44h/0h/0h/1/4"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "b98f570401eb46bded2d3efb41aba9090d014f083a0ad2d232c8229a4d1abea0", 5, + 0xffffffff, amount1, "001412b7954a75efc2a20e86e32dc2d78647d6700779", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "b98f570401eb46bded2d3efb41aba9090d014f083a0ad2d232c8229a4d1abea0", 5, + "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", "2a704760", "44h/0h/0h/1/5"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxInWithPubkey(handle, psbt_handle, + "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", 1, + 0xffffffff, 0x2faf080, "0014978a90460e44671a52f49a09bb59cc6794b63c89", + "", + nullptr); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdSetPsbtTxInBip32Pubkey(handle, psbt_handle, + "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", 1, + "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", "9d6b6d86", "44h/0h/0h/1/1"); + EXPECT_EQ(kCfdSuccess, ret); + + uint32_t index = 0; + ret = CfdAddPsbtTxOutWithPubkey(handle, psbt_handle, + 50000000, "0014b322bddce633b851ac7370ab454f0b367a0654e5", + "", + &index); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(0, index); + + ret = CfdSetPsbtTxOutBip32Pubkey(handle, psbt_handle, index, + "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", "2a704760", "44h/0h/0h/0/2"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdAddPsbtTxOutWithPubkey(handle, psbt_handle, + 50000000, "0014cab8c53a6e8fc0296d1cd3915a307d51c491a555", + "", + &index); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_EQ(1, index); + + ret = CfdSetPsbtTxOutBip32Pubkey(handle, psbt_handle, index, + "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", "9d6b6d86", "44h/0h/0h/0/2"); + EXPECT_EQ(kCfdSuccess, ret); + + ret = CfdGetPsbtData(handle, psbt_handle, &output_base64, &output); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + EXPECT_STREQ(exp_psbt_base64, output_base64); + EXPECT_STREQ(exp_psbt, output); + CfdFreeStringBuffer(output_base64); + output_base64 = nullptr; + CfdFreeStringBuffer(output); + output = nullptr; + } + + ret = CfdFreePsbtHandle(handle, psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + } + + ret = CfdFreeHandle(handle); + EXPECT_EQ(kCfdSuccess, ret); +} + +TEST(cfdcapi_psbt, FundPsbt) { + void* handle = NULL; + int ret = CfdCreateHandle(&handle); + EXPECT_EQ(kCfdSuccess, ret); + EXPECT_FALSE((NULL == handle)); + + int net_type = kCfdNetworkTestnet; + void* psbt_handle = nullptr; + const char* exp_psbt_base64 = "cHNidP8BAP2GAQIAAAAH+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////PEJ8leyO15eW8T031SX76GhUXNkaHrDYn/kJgHMfOosHAAAAAP////8DgPD6AgAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5YDw+gIAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVXykZgAAAAAABYAFCtuFro44oBQD4CyxXBu8C841ZCBAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEBH4CWmAAAAAAAFgAUi/CdYLfjTzSCfY28scdjkKkWyoIiBgInRM+yQ24VYEDsHI/oQtnvmhjLQK1B8xJyU5DDX3vTaxgqcEdgLAAAgAAAAIAAAACAAQAAAAIAAAAAAQEfgJaYAAAAAAAWABTx0+5ngpIl64ksyrAeIvand+fiHiIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgYDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20QYKnBHYCwAAIAAAACAAAAAgAEAAAAEAAAAAAEBH4CWmAAAAAAAFgAUEreVSnXvwqIOhuMtwteGR9ZwB3kiBgL7Bhcw2948gGtKF6mfRUyBKCrs7m1G9Kl1qUzf06BlBBgqcEdgLAAAgAAAAIAAAACAAQAAAAUAAAAAAQEfgPD6AgAAAAAWABSXipBGDkRnGlL0mgm7WcxnlLY8iSIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAABAR+AlpgAAAAAABYAFBzoeOOg2js0MIeX/ey3YiH4VBivIgYDwCMlwyj+1iKp2I+PUxjgUmGjw6lntyEdfWdlfiten74YKnBHYCwAAIAAAACAAAAAgAEAAAAHAAAAACICA0c7/Ix3DBsiCi56rkut9sDX6vKQKNWynTQ4ASuyie+BGCpwR2AsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgNkdK/yYzw1GGVTn7UrYrnW+55OI1dmKOHwoKeZNFjgbBida22GLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIC9/DX0AKJt8Wlgbw1J2BAw0jeSPxBQGfzHqJq2VwAVQwYKnBHYCwAAIAAAACAAAAAgAEAAABkAAAAAA=="; + char* output = nullptr; + ret = CfdCreatePsbtHandle( + handle, net_type, "cHNidP8BAP0+AQIAAAAG+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////AoDw+gIAAAAAFgAUsyK93OYzuFGsc3CrRU8LNnoGVOWA8PoCAAAAABYAFMq4xTpuj8ApbRzTkVowfVHEkaVVAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEBH4CWmAAAAAAAFgAUi/CdYLfjTzSCfY28scdjkKkWyoIiBgInRM+yQ24VYEDsHI/oQtnvmhjLQK1B8xJyU5DDX3vTaxgqcEdgLAAAgAAAAIAAAACAAQAAAAIAAAAAAQEfgJaYAAAAAAAWABTx0+5ngpIl64ksyrAeIvand+fiHiIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgYDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20QYKnBHYCwAAIAAAACAAAAAgAEAAAAEAAAAAAEBH4CWmAAAAAAAFgAUEreVSnXvwqIOhuMtwteGR9ZwB3kiBgL7Bhcw2948gGtKF6mfRUyBKCrs7m1G9Kl1qUzf06BlBBgqcEdgLAAAgAAAAIAAAACAAQAAAAUAAAAAAQEfgPD6AgAAAAAWABSXipBGDkRnGlL0mgm7WcxnlLY8iSIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", "", 0, 0, &psbt_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + void* fund_handle = nullptr; + ret = CfdInitializeFundPsbt(handle, &fund_handle); + EXPECT_EQ(kCfdSuccess, ret); + if (ret == kCfdSuccess) { + uint32_t utxo_list_num = 5; + CfdUtxo utxo_list[] = { + { + "8b3a1f738009f99fd8b01e1ad95c5468e8fb25d5373df19697d78eec957c423c", 7, 10000000, + "wpkh([2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe)", + nullptr + }, + { + "0e5b0e16148da878ee8d9f0524ac0962a22dbbb66e2dc8a6237a1d4c8c4ea9cb", 8, 10000000, + "wpkh([2a704760/44'/0'/0'/1/8]02f505e0b5885959bd2f7e68dd73915940a5540e05c41a42f6c598ceb21bdc55f3)", + nullptr + }, + { + "016b36a0a9df54d55f7b907aba8fdd3d90d797eee2bc46c5ed7dced41218e674", 9, 10000000, + "wpkh([2a704760/44'/0'/0'/1/9]02f7af7e1c3c8627e0e662ba04ec003879d9aeaeba5a02a1e24804ef4d7b497db4)", + nullptr + }, + { + "dcc65ebfb8535f96f8a51e07d5c0a94f965000bd5765159a3cc27e4edd658c69", 10, 10000000, + "wpkh([2a704760/44'/0'/0'/1/10]03c04e76f9bc7d6433a6eb7c0c3c446213c5f361dae22929483f23718e1cee4ece)", + nullptr + }, + { + "0230e7471501f9432f727583f2bc17c11f5c1132140319ba2d7a5a0145684c73", 11, 10000000, + "wpkh([2a704760/44'/0'/0'/1/11]03798ff13e5d17c6f8c1d69ff18d516613be2fa52149b3a8c60ef959d1075a5889)", + nullptr + } + }; + for (uint32_t index=0; index g_test_cfd_descriptor_data { + { + "pk(02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397)#rk5v7uqw", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptPk, + "2102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397ac", + 0, + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz", + AddressType::kP2shAddress, + "", + DescriptorKeyType::kDescriptorKeyPublic, + "02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", + 0 + }, + { + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptPkh, + "76a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac", + 0, + "1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP", + AddressType::kP2pkhAddress, + "", + DescriptorKeyType::kDescriptorKeyPublic, + "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + 0 + }, + { + "wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptWpkh, + "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", + 0, + "bc1q0ht9tyks4vh7p5p904t340cr9nvahy7u3re7zg", + AddressType::kP2wpkhAddress, + "", + DescriptorKeyType::kDescriptorKeyPublic, + "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + 0 + }, + { + "sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a914cc6ffbc0bf31af759451068f90ba7a0272b6b33287", + 0, + "3LKyvRN6SmYXGBNn8fcQvYxW9MGKtwcinN", + AddressType::kP2shP2wpkhAddress, + "", + DescriptorKeyType::kDescriptorKeyPublic, + "03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556", + 0 + }, + { + "combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptCombo, + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + 0, + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + AddressType::kP2wpkhAddress, + "", + DescriptorKeyType::kDescriptorKeyPublic, + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0 + }, + { + "sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a91455e8d5e8ee4f3604aba23c71c2684fa0a56a3a1287", + 0, + "39XGHYpYmJV9sGFoGHZeU2rLkY6r1MJ6C1", + AddressType::kP2shP2wshAddress, + "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac", + DescriptorKeyType::kDescriptorKeyPublic, + "02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13", + 0 + }, + { + "sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a914a6a8b030a38762f4c1f5cbe387b61a3c5da5cd2687", + 0, + "3GtEB3yg3r5de2cDJG48SkQwxfxJumKQdN", + AddressType::kP2shAddress, + "5221022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a012103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe52ae", + DescriptorKeyType::kDescriptorKeyNull, + "", + 2 + }, + { + "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptWsh, + "0020773d709598b76c4e3b575c08aad40658963f9322affc0f8c28d1d9a68d0c944a", + 0, + "bc1qwu7hp9vckakyuw6htsy244qxtztrlyez4l7qlrpg68v6drgvj39qn4zazc", + AddressType::kP2wshAddress, + "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c72103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae", + DescriptorKeyType::kDescriptorKeyNull, + "", + 2 + }, + { + "sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a914aec509e284f909f769bb7dda299a717c87cc97ac87", + 0, + "3Hd7YQStg9gYpEt6hgK14ZHUABxSURzeuQ", + AddressType::kP2shP2wshAddress, + "512103f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa82103499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e42102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e53ae", + DescriptorKeyType::kDescriptorKeyNull, + "", + 1 + }, + { + "addr(bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9)", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptAddr, + "0020c7a1f1a4d6b4c1802a59631966a18359de779e8a6a65973735a3ccdfdabc407d", + 0, + "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9", + AddressType::kP2wshAddress, + "", + DescriptorKeyType::kDescriptorKeyNull, + "", + 0 + }, + { + "raw(6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e)#zf2avljj", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptRaw, + "6a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e", + 0, + "", + AddressType::kP2shAddress, + "", + DescriptorKeyType::kDescriptorKeyNull, + "", + 0 + }, + { + "sh(c:or_i(andor(c:pk_h(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/44),pk_h(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/44),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a9148019628fc9b183af5d52ed7dc5c6d56fe6a6325f87", + 0, + "3DNLpswxUiEVyRnAA6mZSvAt7iG6bCu33f", + AddressType::kP2shAddress, + "6376a914520e6e72bcd5b616bc744092139bd759c31d6bbe88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9145ab62f0be26fe9d6205a155403f33e2ad2d31efe8868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", + DescriptorKeyType::kDescriptorKeyNull, + "", + 0 + }, + { + "sh(wsh(c:or_i(andor(c:pk_h(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/44),pk_h(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/44),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))))", + NetType::kMainnet, + DescriptorScriptType::kDescriptorScriptSh, + "a914a7a9f411001e3e3db96d7f02fc9ab1d0dc6aa69187", + 0, + "3GyYN9WnJBoMn8M5tuqVcFJq1BvbAcdPAt", + AddressType::kP2shP2wshAddress, + "6376a914520e6e72bcd5b616bc744092139bd759c31d6bbe88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9145ab62f0be26fe9d6205a155403f33e2ad2d31efe8868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", + DescriptorKeyType::kDescriptorKeyNull, + "", + 0 + } + }; + + DescriptorScriptData data; + for (auto test_data : g_test_cfd_descriptor_data) { + try { + AddressFactory factory(test_data.net_type); + data = factory.ParseOutputDescriptor( + test_data.descriptor); + + EXPECT_EQ(test_data.type, data.type); + EXPECT_EQ(test_data.locking_script, data.locking_script.GetHex()); + EXPECT_EQ(test_data.depth, data.depth); + EXPECT_EQ(test_data.address, data.address.GetAddress()); + EXPECT_EQ(test_data.address_type, data.address_type); + EXPECT_EQ(test_data.redeem_script, data.redeem_script.GetHex()); + EXPECT_EQ(test_data.key_type, data.key_type); + EXPECT_EQ(test_data.key, data.key); + EXPECT_EQ(test_data.multisig_req_sig_num, data.multisig_req_sig_num); + } catch (const std::exception& e) { + EXPECT_STREQ("", e.what()); + EXPECT_STREQ("", test_data.descriptor.c_str()); + } + } } diff --git a/test/test_cfd_coin_selection.cpp b/test/test_cfd_coin_selection.cpp index f8a27d0a..37325849 100644 --- a/test/test_cfd_coin_selection.cpp +++ b/test/test_cfd_coin_selection.cpp @@ -430,7 +430,7 @@ TEST(CoinSelection, SelectCoins_Simple_SelectCoinsBnB) exp_filter, option_params, tx_fee, &select_value, &fee_value, &use_bnb))); EXPECT_EQ(select_utxos.size(), 2); EXPECT_EQ(select_value.GetSatoshiValue(), static_cast(100001090)); - EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(360)); + EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(368)); if (select_utxos.size() == 2) { EXPECT_EQ(select_utxos[0].amount, static_cast(85062500)); EXPECT_EQ(select_utxos[1].amount, static_cast(14938590)); @@ -472,7 +472,7 @@ TEST(CoinSelection, SelectCoins_Simple_SelectCoinsBnB_single) exp_filter, option_params, tx_fee, &select_value, &fee_value, &use_bnb))); EXPECT_EQ(select_utxos.size(), 1); EXPECT_EQ(select_value.GetSatoshiValue(), static_cast(155062500)); - EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(180)); + EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(184)); if (select_utxos.size() == 1) { EXPECT_EQ(select_utxos[0].amount, static_cast(155062500)); } @@ -514,7 +514,7 @@ TEST(CoinSelection, SelectCoins_Simple_SelectCoinsBnB_empty) exp_filter, option_params, tx_fee, &select_value, &fee_value, &use_bnb))); EXPECT_EQ(select_utxos.size(), 3); EXPECT_EQ(select_value.GetSatoshiValue(), static_cast(115063590)); - EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(270)); + EXPECT_EQ(fee_value.GetSatoshiValue(), static_cast(276)); if (select_utxos.size() == 3) { EXPECT_EQ(select_utxos[0].amount, static_cast(61062500)); EXPECT_EQ(select_utxos[1].amount, static_cast(39062500)); @@ -671,7 +671,7 @@ TEST(CoinSelection, ConvertToUtxo) EXPECT_EQ(utxo.vout, vout); EXPECT_EQ(utxo.script_length, locking_script.GetData().GetDataSize()); EXPECT_EQ(utxo.address_type, static_cast(AddressType::kP2wpkhAddress)); - EXPECT_EQ(utxo.witness_size_max, static_cast(108)); + EXPECT_EQ(utxo.witness_size_max, static_cast(109)); EXPECT_EQ(utxo.uscript_size_max, static_cast(0)); EXPECT_EQ(utxo.amount, amount.GetSatoshiValue()); EXPECT_EQ(utxo.binary_data, binary_data); @@ -694,7 +694,7 @@ TEST(CoinSelection, ConvertToUtxo) EXPECT_EQ(utxo.vout, vout); EXPECT_EQ(utxo.script_length, locking_script.GetData().GetDataSize()); EXPECT_EQ(utxo.address_type, static_cast(AddressType::kP2wpkhAddress)); - EXPECT_EQ(utxo.witness_size_max, static_cast(108)); + EXPECT_EQ(utxo.witness_size_max, static_cast(109)); EXPECT_EQ(utxo.uscript_size_max, static_cast(0)); EXPECT_EQ(utxo.amount, amount.GetSatoshiValue()); EXPECT_EQ(utxo.binary_data, binary_data); @@ -1097,7 +1097,7 @@ TEST(CoinSelection, SelectCoins_SelectCoinsBnB_with_asset) if (map_select_value.size() == 1) { EXPECT_EQ(map_select_value[exp_dummy_asset_a.GetHex()], 100001090); } - EXPECT_EQ(fee.GetSatoshiValue(), 364); + EXPECT_EQ(fee.GetSatoshiValue(), 368); EXPECT_EQ(map_searched_bnb.size(), 1); if (map_searched_bnb.size() == 1) { EXPECT_TRUE(map_searched_bnb[exp_dummy_asset_a.GetHex()]); @@ -1145,7 +1145,7 @@ TEST(CoinSelection, SelectCoins_SelectCoinsBnB_single_with_asset) if (map_select_value.size() == 1) { EXPECT_EQ(map_select_value[exp_dummy_asset_a.GetHex()], 155062500); } - EXPECT_EQ(fee.GetSatoshiValue(), 182); + EXPECT_EQ(fee.GetSatoshiValue(), 184); EXPECT_EQ(map_searched_bnb.size(), 1); if (map_searched_bnb.size() == 1) { EXPECT_TRUE(map_searched_bnb[exp_dummy_asset_a.GetHex()]); @@ -1195,7 +1195,7 @@ TEST(CoinSelection, SelectCoins_SelectCoinsBnB_empty_with_asset) if (map_select_value.size() == 1) { EXPECT_EQ(map_select_value[exp_dummy_asset_a.GetHex()], 115063590); } - EXPECT_EQ(fee.GetSatoshiValue(), 273); + EXPECT_EQ(fee.GetSatoshiValue(), 276); EXPECT_EQ(map_searched_bnb.size(), 1); if (map_searched_bnb.size() == 1) { EXPECT_FALSE(map_searched_bnb[exp_dummy_asset_a.GetHex()]); @@ -1285,7 +1285,7 @@ TEST(CoinSelection, SelectCoins_CoinSelectBnB_with_multiple_asset) EXPECT_EQ(map_select_value[exp_dummy_asset_a.GetHex()], 100001090); EXPECT_EQ(map_select_value[exp_dummy_asset_b.GetHex()], 347180050); } - EXPECT_EQ(fee.GetSatoshiValue(), 728); + EXPECT_EQ(fee.GetSatoshiValue(), 736); EXPECT_EQ(map_searched_bnb.size(), 2); if (map_searched_bnb.size() == 2) { EXPECT_TRUE(map_searched_bnb[exp_dummy_asset_a.GetHex()]); @@ -1343,7 +1343,7 @@ TEST(CoinSelection, SelectCoins_with_multiple_asset_fee_only_target) EXPECT_EQ(map_select_value[exp_dummy_asset_b.GetHex()], 347180050); EXPECT_EQ(map_select_value[exp_dummy_asset_c.GetHex()], 37654200); } - EXPECT_EQ(fee.GetSatoshiValue(), 910); + EXPECT_EQ(fee.GetSatoshiValue(), 920); EXPECT_EQ(map_searched_bnb.size(), 3); if (map_searched_bnb.size() == 3) { EXPECT_TRUE(map_searched_bnb[exp_dummy_asset_a.GetHex()]); diff --git a/test/test_cfd_confidentialtx_context.cpp b/test/test_cfd_confidentialtx_context.cpp index 0d0d2b2c..3c05b1a2 100644 --- a/test/test_cfd_confidentialtx_context.cpp +++ b/test/test_cfd_confidentialtx_context.cpp @@ -486,7 +486,7 @@ TEST(ConfidentialTransactionContext, BlindTransaction) issue_key.token_key = issue_key.asset_key; BlindParameter utxo_param; utxo_param.asset = ConfidentialAssetId("849cabdb3b0df8a05b97c5df0f2e2f891d5a94fccf6dbe9907ee34b477a1e735"); - utxo_param.value = ConfidentialValue(Amount(int64_t{49922540})); + utxo_param.value = ConfidentialValue(Amount(int64_t{49980000})); std::map utxo_info_map; utxo_info_map.emplace(outpoint, utxo_param); diff --git a/test/test_cfd_psbt.cpp b/test/test_cfd_psbt.cpp new file mode 100644 index 00000000..bcc00fa0 --- /dev/null +++ b/test/test_cfd_psbt.cpp @@ -0,0 +1,1485 @@ +#include "gtest/gtest.h" +#include + +#include "cfd/cfd_common.h" +#include "cfd/cfd_psbt.h" +#include "cfd/cfd_utxo.h" +#include "cfd/cfd_transaction_common.h" +#include "cfdcore/cfdcore_amount.h" +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_descriptor.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" +#include "cfdcore/cfdcore_util.h" + +using cfd::core::CfdException; +using cfd::core::Address; +using cfd::core::AddressType; +using cfd::core::Amount; +using cfd::core::Descriptor; +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::ByteData256; +using cfd::core::HDWallet; +using cfd::core::OutPoint; +using cfd::core::Privkey; +using cfd::core::Pubkey; +using cfd::core::NetType; +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; +using cfd::core::HashUtil; +using cfd::Psbt; +using cfd::UtxoData; +using cfd::CoinSelectionOption; +using cfd::TransactionContext; + +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 + +static std::vector CfdTestGenerateUtxo( + NetType net_type, const HDWallet& wallet, const std::string& base_path, + const Amount& amount, uint32_t offset, uint32_t count, + std::vector* privkey_list) { + std::vector list; + for (uint32_t index=offset; index<(offset + count); ++index) { + std::string path = base_path + "/" + std::to_string(index); + auto key = wallet.GeneratePrivkeyData(net_type, path); + UtxoData utxo; + utxo.descriptor = "wpkh(" + key.ToString() + ")"; + utxo.amount = amount; + Descriptor desc = Descriptor::Parse(utxo.descriptor); + auto ref = desc.GetReference(); + utxo.locking_script = ref.GetLockingScript(); + utxo.address = ref.GenerateAddress(net_type); + utxo.address_type = AddressType::kP2wpkhAddress; + auto txid_bytes = HashUtil::Sha256(wallet.GetSeed().GetHex() + "/" + path); + utxo.txid = Txid(ByteData256(txid_bytes)); + utxo.vout = index; + list.emplace_back(utxo); + if (privkey_list != nullptr) { + privkey_list->push_back(key.GetPrivkey()); + } + } + return list; +} + + +TEST(Psbt, UsecaseTest2) { + static const std::string seed1 = "8bc106907003ea0b55f3ed4ce2fcf9a198d8c43f07e6ade8aacc5c20c33db12e"; + static const std::string seed2 = "d3e3539eafb6af1f0ae374ecffd33bed394f5eb2e39f8957be63c258ac32ca97"; + + NetType net_type = NetType::kRegtest; + HDWallet wallet1 = HDWallet(ByteData(g_psbt_seed1)); + HDWallet wallet2 = HDWallet(ByteData(g_psbt_seed2)); + std::string path1 = "44h/0h/0h/1/100"; + auto change_key1 = wallet1.GeneratePubkeyData(net_type, path1); + std::string change_descriptor1 = "wpkh(" + change_key1.ToString() + ")"; + Descriptor change_desc1 = Descriptor::Parse(change_descriptor1); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c)", change_descriptor1.c_str()); + + std::vector privkey_list1; + auto utxo_list1 = CfdTestGenerateUtxo( + net_type, wallet1, "44h/0h/0h/1", Amount(10000000), 1, 11, &privkey_list1); + std::vector privkey_list2; + auto utxo_list2 = CfdTestGenerateUtxo( + net_type, wallet2, "44h/0h/0h/1", Amount(50000000), 1, 3, &privkey_list2); + + Psbt psbt; + std::string out_path1 = "44h/0h/0h/0/2"; + std::string out_path2 = "44h/0h/0h/0/2"; + auto out_key1 = wallet1.GeneratePubkeyData(net_type, out_path1); + auto out_key2 = wallet2.GeneratePubkeyData(net_type, out_path2); + auto out_addr1 = Address(NetType::kTestnet, WitnessVersion::kVersion0, out_key1.GetPubkey()); + auto out_addr2 = Address(NetType::kTestnet, WitnessVersion::kVersion0, out_key2.GetPubkey()); + + // Creator + // add txout (0.5btc * 2) + Amount amount(50000000); + try { + psbt.AddTxOutData(amount, out_addr1, out_key1); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + try { + psbt.AddTxOutData(amount, out_addr2, out_key2); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + + // Updater + // add txin + Psbt psbt1; + Psbt psbt2; + uint32_t use_utxo1_count = 5; + try { + psbt1 = psbt; + for (uint32_t index=0; index selected_utxos; + std::vector target_privkey_list1; + try { + Amount fee; + std::vector selection_utxo; + std::copy(utxo_list1.begin() + use_utxo1_count + 1, utxo_list1.end(), + std::back_inserter(selection_utxo)); + CoinSelectionOption option; + option.SetEffectiveFeeBaserate(2.0); + option.SetKnapsackMinimumChange(0); + EXPECT_EQ(5, selection_utxo.size()); + EXPECT_EQ(10000000, selection_utxo[0].amount.GetSatoshiValue()); + EXPECT_EQ(6, psbt.GetTxInCount()); + + EXPECT_STREQ("8b3a1f738009f99fd8b01e1ad95c5468e8fb25d5373df19697d78eec957c423c", selection_utxo[0].txid.GetHex().c_str()); + EXPECT_EQ(7, selection_utxo[0].vout); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe)", selection_utxo[0].descriptor.c_str()); + EXPECT_STREQ("0e5b0e16148da878ee8d9f0524ac0962a22dbbb66e2dc8a6237a1d4c8c4ea9cb", selection_utxo[1].txid.GetHex().c_str()); + EXPECT_EQ(8, selection_utxo[1].vout); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/8]02f505e0b5885959bd2f7e68dd73915940a5540e05c41a42f6c598ceb21bdc55f3)", selection_utxo[1].descriptor.c_str()); + EXPECT_STREQ("016b36a0a9df54d55f7b907aba8fdd3d90d797eee2bc46c5ed7dced41218e674", selection_utxo[2].txid.GetHex().c_str()); + EXPECT_EQ(9, selection_utxo[2].vout); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/9]02f7af7e1c3c8627e0e662ba04ec003879d9aeaeba5a02a1e24804ef4d7b497db4)", selection_utxo[2].descriptor.c_str()); + EXPECT_STREQ("dcc65ebfb8535f96f8a51e07d5c0a94f965000bd5765159a3cc27e4edd658c69", selection_utxo[3].txid.GetHex().c_str()); + EXPECT_EQ(10, selection_utxo[3].vout); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/10]03c04e76f9bc7d6433a6eb7c0c3c446213c5f361dae22929483f23718e1cee4ece)", selection_utxo[3].descriptor.c_str()); + EXPECT_STREQ("0230e7471501f9432f727583f2bc17c11f5c1132140319ba2d7a5a0145684c73", selection_utxo[4].txid.GetHex().c_str()); + EXPECT_EQ(11, selection_utxo[4].vout); + EXPECT_STREQ("wpkh([2a704760/44'/0'/0'/1/11]03798ff13e5d17c6f8c1d69ff18d516613be2fa52149b3a8c60ef959d1075a5889)", selection_utxo[4].descriptor.c_str()); + + selected_utxos = psbt.FundTransaction(selection_utxo, 2.0, + &change_desc1, &fee, &option); + EXPECT_EQ(fee.GetSatoshiValue(), 1166); + + for (uint32_t index=0; index{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()); + + EXPECT_STREQ("[2a704760/44'/0'/0'/0/12]02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b", key1.ToString().c_str()); + EXPECT_STREQ("[9d6b6d86/44'/0'/0'/0/12]02622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242", key2.ToString().c_str()); + EXPECT_STREQ("ac9aa506601b23ea7362dfcb9350ff3c2edeb67b334258d6489889e67dfa417f", key_in1.GetPrivkey().GetHex().c_str()); + EXPECT_STREQ("973e1d45dca188b0c0ac67bcc5de3ccdbe479d7505bdc43c57231c75f8db55e3", key_in2.GetPrivkey().GetHex().c_str()); + EXPECT_STREQ("[2a704760/44'/0'/0'/0/11]03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3", key_in1.ToString().c_str()); + EXPECT_STREQ("[9d6b6d86/44'/0'/0'/0/11]03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47", key_in2.ToString().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()); + EXPECT_STREQ("70736274ff0100330200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff0000000000000101200858cd1d0000000017a914945fb50391a70637c1ffc5ab7fb65308c2f23175870103040100000001042200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9010547522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae00", psbt.GetData().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); + } 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); + } 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()); + } + } 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; + } +} + + +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()); + } 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()); + } + } + } +} + +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, 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, 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 { + EXPECT_STREQ("KwNwembMPPQpgFfbhb5WPCgENwhnTaNQjf3a6cBuBiZ993Gu5gaR", key_in1.GetPrivkey().GetWif().c_str()); + psbt1 = psbt; + psbt1.Sign(key_in1.GetPrivkey()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", psbt1.GetBase64().c_str()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + try { + EXPECT_STREQ("KyZrkrgAu5EhBbSbt6yMkuL5BexVtdTifCkZQL3HLBZ9wAEhhhMa", key_in2.GetPrivkey().GetWif().c_str()); + psbt2 = psbt; + psbt2.Sign(key_in2.GetPrivkey()); + EXPECT_STREQ("cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiAgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jx0cwRAIgZuyVbpZ4PNYSLJwHcyo66ZMbaava/x82LCjtX6lnKNMCIGoW1K5G/tSNPQSSc3YTvZtR/j4kU7REbkR8Zb8sln4QASIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", psbt2.GetBase64().c_str()); + } 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); + } 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); + } 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()); + } + } 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()); + } +} + +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()); +} + +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()); +} + +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); + + 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()); +} + +TEST(Psbt, GetUtxoDataList) { + Psbt psbt("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"); + auto utxos = psbt.GetUtxoDataAll(NetType::kTestnet); + EXPECT_EQ(2, utxos.size()); + if (utxos.size() == 2) { + EXPECT_EQ("c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", utxos[0].txid.GetHex()); + EXPECT_EQ(1, utxos[0].vout); + EXPECT_EQ("2MzbWwSa1GsmzGLhYbsYRACNhgx4RjqGtTi", utxos[0].address.GetAddress()); + EXPECT_EQ(AddressType::kP2shP2wpkhAddress, utxos[0].address_type); + EXPECT_EQ(100000000, utxos[0].amount.GetSatoshiValue()); + EXPECT_EQ("sh(wpkh([2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe))", utxos[0].descriptor); + + EXPECT_EQ("c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", utxos[1].txid.GetHex()); + EXPECT_EQ(0, utxos[1].vout); + EXPECT_EQ("mtPAE24cZ3k6NMCcxQJUbSpZnSVaCu96Wj", utxos[1].address.GetAddress()); + EXPECT_EQ(AddressType::kP2pkhAddress, utxos[1].address_type); + EXPECT_EQ(499995000, utxos[1].amount.GetSatoshiValue()); + EXPECT_EQ("pkh([9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7)", utxos[1].descriptor); + } +} \ No newline at end of file diff --git a/test/test_cfd_transaction_context.cpp b/test/test_cfd_transaction_context.cpp index 68f26fee..8c67082d 100644 --- a/test/test_cfd_transaction_context.cpp +++ b/test/test_cfd_transaction_context.cpp @@ -7,6 +7,8 @@ #include "cfdcore/cfdcore_coin.h" #include "cfdcore/cfdcore_exception.h" #include "cfdcore/cfdcore_key.h" +#include "cfdcore/cfdcore_schnorrsig.h" +#include "cfdcore/cfdcore_taproot.h" #include "cfdcore/cfdcore_transaction.h" #include "cfdcore/cfdcore_transaction_common.h" #include "cfd/cfd_address.h" @@ -28,6 +30,9 @@ using cfd::core::NetType; using cfd::core::OutPoint; 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; @@ -35,6 +40,9 @@ using cfd::core::ScriptUtil; using cfd::core::SigHashAlgorithm; using cfd::core::SigHashType; using cfd::core::SignatureUtil; +using cfd::core::TapScriptData; +using cfd::core::TaprootScriptTree; +using cfd::core::TaprootUtil; using cfd::core::Transaction; using cfd::core::Txid; using cfd::core::WitnessVersion; @@ -284,8 +292,8 @@ TEST(TransactionContext, Input_Output) { EXPECT_EQ(ctx.GetTxInCount(), 4); EXPECT_EQ(ctx.GetTxOutCount(), 2); - EXPECT_EQ(ctx.GetSizeIgnoreTxIn(), 73); - EXPECT_EQ(ctx.GetVsizeIgnoreTxIn(), 73); + EXPECT_EQ(ctx.GetSizeIgnoreTxIn(), 75); + EXPECT_EQ(ctx.GetVsizeIgnoreTxIn(), 75); if (ctx.GetTxInCount() == 4) { auto ref_list = ctx.GetTxInList(); EXPECT_STREQ(ref_list[0].GetTxid().GetHex().c_str(), utxo1.txid.GetHex().c_str()); @@ -703,3 +711,206 @@ TEST(TransactionContext, VerifyP2shSegwit) OutPoint outpoint1(utxo2.txid, utxo2.vout); EXPECT_NO_THROW(txc.Verify(outpoint1)); } + +TEST(TransactionContext, TaprootSign) +{ + 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); + + AddressFactory addr_factory(NetType::kRegtest); + auto txout1_addr = addr_factory.CreateTaprootAddress(schnorr_pubkey); + + TransactionContext tx1(2, 0); + OutPoint outpoint1( + Txid("1f9866dc0a19c427347c2db0b5910bdc2c20b78fa9f74f8756b21db890dba8ff"), 0); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + + tx1.AddTxIn(outpoint1); + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(txout1_addr, amt1); + EXPECT_EQ("0200000001ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", tx1.GetHex()); + SigHashType sighash_all; + tx1.SignWithPrivkeySimple(outpoint1, key1.GetPubkey(), key1, sighash_all, + Amount(int64_t{2500000000}), AddressType::kP2wpkhAddress); + EXPECT_EQ("02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + UtxoData utxo; + utxo.block_height = 0; + utxo.txid = tx1.GetTxid(); + utxo.vout = 0; + utxo.locking_script = txout1_addr.GetLockingScript(); + utxo.descriptor = "raw(" + utxo.locking_script.GetHex() + ")"; + utxo.address = txout1_addr; + utxo.amount = amt1; + utxo.address_type = txout1_addr.GetAddressType(); + utxo.binary_data = nullptr; + + OutPoint outpoint2(tx1.GetTxid(), 0); + TransactionContext tx2(2, 0); + tx2.AddInput(utxo); + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(addr2, Amount(int64_t{2499998000})); + + auto sighash = tx2.CreateSignatureHashByTaproot(outpoint2, sighash_all); + EXPECT_EQ("e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02", sighash.GetHex()); + + tx2.SignWithKey(outpoint2, Pubkey(), key, sighash_all); + EXPECT_EQ("0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000", tx2.GetHex()); + + try { + tx2.Verify(); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } + EXPECT_TRUE(tx2.VerifyInputSchnorrSignature( + SchnorrSignature("61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f2087201"), + outpoint2, {utxo}, schnorr_pubkey)); + EXPECT_FALSE(tx2.VerifyInputSchnorrSignature( + SchnorrSignature("61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f2087301"), + outpoint2, {utxo}, schnorr_pubkey)); +} + +TEST(TransactionContext, TaprootSignWithNonce) +{ + ByteData256 nonce("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"); + + 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); + + AddressFactory addr_factory(NetType::kRegtest); + auto txout1_addr = addr_factory.CreateTaprootAddress(schnorr_pubkey); + + TransactionContext tx1(2, 0); + OutPoint outpoint1( + Txid("1f9866dc0a19c427347c2db0b5910bdc2c20b78fa9f74f8756b21db890dba8ff"), 0); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + + tx1.AddTxIn(outpoint1); + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(txout1_addr, amt1); + EXPECT_EQ("0200000001ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", tx1.GetHex()); + SigHashType sighash_all; + tx1.SignWithPrivkeySimple(outpoint1, key1.GetPubkey(), key1, sighash_all, + Amount(int64_t{2500000000}), AddressType::kP2wpkhAddress); + EXPECT_EQ("02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + UtxoData utxo; + utxo.block_height = 0; + utxo.txid = tx1.GetTxid(); + utxo.vout = 0; + utxo.locking_script = txout1_addr.GetLockingScript(); + utxo.descriptor = "raw(" + utxo.locking_script.GetHex() + ")"; + utxo.address = txout1_addr; + utxo.amount = amt1; + utxo.address_type = txout1_addr.GetAddressType(); + utxo.binary_data = nullptr; + + OutPoint outpoint2(tx1.GetTxid(), 0); + TransactionContext tx2(2, 0); + tx2.AddInput(utxo); + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(addr2, Amount(int64_t{2499998000})); + tx2.SignWithKey(outpoint2, Pubkey(), key,sighash_all, true, &nonce); + EXPECT_EQ("0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014151df55894d1a024c244e20ecedc39cae39fa6d43653305b7f32605eea6359415a7ceef44c52a2f26be2e06d33d79c2e90b5dfaebcb4f79e242134121e0b9579e0100000000", tx2.GetHex()); + + try { + tx2.Verify(); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + } +} + +TEST(TransactionContext, TapScriptSign) +{ + 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("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54") + }; + TaprootScriptTree tree(redeem_script); + for (const auto& node : nodes) tree.AddBranch(node); + EXPECT_EQ("dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", tree.GetTapLeafHash().GetHex()); + + AddressFactory addr_factory(NetType::kRegtest); + auto txout1_addr = addr_factory.CreateTaprootAddress(tree, schnorr_pubkey); + + TransactionContext tx1(2, 0); + OutPoint outpoint1( + Txid("cd6adc252632eb0768ac6407e586cc74bfed739d6c8b9efa55305eb37cbd76dd"), 0); + Privkey key1 = Privkey::FromWif("cNveTchXQTFjtsMmR7B7MZmebXnU69S7PmDfgrUX6KbT9kyDLH57"); + + tx1.AddTxIn(outpoint1); + Amount amt1(int64_t{2499999000}); + tx1.AddTxOut(txout1_addr, amt1); + EXPECT_EQ("0200000001dd76bd7cb35e3055fa9e8b6c9d73edbf74cc86e50764ac6807eb322625dc6acd0000000000ffffffff0118f50295000000002251203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d200000000", tx1.GetHex()); + SigHashType sighash_all; + tx1.SignWithPrivkeySimple(outpoint1, key1.GetPubkey(), key1, sighash_all, + Amount(int64_t{2500000000}), AddressType::kP2wpkhAddress); + EXPECT_EQ("02000000000101dd76bd7cb35e3055fa9e8b6c9d73edbf74cc86e50764ac6807eb322625dc6acd0000000000ffffffff0118f50295000000002251203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d20247304402201db912bc61dab1c6117b0aec2965ea1b2d1caa42a1372adc16c8cf673f1187d7022062667d8a976b197f7ba33299365eeb68c1e45fa2a255411672d89f7afab12cb20121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", tx1.GetHex()); + + UtxoData utxo; + utxo.block_height = 0; + utxo.txid = tx1.GetTxid(); + utxo.vout = 0; + utxo.locking_script = txout1_addr.GetLockingScript(); + utxo.descriptor = "raw(" + utxo.locking_script.GetHex() + ")"; + utxo.address = txout1_addr; + utxo.amount = amt1; + utxo.address_type = txout1_addr.GetAddressType(); + utxo.binary_data = nullptr; + + OutPoint outpoint2(tx1.GetTxid(), 0); + TransactionContext tx2(2, 0); + tx2.AddInput(utxo); + Address addr2("bcrt1qze8fshg0eykfy7nxcr96778xagufv2w429wx40"); + tx2.AddTxOut(addr2, Amount(int64_t{2499998000})); + EXPECT_EQ("02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", tx2.GetHex()); + + ByteData256 tap_leaf_hash = tree.GetTapLeafHash(); + EXPECT_EQ("dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", tap_leaf_hash.GetHex()); + auto sighash = tx2.CreateSignatureHashByTaproot(outpoint2, sighash_all, &tap_leaf_hash); + EXPECT_EQ("80e53eaee13048aee9c6c13fa5a8529aad7fe2c362bfc16f1e2affc71f591d36", sighash.GetHex()); + + auto sig = SchnorrUtil::Sign(sighash, key); + EXPECT_EQ("f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee9", sig.GetHex()); + sig.SetSigHashType(sighash_all); + SignParameter sign_param(sig.GetData(true)); + tx2.AddTapScriptSign(outpoint2, tree, schnorr_pubkey, {sign_param}); + + EXPECT_EQ("020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000", tx2.GetHex()); + + try { + tx2.Verify(); + } catch (const CfdException& except) { + EXPECT_STREQ("The script analysis of tapscript is not supported.", except.what()); + } + + TransactionContext invalid_sign_tx("020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d3d5400000000"); + invalid_sign_tx.CollectInputUtxo({utxo}); + try { + invalid_sign_tx.Verify(); + } catch (const CfdException& except) { + EXPECT_STREQ("Unmatch locking script.", except.what()); + } +} diff --git a/test/test_cfdapi_address.cpp b/test/test_cfdapi_address.cpp index 74e8a935..293327b1 100644 --- a/test/test_cfdapi_address.cpp +++ b/test/test_cfdapi_address.cpp @@ -16,7 +16,7 @@ #include "cfdcore/cfdcore_hdwallet.h" using cfd::api::AddressApi; -using cfd::api::DescriptorScriptData; +using cfd::DescriptorScriptData; using cfd::core::Address; using cfd::core::AddressType; using cfd::core::DescriptorKeyType; diff --git a/test/test_cfdapi_elements_transaction.cpp b/test/test_cfdapi_elements_transaction.cpp index bd1a4015..6c501090 100644 --- a/test/test_cfdapi_elements_transaction.cpp +++ b/test/test_cfdapi_elements_transaction.cpp @@ -143,9 +143,9 @@ TEST(ElementsTransactionApi, EstimateFee_CheckRealValue) ElementsTransactionApi api; calc_fee = api.EstimateFee(txc.GetHex(), utxos_and_options, utxo1.asset, &tx_fee, &utxo_fee, true, effective_fee_rate); - EXPECT_EQ(calc_fee.GetSatoshiValue(), 2768); + EXPECT_EQ(calc_fee.GetSatoshiValue(), 2770); EXPECT_EQ(tx_fee.GetSatoshiValue(), 2458); - EXPECT_EQ(utxo_fee.GetSatoshiValue(), 310); + EXPECT_EQ(utxo_fee.GetSatoshiValue(), 312); std::vector ct_addrs; ct_addrs.push_back(asset_address); @@ -184,7 +184,8 @@ TEST(ElementsTransactionApi, EstimateFee_CheckRealValue) EXPECT_NO_THROW(tx = txc.Finalize()); - if ((tx.GetDataSize() != 9400) && (tx.GetDataSize() != 9401) && (tx.GetDataSize() != 9402)) { + if ((tx.GetDataSize() != 9399) && (tx.GetDataSize() != 9400) && + (tx.GetDataSize() != 9401) && (tx.GetDataSize() != 9402)) { EXPECT_EQ(tx.GetDataSize(), 0); } @@ -217,7 +218,7 @@ TEST(ElementsTransactionApi, EstimateFee_MinimumValue) // utxo1.redeem_script = Script(""); utxo1.address = factory.GetAddress("ert1qav7q64dhpx9y4m62rrhpa67trmvjf2ptxfddld"); utxo1.descriptor = "wpkh(03f942716865bb9b62678d99aa34de4632249d066d99de2b5a2e542e54908450d6)"; - utxo1.amount = Amount(int64_t{2000000000}); + utxo1.amount = Amount(int64_t{100000200}); utxo1.address_type = AddressType::kP2wpkhAddress; utxo1.asset = ConfidentialAssetId("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"); utxo1.asset_blind_factor = BlindFactor("ebfecaae1665f32a3843ce65c42fb6e3f51136fa9d37274b810887923ae89339"); @@ -379,7 +380,7 @@ TEST(ElementsTransactionApi, EstimateFee_LargeAmount_MinBits36) &tx_fee, &utxo_fee, true, effective_fee_rate, 0, 36); EXPECT_EQ(calc_fee.GetSatoshiValue(), 256); EXPECT_EQ(tx_fee.GetSatoshiValue(), 225); - EXPECT_EQ(utxo_fee.GetSatoshiValue(), 31); + EXPECT_EQ(utxo_fee.GetSatoshiValue(), 32); std::vector ct_addrs; ct_addrs.push_back(asset_address); @@ -430,7 +431,8 @@ TEST(ElementsTransactionApi, EstimateFee_LargeAmount_MinBits36) uint32_t minimum_fee = txc.GetVsize() * static_cast(effective_fee_rate2) / 1000; EXPECT_EQ(minimum_fee, 255); if ((calc_fee.GetSatoshiValue() != minimum_fee) && - (calc_fee.GetSatoshiValue() != (minimum_fee + 1))) { + (calc_fee.GetSatoshiValue() != (minimum_fee + 1)) && + (calc_fee.GetSatoshiValue() != (minimum_fee + 2))) { EXPECT_EQ(calc_fee.GetSatoshiValue(), minimum_fee); } // EXPECT_STREQ(tx.GetHex().c_str(), ""); @@ -537,7 +539,7 @@ static const std::vector kFundCoinSelectElementsTest TEST(ElementsTransactionApi, FundRawTransaction_Reissueasset) { - static const char* const kExpTxData = "0200000000030f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c34600000bfa8774c5f753ce2f801a8106413b470af94edbff5b4242ed4c5a26d20e72b90000000000ffffffff040b0000000000000000000000000000000000000000000000000000000000000000000000ffffffff07010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0100000000000000000000000000000000000000000000000000000000000000aa01000000000000037300000100000000000000000000000000000000000000000000000000000000000000bb010000000001124c1e00160014a53be40113bb50f2b8b2d0bfea1e823e75632b5f0100000000000000000000000000000000000000000000000000000000000000aa0100000000004b57eb0016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000"; + static const char* const kExpTxData = "0200000000030f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c34600000bfa8774c5f753ce2f801a8106413b470af94edbff5b4242ed4c5a26d20e72b90000000000ffffffff040b0000000000000000000000000000000000000000000000000000000000000000000000ffffffff07010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0100000000000000000000000000000000000000000000000000000000000000aa01000000000000037200000100000000000000000000000000000000000000000000000000000000000000bb010000000001124c1e00160014a53be40113bb50f2b8b2d0bfea1e823e75632b5f0100000000000000000000000000000000000000000000000000000000000000aa0100000000004b57ec0016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000"; ElementsUtxoAndOption input_utxo; ConfidentialTransactionContext tx(uint32_t{2}, uint32_t{0}); @@ -625,7 +627,7 @@ TEST(ElementsTransactionApi, FundRawTransaction_Reissueasset) { &filter, &option, &append_txout_addresses, NetType::kElementsRegtest); EXPECT_STREQ(kExpTxData, ctx.GetHex().c_str()); - EXPECT_EQ(883, estimate_fee.GetSatoshiValue()); + EXPECT_EQ(882, estimate_fee.GetSatoshiValue()); // EXPECT_EQ(657, estimate_fee.GetSatoshiValue()); EXPECT_EQ(size_t{2}, append_txout_addresses.size()); if (append_txout_addresses.size() == size_t{2}) @@ -1237,4 +1239,92 @@ TEST(ElementsTransactionApi, FundRawTransaction_AssetLimitAmountValue) { EXPECT_EQ(minimum_fee, 83); } + +TEST(ElementsTransactionApi, FundRawTransaction_prod) { + cfd::Initialize(); + ElementsAddressFactory factory(NetType::kLiquidV1); + ConfidentialAssetId asset1("6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d"); + ConfidentialAssetId asset2("f59c5f3e8141f322276daa63ed5f307085808aea6d4ef9ba61e28154533fdec7"); + // Address1 + UtxoData utxo1; + utxo1.block_height = 0; + utxo1.binary_data = nullptr; + utxo1.txid = Txid("cd4433fd3d014187050aefe878f76ab76aa8b1eef3ba965cb8dc76b0d271d002"); + utxo1.vout = 2; + utxo1.locking_script = Script("0014860d7bed1010c2ccb5247889daa5c58023fa9f8d"); + // utxo1.redeem_script = Script(""); + utxo1.address = factory.GetAddress("ex1qscxhhmgszrpvedfy0zya4fw9sq3l48udevqlh2"); + utxo1.descriptor = "wpkh(02db28ad892aa1e500d1e88ffa24200088bc82a8a87807cd13a1d1a1c7799c41e5)"; + utxo1.amount = Amount(int64_t{999799}); + utxo1.address_type = AddressType::kP2wpkhAddress; + utxo1.asset = asset1; + + // Address2 + UtxoData utxo2; + utxo2.block_height = 0; + utxo2.binary_data = nullptr; + utxo2.txid = Txid("62ac8a5272b67aab0ca60db859bed64729463de72ee1743fd7eb9c72f4437b06"); + utxo2.vout = 0; + utxo2.locking_script = Script("0014891fd3ada0038759b124189ed64ab2343f7de0b1"); + // utxo1.redeem_script = Script(""); + utxo2.address = factory.GetAddress("ex1q3y0a8tdqqwr4nvfyrz0dvj4jxslhmc93hzzdt9"); + utxo2.descriptor = "wpkh(023004f49c61a63e339fa554e218774d6b4752bbfcfca61fe1c567b96af196b524)"; + utxo2.amount = Amount(int64_t{19999}); + utxo2.address_type = AddressType::kP2wpkhAddress; + utxo2.asset = asset2; + + // Address3 + UtxoData utxo3; + utxo3.block_height = 0; + utxo3.binary_data = nullptr; + utxo3.txid = Txid("376906dfec46e2abc2c98cc4a51d725cb8bbc5ece38ae1127c90802823202988"); + utxo3.vout = 2; + utxo3.locking_script = Script("001495f728a019f3ecc3725479b32b230de51df74d4a"); + // utxo1.redeem_script = Script(""); + utxo3.address = factory.GetAddress("ex1qjhmj3gqe70kvxuj50xejkgcdu5wlwn225d0cu6"); + utxo3.descriptor = "wpkh(02272bcdbec1c0a2170e72f2d8cf42188d59ea7eae9296d60d435af3fe465d9bb5)"; + utxo3.amount = Amount(int64_t{1}); + utxo3.address_type = AddressType::kP2wpkhAddress; + utxo3.asset = asset2; + + double fee_rate = 0.11; + ConfidentialAssetId fee_asset = asset1; + std::map map_target_value; + map_target_value.emplace(asset1.GetHex(), Amount()); + map_target_value.emplace(asset2.GetHex(), Amount()); + std::map reserve_txout_address; + reserve_txout_address.emplace(asset1.GetHex(), "lq1qqgv5wwfp4h0pfnyy2kkxl0kg3qnahcpfq7emrxu9xusz879axq0spg9cxu8wf72ktsft5r8vxnkfd8s5kmg32fvy8texp5p6s"); + reserve_txout_address.emplace(asset2.GetHex(), "lq1qqwqawne0jyc2swqv9qp8fstrgxuux2824zxkqew9gdak4yudxvwhha0kwdv2p3j0lyekhchrzmuekp94fpfp6fkeggjkerfr8"); + std::vector selected_txin_utxos; + Amount estimate_fee; + UtxoFilter filter; + std::vector append_txout_addresses; + CoinSelectionOption option; + option.InitializeConfidentialTxSizeInfo(); + option.SetEffectiveFeeBaserate(fee_rate); + option.SetLongTermFeeBaserate(fee_rate); + option.SetFeeAsset(fee_asset); + option.SetBlindInfo(0, 52); + option.SetKnapsackMinimumChange(0); + + ConfidentialTransactionContext txc("0200000000000101c7de3f535481e261baf94e6dea8a808570305fed63aa6d2722f341813e5f9cf5010000000000004e20021f70a7514c145cfb01c8751b92ac5b3518d585afb256dae9bc4be464bf546ac616001429aa90ad4af8ef2859d0573e69cf567957ea330000000000"); + std::vector utxos{utxo1, utxo2, utxo3}; + + try { + ElementsTransactionApi api; + ConfidentialTransactionController ctx = api.FundRawTransaction( + txc.GetHex(), utxos, map_target_value, selected_txin_utxos, + reserve_txout_address, fee_asset, true, fee_rate, &estimate_fee, + &filter, &option, &append_txout_addresses, NetType::kLiquidV1); + txc = ConfidentialTransactionContext(ctx.GetHex()); + } catch (const CfdException& except) { + EXPECT_STREQ("", except.what()); + throw except; + } + + EXPECT_EQ(txc.GetFeeAmount().GetSatoshiValue(), estimate_fee.GetSatoshiValue()); + EXPECT_STREQ(txc.GetHex().c_str(), "02000000000302d071d2b076dcb85c96baf3eeb1a86ab76af778e8ef0a058741013dfd3344cd0200000000ffffffff067b43f4729cebd73f74e12ee73d462947d6be59b80da60cab7ab672528aac620000000000ffffffff882920232880907c12e18ae3ecc5bbb85c721da5c48cc9c2abe246ecdf0669370200000000ffffffff0301c7de3f535481e261baf94e6dea8a808570305fed63aa6d2722f341813e5f9cf5010000000000004e20021f70a7514c145cfb01c8751b92ac5b3518d585afb256dae9bc4be464bf546ac616001429aa90ad4af8ef2859d0573e69cf567957ea3300016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f01000000000000012d0000016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100000000000f404a0219473921adde14cc8455ac6fbec88827dbe02907b3b19b85372023f8bd301f00160014a0b8370ee4f9565c12ba0cec34ec969e14b6d11500000000"); + EXPECT_EQ(txc.GetVsize(), 374); +} + #endif // CFD_DISABLE_ELEMENTS diff --git a/test/test_cfdapi_transaction.cpp b/test/test_cfdapi_transaction.cpp index 96400548..97c78c77 100644 --- a/test/test_cfdapi_transaction.cpp +++ b/test/test_cfdapi_transaction.cpp @@ -105,9 +105,9 @@ TEST(TransactionApi, EstimateFee_CheckRealValue) { TransactionApi api; calc_fee = api.EstimateFee( txc.GetHex(), utxos, &tx_fee, &utxo_fee, effective_fee_rate); - EXPECT_EQ(calc_fee.GetSatoshiValue(), 7660); - EXPECT_EQ(tx_fee.GetSatoshiValue(), 1500); - EXPECT_EQ(utxo_fee.GetSatoshiValue(), 6160); + EXPECT_EQ(calc_fee.GetSatoshiValue(), 7740); + EXPECT_EQ(tx_fee.GetSatoshiValue(), 1540); + EXPECT_EQ(utxo_fee.GetSatoshiValue(), 6200); ByteData tx; @@ -140,6 +140,38 @@ TEST(TransactionApi, EstimateFee_CheckRealValue) { EXPECT_EQ(minimum_fee, 7640); } +TEST(TransactionApi, EstimateFee_TaprootSchnorr) { + std::string tx_hex = "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50000000000"; + AddressFactory factory(NetType::kRegtest); + // Address1 + UtxoData utxo1; + utxo1.block_height = 0; + utxo1.binary_data = nullptr; + utxo1.txid = Txid("2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916"); + utxo1.vout = 0; + utxo1.locking_script = Script("51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb"); + // utxo1.redeem_script = Script(""); + utxo1.address = factory.GetAddressByLockingScript(utxo1.locking_script); + utxo1.descriptor = "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)"; + utxo1.amount = Amount(int64_t{2499999000}); + utxo1.address_type = AddressType::kTaprootAddress; + + TransactionContext txc(tx_hex); + std::vector utxos{utxo1}; + EXPECT_NO_THROW(txc.CollectInputUtxo(utxos)); + + // check estimateFee + double effective_fee_rate = 2.0; + Amount calc_fee; + Amount utxo_fee; + Amount tx_fee; + TransactionApi api; + calc_fee = api.EstimateFee( + txc.GetHex(), utxos, &tx_fee, &utxo_fee, effective_fee_rate); + EXPECT_EQ(calc_fee.GetSatoshiValue(), 101*2); // upper + EXPECT_EQ(tx_fee.GetSatoshiValue(), 43*2); // upper + EXPECT_EQ(utxo_fee.GetSatoshiValue(), 58*2); +} TEST(TransactionApi, FundRawTransaction_MillionAmountValue) { AddressFactory factory(NetType::kRegtest); @@ -212,7 +244,7 @@ TEST(TransactionApi, FundRawTransaction_MillionAmountValue) { auto tx_obj = api.FundRawTransaction( txc.GetHex(), utxos, target_value, selected_txin_utxos, address3.GetAddress(), effective_fee_rate, &estimate_fee, nullptr, &option, nullptr, NetType::kRegtest); - EXPECT_EQ(estimate_fee.GetSatoshiValue(), 8280); + EXPECT_EQ(estimate_fee.GetSatoshiValue(), 8360); txc = TransactionContext(tx_obj.GetHex()); EXPECT_NO_THROW(txc.CollectInputUtxo(utxos)); @@ -241,7 +273,7 @@ TEST(TransactionApi, FundRawTransaction_MillionAmountValue) { EXPECT_NO_THROW(tx = txc.Finalize()); EXPECT_EQ(txc.GetFeeAmount().GetSatoshiValue(), estimate_fee.GetSatoshiValue()); - EXPECT_STREQ(tx.GetHex().c_str(), "02000000000103e3b9639791a30193e253c431ed93e3ca8a77334da50cccb252fd19b692915531000000001716001445663e592ff613587f0fdd6e74034c5239710dcaffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a0100000000ffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a000000006a473044022059cdb9324ed00b687a40d79b571b6fdd6a6722f1c06990cb59d7f6285c3fe10a022019c6a8effa8d2424c7776360dbebb7bf4109dc8ae4a1f365096140cfe866d9b001210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567bffffffff03f058dd62b22102001976a9149157a10c10924d7550ee7079cda55db1d11a278a88ac0000e941cc6b010016001445663e592ff613587f0fdd6e74034c5239710dcab806000000000000160014f330ed8383f8afdc977dd88600eb8ff120ba15e4024730440220472e5bfb0d4d76fbe3a5803582d6255c8d50281c83952db666bf51a52090ddf702202022598b524d76e26dff12a0bb9e5a97548c396675166bb8a6b559768a29ff9501210206d4fabad19c61ffb180fa8a6d0f973e11485e60115557179786f7ea5d806a2702473044022040d76c0f8e164c1a8623e299fc7a04a29138a40557ea07a7f535a14d0c684cda02201d33ee4ae92e673f0b6b5cb55bc5d5eef7b6eca31b1fb636fe92dce50a99daf801210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567b0000000000"); + EXPECT_STREQ(tx.GetHex().c_str(), "02000000000103e3b9639791a30193e253c431ed93e3ca8a77334da50cccb252fd19b692915531000000001716001445663e592ff613587f0fdd6e74034c5239710dcaffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a0100000000ffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a000000006a4730440220644d3014684db32df93e6e342e2d0872aa6272d3a9cdce021138592ef78fc2a8022054a2a85c6339dee9578d9989920100f0660bd45b9cf7b14444f012bf5ee0bd7601210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567bffffffff03f058dd62b22102001976a9149157a10c10924d7550ee7079cda55db1d11a278a88ac0000e941cc6b010016001445663e592ff613587f0fdd6e74034c5239710dca6806000000000000160014f330ed8383f8afdc977dd88600eb8ff120ba15e40247304402202ece1dae47832c07863b5052133c9af652e9521e0fbaf4ef7558ca45ec739ca902205ad29147c0f5f16277a7a00391f8b5d43b9b6c149721cdb4591ba18c701dbd5f01210206d4fabad19c61ffb180fa8a6d0f973e11485e60115557179786f7ea5d806a2702473044022058e88c67379c5b5c49dfef09eb069833ba9ae7cd6f8a5f9bdcf657ed8ad91b820220039739df97dc8488a93f61000cbade6d48df737238519660a02071795aabd74801210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567b0000000000"); EXPECT_EQ(txc.GetVsize(), 413); uint32_t minimum_fee = txc.GetVsize() * static_cast(effective_fee_rate); @@ -320,7 +352,7 @@ TEST(TransactionApi, FundRawTransaction_LimitAmountValue) { auto tx_obj = api.FundRawTransaction( txc.GetHex(), utxos, target_value, selected_txin_utxos, address3.GetAddress(), effective_fee_rate, &estimate_fee, nullptr, &option, nullptr, NetType::kRegtest); - EXPECT_EQ(estimate_fee.GetSatoshiValue(), 8280); + EXPECT_EQ(estimate_fee.GetSatoshiValue(), 8360); txc = TransactionContext(tx_obj.GetHex()); EXPECT_NO_THROW(txc.CollectInputUtxo(utxos)); @@ -349,7 +381,7 @@ TEST(TransactionApi, FundRawTransaction_LimitAmountValue) { EXPECT_NO_THROW(tx = txc.Finalize()); EXPECT_EQ(txc.GetFeeAmount().GetSatoshiValue(), estimate_fee.GetSatoshiValue()); - EXPECT_STREQ(tx.GetHex().c_str(), "02000000000103e3b9639791a30193e253c431ed93e3ca8a77334da50cccb252fd19b692915531000000001716001445663e592ff613587f0fdd6e74034c5239710dcaffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a0100000000ffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a000000006a473044022011d7db13ea4f19e2768dcd172638bc425d7bfb96d0da1b13dda3eccbeb2dca6e02203b0d30ffb300391da30f53f2c4827383b7199e4b2ad4eae7e26120cef2edc63f01210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567bffffffff03f0187a10f35a00001976a9149157a10c10924d7550ee7079cda55db1d11a278a88ac00008d49fd1a070016001445663e592ff613587f0fdd6e74034c5239710dcab806000000000000160014f330ed8383f8afdc977dd88600eb8ff120ba15e40247304402204aa7696f33a2fbf5177d1eea9539aea9b668cb7f71f4cc040f679a1595dd369a02203c3f88e4e18d0514d303a78026c22cfdc9a1c8f62e8a2083ed48d8df9f2fc15a01210206d4fabad19c61ffb180fa8a6d0f973e11485e60115557179786f7ea5d806a27024730440220090a80c1f5a5c0299725342894a972fe1841d5a82c0836bb304d88b59f5ca96d02201399cbb2264b7d00a59c2c9534169688ba8830e69b9bb7a1709bfea50deebc8d01210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567b0000000000"); + EXPECT_STREQ(tx.GetHex().c_str(), "02000000000103e3b9639791a30193e253c431ed93e3ca8a77334da50cccb252fd19b692915531000000001716001445663e592ff613587f0fdd6e74034c5239710dcaffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a0100000000ffffffffa38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a000000006a473044022029f4ce58206fe107b1dd6cbaf3246391a55cdd639cce10fdf545a8c131e17ebf022022b47d656c8666b188c6b16f9e10f6718bbbd420c390fe7a039f9c4323596d1c01210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567bffffffff03f0187a10f35a00001976a9149157a10c10924d7550ee7079cda55db1d11a278a88ac00008d49fd1a070016001445663e592ff613587f0fdd6e74034c5239710dca6806000000000000160014f330ed8383f8afdc977dd88600eb8ff120ba15e40247304402204f83d997acfadec69e45d1719794850ebc3f5bee41057aeefefed8e678e0ed6802206c7653837d7b70343b0dac4f47170b261885c4fcad35b346d0da02d0cc342b7b01210206d4fabad19c61ffb180fa8a6d0f973e11485e60115557179786f7ea5d806a270247304402206089cce85471b83b7afafc4c8363ca10124d831914a27817fa77e27e5a5f5b2e02200cf2c185a1caa93f84ecd25cc4b67cbf04ba3857ca4310688245a53491b9e6f601210359bc91953b251ae501758673b9d6dd78eafa327190741536025d92217a3f567b0000000000"); EXPECT_EQ(txc.GetVsize(), 413); uint32_t minimum_fee = txc.GetVsize() * static_cast(effective_fee_rate); diff --git a/tools/format.bat b/tools/format.bat new file mode 100644 index 00000000..d163dc8a --- /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 include/cfd/*.h src/capi/*.cpp src/capi/*.h include/cfdc/*.h diff --git a/tools/format.sh b/tools/format.sh new file mode 100755 index 00000000..9219e917 --- /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 include/cfd/*.h src/capi/*.cpp src/capi/*.h include/cfdc/*.h diff --git a/tools/generate_json_map_class.ts b/tools/generate_json_map_class.ts new file mode 100644 index 00000000..757271b6 --- /dev/null +++ b/tools/generate_json_map_class.ts @@ -0,0 +1,2176 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable require-jsdoc */ +'use strict'; +import fs from 'fs'; +import path from 'path'; +import {Project} from 'ts-morph'; +// FIXME(k-matsuzawa): Consider splitting the file. + +interface JsonObjectCommonType { + namespace: string | string[]; + commonHeader: string; +} + +interface ClassMapType { + [key: string]: DetailClassParameterType; +} + +interface ClassParameterType { + name: string; + comment: string; +} + +interface CollectMapDataResponse { + type: string; + comment: string; +} + +interface DetailClassParameterType { + data: JsonMappingData; + childList: DetailParameterType[]; + parentList: string[]; +} + +interface DetailParameterType { + param: ParameterType; + data: JsonMappingData; +} + +interface ParameterType { + name: string; + type: string; + comment: string; +} + +interface TsAppendFunctionData { + name: string; + parameters: ParameterType[]; + returnType: string; + comment: string; +} + +interface ReferenceClassInfo { + name: string; + references: Set; + weight: number; +} + +// ---------------------------------------------------------------------------- +// debug log function +// ---------------------------------------------------------------------------- +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let debugLog = function(...args: any | any[]) { + // do nothing + // console.log(...args); +}; + +// eslint-disable-next-line prefer-const +let requireOptionFunc = function(requireInfo: string) { + return requireInfo !== 'optional'; +}; + +// ---------------------------------------------------------------------------- +// json data class +// ---------------------------------------------------------------------------- +class JsonMappingData { + name: string; + methodName: string; + variableName: string; + initValue: string | number | boolean; + className: string; + classComment: string; + childList: {[key: string]: JsonMappingData}; + parent: null; + isOutputStruct: boolean; + isArray: boolean; + isObject: boolean; + isRequire: boolean; + comment: string; + type: string; + structType: string; + constructor(name: string, type: string, initValue: string | number | boolean, + className: string, isOutputStruct = true) { + this.name = name; + this.methodName = (() => { + const replacedMethodName = this.name.replace(/-/gi, '_'); + return replacedMethodName.charAt(0).toUpperCase() + + replacedMethodName.slice(1); + })(); + this.variableName = (() => { + const replacedVariableName = this.name.replace(/-/gi, '_'); + return replacedVariableName.split(/(?=[A-Z])/).join('_').toLowerCase(); + })(); + this.type = type; + this.structType = `${type}Struct`; + this.setType(type); + this.initValue = initValue; + this.className = className; + this.childList = {}; + this.parent = null; + this.isOutputStruct = isOutputStruct; + this.isArray = false; + this.isObject = false; + this.isRequire = false; + this.comment = ''; + this.classComment = ''; + // Reserved word support. + // TODO(k-matsuzawa): If the number increases, make a list. + if (this.variableName == 'asm') this.variableName = `${this.variableName}_`; + } + + setType(type: string) { + this.type = type; + this.structType = `${type}Struct`; + if (type.startsWith('JsonValueVector')) { + const typeName = type.split('<')[1].split('>')[0]; + this.structType = `std::vector<${typeName}>`; + } else if (type.startsWith('JsonObjectVector')) { + const typeName = type.split('<')[1].split(',')[0].split('>')[0]; + this.structType = `std::vector<${typeName}Struct>`; + } + } + + setRequired(requireInfo: string) { + this.isRequire = requireOptionFunc(requireInfo); + } + setComment(comment: string, hint: string) { + if (comment) { + this.comment = comment; + if (hint) { + this.comment = `${comment} (${hint})`; + } + } + } + + setTypeStruct(type: string, structType: any) { + this.type = type; + this.structType = structType; + if (type.startsWith('JsonValueVector')) { + const typeName = type.split('<')[1].split('>')[0]; + this.structType = `std::vector<${typeName}>`; + } else if (type.startsWith('JsonObjectVector')) { + const typeName = type.split('<')[1].split(',')[0].split('>')[0]; + this.structType = `std::vector<${typeName}Struct>`; + } + } + + join(data: JsonMappingData) { + const newList: {[key: string]: JsonMappingData} = {}; + for (const key2 in this.childList) { + if (this.childList[key2]) { + newList[key2] = this.childList[key2]; + } + } + for (const key1 in data.childList) { + if (data.childList[key1]) { + let exist = false; + for (const key2 in this.childList) { + if (key1 == key2) { + exist = true; + break; + } + } + if (exist) { + newList[key1] = data.childList[key1]; + } + } + } + + const obj = new JsonMappingData( + this.name, this.type, this.initValue, this.className); + obj.name = this.name; + obj.methodName = this.methodName; + obj.variableName = this.variableName; + obj.initValue = this.initValue; + obj.className = this.className; + obj.classComment = this.classComment; + obj.childList = newList; + obj.parent = this.parent; + obj.isOutputStruct = this.isOutputStruct; + obj.isArray = this.isArray; + obj.isObject = this.isObject; + obj.isRequire = this.isRequire; + obj.comment = this.comment; + obj.type = this.type; + obj.structType = this.structType; + return obj; + } + + toString() { + const str = `[JsonMappingData] ${this.name}:${this.type}:${this.className}`; + // for debug code. + // for (const key in this.childList) { + // str += "\n - " + // str += this.childList[key].toString() + // } + return str; + } + + collectMapData(map: ClassMapType, list: ClassParameterType[], + isRequest: boolean, parentInfo: JsonMappingData): CollectMapDataResponse { + if (this.type.startsWith('JsonValueVector') || + this.type.startsWith('JsonObjectVector')) { + for (const key in this.childList) { + if (!{}.hasOwnProperty.call(this.childList, key)) continue; + if (this.childList[key]) { + const ret = this.childList[key].collectMapData( + map, list, isRequest, parentInfo); + const comment = ret['comment'] || this.comment; + return { + type: ret['type'] + '[]', + comment, + }; + } + break; + } + throw Error('Illegal state.'); + } else if (this.type === 'ErrorResponseBase') { + const clsName = 'ErrorResponse'; + const props: DetailParameterType[] = []; + for (const key in this.childList) { + if (this.childList[key]) { + const name = this.childList[key].name + (this.childList[key].isRequire ? '' : '?'); + const ret = this.childList[key].collectMapData( + map, list, isRequest, parentInfo); + const type = ret['type']; + const comment = ret['comment']; + if (name === 'isOutputStruct') { + continue; + } + props.push({ + param: {name: name, type: type, comment}, + data: this.childList[key], + }); + } + } + map[clsName] = {data: this, childList: props, parentList: []}; + list.push({name: clsName, comment: this.classComment}); + return { + type: clsName, + comment: this.classComment, + }; + } else if (Object.keys(this.childList).length > 0) { + // my class name + const props: DetailParameterType[] = []; + for (const key in this.childList) { + if (this.childList[key]) { + let name = this.childList[key].name + (this.childList[key].isRequire ? '' : '?'); + const ret = this.childList[key].collectMapData( + map, list, isRequest, this); + debugLog('prop : ', ret); + const type = ret['type']; + const comment = ret['comment']; + if (name.indexOf('-') > 0) { + name = '\'' + name + '\''; + } + props.push({ + param: {name: name, type: type, comment}, + data: this.childList[key], + }); + } + } + debugLog(`type = ${this.type}, comment = ${this.comment}`); + debugLog(`class = ${this.className}, clsComment = ${this.classComment}`); + debugLog('props = ', props); + if (map[this.type]) { + // property check + const appendProps = []; + const existDataProps = map[this.type].childList; + const removeProps: string[] = []; + for (const newProp of props) { + let exist = false; + const srcName = newProp.param.name.replace('?', ''); + for (const prop of existDataProps) { + const dstName = prop.param.name.replace('?', ''); + if (newProp.param.name == prop.param.name) { + if (newProp.data.isRequire != prop.data.isRequire) { + throw new Error(`unmatch require. caller:${this.type}, name=${prop.param.name} type=${prop.param.type},${newProp.param.type}`); + } + if (newProp.param.type != prop.param.type) { + if ((newProp.param.type.indexOf('bigint') >= 0) && + (prop.param.type.indexOf('bigint') >= 0)) { + if (newProp.param.type == 'bigint') { + // removeProps.push(prop.param.name); + exist = true; + } + break; + } + throw new Error(`unmatch type. caller:${this.type}, name=${prop.param.name} type=${prop.param.type},${newProp.param.type}`); + } + exist = true; + break; + } else if (newProp.param.type == prop.param.type) { + if (srcName == dstName) { + throw new Error(`unmatch option. caller:${this.type}, name=${prop.param.name} type=${prop.param.type},${newProp.param.type}`); + } + } else if (srcName == dstName) { + throw new Error(`unmatch option. caller:${this.type}, name=${prop.param.name} type=${prop.param.type},${newProp.param.type}`); + } + } + if (!exist) appendProps.push(newProp); + } + if (appendProps) { + const newProps = (!removeProps) ? existDataProps : + existDataProps.filter( + (value: DetailParameterType) => + (removeProps.indexOf(value.param.name) == -1)); + const parentList = map[this.type].parentList; + if (parentInfo != null) { + parentList.push(parentInfo.type); + } + const joinData = map[this.type].data.join(this); + for (const prop of appendProps) { + newProps.push(prop); + } + map[this.type] = { + data: joinData, childList: newProps, + parentList: parentList, + }; + } + } else { + let parentName = ''; + if (parentInfo != null) parentName = parentInfo.type; + map[this.type] = { + data: this, childList: props, parentList: [parentName], + }; + list.push({name: this.type, comment: this.classComment}); + } + return { + type: this.type, + comment: this.comment || this.classComment, + }; + } else { + let type = ''; + if (this.type === 'std::string') { + type = 'string'; + } else if (this.type === 'bool') { + type = 'boolean'; + } else if ((this.type === 'int64_t') || (this.type === 'uint64_t')) { + type = (isRequest) ? 'bigint | number' : 'bigint'; + } else { + type = 'number'; + } + return {type: type, comment: this.comment}; + } + } + + getFunctionName() { + let result = ''; + if (this.type.indexOf('Request') >= 0) { + result = this.type.substring(0, this.type.indexOf('Request')); + } else if (this.type.indexOf('Response') >= 0) { + result = this.type.substring(0, this.type.indexOf('Response')); + } + // ignore target + if (result === 'Error') { + return ''; + } + return result; + } +} + +// ---------------------------------------------------------------------------- +// json data class +// ---------------------------------------------------------------------------- +class JsonData { + filename: string; + inputJsonData: any; + requestData: JsonMappingData | null | undefined; + responseData: JsonMappingData | null | undefined; + constructor(filename: string, inputJsonData: any, + requestData: JsonMappingData | null | undefined, + responseData: JsonMappingData | null | undefined) { + this.filename = path.basename(filename).split('.').shift() || ''; + this.inputJsonData = inputJsonData; + this.requestData = requestData; + this.responseData = responseData; + } +} + +// interface ClassCache { +// cache: Map; +// } + +// ---------------------------------------------------------------------------- +// array check function +// ---------------------------------------------------------------------------- +function isArray(obj: any) { + return (obj instanceof Array); + // return Object.prototype.toString.call(obj) === '[object Array]'; +} + +// ---------------------------------------------------------------------------- +// analyze function +// ---------------------------------------------------------------------------- +function analyzeJson(jsonObj: any | any[], + objName = '', arrayType = '') { + debugLog(`analyzeJson obj=${objName}`); + let result: JsonMappingData; + if (typeof jsonObj == 'string') { + return new JsonMappingData(objName, 'std::string', jsonObj, ''); + } else if (typeof jsonObj == 'number') { + return new JsonMappingData(objName, 'int64_t', jsonObj, ''); + } else if (typeof jsonObj == 'boolean') { + return new JsonMappingData(objName, 'boolean', jsonObj, ''); + } else if (jsonObj) { + const objKey = Object.keys(jsonObj); + const objValues = Object.values(jsonObj); + // if (objKey == 0) { // array + if (isArray(jsonObj)) { + debugLog(`read arr = ${objValues}`); + let pastType = ''; + let firstMap: JsonMappingData | null = null; + for (const item in jsonObj) { + if (!{}.hasOwnProperty.call(jsonObj, item)) continue; + const tempChild = analyzeJson(jsonObj[item], objName); + if (!tempChild) { + // error + } else if (pastType == '') { + firstMap = tempChild; + pastType = tempChild.type; + } else if (pastType != tempChild.type) { + console.log('illegal list elements. fail.'); + throw new Error('illegal list elements. fail.'); + } + } + debugLog(`pastType = ${pastType}`); + if (pastType == '') { + // field and class name is set by the caller. + result = new JsonMappingData('', '', '', ''); + } else { + if ((typeof objValues[0] == 'string') || (typeof objValues[0] == 'number') || + (typeof objValues[0] == 'boolean')) { + // array of string or number. + if ((typeof objValues[0] == 'number') && (arrayType)) { + result = new JsonMappingData(objName, `JsonValueVector<${arrayType}>`, '', ''); + if (firstMap !== null) firstMap.setType(arrayType); + } else { + result = new JsonMappingData(objName, `JsonValueVector<${pastType}>`, '', ''); + } + } else { + // array of object + result = new JsonMappingData(objName, `JsonObjectVector<${pastType}, ${pastType}Struct>`, '', ''); + } + } + if (firstMap !== null) { + result.childList[0] = firstMap; + } + result.isArray = true; + debugLog(`list_type = ${result.type}`); + debugLog(`childList_type = ${result.childList[0].type}`); + return result; + } else { // object + debugLog(`read keys = ${objKey}`); + let className = objName; + let classComment = ''; + if (':class' in jsonObj) { + if (typeof jsonObj[':class'] === 'string') { + className = jsonObj[':class']; + debugLog(`read className = ${className}`); + } + } + if (':class:comment' in jsonObj) { + if (typeof jsonObj[':class:comment'] === 'string') { + classComment = jsonObj[':class:comment']; + debugLog(`read classComment = ${classComment}`); + } + } + let isOutputStruct = true; + if (':isOutputStruct' in jsonObj) { + if (typeof jsonObj[':isOutputStruct'] === 'boolean') { + isOutputStruct = jsonObj[':isOutputStruct']; + debugLog(`set ${className}, isOutputStruct=${isOutputStruct}`); + } + } + // Class name is set by the caller. + result = new JsonMappingData(objName, className, '', '', isOutputStruct); + result.isObject = true; + result.classComment = classComment; + // Stored in temporary map to maintain sort order. + const tmpMap: {[key: string]: JsonMappingData} = {}; + const requireMap: {[key: string]: string} = {}; + const clsCommentMap: {[key: string]: string} = {}; + const commentMap: {[key: string]: string} = {}; + const hintMap: {[key: string]: string} = {}; + const arrayTypeMap: {[key: string]: string} = {}; + for (const key in jsonObj) { + if (!{}.hasOwnProperty.call(jsonObj, key)) continue; + if ((key != ':class') && (key != ':class:comment')) { + if (key.lastIndexOf(':type') >= 0) { + const keyName = key.split(':')[0]; + if (tmpMap[keyName]) { + tmpMap[keyName].setType(jsonObj[key]); + } else { + const data = new JsonMappingData(keyName, jsonObj[key], '', className, isOutputStruct); + data.classComment = classComment; + tmpMap[keyName] = data; + debugLog(`set JsonMappingData = ${keyName}`); + } + } + if (key.lastIndexOf(':require') >= 0) { + const keyName = key.split(':')[0]; + requireMap[keyName] = jsonObj[key]; + } + if (key.lastIndexOf(':arraytype') >= 0) { + const keyName = key.split(':')[0]; + arrayTypeMap[keyName] = jsonObj[key]; + } + if (key.lastIndexOf(':comment') >= 0) { + const keyName = key.split(':')[0]; + commentMap[keyName] = jsonObj[key]; + } + if (key.lastIndexOf(':hint') >= 0) { + const keyName = key.split(':')[0]; + hintMap[keyName] = jsonObj[key]; + } + } else { + if (key == ':class:comment') { + const tmpKeyName = jsonObj[':class']; + clsCommentMap[tmpKeyName] = jsonObj[key]; + } + } + } + + for (const key in jsonObj) { + if (!{}.hasOwnProperty.call(jsonObj, key)) continue; + if (key.indexOf(':') == -1) { + debugLog(`read key = ${key}`); + const value = jsonObj[key]; + if (tmpMap[key]) { + result.childList[key] = tmpMap[key]; + result.childList[key].initValue = value; + } else { + // type check + let typeStr = ''; + if (typeof value == 'string') { + typeStr = 'std::string'; + } else if (typeof value == 'number') { + typeStr = 'int64_t'; + } else if (typeof value == 'boolean') { + typeStr = 'bool'; + } else if (value) { + const objKey2 = Object.keys(value); + if ((typeof objKey2 === 'number') && (objKey2 == 0)) { // array + // Should I examine the element first? + typeStr = ''; + } else { // object + typeStr = ''; + } + } + result.childList[key] = new JsonMappingData( + key, typeStr, value, className); + } + // if (requireMap[key]) { + // result.childList[key].setRequired(requireMap[key]); + // } + result.childList[key].setRequired(requireMap[key]); + if (commentMap[key]) { + result.childList[key].setComment(commentMap[key], hintMap[key]); + } + const tempChild = analyzeJson(value, key, arrayTypeMap[key]); + if (tempChild) { + if (result.childList[key].type == '') { + result.childList[key].setTypeStruct( + tempChild.type, tempChild.structType); + if ((result.childList[key].type.indexOf('JsonObjectVector') >= 0) || + (result.childList[key].type.indexOf('JsonValueVector') >= 0)) { + result.childList[key].isArray = true; + } else { + result.childList[key].isObject = true; + result.childList[key].classComment = tempChild.classComment; + } + } + result.childList[key].childList = tempChild.childList; + result.childList[key].className = className; + } + } + } + } + return result; + } else { + console.log('empty value.'); + throw new Error('empty value.'); + } +} + +// ---------------------------------------------------------------------------- +// analyze child class function +// ---------------------------------------------------------------------------- +function getChildClasses(jsonMapData: JsonMappingData, + list: JsonMappingData[]) { + if (!jsonMapData) { + // do nothing + } else if (jsonMapData.isObject) { + for (const key in jsonMapData.childList) { + if (jsonMapData.childList[key]) { + if (jsonMapData.childList[key].isObject || + jsonMapData.childList[key].isArray) { + getChildClasses(jsonMapData.childList[key], list); + } + } + } + list.push(jsonMapData); + } else if (jsonMapData.isArray) { + getChildClasses(jsonMapData.childList[0], list); + } +} + +// ---------------------------------------------------------------------------- +// generate cpp file source function +// ---------------------------------------------------------------------------- +function generateFileSource(copyright: string, filename: string, + headerName: string | string[], + classList: any[], jsonSetting: JsonObjectCommonType | undefined) { + const result = []; + const namespace = (!jsonSetting) ? '' : jsonSetting.namespace; + const includeNolint = (headerName.indexOf('/') >= 0) ? '' : ' // NOLINT'; + + // header + const sourceFileHeader = `// ${copyright} +/** + * @file ${filename} + * + * @brief JSON mapping file (auto generate) + */ +#include +#include +#include + +#include "${headerName}"${includeNolint} +`; + result.push(sourceFileHeader); + + if (isArray(namespace)) { + for (let idx = 0; idx < namespace.length; ++idx) { + result.push(`namespace ${namespace[idx]} {`); + } + } else { + result.push(`namespace ${namespace} {`); + } + + const sourceFileHeader2 = ` +using cfd::core::JsonClassBase; +using cfd::core::JsonObjectVector; +using cfd::core::JsonValueVector; +using cfd::core::JsonVector; +// clang-format off +// @formatter:off\ +`; + const sourceFileFooter = ` +// @formatter:on +// clang-format on +`; + result.push(sourceFileHeader2); + + if (classList) { + for (const data in classList) { + if (!{}.hasOwnProperty.call(classList, data)) continue; + result.push(classList[data]); + } + } + + result.push(sourceFileFooter); + + if (isArray(namespace)) { + for (let idx = namespace.length - 1; idx >= 0; --idx) { + result.push(`} // namespace ${namespace[idx]}`); + } + } else { + result.push(`} // namespace ${namespace}`); + } + result.push(''); + + return result.join('\n'); +} + +// ---------------------------------------------------------------------------- +// generate cpp class source direct function +// ---------------------------------------------------------------------------- +function generateClassSourceDirect(mapData: JsonMappingData, + responseList: string[] | undefined) { + const result = []; + const sourceClassHeader = ` +// ------------------------------------------------------------------------ +// ${mapData.type} +// ------------------------------------------------------------------------ +cfd::core::JsonTableMap<${mapData.type}> + ${mapData.type}::json_mapper; +std::vector ${mapData.type}::item_list; + +void ${mapData.type}::CollectFieldName() { + if (!json_mapper.empty()) { + return; + } + cfd::core::CLASS_FUNCTION_TABLE<${mapData.type}> func_table; // NOLINT +`; + result.push(sourceClassHeader); + + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + // start + const addJsonMapperComment = `\ + func_table = { + ${mapData.type}::Get${childData.methodName}String, + ${mapData.type}::Set${childData.methodName}String, + ${mapData.type}::Get${childData.methodName}FieldType, + }; + json_mapper.emplace("${childData.name}", func_table); + item_list.push_back("${childData.name}");\ +`; + // end + result.push(addJsonMapperComment); + } + result.push('}'); + + if (mapData.isOutputStruct) { + result.push(''); + result.push(`void ${mapData.type}::ConvertFromStruct(`); + result.push(` const ${mapData.structType}& data) {`); + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + if (childData.isObject || childData.isArray) { + const str = ` ${childData.variableName}_.ConvertFromStruct(data.${childData.variableName});`; + if (str.length > 80) { + result.push(` ${childData.variableName}_.ConvertFromStruct(`); + result.push(` data.${childData.variableName});`); + } else { + result.push(` ${childData.variableName}_.ConvertFromStruct(data.${childData.variableName});`); + } + } else { + result.push(` ${childData.variableName}_ = data.${childData.variableName};`); + } + } + result.push(` ignore_items = data.ignore_items;`); + result.push('}'); + + result.push(''); + result.push(`${mapData.structType} ${mapData.type}::ConvertToStruct() const { // NOLINT`); + result.push(` ${mapData.structType} result;`); + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + if (childData.isObject || childData.isArray) { + const str = ` result.${childData.variableName} = ${childData.variableName}_.ConvertToStruct();`; + if (str.length > 80) { + result.push(` result.${childData.variableName} = ${childData.variableName}_.ConvertToStruct(); // NOLINT`); + } else { + result.push(` result.${childData.variableName} = ${childData.variableName}_.ConvertToStruct();`); + } + } else { + result.push(` result.${childData.variableName} = ${childData.variableName}_;`); + } + } + result.push(` result.ignore_items = ignore_items;`); + result.push(' return result;'); + result.push('}'); + } + + if (responseList) { + for (const str of result) { + responseList.push(str); + } + } + return result.join('\n'); +} + + +// ---------------------------------------------------------------------------- +// generate cpp class source function +// ---------------------------------------------------------------------------- +function generateClassSource(req: JsonMappingData | null | undefined, + res: JsonMappingData | null | undefined, + outputList: Set) { + const result: string[] = []; + + if (req || res) { + const list: JsonMappingData[] = []; + if (req) list.push(req); + if (res) list.push(res); + for (const data of list) { + if (!data) continue; + // sort by class name + // for child elements + const mapList: JsonMappingData[] = []; + getChildClasses(data, mapList); + debugLog(`mapList = ${mapList}`); + + for (const mapKey in mapList) { + if (!{}.hasOwnProperty.call(mapList, mapKey)) continue; + const mapData = mapList[mapKey]; + if (outputList.has(mapData.type)) continue; + + generateClassSourceDirect(mapData, result); + + outputList.add(mapData.type); + } + } + } + + return result.join('\n'); +} + + +// ---------------------------------------------------------------------------- +// generate class header function +// ---------------------------------------------------------------------------- +function generateClassHeaderData(mapData: JsonMappingData, + exportDefine: string) { + const classHeader = ` +// ------------------------------------------------------------------------ +// ${mapData.type} +// ------------------------------------------------------------------------ +/** + * @brief JSON-API (${mapData.type}) class + */ +class ${exportDefine}${mapData.type} + : public cfd::core::JsonClassBase<${mapData.type}> { + public: + ${mapData.type}() { + CollectFieldName(); + } + virtual ~${mapData.type}() { + // do nothing + } + /** + * @brief collect field name. + */ + static void CollectFieldName(); +`; + return classHeader; +} + + +// ---------------------------------------------------------------------------- +// generate object function by header +// ---------------------------------------------------------------------------- +function generateObjectFunctionByHeader(mapData: JsonMappingData, + childData: JsonMappingData) { + // Rename method name because equals windows macro's function. + const methodName = (childData.methodName === 'KValue') ? + 'K_Value' : childData.methodName; + + const objectFunctions = `\ + /** + * @brief Get of ${childData.name}. + * @return ${childData.name} + */ + ${childData.type}& Get${methodName}() { // NOLINT + return ${childData.variableName}_; + } + /** + * @brief Set to ${childData.name}. + * @param[in] ${childData.variableName} setting value. + */ + void Set${methodName}( // line separate + const ${childData.type}& ${childData.variableName}) { // NOLINT + this->${childData.variableName}_ = ${childData.variableName}; + } + /** + * @brief Get data type of ${childData.name}. + * @return Data type of ${childData.name}. + */ + static std::string Get${childData.methodName}FieldType() { + return "${childData.type}"; // NOLINT + } + /** + * @brief Get json string of ${childData.name} field. + * @param[in,out] obj class object + * @return JSON string. + */ + static std::string Get${childData.methodName}String( // line separate + const ${mapData.type}& obj) { // NOLINT + // Do not set to const, because substitution of member variables + // may occur in pre / post processing inside Serialize + return obj.${childData.variableName}_.Serialize(); + } + /** + * @brief Set json object to ${childData.name} field. + * @param[in,out] obj class object + * @param[in] json_value JSON object + */ + static void Set${childData.methodName}String( // line separate + ${mapData.type}& obj, // NOLINT + const UniValue& json_value) { + obj.${childData.variableName}_.DeserializeUniValue(json_value); + } +`; + return objectFunctions; +} + +// ---------------------------------------------------------------------------- +// generate value function by header +// ---------------------------------------------------------------------------- +function generateValueFunctionByHeader(mapData: JsonMappingData, + childData: JsonMappingData) { + // Rename method name because equals windows macro's function. + const methodName = (childData.methodName === 'KValue') ? + 'K_Value' : childData.methodName; + + const valueFunctions = `\ + /** + * @brief Get of ${childData.name} + * @return ${childData.name} + */ + ${childData.type} Get${methodName}() const { + return ${childData.variableName}_; + } + /** + * @brief Set to ${childData.name} + * @param[in] ${childData.variableName} setting value. + */ + void Set${methodName}( // line separate + const ${childData.type}& ${childData.variableName}) { // NOLINT + this->${childData.variableName}_ = ${childData.variableName}; + } + /** + * @brief Get data type of ${childData.name} + * @return Data type of ${childData.name} + */ + static std::string Get${childData.methodName}FieldType() { + return "${childData.type}"; + } + /** + * @brief Get json string of ${childData.name} field. + * @param[in,out] obj class object. + * @return JSON string + */ + static std::string Get${childData.methodName}String( // line separate + const ${mapData.type}& obj) { // NOLINT + return cfd::core::ConvertToString(obj.${childData.variableName}_); + } + /** + * @brief Set json object to ${childData.name} field. + * @param[in,out] obj class object. + * @param[in] json_value JSON object. + */ + static void Set${childData.methodName}String( // line separate + ${mapData.type}& obj, // NOLINT + const UniValue& json_value) { + cfd::core::ConvertFromUniValue( // line separate + obj.${childData.variableName}_, json_value); + } +`; + return valueFunctions; +} + +// ---------------------------------------------------------------------------- +// generate class field by header +// ---------------------------------------------------------------------------- +function generateClassFieldByHeader(mapData: JsonMappingData) { + let structConvertFunction = ''; + if (mapData.isOutputStruct) { + structConvertFunction = `\ + /** + * @brief Convert struct to class. + * @param[in] data struct data. + */ + void ConvertFromStruct( + const ${mapData.structType}& data); + + /** + * @brief Convert class to struct. + * @return struct data. + */ + ${mapData.structType} ConvertToStruct() const;`; + } + + const commonFields = `\ + /** + * @brief Set ignore item. + * @param[in] key ignore target key name. + */ + void SetIgnoreItem(const std::string& key) { + ignore_items.insert(key); + } + +${structConvertFunction} + + protected: + /** + * @brief definition type of Map table. + */ + using ${mapData.type}MapTable = + cfd::core::JsonTableMap<${mapData.type}>; + + /** + * @brief Get JSON mapping object. + * @return JSON mapping object. + * @see cfd::core::JsonClassBase::GetJsonMapper() + */ + virtual const ${mapData.type}MapTable& GetJsonMapper() const { // NOLINT + return json_mapper; + } + /** + * @brief Get item lists of JSON mapping. + * Fetch a list of target variable names in the order of definition. + * @return Item lists of JSON mapping. + * @see cfd::core::JsonClassBase::GetJsonItemList() + */ + virtual const std::vector& GetJsonItemList() const { + return item_list; + } + /** + * @brief Get ignore item lists of JSON mapping. + * Ignore the target variable at Serialize. + * @return Item list of JSON mapping. + * @see cfd::core::JsonClassBase::GetIgnoreItem() + */ + virtual const std::set& GetIgnoreItem() const { + return ignore_items; + } + + private: + /** + * @brief JsonFunctionMap table + */ + static ${mapData.type}MapTable json_mapper; + /** + * @brief field name list. + */ + static std::vector item_list; + /** + * @brief ignore item list. + */ + std::set ignore_items; +`; + return commonFields; +} + +// ---------------------------------------------------------------------------- +// generate header function +// ---------------------------------------------------------------------------- +function generateFileHeader(copyright: string, filename: string, + dirname: string, classList: any[], + jsonSetting: JsonObjectCommonType | undefined, appendHeaderName = '') { + const result = []; + + const namespace = (!jsonSetting) ? '' : jsonSetting.namespace; + const commonHeader = (!jsonSetting) ? '' : jsonSetting.commonHeader; + let path = `${dirname}/${filename}_`; + if (path.startsWith(__dirname)) { + path = path.substr(__dirname.length); + } + while (path.indexOf('/', 0) == 0) { + path = path.substr(1); + } + while (path.indexOf('../') >= 0) { + path = path.replace('../', ''); + } + while (path.indexOf('./') >= 0) { + path = path.replace('./', ''); + } + while (path.indexOf('//') >= 0) { + path = path.replace('//', '/'); + } + while (path.indexOf('external/') >= 0) { + path = path.replace('external/', ''); + } + while (path.indexOf('/') >= 0) { + path = path.replace('/', '_'); + } + while (path.indexOf('.') >= 0) { + path = path.replace('.', '_'); + } + while (path.indexOf('-') >= 0) { + path = path.replace('-', '_'); + } + const defName = path.toUpperCase(); + const includeHeader = (commonHeader) ? `#include "${commonHeader}"\n` : ''; + const includeHeader2 = (appendHeaderName.length > 0) ? `#include "${appendHeaderName}"\n` : ''; + + // header + const headerFileHeader = `// ${copyright} +/** + * @file ${filename} + * + * @brief JSON mapping file. (auto generate) + */ +#ifndef ${defName} +#define ${defName} + +#include +#include +#include + +#include "cfdcore/cfdcore_json_mapping_base.h" +${includeHeader} +${includeHeader2}`; + + result.push(headerFileHeader); + + if (typeof namespace !== 'string') { + for (let idx = 0; idx < namespace.length; ++idx) { + result.push(`namespace ${namespace[idx]} {`); + } + } else { + result.push(`namespace ${namespace} {`); + } + + const headerFileHeader2 = ` +using cfd::core::JsonClassBase; +using cfd::core::JsonObjectVector; +using cfd::core::JsonValueVector; +using cfd::core::JsonVector; +// clang-format off +// @formatter:off\ +`; + + const headerFileFooter = ` +// @formatter:on +// clang-format on +`; + const headerFileFooter2 = ` +#endif // ${defName} +`; + result.push(headerFileHeader2); + + if (classList) { + for (const data in classList) { + if (!{}.hasOwnProperty.call(classList, data)) continue; + result.push(classList[data]); + } + } + + result.push(headerFileFooter); + if (typeof namespace !== 'string') { + for (let idx = namespace.length - 1; idx >= 0; --idx) { + result.push(`} // namespace ${namespace[idx]}`); + } + } else { + result.push(`} // namespace ${namespace}`); + } + result.push(headerFileFooter2); + return result.join('\n'); +} + +// ---------------------------------------------------------------------------- +// generate class header direct function +// ---------------------------------------------------------------------------- +function generateClassHeaderDirect( + mapData: JsonMappingData, + jsonSetting: { export: any }, + responseList: string[] | undefined) { + const result = []; + const exportDefine = (jsonSetting && jsonSetting.export) ? `${jsonSetting.export} ` : ''; + const classHeader = generateClassHeaderData(mapData, exportDefine); + result.push(classHeader); + + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + if (childData.isObject || childData.isArray) { + const objectFunctions = generateObjectFunctionByHeader( + mapData, childData); + result.push(`${objectFunctions}`); + } else { + const valueFunctions = generateValueFunctionByHeader( + mapData, childData); + result.push(valueFunctions); + } + } + const commonFields = generateClassFieldByHeader(mapData); + result.push(commonFields); + + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + const objectFields = `\ + /** + * @brief JsonAPI(${childData.name}) value + */`; + result.push(objectFields); + if (childData.isObject || childData.isArray) { + result.push(` ${childData.type} ${childData.variableName}_; // NOLINT`); + } else if (childData.type == 'std::string') { + // string + result.push(` ${childData.type} ${childData.variableName}_ = "${childData.initValue}";`); + } else { + result.push(` ${childData.type} ${childData.variableName}_ = ${childData.initValue};`); + } + } + + result.push('};'); + if (responseList) { + for (const str of result) { + responseList.push(str); + } + } + return result.join('\n'); +} + +// ---------------------------------------------------------------------------- +// generate class header function +// ---------------------------------------------------------------------------- +function generateClassHeader(req: JsonMappingData | null | undefined, + res: JsonMappingData | null | undefined, + jsonSetting: { export: any }, + outputList: Set) { + const result: string[] = []; + + // header + if (req || res) { + const list: JsonMappingData[] = []; + if (req) list.push(req); + if (res) list.push(res); + for (const data of list) { + if (!data) continue; + // sort by generate class + // Child element in order + const mapList: JsonMappingData[] = []; + getChildClasses(data, mapList); + debugLog(`mapList = ${mapList}`); + + for (const mapKey in mapList) { + if (!{}.hasOwnProperty.call(mapList, mapKey)) continue; + const mapData = mapList[mapKey]; + if (outputList.has(mapData.type)) continue; + + generateClassHeaderDirect(mapData, jsonSetting, result); + + outputList.add(mapData.type); + } + } + } + + return result.join('\n'); +} + +// ---------------------------------------------------------------------------- +// generate reference class list function +// ---------------------------------------------------------------------------- +function generateReferenceClassList( + jsonClassMap: ClassMapType, + rootFuncList: TsAppendFunctionData[], + referenceSet: Set) { + const refList: Map = new Map(); + const list: ReferenceClassInfo[] = []; + for (const key in jsonClassMap) { + if (jsonClassMap[key]) { + const data = jsonClassMap[key]; + if (data.data.isObject) { + for (const cKey of data.parentList) { + if (!cKey) { + // do nothing + } else if (jsonClassMap[cKey]) { + if (refList.has(key)) { + // add ref list + const obj = refList.get(key); + if (obj) { + obj.references.add(cKey); + refList.set(key, obj); + } + } else { + const obj: ReferenceClassInfo = { + name: key, + references: new Set(), + weight: 1, + }; + obj.references.add(cKey); + refList.set(key, obj); + referenceSet.add(key); + } + } else if (cKey == 'ErrorResponseBase') { + // do nothing + } else { + console.log(`unknown key: ${cKey}`); + } + } + } + } + } + + /* + for (const func of rootFuncList) { + if (!func) continue; + if (func.parameters.length > 0 && + refList.has(func.parameters[0].type)) { + const obj = refList.get(func.parameters[0].type); + if (obj && obj.references.keys.length > 1) { + obj.weight += 1; + refList.set(func.parameters[0].type, obj); + } + } + if (func.returnType && refList.has(func.returnType)) { + const obj = refList.get(func.returnType); + if (obj && obj.references.keys.length > 1) { + obj.weight += 1; + refList.set(func.returnType, obj); + } + } + } + */ + + let changeWeight = true; + const refKeyList: string[] = []; + while (changeWeight) { + changeWeight = false; + // const newRefKeyList = []; + if (refKeyList.length == 0) { + for (const [key] of refList) { + if (key) refKeyList.push(key); + } + } + for (const key of refKeyList) { + if (refList.get(key)) { + const src = refList.get(key); + let targetCount = 0; + if (src) { + for (const ref of src.references) { + if (key == ref) continue; + if (refList.get(ref)) { + const dst = refList.get(ref); + if (dst) { + if (dst.weight >= src.weight) { // update ref weight + src.weight = dst.weight + 1; + changeWeight = true; + targetCount += 1; + // console.log(`update weight: ${key} weight=${src.weight}`); + } + } + } + } + if (targetCount > 0) { + refList.set(key, src); + // newRefKeyList.push(key); + } + } + } + } + } + + for (const [key, value] of refList) { + if (value) { + if ((value.references.size > 1) || (value.weight > 1)) { + list.push(value); + console.log('multiple reference type:', key); + } + } + } + + list.sort((a, b) => { + if (a.weight == b.weight) { + return a.name.localeCompare(b.name); + } + return (a.weight > b.weight) ? -1 : 1; + }); + + // console.log(list); + return list; +} + + +// ---------------------------------------------------------------------------- +// generate struct header function +// ---------------------------------------------------------------------------- +function generateStructHeaderArea(mapData: { structType: any }) { + const structHeader = ` +// ------------------------------------------------------------------------ +// ${mapData.structType} +// ------------------------------------------------------------------------ +/** + * @brief ${mapData.structType} struct + */ +struct ${mapData.structType} {`; + return structHeader; +} + + +// ---------------------------------------------------------------------------- +// generate struct item data function +// ---------------------------------------------------------------------------- +function generateStructItemDataDirect(textArray: string[], + mapData: JsonMappingData, hasErrorOutput: boolean, + namespace: string, libNamespace: string, + responseTypeSet: Set) { + const structHeader = generateStructHeaderArea(mapData); + textArray.push(structHeader); + + let nameLengthMax = 0; + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + let nameLength = childData.type.length + + childData.variableName.length; + if (childData.isObject || childData.isArray) { + nameLength = childData.structType.length + + childData.variableName.length; + } else if (childData.type == 'std::string') { + if (typeof childData.initValue === 'string') { + nameLength += childData.initValue.length + 5; + } + } else { + const stringText = `${childData.initValue}`; + nameLength += stringText.length + 3; + } + if (nameLengthMax < nameLength) nameLengthMax = nameLength; + } + + for (const childKey in mapData.childList) { + if (!{}.hasOwnProperty.call(mapData.childList, childKey)) continue; + const childData = mapData.childList[childKey]; + let nameLength = childData.type.length + + childData.variableName.length; + if (childData.isObject || childData.isArray) { + nameLength = childData.structType.length + + childData.variableName.length; + } else if (childData.type == 'std::string') { + if (typeof childData.initValue === 'string') { + nameLength += childData.initValue.length + 5; + } + } else { + const stringText = `${childData.initValue}`; + nameLength += stringText.length + 3; + } + let space = ''; + if (nameLengthMax > nameLength) { + nameLength = nameLengthMax - nameLength; + for (let i = 0; i < nameLength; ++i) space += ' '; + } + const comment = `${space}//!< ${childData.variableName} // NOLINT`; + + if (childData.isObject || childData.isArray) { + textArray.push(` ${childData.structType} ${childData.variableName}; ${comment}`); + } else if (childData.type == 'std::string') { + // string + textArray.push(` ${childData.type} ${childData.variableName} = "${childData.initValue}"; ${comment}`); + } else { + textArray.push(` ${childData.type} ${childData.variableName} = ${childData.initValue}; ${comment}`); + } + } + + if (hasErrorOutput && responseTypeSet.has(mapData.type)) { + if (namespace == libNamespace) { + textArray.push(` InnerErrorResponseStruct error; //!< error information`); + } else { + textArray.push(` ${libNamespace}::InnerErrorResponseStruct error; //!< error information`); + } + } + textArray.push(` std::set ignore_items; //!< using on JSON mapping convert.`); + textArray.push('};'); +} + + +// ---------------------------------------------------------------------------- +// generate struct item data function +// ---------------------------------------------------------------------------- +function generateStructItemData(textArray: string[], + req: JsonMappingData | null | undefined, + res: JsonMappingData | null | undefined, + inputJsonData: { namespace: string | string[] }, + lastNamespaces: string | any[], hasErrorOutput: boolean, + processedList: Set, libNamespace: string, + responseTypeSet: Set) { + if (req || res) { + let namespace = ''; + let lastNamespace = ''; + if (typeof inputJsonData.namespace === 'string') { + namespace = inputJsonData.namespace; + } else { + for (let idx = 0; idx < inputJsonData.namespace.length; ++idx) { + if (namespace.length > 0) { + namespace += '::' + inputJsonData.namespace[idx]; + } else { + namespace = inputJsonData.namespace[idx]; + } + } + } + if (typeof lastNamespaces === 'string') { + lastNamespace = lastNamespaces; + } else { + for (let idx = 0; idx < lastNamespaces.length; ++idx) { + if (lastNamespace.length > 0) { + lastNamespace += '::' + lastNamespaces[idx]; + } else { + lastNamespace = lastNamespaces[idx]; + } + } + } + + if (namespace != lastNamespace) { + if (lastNamespace.length > 0) { + textArray.push(''); + if (typeof lastNamespaces !== 'string') { + for (let idx = lastNamespaces.length - 1; idx >= 0; --idx) { + if (lastNamespaces[idx] != 'json') { + textArray.push(`} // namespace ${lastNamespaces[idx]}`); + } + } + } else { + textArray.push(`} // namespace ${lastNamespace}`); + } + textArray.push(''); + } + + if (typeof inputJsonData.namespace !== 'string') { + for (let idx = 0; idx < inputJsonData.namespace.length; ++idx) { + if (inputJsonData.namespace[idx] != 'json') { + textArray.push(`namespace ${inputJsonData.namespace[idx]} {`); + } + } + } else { + textArray.push(`namespace ${namespace} {`); + } + } + + const list: JsonMappingData[] = []; + if (req) list.push(req); + if (res) list.push(res); + for (const data in list) { + if (!data) continue; + const mapList: JsonMappingData[] = []; + getChildClasses(list[data], mapList); + + for (const mapKey in mapList) { + if (!{}.hasOwnProperty.call(mapList, mapKey)) continue; + const mapData = mapList[mapKey]; + if (!mapData.isOutputStruct) { + console.log(`skip output struct: ${mapData.structType}`); + continue; + } + if (processedList.has(mapData.structType)) { + continue; + } + generateStructItemDataDirect(textArray, + mapData, hasErrorOutput, namespace, libNamespace, + responseTypeSet); + + processedList.add(mapData.structType); + } + } + } +} + +// ---------------------------------------------------------------------------- +// generate struct header function +// ---------------------------------------------------------------------------- +function generateStructHeader(copyright: string, dirname: string, + filename: string, jsonList: any[], libNamespace: string, + referenceList: ReferenceClassInfo[], jsonClassMap: ClassMapType, + responseTypeSet: Set, hasErrorOutput: boolean) { + const result = []; + const processedStructTypes: Set = new Set(); + + let path = `${dirname}/${filename}_`; + if (path.startsWith(__dirname)) { + path = path.substr(__dirname.length); + } + while (path.indexOf('/', 0) == 0) { + path = path.substr(1); + } + while (path.indexOf('../') >= 0) { + path = path.replace('../', ''); + } + while (path.indexOf('./') >= 0) { + path = path.replace('./', ''); + } + while (path.indexOf('//') >= 0) { + path = path.replace('//', '/'); + } + while (path.indexOf('external/') >= 0) { + path = path.replace('external/', ''); + } + while (path.indexOf('/') >= 0) { + path = path.replace('/', '_'); + } + while (path.indexOf('.') >= 0) { + path = path.replace('.', '_'); + } + while (path.indexOf('-') >= 0) { + path = path.replace('-', '_'); + } + const defName = path.toUpperCase(); + + // header + const headerFileHeader = `// ${copyright} +/** + * @file ${filename} + * + * @brief Struct mapping file. (auto generate) + */ +#ifndef ${defName} +#define ${defName} + +#include +#include +#include +#include + +// clang-format off +// @formatter:off\ +`; + const headerFileFooter = ` +// @formatter:on +// clang-format on + +#endif // ${defName} +`; + result.push(headerFileHeader); + + let lastNamespace = ''; + for (const jsonDataIndex in jsonList) { + if (!{}.hasOwnProperty.call(jsonList, jsonDataIndex)) continue; + if (('priority' in jsonList[jsonDataIndex].inputJsonData) && + (jsonList[jsonDataIndex].inputJsonData['priority'] == 'high')) { + const req = jsonList[jsonDataIndex].requestData; + const res = jsonList[jsonDataIndex].responseData; + generateStructItemData(result, req, res, + jsonList[jsonDataIndex].inputJsonData, lastNamespace, false, + processedStructTypes, libNamespace, responseTypeSet); + lastNamespace = jsonList[jsonDataIndex].inputJsonData.namespace; + } + } + + // multiple reference target list + for (const refData of referenceList) { + if (refData && jsonClassMap[refData.name]) { + generateStructItemDataDirect(result, + jsonClassMap[refData.name].data, hasErrorOutput, + lastNamespace, libNamespace, responseTypeSet); + processedStructTypes.add(jsonClassMap[refData.name].data.structType); + } + } + + for (const jsonDataIndex in jsonList) { + if (!{}.hasOwnProperty.call(jsonList, jsonDataIndex)) continue; + if (('priority' in jsonList[jsonDataIndex].inputJsonData) && + (jsonList[jsonDataIndex].inputJsonData['priority'] == 'high')) { + // do nothing + } else { + const req = jsonList[jsonDataIndex].requestData; + const res = jsonList[jsonDataIndex].responseData; + generateStructItemData(result, req, res, + jsonList[jsonDataIndex].inputJsonData, lastNamespace, hasErrorOutput, + processedStructTypes, libNamespace, responseTypeSet); + // const inputJsonData = jsonList[jsonDataIndex].inputJsonData; + lastNamespace = jsonList[jsonDataIndex].inputJsonData.namespace; + } + } + result.push(''); + if (isArray(lastNamespace)) { + for (let idx = lastNamespace.length - 1; idx >= 0; --idx) { + if (lastNamespace[idx] != 'json') { + result.push(`} // namespace ${lastNamespace[idx]}`); + } + } + } else { + result.push(`} // namespace ${lastNamespace}`); + } + + result.push(headerFileFooter); + return result.join('\n'); +} + + +// ---------------------------------------------------------------------------- +// generate typescript data file function +// ---------------------------------------------------------------------------- +function generateTsData(dirname: string, filename: string, + jsonClassMap: ClassMapType, jsonTypeList: ClassParameterType[], + functionList: TsAppendFunctionData[], loadCfdjsIndexFile: fs.PathLike, + promiseMode: boolean, tsClassName: string, + insertFunctions: TsAppendFunctionData[], + errorClassName: string, + insertErrorFunctions: TsAppendFunctionData[]) { + let outPath = `${dirname}/${filename}`; + if (outPath.startsWith(__dirname)) { + outPath = outPath.substr(__dirname.length); + } + while (outPath.indexOf('/', 0) == 0) { + outPath = outPath.substr(1); + } + while (outPath.indexOf('//') >= 0) { + outPath = outPath.replace('//', '/'); + } + const classCommentMap: {[key: string]: string} = {}; + for (const typeData of jsonTypeList) { + classCommentMap[typeData.name] = typeData.comment; + } + jsonTypeList.sort((a, b) => a.name.localeCompare(b.name)); + functionList.sort((a, b) => a.name.localeCompare(b.name)); + + // initialize + const project = new Project({ + tsConfigFilePath: `${__dirname}/../tsconfig.json`, + // addFilesFromTsConfig: false, + }); + + if (loadCfdjsIndexFile) { + fs.copyFileSync(loadCfdjsIndexFile, outPath); + } + + // add source files + const file = (!loadCfdjsIndexFile) ? project.createSourceFile(outPath, '\n') : + project.addSourceFileAtPath(outPath); + + if (loadCfdjsIndexFile) { + const internalErrorObj = file.getInterface('InnerErrorResponse'); + if (internalErrorObj !== undefined) { + internalErrorObj.remove(); + } + const errorObj = file.getInterface('ErrorResponse'); + if (errorObj !== undefined) { + errorObj.remove(); + } + } else { + file.insertStatements(0, '/* eslint-disable max-len */'); + file.insertStatements(1, '/* eslint-disable indent */'); + } + + for (let i = 0; i < jsonTypeList.length; ++i) { + const clsName = jsonTypeList[i].name; + const comment = jsonTypeList[i].comment || classCommentMap[clsName]; + const props: DetailParameterType[] = jsonClassMap[clsName].childList; + const properties = []; + const tags = []; + const docs = []; + for (const prop of props) { + if (prop && prop.param.comment) { + const type = prop.param.type; + const name = prop.param.name; + tags.push({tagName: 'property', text: `\{${type}\} ${name} - ${prop.param.comment}`}); + } + properties.push(prop.param); + } + debugLog(`${clsName} = `, props); + if ((tags.length > 0) || comment) { + docs.push({ + description: comment, + tags, + }); + } + file.addInterface({ + name: clsName, + isExported: true, + properties, + docs, + }); + } + + let classObj = undefined; + if (tsClassName !== '') { + classObj = file.addClass({ + name: tsClassName, + isExported: true, + docs: [{ + description: 'function definition class.', + }], + }); + } + + for (let i = 0; i < functionList.length; ++i) { + // manipulate + const funcName = functionList[i].name; + const funcComment = functionList[i].comment || ''; + const params = functionList[i].parameters; + const reqType = (params.length > 0) ? params[0].type : ''; + const reqName = (params.length > 0) ? params[0].name : ''; + const resName = functionList[i].returnType; + const resDataName = (promiseMode) ? `Promise<${resName}>` : resName; + const retType = (resName in jsonClassMap) ? resDataName : undefined; + const inputDoc = {tagName: 'param', text: `\{${reqType}\} ${reqName} - request data.`}; + const returnDoc = {tagName: 'return', text: `\{${retType}\} - response data.`}; + const tags = []; + const docs = []; + if (params.length > 0) tags.push(inputDoc); + if (resName in jsonClassMap) tags.push(returnDoc); + if ((tags.length > 0) || funcComment) { + docs.push({ + description: funcComment, + tags, + }); + } + if (classObj === undefined) { + file.addFunction({ + name: funcName, + isExported: true, + parameters: params, + returnType: retType, + docs, + }); + } else { + classObj.addMethod({ + name: funcName, + parameters: params, + returnType: retType, + docs, + }); + } + } + + for (let i = 0; i < insertFunctions.length; ++i) { + const funcName = insertFunctions[i].name; + const params = insertFunctions[i].parameters; + const retType = insertFunctions[i].returnType; + const comment = insertFunctions[i].comment; + const tags = []; + const docs = []; + for (const prop of params) { + if (prop && prop.comment) { + const type = prop.type; + const name = prop.name; + tags.push({tagName: 'param', text: `\{${type}\} ${name} - ${prop.comment}`}); + } + } + if (retType && retType != 'void') { + tags.push({tagName: 'return', text: `\{${retType}\} - ${retType} data.`}); + } + if ((tags.length > 0) || comment) { + docs.push({ + description: comment, + tags, + }); + } + file.addFunction({ + name: funcName, + isExported: true, + parameters: params, + returnType: retType, + docs, + }); + } + + if (errorClassName) { + const errorClassObj = file.addClass({ + name: errorClassName, + isExported: true, + docs: [{ + description: 'error class.', + }], + }); + errorClassObj.setExtends('Error'); + for (let i = 0; i < insertErrorFunctions.length; ++i) { + const funcName = insertErrorFunctions[i].name; + const params = insertErrorFunctions[i].parameters; + const retType = insertErrorFunctions[i].returnType; + const comment = insertErrorFunctions[i].comment; + const tags = []; + const docs = []; + for (const prop of params) { + if (prop) { + const type = prop.type; + const name = prop.name; + tags.push({tagName: 'param', text: `\{${type}\} ${name} - ${prop.comment}`}); + } + } + if (funcName == 'constructor') { + if ((tags.length > 0) || comment) { + docs.push({ + description: 'constructor.', + tags, + }); + } + errorClassObj.addConstructor({ + parameters: params, + returnType: retType, + docs, + }); + } else { + if (retType) { + tags.push({tagName: 'return', text: `\{${retType}\} - ${retType} data.`}); + } + if ((tags.length > 0) || comment) { + docs.push({ + description: comment, + tags, + }); + } + errorClassObj.addMethod({ + name: funcName, + parameters: params, + returnType: retType, + docs, + }); + } + } + } + + // asynchronously save all the changes above + project.save().then(() => console.log(`output: ${outPath}`)); +} + + +// ---------------------------------------------------------------------------- +// search file +// ---------------------------------------------------------------------------- +async function convertFile() { + // const today = new Date(); + // const year = today.getFullYear(); + const year = 2020; + const copyright = `Copyright ${year} CryptoGarage`; + + // const fileList = []; + let cfdBaseDir: string; + requireOptionFunc = function(requireInfo: string) { + return requireInfo === 'require'; + }; + const libraryName = 'cfd'; + const libPrefix = 'cfd'; + const structPrefix = 'jsonapi'; + const libNamespace = 'cfd::api'; + const errorClassName = 'CfdError'; + const hasStructErrorOutput = false; + const cfdPath = `${__dirname}/../external/${libraryName}/`; + const cfdPath2 = `${__dirname}/../../${libraryName}/`; + let folderPath = `src/jsonapi/input_json_format/`; + const outJsonSourceFolderPath = `${__dirname}/../../${libraryName}/src/jsonapi/autogen/`; + const outJsonHeaderFolderPath = `${__dirname}/../../${libraryName}/src/jsonapi/autogen/`; + let outStructDirPath = `src/jsonapi/`; + const loadCfdjsIndexFile = ''; + let outTsFolderPath = ``; + const outStructFileName = `${libPrefix}_struct.h`; + const outTsFileName = ``; + const promiseMode = false; + const tsClassName = ''; + const classHeaderList: string[] = []; + const classSourceList: string[] = []; + const jsonDataList: JsonData[] = []; + const jsonClassMap: ClassMapType = {}; + const jsonTypeList: ClassParameterType[] = []; + const functionList: TsAppendFunctionData[] = []; + const responseTypeSet: Set = new Set(); + const insertFunctions: TsAppendFunctionData[] = []; + const insertErrorFunctions: TsAppendFunctionData[] = [{ + name: 'constructor', + parameters: [{ + name: 'message', + type: 'string', + comment: 'Error message.', + }, { + name: 'errorInformation', + type: 'InnerErrorResponse', + comment: 'Error information data.', + }, { + name: 'cause', + type: 'Error', + comment: 'Cause of the error.', + }], + returnType: 'void', + comment: 'constructor.', + }, { + name: 'toString', + parameters: [], + returnType: 'string', + comment: 'get error string.', + }, { + name: 'getErrorInformation', + parameters: [], + returnType: 'InnerErrorResponse', + comment: 'get error information.', + }, { + name: 'getCause', + parameters: [], + returnType: 'Error', + comment: 'get error cause.', + }]; + + if (fs.existsSync(cfdPath) && fs.statSync(cfdPath).isDirectory()) { + cfdBaseDir = cfdPath; + outTsFolderPath = cfdPath + outTsFolderPath; + folderPath = cfdPath + folderPath; + outStructDirPath = cfdPath + outStructDirPath; + } else { + cfdBaseDir = cfdPath2; + outTsFolderPath = `${__dirname}/`; // relative path from tsconfig.json + folderPath = cfdPath2 + folderPath; + outStructDirPath = cfdPath2 + outStructDirPath; + } + + let jsonObjectCommon: JsonObjectCommonType | undefined = undefined; + const fileList: string[] = []; + const fsAsync = fs.promises; + + const searchFunc = async function(dirPath: string) { + const tempDirList = await fsAsync.readdir(dirPath); + for (const target of tempDirList) { + if (target) { + const curPath = path.join(dirPath, target); + const statInfo = fs.statSync(curPath); + if (statInfo.isDirectory()) { + if ((target != '.') && (target != '..')) { + await searchFunc(curPath); + } + } else if (statInfo.isFile() && /.*\.json$/.test(target)) { + fileList.push(curPath); + } + } + } + }; + await searchFunc(folderPath); + + for (const file of fileList) { + if (file) { + try { + console.log(`file = ${file}`); + const inFile = file; + // const outFile = file.replace(/\.json$/i, '_json'); + // const outHeaderFile = `${outFile}.h`; + // const outSourceFile = `${outFile}.cpp`; + + // read json + const jsonObject = JSON.parse(fs.readFileSync(inFile, 'utf8')); + const reqData = (jsonObject.request) ? analyzeJson(jsonObject.request, 'root') : null; + const resData = (jsonObject.response) ? analyzeJson(jsonObject.response, 'root') : null; + let funcName = (jsonObject.functionName) ? jsonObject.functionName : ''; + let reqType = ''; + let resType = ''; + if (reqData != null) { + reqData.collectMapData(jsonClassMap, jsonTypeList, true, reqData); + if (funcName == '') funcName = reqData.getFunctionName(); + reqType = reqData.type; + } + if (resData != null) { + resData.collectMapData(jsonClassMap, jsonTypeList, false, resData); + resType = resData.type; + responseTypeSet.add(resData.type); + if (funcName == '') funcName = resData.getFunctionName(); + } + if (funcName != '') { + const comm = jsonObject.comment || ''; + const param = []; + if (reqType) { + param.push({ + name: 'jsonObject', + type: reqType, + comment: '', + }); + } + functionList.push({ + name: funcName, + comment: comm, + returnType: resType, + parameters: param, + }); + } else if (file.indexOf('error_base.json') == -1) { + console.log(`---- empty function name: ${file}`); + } + debugLog(`reqData = ${reqData}`); + debugLog(`resData = ${resData}`); + jsonDataList.push(new JsonData(file, jsonObject, reqData, resData)); + + if (jsonObjectCommon === undefined) { + jsonObjectCommon = jsonObject; + } else { + if (jsonObject.namespace && jsonObject.namespace.length > 0) { + jsonObjectCommon['namespace'] = jsonObject.namespace; + } + if (jsonObject.commonHeader && jsonObject.commonHeader.length > 0) { + jsonObjectCommon['commonHeader'] = jsonObject.commonHeader; + } + } + } catch (e) { + console.log('Exception: ' + path.basename(file)); + throw e; + } + } + } + + const referenceSet: Set = new Set(); + const referenceList = generateReferenceClassList( + jsonClassMap, functionList, referenceSet); + if (jsonDataList.length > 0) { + jsonDataList.sort((a, b) => a.filename.localeCompare(b.filename)); + const classHeaderSet: Set = new Set(); + const classSourceSet: Set = new Set(); + const dummyJson = {export: ''}; + for (const refData of referenceList) { + if (jsonClassMap[refData.name]) { + const headerStr = generateClassHeaderDirect( + jsonClassMap[refData.name].data, dummyJson, undefined); + classHeaderList.push(headerStr); + const srcStr = generateClassSourceDirect( + jsonClassMap[refData.name].data, undefined); + classSourceList.push(srcStr); + classHeaderSet.add(refData.name); + classSourceSet.add(refData.name); + } + } + for (const data of jsonDataList) { + const headerStr = generateClassHeader( + data.requestData, data.responseData, data.inputJsonData, + classHeaderSet); + classHeaderList.push(headerStr); + const srcStr = generateClassSource( + data.requestData, data.responseData, classSourceSet); + classSourceList.push(srcStr); + } + } + + { + let namespaceName = ''; + const namespace = (jsonObjectCommon) ? jsonObjectCommon.namespace || '' : ''; + if (typeof namespace !== 'string') { + for (let idx = 0; idx < namespace.length; ++idx) { + if (idx !== 0) namespaceName += '_'; + namespaceName += namespace[idx]; + } + } else { + namespaceName += namespace; + } + + if (outStructFileName !== '') { + const outHeaderFile = `${namespaceName}_autogen.h`; + const outSourceFile = `${namespaceName}_autogen.cpp`; + const headerStr = generateFileHeader(copyright, outHeaderFile, + outJsonHeaderFolderPath, + classHeaderList, jsonObjectCommon, `${structPrefix}/${outStructFileName}`); + fs.writeFileSync(`${outJsonHeaderFolderPath}${outHeaderFile}`, headerStr); + const srcStr = generateFileSource(copyright, outSourceFile, + outHeaderFile, classSourceList, jsonObjectCommon); + fs.writeFileSync(`${outJsonSourceFolderPath}${outSourceFile}`, srcStr); + } + } + + if ((jsonDataList.length > 0) && (outStructFileName !== '')) { + const headerStr = generateStructHeader(copyright, outStructDirPath, + outStructFileName, jsonDataList, libNamespace, + referenceList, jsonClassMap, responseTypeSet, hasStructErrorOutput); + fs.writeFileSync(path.resolve(`${outStructDirPath}${outStructFileName}`), headerStr); + console.log(`output: ${outStructFileName}`); + } + + if ((jsonTypeList.length > 0) && (outTsFileName.length > 0)) { + try { + fs.unlinkSync(path.resolve(`${cfdBaseDir}${outTsFileName}`)); + } catch (err) { + // do nothing + } + generateTsData(outTsFolderPath, outTsFileName, jsonClassMap, + jsonTypeList, functionList, loadCfdjsIndexFile, promiseMode, + tsClassName, insertFunctions, errorClassName, insertErrorFunctions); + } +} + + +const main = async function() { + for (let i = 2; i < process.argv.length; i++) { + if (process.argv[i]) { + if (process.argv[i] === 'mode=debug') { + debugLog = function(...args: any | any[]) { + if (!isArray(args)) { + console.log(args); + return; + } + switch (args.length) { + case 1: + console.log(args[0]); + break; + case 2: + console.log(args[0], args[1]); + break; + case 3: + console.log(args[0], args[1], args[2]); + break; + default: + console.log(Array.prototype.join.call(args)); + break; + } + }; + } + } + } + + await convertFile(); +}; +main();