From a57e499423afd5675c63b23fa36e068d121248bc Mon Sep 17 00:00:00 2001 From: "K.Matsuzawa" <49175372+k-matsuzawa@users.noreply.github.com> Date: Tue, 30 Mar 2021 16:38:06 +0900 Subject: [PATCH] update to v0.3.0 (#5) update from cryptogarageinc v0.3.2 --- .github/workflows/check_pre-merge_develop.yml | 28 +- .github/workflows/check_pre-merge_master.yml | 35 +- .github/workflows/check_pre-merge_sprint.yml | 32 +- .github/workflows/code_scanner.yml | 7 + .../docker/test_bitcoin_entrypoint.sh | 46 + .github/workflows/docker/test_entrypoint.sh | 3 + .gitignore | 1 + .vscode/settings.json | 12 + README.md | 56 +- VERSION | 2 +- cfd-python.code-workspace | 15 + cfd/__init__.py | 3 + cfd/address.py | 78 +- cfd/confidential_address.py | 1 - cfd/confidential_transaction.py | 215 +- cfd/crypto.py | 170 + cfd/descriptor.py | 8 +- cfd/hdwallet.py | 112 +- cfd/key.py | 60 +- cfd/psbt.py | 1966 +++++++++++ cfd/script.py | 23 +- cfd/taproot.py | 524 +++ cfd/transaction.py | 517 ++- cfd/util.py | 131 +- cmake/CfdCommonOption.cmake | 1 + cmake/CfdWallyOption.cmake | 3 +- cmake/Cpp11Setting.cmake | 5 + external/CMakeLists.txt | 2 +- integration_test/elements.conf | 4 +- integration_test/tests/test_bitcoin.py | 444 ++- integration_test/tests/test_elements.py | 5 +- setup.cfg | 1 + tests/data/address_test.json | 885 ++++- tests/data/bitcoin_coin_test.json | 38 +- tests/data/common_test.json | 212 +- tests/data/descriptor_test.json | 6 +- tests/data/elements_coin_test.json | 65 +- tests/data/elements_transaction_test.json | 102 +- tests/data/key_test.json | 25 + tests/data/psbt_test.json | 3073 +++++++++++++++++ tests/data/transaction_test.json | 1331 ++++++- tests/test_address.py | 211 +- tests/test_common.py | 60 + tests/test_confidential_transaction.py | 17 +- tests/test_key.py | 5 + tests/test_psbt.py | 670 ++++ tests/test_transaction.py | 133 +- tests/util.py | 7 +- tools/function_converter.py | 4 + 49 files changed, 10876 insertions(+), 478 deletions(-) create mode 100755 .github/workflows/docker/test_bitcoin_entrypoint.sh create mode 100644 .vscode/settings.json create mode 100644 cfd-python.code-workspace create mode 100644 cfd/crypto.py create mode 100644 cfd/psbt.py create mode 100644 cfd/taproot.py create mode 100644 tests/data/psbt_test.json create mode 100644 tests/test_psbt.py diff --git a/.github/workflows/check_pre-merge_develop.yml b/.github/workflows/check_pre-merge_develop.yml index 7842045..bb421e8 100644 --- a/.github/workflows/check_pre-merge_develop.yml +++ b/.github/workflows/check_pre-merge_develop.yml @@ -7,6 +7,7 @@ on: - test_ci paths-ignore: - '.github/workflows/create_release-and-upload.yml' + - '.github/workflows/code_scanner.yml' - 'README.md' pull_request: branches: @@ -14,10 +15,12 @@ on: - test_ci env: - GITHUB_VERSION: v0.0.5 + GITHUB_VERSION: v0.0.8 + GITHUB_BITCOIN_VERSION: v0.0.8 DOCKER_PYTHON_VERSION: 3.7 GITHUB_DOCKER_IMAGE: docker.pkg.github.com/cryptogarageinc/elements-testing-dockerfile/elements-testing ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_entrypoint.sh + BITCOIN_ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_bitcoin_entrypoint.sh jobs: build-and-test: @@ -111,6 +114,29 @@ jobs: docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} + bitcoin-e2e-test: + timeout-minutes: 20 + name: bitcoin e2e test + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ env.DOCKER_PYTHON_VERSION }} + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: install pip + run: python -m pip install -U pip + - name: create wheel + run: pip wheel . + - name: e2e-test + run: | + docker login docker.pkg.github.com -u owner -p ${{ secrets.GITHUB_TOKEN }} + docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.BITCOIN_ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + doxygen-ubuntu: name: doxygen-ubuntu runs-on: ubuntu-20.04 diff --git a/.github/workflows/check_pre-merge_master.yml b/.github/workflows/check_pre-merge_master.yml index fd7bd0b..08ca4bd 100644 --- a/.github/workflows/check_pre-merge_master.yml +++ b/.github/workflows/check_pre-merge_master.yml @@ -6,16 +6,19 @@ on: - master paths-ignore: - '.github/workflows/create_release-and-upload.yml' + - '.github/workflows/code_scanner.yml' - 'README.md' pull_request: branches: - master env: - GITHUB_VERSION: v0.0.5 + GITHUB_VERSION: v0.0.8 + GITHUB_BITCOIN_VERSION: v0.0.8 DOCKER_PYTHON_VERSION: 3.7 GITHUB_DOCKER_IMAGE: docker.pkg.github.com/cryptogarageinc/elements-testing-dockerfile/elements-testing ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_entrypoint.sh + BITCOIN_ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_bitcoin_entrypoint.sh jobs: build-and-test: @@ -57,7 +60,8 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-10.15, macos-11.0, windows-2019, ubuntu-18.04, ubuntu-20.04] +# os: [macos-10.15, macos-11.0, windows-2019, ubuntu-18.04, ubuntu-20.04] + os: [macos-10.15, windows-2019, ubuntu-18.04, ubuntu-20.04] py-ver: [3.6, 3.7, 3.8, 3.9, pypy3] exclude: - os: windows-2019 @@ -66,8 +70,8 @@ jobs: py-ver: 3.7 - os: windows-2019 py-ver: pypy3 - - os: macos-11.0 - py-ver: pypy3 +# - os: macos-11.0 +# py-ver: pypy3 steps: - uses: actions/checkout@v2 @@ -109,6 +113,29 @@ jobs: docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} + bitcoin-e2e-test: + timeout-minutes: 20 + name: bitcoin e2e test + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ env.DOCKER_PYTHON_VERSION }} + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: install pip + run: python -m pip install -U pip + - name: create wheel + run: pip wheel . + - name: e2e-test + run: | + docker login docker.pkg.github.com -u owner -p ${{ secrets.GITHUB_TOKEN }} + docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.BITCOIN_ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + doxygen-ubuntu: name: doxygen-ubuntu runs-on: ubuntu-20.04 diff --git a/.github/workflows/check_pre-merge_sprint.yml b/.github/workflows/check_pre-merge_sprint.yml index 4f7527f..85302b1 100644 --- a/.github/workflows/check_pre-merge_sprint.yml +++ b/.github/workflows/check_pre-merge_sprint.yml @@ -6,16 +6,19 @@ on: - features/sprint* paths-ignore: - '.github/workflows/create_release-and-upload.yml' + - '.github/workflows/code_scanner.yml' - 'README.md' pull_request: branches: - features/sprint* env: - GITHUB_VERSION: v0.0.5 + GITHUB_VERSION: v0.0.8 + GITHUB_BITCOIN_VERSION: v0.0.8 DOCKER_PYTHON_VERSION: 3.7 GITHUB_DOCKER_IMAGE: docker.pkg.github.com/cryptogarageinc/elements-testing-dockerfile/elements-testing ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_entrypoint.sh + BITCOIN_ENTRYPOINT_PATH: /github/workspace/.github/workflows/docker/test_bitcoin_entrypoint.sh jobs: build-and-test: @@ -58,9 +61,9 @@ jobs: - name: test run: python -m unittest discover -v tests - e2e-test: + elements-e2e-test: timeout-minutes: 20 - name: e2e-test + name: elements e2e test runs-on: ubuntu-18.04 steps: @@ -81,6 +84,29 @@ jobs: docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_VERSION }} + bitcoin-e2e-test: + timeout-minutes: 20 + name: bitcoin e2e test + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ env.DOCKER_PYTHON_VERSION }} + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: install pip + run: python -m pip install -U pip + - name: create wheel + run: pip wheel . + - name: e2e-test + run: | + docker login docker.pkg.github.com -u owner -p ${{ secrets.GITHUB_TOKEN }} + docker pull ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + docker run -v ${{ github.workspace }}:/github/workspace --entrypoint ${{ env.BITCOIN_ENTRYPOINT_PATH }} ${{ env.GITHUB_DOCKER_IMAGE }}:${{ env.GITHUB_BITCOIN_VERSION }} + doxygen-ubuntu: name: doxygen-ubuntu runs-on: ubuntu-20.04 diff --git a/.github/workflows/code_scanner.yml b/.github/workflows/code_scanner.yml index 832f575..bc5753c 100644 --- a/.github/workflows/code_scanner.yml +++ b/.github/workflows/code_scanner.yml @@ -7,6 +7,13 @@ on: - develop - features/sprint* - feature/code_scanning + paths: + - '**.py' + - '**.cpp' + - '**.h' + - '**/CMakeLists.txt' + - '**/code_scanner.yml' + - '**/external_project_local_setting.config' pull_request: branches: - master diff --git a/.github/workflows/docker/test_bitcoin_entrypoint.sh b/.github/workflows/docker/test_bitcoin_entrypoint.sh new file mode 100755 index 0000000..e316d22 --- /dev/null +++ b/.github/workflows/docker/test_bitcoin_entrypoint.sh @@ -0,0 +1,46 @@ +#!/bin/bash -u + +# while :; do sleep 10; done +export WORKDIR_ROOT=github +export WORK_DIR=workspace +export WORKDIR_PATH=/${WORKDIR_ROOT}/${WORK_DIR} + +cd /${WORKDIR_ROOT} +if [ ! -d ${WORK_DIR} ]; then + mkdir ${WORK_DIR} +fi + +cd ${WORKDIR_PATH} +rm -rf bitcoind_datadir + +mkdir bitcoind_datadir +chmod 777 bitcoind_datadir +# cp /root/.bitcoin/bitcoin.conf bitcoind_datadir/ +cp ./integration_test/bitcoin.conf bitcoind_datadir/ + +# boot daemon +bitcoind --regtest -datadir=${WORKDIR_PATH}/bitcoind_datadir +bitcoin-cli --regtest -datadir=${WORKDIR_PATH}/bitcoind_datadir ping > /dev/null 2>&1 +while [ $? -ne 0 ] +do + bitcoin-cli --regtest -datadir=${WORKDIR_PATH}/bitcoind_datadir ping > /dev/null 2>&1 +done +echo "start bitcoin node" + +# load or create wallet +bitcoin-cli --regtest -datadir=${WORKDIR_PATH}/bitcoind_datadir createwallet wallet + +set -e + +python3 --version + +pip3 install *.whl +pip3 install python-bitcoinrpc + +cd integration_test + +python3 tests/test_bitcoin.py -v +if [ $? -gt 0 ]; then + cd ../.. + exit 1 +fi diff --git a/.github/workflows/docker/test_entrypoint.sh b/.github/workflows/docker/test_entrypoint.sh index ac2eceb..8dd05ed 100755 --- a/.github/workflows/docker/test_entrypoint.sh +++ b/.github/workflows/docker/test_entrypoint.sh @@ -40,6 +40,9 @@ do done echo "start elements node" +# load or create wallet +bitcoin-cli --regtest -datadir=${WORKDIR_PATH}/bitcoind_datadir createwallet wallet + set -e python3 --version diff --git a/.gitignore b/.gitignore index 686584c..ba8f92a 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,4 @@ cmake_build/ /integration_test/localdata /integration_test/bitcoind_datadir /integration_test/elementsd_datadir +/integration_test/local_test diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..66bfd50 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "test_*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/README.md b/README.md index f5a4658..d5444a2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,56 @@ # Crypto Finance Development Kit for Python (CFD-PYTHON) +CFD library for Python. + +## Overview + +This library is development kit for crypto finance application. +Useful when developing applications for cryptocurrencies. + +### Target Network + +- Bitcoin +- Liquid Network + +### Support function by cfd + +- Bitcoin + - Bitcoin Script (builder, viewer) + - Transaction + - Create, Parse, Decode + - Simple pubkey-hash sign / verify + - Estimate Fee + - Coin Selection (FundRawTransaction) + - PSBT (v0. v2 & taproot is not yet.) + - Create, Parse, Decode + - Simple pubkey-hash sign / verify + - Estimate Fee + - Coin Selection (FundRawTransaction) + - 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 + - Reissuance + - Confidential Address + +### Libraries for each language + +- Python : cfd-python + - C/C++ : cfd + - Extend the cfd-core library. Defines the C language API and extension classes. + - C++ : cfd-core + - Core library. Definition base class. +- other language: + - JavaScript : cfd-js + - WebAssembly : cfd-js-wasm + - C# : cfd-csharp + - Go : cfd-go + - Rust : cfd-rust + ## Dependencies - Python(CPython) (3.6 or higher) @@ -35,7 +86,6 @@ brew install cmake python # install dependencies using APT package Manager apt-get install -y build-essential cmake python3 python3-dev (Ubuntu 20.04 or higher) apt-get install -y python-is-python3 -curl https://sh.rustup.rs -sSf | sh (select is 1) ``` cmake version 3.14.2 or lower, download from website and install cmake. @@ -83,10 +133,10 @@ pip install --user . ### install from wheel -1. get releases asset. (ex. https://github.com/p2pderivatives/cfd-python/releases/download/v0.2.2/cfd-0.2.2-py3-none-win_amd64.whl ) +1. get releases asset. (ex. https://github.com/p2pderivatives/cfd-python/releases/download/v0.3.0/cfd-0.3.0-py3-none-win_amd64.whl ) 2. install pip ``` - pip install --user cfd-0.2.2-py3-none-win_amd64.whl + pip install --user cfd-0.3.0-py3-none-win_amd64.whl ``` ### uninstall diff --git a/VERSION b/VERSION index 373f8c6..9325c3c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.3 \ No newline at end of file +0.3.0 \ No newline at end of file diff --git a/cfd-python.code-workspace b/cfd-python.code-workspace new file mode 100644 index 0000000..6126db9 --- /dev/null +++ b/cfd-python.code-workspace @@ -0,0 +1,15 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "editor.formatOnSave": true, + "editor.rulers": [ + 80 + ], + "python.linting.flake8Enabled": true, + "python.linting.mypyEnabled": true + } +} \ No newline at end of file diff --git a/cfd/__init__.py b/cfd/__init__.py index a3c6560..782b73a 100644 --- a/cfd/__init__.py +++ b/cfd/__init__.py @@ -12,10 +12,13 @@ 'address', 'confidential_address', 'confidential_transaction', +'crypto', 'descriptor', 'hdwallet', 'key', +'psbt', 'script', 'transaction', +'taproot', 'util' """ diff --git a/cfd/address.py b/cfd/address.py index 1ce7b56..2bfd594 100644 --- a/cfd/address.py +++ b/cfd/address.py @@ -3,10 +3,11 @@ # @file address.py # @brief address function implements file. # @note Copyright 2020 CryptoGarage -import typing +from typing import Union, Optional, List from .util import get_util, CfdError, JobHandle, to_hex_string -from .key import Network, Pubkey +from .key import Network, Pubkey, SchnorrPubkey from .script import HashType, Script +from .taproot import TaprootScriptTree ## @@ -20,19 +21,19 @@ class Address: ## # @var locking_script # locking script (scriptPubkey) - locking_script: typing.Union[str, 'Script'] + locking_script: Union[str, 'Script'] ## # @var pubkey # pubkey for pubkey hash. - pubkey: typing.Union[str, 'Pubkey'] + pubkey: Union[str, 'Pubkey', 'SchnorrPubkey'] ## # @var redeem_script # redeem script for script hash. - redeem_script: typing.Union[str, 'Script'] + redeem_script: Union[str, 'Script'] ## # @var p2sh_wrapped_script # witness locking script for p2sh. - p2sh_wrapped_script: typing.Union[str, 'Script'] + p2sh_wrapped_script: Union[str, 'Script'] ## # @var hash_type # hash type. @@ -45,6 +46,10 @@ class Address: # @var witness_version # witness version. witness_version: int + ## + # @var taproot_script_tree + # Taproot script tree. + taproot_script_tree: Optional['TaprootScriptTree'] = None ## # @brief constructor. @@ -70,19 +75,28 @@ def __init__( self.address = address self.locking_script = _locking_script if len( _locking_script) == 0 else Script(locking_script) - self.pubkey = _pubkey if len(_pubkey) == 0 else Pubkey(pubkey) + if len(_pubkey) == 0: + self.pubkey = _pubkey + elif hash_type == HashType.TAPROOT: + self.pubkey = SchnorrPubkey(pubkey) + else: + self.pubkey = Pubkey(pubkey) self.redeem_script = _redeem_script if len( _redeem_script) == 0 else Script(redeem_script) self.p2sh_wrapped_script = p2sh_wrapped_script self.hash_type = HashType.get(hash_type) self.network = Network.get(network) self.witness_version = -1 + self.taproot_script_tree = None if p2sh_wrapped_script and len(p2sh_wrapped_script) > 2: - if int(_locking_script[0:2], 16) < 16: - self.witness_version = int(p2sh_wrapped_script[0:2]) + if int(p2sh_wrapped_script[0:2], 16) == 0: + self.witness_version = 0 elif len(_locking_script) > 2: - if int(_locking_script[0:2], 16) < 16: - self.witness_version = int(_locking_script[0:2]) + ver = int(_locking_script[0:2], 16) + if ver > 80: + ver -= 80 + if ver <= 16: + self.witness_version = ver ## # @brief get string. @@ -104,9 +118,8 @@ class AddressUtil: def parse(cls, address, hash_type=HashType.P2WPKH) -> 'Address': util = get_util() with util.create_handle() as handle: - network, _hash_type, _witness_version,\ - locking_script, _ = util.call_func( - 'CfdGetAddressInfo', handle.get_handle(), str(address)) + network, _hash_type, _, locking_script, _ = util.call_func( + 'CfdGetAddressInfo', handle.get_handle(), str(address)) _hash_type = HashType.get(_hash_type) try: if _hash_type == HashType.P2SH: @@ -182,6 +195,31 @@ def p2sh_p2wsh(cls, redeem_script, network=Network.MAINNET) -> 'Address': return cls.from_script_hash( redeem_script, HashType.P2SH_P2WSH, network) + ## + # @brief get taproot address. + # @param[in] pubkey schnorr public key + # (or taproot script tree contain internal pubkey) + # @param[in] network network + # @param[in] script_tree taproot script tree + # @return address object. + @classmethod + def taproot( + cls, pubkey: Union['SchnorrPubkey', 'TaprootScriptTree'], + network=Network.MAINNET, + script_tree: Optional['TaprootScriptTree'] = None) -> 'Address': + if isinstance(pubkey, TaprootScriptTree): + pk, _, _, _ = pubkey.get_taproot_data() + addr = cls.from_pubkey_hash(pk, HashType.TAPROOT, network) + addr.taproot_script_tree = script_tree + return addr + elif isinstance(script_tree, TaprootScriptTree): + pk, _, _, _ = script_tree.get_taproot_data(pubkey) + addr = cls.from_pubkey_hash(pk, HashType.TAPROOT, network) + addr.taproot_script_tree = script_tree + return addr + else: + return cls.from_pubkey_hash(pubkey, HashType.TAPROOT, network) + ## # @brief get pubkey hash address. # @param[in] pubkey public key @@ -208,7 +246,7 @@ def from_pubkey_hash( locking_script, hash_type=_hash_type, network=_network, - pubkey=Pubkey(_pubkey), + pubkey=_pubkey, p2sh_wrapped_script=segwit_locking_script) ## @@ -265,12 +303,12 @@ def multisig( _network = Network.get(network) util = get_util() with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeMultisigScript', handle.get_handle(), _network.value, _hash_type.value) with JobHandle( handle, - word_handle, + work_handle, 'CfdFreeMultisigScriptHandle') as addr_handle: for pubkey in pubkey_list: util.call_func( @@ -325,19 +363,19 @@ def get_multisig_address_list( cls, redeem_script, hash_type, - network=Network.MAINNET) -> typing.List['Address']: + network=Network.MAINNET) -> List['Address']: _script = str(redeem_script) _hash_type = HashType.get(hash_type) _network = Network.get(network) util = get_util() addr_list = [] with util.create_handle() as handle: - word_handle, max_index = util.call_func( + work_handle, max_index = util.call_func( 'CfdGetAddressesFromMultisig', handle.get_handle(), _script, _network.value, _hash_type.value) with JobHandle( handle, - word_handle, + work_handle, 'CfdFreeAddressesMultisigHandle') as addr_handle: for i in range(max_index): addr, pubkey = util.call_func( diff --git a/cfd/confidential_address.py b/cfd/confidential_address.py index c5d5c42..f85fcb9 100644 --- a/cfd/confidential_address.py +++ b/cfd/confidential_address.py @@ -3,7 +3,6 @@ # @file confidential_address.py # @brief elements confidential address function implements file. # @note Copyright 2020 CryptoGarage -import typing from .address import Address, AddressUtil from .key import Pubkey from .util import get_util, to_hex_string, CfdError diff --git a/cfd/confidential_transaction.py b/cfd/confidential_transaction.py index bc6aaac..2729ca8 100644 --- a/cfd/confidential_transaction.py +++ b/cfd/confidential_transaction.py @@ -3,7 +3,7 @@ # @file confidential_transaction.py # @brief elements confidential transaction function implements file. # @note Copyright 2020 CryptoGarage -from typing import AnyStr, Dict, List, Optional, Union +from typing import Dict, List, Optional, Tuple, Union import typing from .util import ReverseByteData, CfdError, JobHandle,\ CfdErrorCode, to_hex_string, get_util, ByteData @@ -54,14 +54,14 @@ def __init__(self, data): super().__init__(data) if len(self.hex) != 64: raise CfdError( - error_code=1, message=f'Error: Invalid blind factor.') + error_code=1, message='Error: Invalid blind factor.') ## # @brief check empty. # @retval True empty # @retval False not empty def is_empty(self): - return True if self.hex == '0'*64 else False + return True if self.hex == '0' * 64 else False ## @@ -341,7 +341,7 @@ def __init__( # @brief equal method. # @param[in] other other object. # @return true or false. - def __eq__(self, other: 'ElementsUtxoData') -> bool: + def __eq__(self, other: object) -> bool: if not isinstance(other, ElementsUtxoData): return NotImplemented return self.outpoint == other.outpoint @@ -359,7 +359,7 @@ def __lt__(self, other: 'ElementsUtxoData') -> bool: # @brief equal method. # @param[in] other other object. # @return true or false. - def __ne__(self, other: 'ElementsUtxoData') -> bool: + def __ne__(self, other: object) -> bool: return not self.__eq__(other) ## @@ -391,7 +391,7 @@ class UnblindData: ## # @var asset # asset - asset: Union[AnyStr, 'ConfidentialAsset'] + asset: Union[str, 'ConfidentialAsset'] ## # @var value # value @@ -544,7 +544,8 @@ def __init__(self, entropy='', nonce='', # @brief get string. # @return hex def __str__(self) -> str: - return '{},{},{}'.format(str(self.entropy), self.asset_value, self.token_value) + return '{},{},{}'.format( + str(self.entropy), self.asset_value, self.token_value) ## @@ -621,11 +622,11 @@ class ConfidentialTxOut(TxOut): ## # @var surjectionproof # surjection proof - surjectionproof: Union[List[int], AnyStr, 'ByteData'] + surjectionproof: Union[List[int], str, 'ByteData'] ## # @var rangeproof # range proof - rangeproof: Union[List[int], AnyStr, 'ByteData'] + rangeproof: Union[List[int], str, 'ByteData'] ## # @brief get destroy amount txout. @@ -686,9 +687,9 @@ def has_blind(self): # @param[in] network network # @param[in] is_confidential Returns Confidential Address if possible. # @return address. - def get_address(self, network=Network.LIQUID_V1, - is_confidential: bool = False, - ) -> Union['Address', 'ConfidentialAddress']: + def _get_address(self, network=Network.LIQUID_V1, + is_confidential: bool = False, + ) -> Union['Address', 'ConfidentialAddress', None]: _network = Network.get(network) if _network not in [Network.LIQUID_V1, Network.ELEMENTS_REGTEST]: raise CfdError(error_code=1, @@ -701,16 +702,37 @@ def get_address(self, network=Network.LIQUID_V1, ca = ConfidentialAddress.parse(self.address) return ca if is_confidential else ca.address addr = AddressUtil.parse(self.address) - if addr is None: + if (addr is None) and ((is_confidential is False) or ( + len(self.locking_script.hex) > 0)): addr = AddressUtil.from_locking_script( self.locking_script, _network) if self.has_blind() or self.nonce.is_empty() or ( - not is_confidential): + not is_confidential) or (addr is None): return addr else: return ConfidentialAddress(addr, Pubkey(self.nonce)) + ## + # @brief get address. + # @param[in] network network + # @return address. + def get_address(self, network=Network.LIQUID_V1) -> 'Address': + ret = self._get_address(network, False) + if isinstance(ret, Address): + return ret + raise CfdError(error_code=-2, message='Error: Internal error.') + + ## + # @brief get confidential address. + # @param[in] network network + # @return address. + def get_confidential_address( + self, + network=Network.LIQUID_V1) -> Optional['ConfidentialAddress']: + addr = self._get_address(network, True) + return addr if isinstance(addr, ConfidentialAddress) else None + ## # @class TargetAmountData @@ -734,8 +756,12 @@ class TargetAmountData: # @param[in] amount amount # @param[in] asset asset # @param[in] reserved_address reserved address - def __init__(self, amount: int, asset, - reserved_address: Union[str, 'Address', 'ConfidentialAddress'] = ''): + def __init__(self, + amount: int, + asset, + reserved_address: Union[str, + 'Address', + 'ConfidentialAddress'] = ''): self.amount = amount self.asset = ConfidentialAsset(asset) if isinstance(reserved_address, Address) or \ @@ -909,7 +935,10 @@ def create(cls, version: int, locktime: int, # @param[in] enable_cache enable tx cache # @return transaction object @classmethod - def from_hex(cls, hex, enable_cache: bool = True) -> 'ConfidentialTransaction': + def from_hex( + cls, + hex, + enable_cache: bool = True) -> 'ConfidentialTransaction': return ConfidentialTransaction(hex, enable_cache) ## @@ -999,6 +1028,27 @@ def _update_info(self) -> None: self.version = ret[6] self.locktime = ret[7] + ## + # @brief update transaction input. + # @param[in] outpoint outpoint + # @param[in] handle cfd handle + # @param[in] tx_handle tx handle + # @return void + def _update_txin_internal(self, handle, tx_handle, outpoint: 'OutPoint'): + if self.enable_cache is False: + return + util = get_util() + self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ + self.weight, self.version, self.locktime = util.call_func( + 'CfdGetConfidentialTxInfoByHandle', + handle.get_handle(), tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + # update txin + txin, index = self._get_txin( + handle, tx_handle, outpoint=outpoint) + self.txin_list[index] = txin + ## # @brief update transaction input. # @param[in] outpoint outpoint @@ -1007,22 +1057,9 @@ def _update_txin(self, outpoint: 'OutPoint'): if self.enable_cache is False: return util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTxDataHandle', handle.get_handle(), - self.NETWORK, self.hex) - with JobHandle(handle, _tx_handle, - self.FREE_FUNC_NAME) as tx_handle: - self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ - self.weight, self.version, self.locktime = util.call_func( - 'CfdGetConfidentialTxInfoByHandle', - handle.get_handle(), tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - # update txin - txin, index = self._get_txin( - handle, tx_handle, outpoint=outpoint) - self.txin_list[index] = txin + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + self._update_txin_internal(handle, tx_handle, outpoint) ## # @brief get transaction all data. @@ -1058,22 +1095,17 @@ def get_txout_list(handle, tx_handle): return txout_list util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTxDataHandle', handle.get_handle(), - self.NETWORK, self.hex) - with JobHandle( - handle, _tx_handle, - self.FREE_FUNC_NAME) as tx_handle: - self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ - self.weight, self.version, self.locktime = util.call_func( - 'CfdGetConfidentialTxInfoByHandle', - handle.get_handle(), tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - self.txin_list = get_txin_list(handle, tx_handle) - self.txout_list = get_txout_list(handle, tx_handle) - return self.txin_list, self.txout_list + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ + self.weight, self.version, self.locktime = util.call_func( + 'CfdGetConfidentialTxInfoByHandle', + handle.get_handle(), tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + self.txin_list = get_txin_list(handle, tx_handle) + self.txout_list = get_txout_list(handle, tx_handle) + return self.txin_list, self.txout_list ## # @brief get transaction output fee index. @@ -1136,38 +1168,34 @@ def add_destroy_amount_txout( def add(self, txins: List['ConfidentialTxIn'], txouts: List['ConfidentialTxOut']) -> None: util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTransaction', handle.get_handle(), - self.NETWORK, 0, 0, self.hex) - with JobHandle( - handle, _tx_handle, self.FREE_FUNC_NAME) as tx_handle: - for txin in txins: - sec = TxIn.get_sequence_number( - self.locktime, txin.sequence) - util.call_func( - 'CfdAddTransactionInput', handle.get_handle(), - tx_handle.get_handle(), str(txin.outpoint.txid), - txin.outpoint.vout, sec) - for txout in txouts: - util.call_func( - 'CfdAddConfidentialTxOutput', - handle.get_handle(), - tx_handle.get_handle(), txout.amount, - str(txout.address), - str(txout.locking_script), - str(txout.asset), str(txout.nonce)) - self.hex = util.call_func( - 'CfdFinalizeTransaction', handle.get_handle(), - tx_handle.get_handle()) - self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ - self.weight, self.version, self.locktime = util.call_func( - 'CfdGetConfidentialTxInfoByHandle', - handle.get_handle(), tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - self.txin_list += copy.deepcopy(txins) - self.txout_list += copy.deepcopy(txouts) + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + for txin in txins: + sec = TxIn.get_sequence_number( + self.locktime, txin.sequence) + util.call_func( + 'CfdAddTransactionInput', handle.get_handle(), + tx_handle.get_handle(), str(txin.outpoint.txid), + txin.outpoint.vout, sec) + for txout in txouts: + util.call_func( + 'CfdAddConfidentialTxOutput', + handle.get_handle(), + tx_handle.get_handle(), txout.amount, + str(txout.address), + str(txout.locking_script), + str(txout.asset), str(txout.nonce)) + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self.txid, self.wtxid, self.wit_hash, self.size, self.vsize,\ + self.weight, self.version, self.locktime = util.call_func( + 'CfdGetConfidentialTxInfoByHandle', + handle.get_handle(), tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + self.txin_list += copy.deepcopy(txins) + self.txout_list += copy.deepcopy(txouts) ## # @brief update transaction output amount. @@ -1231,9 +1259,10 @@ def blind( issuance_key_map={}, confidential_address_list=[], direct_confidential_key_map={}, - minimum_range_value: int = 1, exponent: int = 0, minimum_bits: int = -1, - collect_blinder: bool = False, - ) -> List[Union['BlindData', 'IssuanceAssetBlindData', 'IssuanceTokenBlindData']]: + minimum_range_value: int = 1, exponent: int = 0, + minimum_bits: int = -1, collect_blinder: bool = False, + ) -> List[Union['BlindData', 'IssuanceAssetBlindData', + 'IssuanceTokenBlindData']]: if minimum_bits == -1: minimum_bits = self.DEFAULT_BLIND_MINIMUM_BITS @@ -1292,7 +1321,8 @@ def set_opt(handle, tx_handle, key, i_val=0): 'CfdFinalizeBlindTx', handle.get_handle(), tx_handle.get_handle(), self.hex) self._update_tx_all() - blinder_list = [] + blinder_list: List[Union['BlindData', 'IssuanceAssetBlindData', + 'IssuanceTokenBlindData']] = [] if bool(collect_blinder): try: i = 0 @@ -1346,7 +1376,7 @@ def unblind_txout(self, index: int, blinding_key) -> 'UnblindData': # @retval [0] asset unblind data # @retval [1] token unblind data def unblind_issuance(self, index: int, asset_key, - token_key='') -> 'UnblindData': + token_key='') -> Tuple['UnblindData', 'UnblindData']: util = get_util() with util.create_handle() as handle: asset, asset_amount, asset_blinder, amount_blinder, token,\ @@ -1392,8 +1422,14 @@ def set_raw_reissue_asset(self, utxo: 'ElementsUtxoData', amount: int, # @param[in] redeem_script redeem script # @param[in] sighashtype sighash type # @return sighash - def get_sighash(self, outpoint: 'OutPoint', hash_type, value, pubkey='', - redeem_script='', sighashtype=SigHashType.ALL) -> 'ByteData': + def get_sighash( + self, + outpoint: 'OutPoint', + hash_type, + value, + pubkey='', + redeem_script='', + sighashtype=SigHashType.ALL) -> 'ByteData': _hash_type = HashType.get(hash_type) _pubkey = to_hex_string(pubkey) _script = to_hex_string(redeem_script) @@ -1684,7 +1720,8 @@ def fund_raw_transaction( long_term_fee_rate: float = -1.0, dust_fee_rate: float = -1.0, knapsack_min_change: int = -1, is_blind: bool = True, - exponent: int = 0, minimum_bits: int = 52) -> typing.Tuple[int, List[str]]: + exponent: int = 0, + minimum_bits: int = 52) -> typing.Tuple[int, List[str]]: util = get_util() def set_opt(handle, tx_handle, key, i_val=0, f_val=0, b_val=False): diff --git a/cfd/crypto.py b/cfd/crypto.py new file mode 100644 index 0000000..1c5b3c5 --- /dev/null +++ b/cfd/crypto.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +## +# @file crypto.py +# @brief crypto (encrypto, hash, etc.) function implements file. +# @note Copyright 2021 CryptoGarage +from .util import get_util, to_hex_string, CfdError, ByteData + + +## +# @class CryptoUtil +# @brief crypto utility class. +class CryptoUtil: + ## + # @brief encrypto AES. + # @param[in] key encrypto key data + # @param[in] data encrypto target data + # @param[in] iv initial vector. (for CBC mode) + # @return aes data. + @classmethod + def encrypto_aes(cls, key, data, iv=None) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + cbc_iv = '' if iv is None else to_hex_string(iv) + encoded_data = util.call_func( + 'CfdEncryptAES', handle.get_handle(), + to_hex_string(key), cbc_iv, to_hex_string(data)) + return ByteData(encoded_data) + + ## + # @brief encrypto AES. + # @param[in] key encrypto key data + # @param[in] data encrypted data + # @param[in] iv initial vector. (for CBC mode) + # @return aes data. + @classmethod + def decrypto_aes(cls, key, data, iv=None) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + cbc_iv = '' if iv is None else to_hex_string(iv) + decoded_data = util.call_func( + 'CfdDecryptAES', handle.get_handle(), + to_hex_string(key), cbc_iv, to_hex_string(data)) + return ByteData(decoded_data) + + ## + # @brief Encode base64. + # @param[in] data encode target data + # @return base64 encoded data + @classmethod + def encode_base64(cls, data) -> str: + util = get_util() + with util.create_handle() as handle: + encoded_data = util.call_func( + 'CfdEncodeBase64', handle.get_handle(), to_hex_string(data)) + return encoded_data + + ## + # @brief Decode base64. + # @param[in] data base64 encoded data + # @return data + @classmethod + def decode_base64(cls, data: str) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + decoded_data = util.call_func( + 'CfdDecodeBase64', handle.get_handle(), data) + if (len(decoded_data) == 0) and (len(data) != 0): + raise CfdError(error_code=1, message='Decode base64 error.') + return ByteData(decoded_data) + + ## + # @brief Encode base58. + # @param[in] data encode target data + # @param[in] use_checksum use base58 checksum + # @return base58 encoded data + @classmethod + def encode_base58(cls, data, use_checksum: bool = True) -> str: + util = get_util() + with util.create_handle() as handle: + encoded_data = util.call_func( + 'CfdEncodeBase58', + handle.get_handle(), + to_hex_string(data), + use_checksum) + return encoded_data + + ## + # @brief Decode base58. + # @param[in] data base58 encoded data + # @param[in] use_checksum use base58 checksum + # @return data + @classmethod + def decode_base58(cls, data: str, use_checksum: bool = True) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + decoded_data = util.call_func( + 'CfdDecodeBase58', handle.get_handle(), data, use_checksum) + return ByteData(decoded_data) + + +## +# @class HashUtil +# @brief hash utility class. +class HashUtil: + ## + # @brief hash function. + # @param[in] func_name function name + # @param[in] message message string. (text or binary data) + # @param[in] has_text message has text. + # @return hashed data. + @classmethod + def _hash_function( + cls, + func_name, + message, + has_text: bool = False) -> 'ByteData': + if (has_text is True) and (isinstance(message, str) is False): + raise CfdError( + error_code=1, + message='Error: Text mode requires a string message.') + util = get_util() + with util.create_handle() as handle: + data = message if has_text is True else to_hex_string(message) + hashed_data = util.call_func( + func_name, handle.get_handle(), data, bool(has_text)) + return ByteData(hashed_data) + + ## + # @brief hash ripemd160. + # @param[in] message message string. (text or binary data) + # @param[in] has_text message has text. + # @return hashed data. + @classmethod + def ripemd160(cls, message, has_text: bool = False) -> 'ByteData': + return cls._hash_function('CfdRipemd160', message, has_text) + + ## + # @brief hash sha256. + # @param[in] message message string. (text or binary data) + # @param[in] has_text message has text. + # @return hashed data. + @classmethod + def sha256(cls, message, has_text: bool = False) -> 'ByteData': + return cls._hash_function('CfdSha256', message, has_text) + + ## + # @brief hash hash160. + # @param[in] message message string. (text or binary data) + # @param[in] has_text message has text. + # @return hashed data. + @classmethod + def hash160(cls, message, has_text: bool = False) -> 'ByteData': + return cls._hash_function('CfdHash160', message, has_text) + + ## + # @brief hash hash256. + # @param[in] message message string. (text or binary data) + # @param[in] has_text message has text. + # @return hashed data. + @classmethod + def hash256(cls, message, has_text: bool = False) -> 'ByteData': + return cls._hash_function('CfdHash256', message, has_text) + + +## +# All import target. +__all__ = [ + 'CryptoUtil', + 'HashUtil', +] diff --git a/cfd/descriptor.py b/cfd/descriptor.py index 2173be1..599c3c7 100644 --- a/cfd/descriptor.py +++ b/cfd/descriptor.py @@ -229,7 +229,7 @@ class DescriptorScriptData: ## # @var key_data # key data - key_data: Optional['DescriptorKeyType'] + key_data: Optional['DescriptorKeyData'] ## # @var key_list # key list @@ -256,7 +256,7 @@ def __init__( locking_script, redeem_script='', key_data: Optional['DescriptorKeyData'] = None, - key_list: List['DescriptorKeyType'] = [], + key_list: List['DescriptorKeyData'] = [], multisig_require_num: int = 0): self.script_type = script_type self.depth = depth @@ -324,12 +324,12 @@ def _verify(self, descriptor: str) -> str: def _parse(self) -> List['DescriptorScriptData']: util = get_util() with util.create_handle() as handle: - word_handle, max_index = util.call_func( + work_handle, max_index = util.call_func( 'CfdParseDescriptor', handle.get_handle(), self.descriptor, self.network.value, self.path) with JobHandle( handle, - word_handle, + work_handle, 'CfdFreeDescriptorHandle') as desc_handle: def get_key(index): diff --git a/cfd/hdwallet.py b/cfd/hdwallet.py index e5cfa53..ba8f3f4 100644 --- a/cfd/hdwallet.py +++ b/cfd/hdwallet.py @@ -4,8 +4,9 @@ # @brief hdwallet function implements file. # @note Copyright 2020 CryptoGarage import typing -from typing import List, Tuple, Union -from .util import ByteData, CfdUtil, get_util, JobHandle, to_hex_string, CfdError +from typing import List, Tuple, Union, Optional +from .util import ByteData, CfdUtil, get_util, JobHandle,\ + to_hex_string, CfdError from .key import Network, Privkey, Pubkey from enum import Enum import unicodedata @@ -325,7 +326,7 @@ def derive(self, path: str = '', number: int = 0, # @param[in] number_list bip32 number list # @return ExtPubkey def derive_pubkey(self, path: str = '', number: int = 0, - number_list: typing.List[int] = []) -> 'ExtPrivkey': + number_list: typing.List[int] = []) -> 'ExtPubkey': return self.derive( path=path, number=number, @@ -590,9 +591,13 @@ def from_seed(cls, seed, network=Network.MAINNET) -> 'HDWallet': # @param[in] strict_check strict check # @return HDWallet @classmethod - def from_mnemonic( - cls, mnemonic: Union[str, List[str]], language='en', passphrase: str = '', - network=Network.MAINNET, strict_check: bool = True) -> 'HDWallet': + def from_mnemonic(cls, + mnemonic: Union[str, + List[str]], + language='en', + passphrase: str = '', + network=Network.MAINNET, + strict_check: bool = True) -> 'HDWallet': return HDWallet( mnemonic=mnemonic, language=language, passphrase=passphrase, network=network, strict_check=strict_check) @@ -655,6 +660,100 @@ def _convert_mnemonic(cls, mnemonic): return _words.replace(' ', ' ') if isinstance(_words, str) else _words +## +# @class KeyData +# @brief KeyData class. +class KeyData: + ## + # @var pubkey + # pubkey + pubkey: 'Pubkey' + ## + # @var privkey + # privkey + privkey: Optional['Privkey'] = None + ## + # @var ext_pubkey + # ext pubkey + ext_pubkey: Optional['ExtPubkey'] = None + ## + # @var ext_privkey + # ext privkey + ext_privkey: Optional['ExtPrivkey'] = None + ## + # @var fingerprint + # fingerprint + fingerprint: Optional['ByteData'] = None + ## + # @var bip32_path + # bip32 path + bip32_path: str + + ## + # @brief constructor. + # @param[in] key key. + # @param[in] fingerprint fingerprint or parent key. + # @param[in] bip32_path bip32 path + def __init__(self, + key: Union['Pubkey', + 'Privkey', + 'ExtPubkey', + 'ExtPrivkey'], + fingerprint: Optional[Union['ByteData', + 'Pubkey', + 'Privkey', + 'ExtPubkey', + 'ExtPrivkey']], + bip32_path: str = ''): + if isinstance(key, ExtPrivkey): + self.ext_privkey = key + self.privkey = self.ext_privkey.privkey + self.pubkey = self.privkey.pubkey + elif isinstance(key, ExtPubkey): + self.ext_pubkey = key + self.pubkey = self.ext_pubkey.pubkey + elif isinstance(key, Privkey): + self.privkey = key + self.pubkey = self.privkey.pubkey + elif isinstance(key, Pubkey): + self.pubkey = key + else: + raise CfdError(error_code=1, message='Error: Unsupported key.') + + if isinstance(fingerprint, ByteData): + if len(fingerprint.hex) < 8: + raise CfdError( + error_code=1, message='Error: fingerprint is low size.') + self.fingerprint = fingerprint + elif isinstance(fingerprint, ExtPrivkey): + self.fingerprint = fingerprint.privkey.pubkey.get_fingerprint() + elif isinstance(fingerprint, ExtPubkey) or isinstance( + fingerprint, Privkey): + self.fingerprint = fingerprint.pubkey.get_fingerprint() + elif isinstance(fingerprint, Pubkey): + self.fingerprint = fingerprint.get_fingerprint() + elif fingerprint is None: + pass + else: + raise CfdError( + error_code=1, message='Error: Unsupported fingerprint.') + self.bip32_path = str(bip32_path) + + ## + # @brief get string. + # @return pubkey & bip32 data. + def __str__(self) -> str: + if (not self.fingerprint) or (not self.bip32_path): + return str(self.pubkey) + fp_str = to_hex_string(self.fingerprint) + if len(fp_str) < 8: + return str(self.pubkey) + path = self.bip32_path + path = path[1:] if path[0] == 'm' else path + path = path[1:] if path[0] == '/' else path + return f'[{fp_str}/{path}]{self.pubkey._hex}' + + ## # All import target. __all__ = [ @@ -664,6 +763,7 @@ def _convert_mnemonic(cls, mnemonic): 'ExtPubkey', 'MnemonicLanguage', 'HDWallet', + 'KeyData', 'XPRIV_MAINNET_VERSION', 'XPRIV_TESTNET_VERSION', 'XPUB_MAINNET_VERSION', diff --git a/cfd/key.py b/cfd/key.py index bc10d61..f6e4f5d 100644 --- a/cfd/key.py +++ b/cfd/key.py @@ -3,9 +3,10 @@ # @file key.py # @brief key function implements file. # @note Copyright 2020 CryptoGarage -from typing import Optional, Union +from typing import Optional, Tuple, Union import typing -from .util import ByteData, get_util, CfdError, to_hex_string, CfdErrorCode, JobHandle +from .util import ByteData, get_util, CfdError,\ + to_hex_string, CfdErrorCode, JobHandle import hashlib from enum import Enum @@ -89,6 +90,9 @@ def get_mainchain(cls, network) -> 'Network': # @class SigHashType # @brief Signature hash type class SigHashType(Enum): + ## + # SigHashType: default + DEFAULT = 0 ## # SigHashType: all ALL = 1 @@ -222,7 +226,11 @@ def generate(cls, is_compressed: bool = True, network=Network.MAINNET): # @param[in] is_compressed pubkey compressed # @return private key @classmethod - def from_hex(cls, hex, network=Network.MAINNET, is_compressed: bool = True): + def from_hex( + cls, + hex, + network=Network.MAINNET, + is_compressed: bool = True): return Privkey(hex=hex, network=network, is_compressed=is_compressed) @@ -336,7 +344,10 @@ def negate(self) -> 'Privkey': # @param[in] sighash sighash # @param[in] grind_r grind-r flag # @return signature - def calculate_ec_signature(self, sighash, grind_r: bool = True) -> 'SignParameter': + def calculate_ec_signature( + self, + sighash, + grind_r: bool = True) -> 'SignParameter': _sighash = to_hex_string(sighash) util = get_util() with util.create_handle() as handle: @@ -370,9 +381,9 @@ def combine(cls, pubkey_list) -> 'Pubkey': message='Error: Invalid pubkey list.') util = get_util() with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeCombinePubkey', handle.get_handle()) - with JobHandle(handle, word_handle, + with JobHandle(handle, work_handle, 'CfdFreeCombinePubkeyHandle') as key_handle: for pubkey in pubkey_list: util.call_func( @@ -405,6 +416,16 @@ def __init__(self, pubkey): def __str__(self) -> str: return self._hex + ## + # @brief get fingerprint. + # @return fingerprint. + def get_fingerprint(self) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + fingerprint = util.call_func( + 'CfdGetPubkeyFingerprint', handle.get_handle(), self._hex) + return ByteData(fingerprint) + ## # @brief compress pubkey. # @return compressed pubkey. @@ -472,8 +493,11 @@ def verify_ec_signature(self, sighash, signature) -> bool: util = get_util() with util.create_handle() as handle: util.call_func( - 'CfdVerifyEcSignature', handle.get_handle(), - to_hex_string(sighash), self._hex, to_hex_string(signature)) + 'CfdVerifyEcSignature', + handle.get_handle(), + to_hex_string(sighash), + self._hex, + to_hex_string(signature)) return True except CfdError as err: if err.error_code == CfdErrorCode.SIGN_VERIFICATION.value: @@ -509,7 +533,10 @@ class SignParameter: # @param[in] sighashtype sighash type # @return der encoded signature @classmethod - def encode_by_der(cls, signature, sighashtype=SigHashType.ALL) -> 'SignParameter': + def encode_by_der( + cls, + signature, + sighashtype=SigHashType.ALL) -> 'SignParameter': _signature = to_hex_string(signature) _sighashtype = SigHashType.get(sighashtype) util = get_util() @@ -632,7 +659,11 @@ def adapt(cls, adaptor_signature, adaptor_secret) -> 'ByteData': # @param[in] adaptor adaptor bytes # @return adaptor secret key @classmethod - def extract_secret(cls, adaptor_signature, signature, adaptor) -> 'Privkey': + def extract_secret( + cls, + adaptor_signature, + signature, + adaptor) -> 'Privkey': _adaptor_signature = to_hex_string(adaptor_signature) _signature = to_hex_string(signature) _adaptor = to_hex_string(adaptor) @@ -695,7 +726,7 @@ class SchnorrPubkey: # @retval [0] SchnorrPubkey # @retval [1] parity flag @classmethod - def from_privkey(cls, privkey) -> 'SchnorrPubkey': + def from_privkey(cls, privkey) -> Tuple['SchnorrPubkey', bool]: if isinstance(privkey, Privkey): _privkey = privkey.hex elif isinstance(privkey, str) and (len(privkey) != 64): @@ -716,7 +747,7 @@ def from_privkey(cls, privkey) -> 'SchnorrPubkey': # @retval [0] SchnorrPubkey # @retval [1] parity flag @classmethod - def from_pubkey(cls, pubkey) -> 'SchnorrPubkey': + def from_pubkey(cls, pubkey) -> Tuple['SchnorrPubkey', bool]: _pubkey = to_hex_string(pubkey) util = get_util() with util.create_handle() as handle: @@ -733,7 +764,8 @@ def from_pubkey(cls, pubkey) -> 'SchnorrPubkey': # @retval [1] tweaked parity flag # @retval [2] tweaked Privkey @classmethod - def add_tweak_from_privkey(cls, privkey, tweak) -> 'SchnorrPubkey': + def add_tweak_from_privkey( + cls, privkey, tweak) -> Tuple['SchnorrPubkey', bool, 'Privkey']: if isinstance(privkey, Privkey): _privkey = privkey.hex elif isinstance(privkey, str) and (len(privkey) != 64): @@ -770,7 +802,7 @@ def __str__(self) -> str: # @param[in] tweak tweak data # @retval [0] tweaked SchnorrPubkey # @retval [1] tweaked parity flag - def add_tweak(self, tweak) -> 'SchnorrPubkey': + def add_tweak(self, tweak) -> Tuple['SchnorrPubkey', bool]: _tweak = to_hex_string(tweak) util = get_util() with util.create_handle() as handle: diff --git a/cfd/psbt.py b/cfd/psbt.py new file mode 100644 index 0000000..aeabac8 --- /dev/null +++ b/cfd/psbt.py @@ -0,0 +1,1966 @@ +# -*- coding: utf-8 -*- +## +# @file psbt.py +# @brief Partially Signed Bitcoin Transaction function implements file. +# @note Copyright 2021 CryptoGarage +from typing import Any, List, Optional, Tuple, Union +import typing +from .address import Address, AddressUtil +from .descriptor import Descriptor, parse_descriptor +from .hdwallet import KeyData, ExtPubkey +from .key import Network, Pubkey, Privkey, SignParameter, SigHashType +from .script import Script +from .transaction import Transaction, OutPoint, TxIn, TxOut, UtxoData +from .util import ByteData, get_util, CfdError,\ + to_hex_string, CfdErrorCode, JobHandle +from enum import Enum + + +## +# signature error message +NOT_PERMIT_SIG_ERR_MSG = \ + 'Error: tx inputs must not have scriptSigs and witness stacks.' + + +## +# @class PsbtDefinition +# @brief Psbt definition class. +class PsbtDefinition(Enum): + ## + # PSBT_GLOBAL_UNSIGNED_TX + PSBT_GLOBAL_UNSIGNED_TX = '00' + ## + # PSBT_GLOBAL_XPUB + PSBT_GLOBAL_XPUB = '01' + ## + # PSBT_GLOBAL_VERSION + PSBT_GLOBAL_VERSION = 'fb' + ## + # PSBT_GLOBAL_PROPRIETARY + PSBT_GLOBAL_PROPRIETARY = 'fc' + ## + # PSBT_IN_NON_WITNESS_UTXO + PSBT_IN_NON_WITNESS_UTXO = '00' + ## + # PSBT_IN_WITNESS_UTXO + PSBT_IN_WITNESS_UTXO = '01' + ## + # PSBT_IN_PARTIAL_SIG + PSBT_IN_PARTIAL_SIG = '02' + ## + # PSBT_IN_SIGHASH_TYPE + PSBT_IN_SIGHASH_TYPE = '03' + ## + # PSBT_IN_REDEEM_SCRIPT + PSBT_IN_REDEEM_SCRIPT = '04' + ## + # PSBT_IN_WITNESS_SCRIPT + PSBT_IN_WITNESS_SCRIPT = '05' + ## + # PSBT_IN_BIP32_DERIVATION + PSBT_IN_BIP32_DERIVATION = '06' + ## + # PSBT_IN_FINAL_SCRIPTSIG + PSBT_IN_FINAL_SCRIPTSIG = '07' + ## + # PSBT_IN_FINAL_SCRIPTWITNESS + PSBT_IN_FINAL_SCRIPTWITNESS = '08' + ## + # PSBT_IN_POR_COMMITMENT + PSBT_IN_POR_COMMITMENT = '09' + ## + # PSBT_IN_RIPEMD160 + PSBT_IN_RIPEMD160 = '0a' + ## + # PSBT_IN_SHA256 + PSBT_IN_SHA256 = '0b' + ## + # PSBT_IN_HASH160 + PSBT_IN_HASH160 = '0c' + ## + # PSBT_IN_HASH256 + PSBT_IN_HASH256 = '0d' + ## + # PSBT_IN_PROPRIETARY + PSBT_IN_PROPRIETARY = 'fc' + ## + # PSBT_OUT_REDEEM_SCRIPT + PSBT_OUT_REDEEM_SCRIPT = '00' + ## + # PSBT_OUT_WITNESS_SCRIPT + PSBT_OUT_WITNESS_SCRIPT = '01' + ## + # PSBT_OUT_BIP32_DERIVATION + PSBT_OUT_BIP32_DERIVATION = '02' + ## + # PSBT_OUT_PROPRIETARY + PSBT_OUT_PROPRIETARY = 'fc' + + +## +# @class PsbtAppendInputData +# @brief Psbt append input data class. +class PsbtAppendInputData: + ## + # @var txin + # transaction input + txin: TxIn + ## + # @var utxo_amount + # witness utxo amount + utxo_amount: int + ## + # @var utxo_locking_script + # witness utxo locking script + utxo_locking_script: str + ## + # @var utxo_tx + # transaction hex + utxo_tx: str + ## + # @var descriptor + # descriptor + descriptor: str + ## + # @var redeem_script + # redeem script + redeem_script: str + ## + # @var is_scripthash + # is scripthash + is_scripthash: bool + + ## + # @brief constructor. + # @param[in] outpoint outpoint + # @param[in] utxo utxo + # @param[in] descriptor descriptor + # @param[in] redeem_script redeem script + # @param[in] utxo_tx utxo tx + # @param[in] sequence sequence + # @param[in] network network type + def __init__(self, outpoint: 'OutPoint', utxo: 'TxOut', + descriptor: Union['Descriptor', str] = '', + redeem_script: Union['Script', str] = '', + utxo_tx: Union['Transaction', str] = '', + sequence: int = TxIn.SEQUENCE_DISABLE, + network=Network.MAINNET): + _network = Network.get(network) + _locking_script: Union['Script', str] = utxo.locking_script + if utxo.address: + if isinstance(utxo.address, Address): + _locking_script = utxo.address.locking_script + else: + _locking_script = AddressUtil.parse( + utxo.address).locking_script + self.utxo_tx = to_hex_string(utxo_tx) + _script = to_hex_string(redeem_script) + _desc = str(descriptor) + is_scripthash = True if _script else False + if (not is_scripthash) and _desc: + desc_obj = descriptor if isinstance( + descriptor, Descriptor) else parse_descriptor(_desc, _network) + is_scripthash = True if desc_obj.data.redeem_script else False + self.txin = TxIn(outpoint, sequence=sequence) + self.descriptor = _desc + self.utxo_amount = utxo.amount + self.utxo_locking_script = to_hex_string(_locking_script) + self.redeem_script = _script + self.is_scripthash = is_scripthash + + +## +# @class PsbtAppendOutputData +# @brief Psbt append output data class. +class PsbtAppendOutputData: + ## + # @var amount + # witness amount + amount: int + ## + # @var locking_script + # witness locking script + locking_script: str + ## + # @var descriptor + # descriptor + descriptor: str + ## + # @var redeem_script + # redeem script + redeem_script: str + ## + # @var is_scripthash + # is scripthash + is_scripthash: bool + + ## + # @brief constructor. + # @param[in] amount amount + # @param[in] locking_script locking script + # @param[in] address address + # @param[in] descriptor descriptor + # @param[in] redeem_script redeem script + # @param[in] network network type + def __init__(self, amount: int, + locking_script: Union['Script', str] = '', + address: Union['Address', str] = '', + descriptor: Union['Descriptor', str] = '', + redeem_script: Union['Script', str] = '', + network=Network.MAINNET): + _network = Network.get(network) + _locking_script = locking_script + if address: + if isinstance(address, Address): + _locking_script = address.locking_script + else: + _locking_script = AddressUtil.parse(address).locking_script + _script = to_hex_string(redeem_script) + _desc = str(descriptor) + is_scripthash = True if _script else False + if (not is_scripthash) and _desc: + desc_obj = descriptor if isinstance( + descriptor, Descriptor) else parse_descriptor(_desc, _network) + is_scripthash = True if desc_obj.data.redeem_script else False + self.descriptor = _desc + self.amount = amount + self.locking_script = to_hex_string(_locking_script) + self.redeem_script = _script + self.is_scripthash = is_scripthash + + +## +# @class Psbt +# @brief Psbt class. +class Psbt: + ## + # @var base64 + # base64 string + base64: str + ## + # @var network + # network type. + network: 'Network' + + ## + # @brief create psbt. + # @param[in] tx_version transaction version + # @param[in] locktime locktime + # @param[in] network network type + # @return psbt object + @classmethod + def create(cls, tx_version: int = Transaction.DEFAULT_VERSION, + locktime: int = 0, + network=Network.MAINNET) -> 'Psbt': + _network = Network.get(network) + util = get_util() + with util.create_handle() as handle: + work_handle = util.call_func( + 'CfdCreatePsbtHandle', handle.get_handle(), + _network.value, '', '', tx_version, locktime) + with JobHandle(handle, work_handle, + 'CfdFreePsbtHandle') as tx_handle: + base64, _ = util.call_func( + 'CfdGetPsbtData', handle.get_handle(), + tx_handle.get_handle()) + obj = Psbt('', _network) + obj.base64 = base64 + return obj + + ## + # @brief convert transaction. + # @param[in] transaction bitcoin transaction + # @param[in] permit_sig_data permit signature data + # @param[in] network network type + # @return psbt object + @classmethod + def from_transaction( + cls, transaction: 'Transaction', + permit_sig_data: bool = False, + network=Network.MAINNET) -> 'Psbt': + tx = transaction if isinstance( + transaction, Transaction) else Transaction( + to_hex_string(transaction)) + _network = Network.get(network) + util = get_util() + with util.create_handle() as handle: + if permit_sig_data: + tx.clear_sign_data() + else: + + for txin in tx.txin_list: + if str(txin.script_sig) or txin.witness_stack: + raise CfdError(error_code=1, + message=NOT_PERMIT_SIG_ERR_MSG) + + work_handle = util.call_func( + 'CfdCreatePsbtHandle', handle.get_handle(), + _network.value, '', tx.hex, 0, 0) + with JobHandle(handle, work_handle, + 'CfdFreePsbtHandle') as tx_handle: + base64, _ = util.call_func( + 'CfdGetPsbtData', handle.get_handle(), + tx_handle.get_handle()) + obj = Psbt('', _network) + obj.base64 = base64 + return obj + + ## + # @brief join psbts. + # @param[in] psbts psbt (object or list) + # @param[in] network network type + # @return psbt object + @classmethod + def join_psbts( + cls, psbts: List[Any], + network=Network.MAINNET) -> 'Psbt': + if len(psbts) < 0: + raise CfdError(error_code=1, message='Error: list is empty.') + + psbt = Psbt(psbts[0], network) + if len(psbts) == 1: + return psbt + + psbt.join(psbts[1:]) + return psbt + + ## + # @brief combine psbts. + # @param[in] psbts psbt (object or list) + # @param[in] network network type + # @return psbt object + @classmethod + def combine_psbts( + cls, psbts: List[Any], + network=Network.MAINNET) -> 'Psbt': + if len(psbts) < 0: + raise CfdError(error_code=1, message='Error: list is empty.') + + psbt = Psbt(psbts[0], network) + if len(psbts) == 1: + return psbt + + psbt.combine(psbts[1:]) + return psbt + + ## + # @brief decode psbt. + # @param[in] psbt psbt + # @param[in] network network type + # @param[in] has_detail detail output flag + # @param[in] has_simple simple output flag + # @return json string + @classmethod + def parse_to_json( + cls, psbt, network=Network.MAINNET, + has_detail: bool = False, has_simple: bool = False) -> str: + _network = Network.get(network) + network_str = 'mainnet' + if _network == Network.TESTNET: + network_str = 'testnet' + elif _network == Network.REGTEST: + network_str = 'regtest' + has_detail_str = 'true' if has_detail else 'false' + has_simple_str = 'true' if has_simple else 'false' + request_json = \ + f'{{"psbt":"{str(psbt)}","network":"{network_str}",' + \ + f'"hasDetail":{has_detail_str},"hasSimple":{has_simple_str}}}' + util = get_util() + with util.create_handle() as handle: + return util.call_func( + 'CfdRequestExecuteJson', handle.get_handle(), + 'DecodePsbt', request_json) + + ## + # @brief constructor. + # @param[in] psbt psbt string (base64 or bytes) + # @param[in] network network + def __init__(self, psbt, network=Network.MAINNET): + self.base64 = '' + self.network = Network.get(network) + if isinstance(psbt, str): + _psbt = psbt + elif isinstance(psbt, Psbt): + _psbt = psbt.base64 + else: + _psbt = to_hex_string(psbt) + if len(_psbt) > 0: + util = get_util() + with util.create_handle() as handle: + work_handle = util.call_func( + 'CfdCreatePsbtHandle', handle.get_handle(), + self.network.value, _psbt, '', 0, 0) + with JobHandle(handle, work_handle, + 'CfdFreePsbtHandle') as tx_handle: + self.base64, _ = util.call_func( + 'CfdGetPsbtData', handle.get_handle(), + tx_handle.get_handle()) + + ## + # @brief get string. + # @return base64. + def __str__(self) -> str: + return self.base64 + + ## + # @brief get byte data. + # @return byte data + def get_bytes(self) -> 'ByteData': + util = get_util() + with util.create_handle() as handle: + _hex = util.call_func( + 'CfdDecodeBase64', handle.get_handle(), self.base64) + return ByteData(_hex) + + ## + # @brief get global data. + # @retval [0] transaction + # @retval [1] psbt version + # @retval [2] transaction input count + # @retval [3] transaction output count + def get_global_data(self) -> typing.Tuple['Transaction', int, int, int]: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + _version, _tx, _in_count, _out_count = util.call_func( + 'CfdGetPsbtGlobalData', handle.get_handle(), + tx_handle.get_handle()) + return Transaction(_tx), _version, _in_count, _out_count + + ## + # @brief get transaction. + # @return transaction + def get_tx(self) -> 'Transaction': + _ret = self.get_global_data() + return _ret[0] + + ## + # @brief get transaction input/output count. + # @retval [0] txin count + # @retval [1] txout count + def get_tx_count(self) -> Tuple[int, int]: + tx = self.get_tx() + return len(tx.txin_list), len(tx.txout_list) + + ## + # @brief get psbt version. + # @return psbt version + def get_version(self) -> int: + _ret = self.get_global_data() + return _ret[1] + + ## + # @brief join. + # @param[in] psbts psbt (object or list) + # @return void + def join(self, psbts) -> None: + util = get_util() + + def join_func(handle, psbt_handle, psbt): + if isinstance(psbt, Psbt): + _psbt = psbt.base64 + else: + _psbt = str(psbt) + util.call_func('CfdJoinPsbt', handle.get_handle(), + psbt_handle.get_handle(), _psbt) + + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + if isinstance(psbts, list): + for psbt in psbts: + join_func(handle, tx_handle, psbt) + else: + join_func(handle, tx_handle, psbts) + self._update_base64(util, handle, tx_handle) + + ## + # @brief sign. + # @param[in] privkey privkey + # @param[in] has_grind_r grind-r flag + # @return void + def sign(self, privkey, has_grind_r: bool = True) -> None: + util = get_util() + if isinstance(privkey, Privkey): + _privkey = privkey + elif isinstance(privkey, str) and (len(privkey) != 64): + _privkey = Privkey(wif=privkey) + else: + _privkey = Privkey(hex=privkey) + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSignPsbt', handle.get_handle(), + tx_handle.get_handle(), str(_privkey), has_grind_r) + self._update_base64(util, handle, tx_handle) + + ## + # @brief combine. + # @param[in] psbts psbt (object or list) + # @return void + def combine(self, psbts) -> None: + util = get_util() + + def combine_func(handle, psbt_handle, psbt): + if isinstance(psbt, Psbt): + _psbt = psbt.base64 + else: + _psbt = str(psbt) + util.call_func( + 'CfdCombinePsbt', handle.get_handle(), + psbt_handle.get_handle(), _psbt) + + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + if isinstance(psbts, list): + for psbt in psbts: + combine_func(handle, tx_handle, psbt) + else: + combine_func(handle, tx_handle, psbts) + self._update_base64(util, handle, tx_handle) + + ## + # @brief finalize. + # @return void + def finalize(self) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdFinalizePsbt', handle.get_handle(), + tx_handle.get_handle()) + self._update_base64(util, handle, tx_handle) + + ## + # @brief extract. + # @param[in] exec_finalize execute finalize (if finalized is false) + # @return Transaction + def extract(self, exec_finalize: bool = True) -> 'Transaction': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + is_finalized = False + try: + util.call_func('CfdIsFinalizedPsbt', handle.get_handle(), + tx_handle.get_handle()) + is_finalized = True + except CfdError as err: + if err.error_code != CfdErrorCode.SIGN_VERIFICATION.value: + raise err + if exec_finalize and (not is_finalized): + util.call_func('CfdFinalizePsbt', handle.get_handle(), + tx_handle.get_handle()) + self._update_base64(util, handle, tx_handle) + _tx = util.call_func( + 'CfdExtractPsbtTransaction', handle.get_handle(), + tx_handle.get_handle()) + return Transaction(_tx) + + ## + # @brief check finalized. + # @retval True finalized + # @retval False not finalized + def is_finalized(self) -> bool: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + try: + util.call_func('CfdIsFinalizedPsbt', handle.get_handle(), + tx_handle.get_handle()) + return True + except CfdError as err: + if err.error_code != CfdErrorCode.SIGN_VERIFICATION.value: + raise err + return False + + ## + # @brief check finalized. + # @param[in] outpoint outpoint + # @retval True finalized + # @retval False not finalized + def is_finalized_input(self, outpoint: 'OutPoint') -> bool: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + try: + util.call_func('CfdIsFinalizedPsbtInput', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), + outpoint.vout) + return True + except CfdError as err: + if err.error_code != CfdErrorCode.SIGN_VERIFICATION.value: + raise err + return False + + ## + # @brief add input. + # @param[in] outpoint outpoint + # @param[in] utxo utxo + # @param[in] descriptor descriptor + # @param[in] redeem_script redeem script + # @param[in] utxo_tx utxo tx + # @param[in] sequence sequence + # @return void + def add_input(self, outpoint: 'OutPoint', utxo: 'TxOut' = TxOut(0), + descriptor: Union['Descriptor', str] = '', + redeem_script: Union['Script', str] = '', + utxo_tx: Union['Transaction', str] = '', + sequence: int = TxIn.SEQUENCE_DISABLE) -> None: + input = PsbtAppendInputData(outpoint, utxo, descriptor, redeem_script, + utxo_tx, sequence, self.network) + self.add(inputs=[input]) + + ## + # @brief add output. + # @param[in] amount amount + # @param[in] locking_script locking script + # @param[in] address address + # @param[in] descriptor descriptor + # @param[in] redeem_script redeem script + # @return void + def add_output(self, amount: int, + locking_script: Union['Script', str] = '', + address: Union['Address', str] = '', + descriptor: Union['Descriptor', str] = '', + redeem_script: Union['Script', str] = '') -> None: + output = PsbtAppendOutputData(amount, locking_script, address, + descriptor, redeem_script, self.network) + self.add(outputs=[output]) + + ## + # @brief add input/output list. + # @param[in] inputs input list. + # @param[in] outputs output list. + # @return void + def add(self, inputs: List['PsbtAppendInputData'] = [], + outputs: List['PsbtAppendOutputData'] = []) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + for input in inputs: + if input.is_scripthash: + util.call_func( + 'CfdAddPsbtTxInWithScript', handle.get_handle(), + tx_handle.get_handle(), str(input.txin.outpoint.txid), + input.txin.outpoint.vout, + input.txin.sequence, input.utxo_amount, + input.utxo_locking_script, + input.redeem_script, input.descriptor, input.utxo_tx) + else: + util.call_func( + 'CfdAddPsbtTxInWithPubkey', handle.get_handle(), + tx_handle.get_handle(), str(input.txin.outpoint.txid), + input.txin.outpoint.vout, + input.txin.sequence, input.utxo_amount, + input.utxo_locking_script, + input.descriptor, input.utxo_tx) + for output in outputs: + if output.is_scripthash: + _ = util.call_func( + 'CfdAddPsbtTxOutWithScript', + handle.get_handle(), + tx_handle.get_handle(), + output.amount, + output.locking_script, + output.redeem_script, + output.descriptor) + else: + _ = util.call_func( + 'CfdAddPsbtTxOutWithPubkey', + handle.get_handle(), + tx_handle.get_handle(), + output.amount, + output.locking_script, + output.descriptor) + self._update_base64(util, handle, tx_handle) + + ## + # @brief verify sign. + # @param[in] outpoint outpoint + # @return void + def verify(self, outpoint: Optional['OutPoint'] = None) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + if isinstance(outpoint, OutPoint): + util.call_func( + 'CfdVerifyPsbtTxIn', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return + # all check + _, _tx, _, _ = util.call_func( + 'CfdGetPsbtGlobalData', handle.get_handle(), + tx_handle.get_handle()) + tx = Transaction(_tx) + for txin in tx.txin_list: + util.call_func('CfdVerifyPsbtTxIn', handle.get_handle(), + tx_handle.get_handle(), str(txin.outpoint.txid), + txin.outpoint.vout) + + ## + # @brief fund psbt. + # @param[in] utxo_list utxo list + # @param[in] reserved_address_descriptor \ + # sending reserved address descriptor + # @param[in] effective_fee_rate effective fee rate + # @param[in] long_term_fee_rate long term fee rate + # @param[in] dust_fee_rate dust fee rate + # @param[in] knapsack_min_change minimum change threshold for knapsack + # @return fee amount + def fund(self, utxo_list: List['UtxoData'], + reserved_address_descriptor: Union['Descriptor', str], + effective_fee_rate: float = 20.0, + long_term_fee_rate: float = 20.0, + dust_fee_rate: float = -1.0, + knapsack_min_change: int = -1) -> int: + util = get_util() + + def set_opt(handle, tx_handle, key, i_val=0, f_val=0, b_val=False): + util.call_func( + 'CfdSetOptionFundPsbt', handle.get_handle(), + tx_handle.get_handle(), int(key.value), + int(i_val), float(f_val), b_val) + + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle,\ + JobHandle(handle, util.call_func( + 'CfdInitializeFundPsbt', handle.get_handle()), + 'CfdFreeFundPsbt') as fund_handle: + for utxo in utxo_list: + util.call_func( + 'CfdFundPsbtAddToUtxoList', + handle.get_handle(), fund_handle.get_handle(), + str(utxo.outpoint.txid), utxo.outpoint.vout, + utxo.amount, '', str(utxo.descriptor), + to_hex_string(utxo.scriptsig_template), '') + + set_opt(handle, fund_handle, _FundPsbtOpt.EFFECTIVE_FEE_RATE, + f_val=effective_fee_rate) + set_opt(handle, fund_handle, _FundPsbtOpt.DUST_FEE_RATE, + f_val=dust_fee_rate) + set_opt(handle, fund_handle, _FundPsbtOpt.LONG_TERM_FEE_RATE, + f_val=long_term_fee_rate) + set_opt(handle, fund_handle, _FundPsbtOpt.KNAPSACK_MIN_CHANGE, + i_val=knapsack_min_change) + + fee, _ = util.call_func( + 'CfdFinalizeFundPsbt', handle.get_handle(), + tx_handle.get_handle(), + fund_handle.get_handle(), str(reserved_address_descriptor)) + self._update_base64(util, handle, tx_handle) + return fee + + ## + # @brief get input index. + # @param[in] outpoint outpoint + # @return input index + def get_input_index(self, outpoint: 'OutPoint') -> int: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + + ## + # @brief get input outpoint. + # @param[in] index input index + # @return input outpoint + def get_input_outpoint(self, index: int) -> 'OutPoint': + tx = self.get_tx() + if index >= len(tx.txin_list): + raise CfdError(error_code=3, message='Error: out of range.') + return tx.txin_list[index].outpoint + + ## + # @brief set input utxo. + # @param[in] outpoint outpoint + # @param[in] utxo utxo + # @param[in] utxo_tx utxo full tx + # @return void + def set_input_utxo(self, outpoint: 'OutPoint', utxo: 'TxOut' = TxOut(0), + utxo_tx: Union['Transaction', str] = '') -> None: + _locking_script: Union['Script', str] = utxo.locking_script + if utxo.address: + if isinstance(utxo.address, Address): + _locking_script = utxo.address.locking_script + else: + _locking_script = AddressUtil.parse( + utxo.address).locking_script + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSetPsbtTxInUtxo', handle.get_handle(), + tx_handle.get_handle(), + str(outpoint.txid), outpoint.vout, utxo.amount, + to_hex_string(_locking_script), to_hex_string(utxo_tx)) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input bip32 key. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey or descriptorPubkey + # @param[in] fingerprint fingerprint + # @param[in] bip32_path bip32 path + # @param[in] key_data keyData object + # @return void + def set_input_bip32_key( + self, + outpoint: 'OutPoint', + pubkey=None, + fingerprint='00000000', + bip32_path: str = '', + key_data: Optional['KeyData'] = None) -> None: + if isinstance(key_data, KeyData): + pk = to_hex_string(key_data.pubkey) + fp = key_data.fingerprint if key_data.fingerprint else '' + path = key_data.bip32_path + elif pubkey is None: + raise CfdError(error_code=1, message='Error: pubkey is None.') + else: + pk = pubkey if isinstance(pubkey, str) else to_hex_string(pubkey) + fp = fingerprint if fingerprint else '' + path = bip32_path + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSetPsbtTxInBip32Pubkey', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout, pk, + to_hex_string(fp), str(path)) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input signature. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey or descriptorPubkey + # @param[in] signature signature + # @param[in] sign_data sign parameter object + # @return void + def set_input_signature( + self, + outpoint: 'OutPoint', + pubkey=None, + signature=None, + sign_data: Optional['SignParameter'] = None) -> None: + if isinstance(sign_data, SignParameter): + pk = sign_data.related_pubkey + sig = sign_data.hex + elif pubkey is None: + raise CfdError(error_code=1, message='Error: pubkey is None.') + elif signature is None: + raise CfdError( + error_code=1, message='Error: signature is None.') + else: + pk = pubkey + sig = signature + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func('CfdSetPsbtSignature', + handle.get_handle(), + tx_handle.get_handle(), + str(outpoint.txid), + outpoint.vout, + to_hex_string(pk), + to_hex_string(sig)) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input sighash type. + # @param[in] outpoint outpoint + # @param[in] sighash_type sighash type + # @return void + def set_input_sighash_type(self, outpoint: 'OutPoint', + sighash_type: 'SigHashType') -> None: + sighash = SigHashType.get(sighash_type) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSetPsbtSighashType', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout, sighash.get_type()) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input finalize script. + # @param[in] outpoint outpoint + # @param[in] data finalize script + # @return void + def set_input_finalize(self, outpoint: 'OutPoint', + data: Union[List['Script'], Script]) -> None: + _script: Union['Script', str] = '' + if isinstance(data, list): + script_list = [] + for script in data: + if not isinstance(script, Script): + pass + elif ' ' in script.asm: + script_list.append(script.hex) + else: # single data + script_list.append(script.asm) + _script = Script.from_asm(script_list) + elif isinstance(data, Script): + _script = data + + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSetPsbtFinalizeScript', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout, to_hex_string(_script)) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input finalize script. + # @param[in] outpoint outpoint + # @param[in] redeem_script redeem script + # @param[in] index input index + # @return void + def set_input_script(self, outpoint: Optional['OutPoint'], + redeem_script, index: int = 0) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + if isinstance(outpoint, OutPoint): + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + self._set_redeem_script( + util, + handle, + tx_handle, + _PsbtRecordType.INPUT, + index, + redeem_script) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input record. + # @param[in] outpoint outpoint + # @param[in] key record key + # @param[in] value record value + # @param[in] index input index + # @return void + def set_input_record(self, outpoint: Optional['OutPoint'], + key, value, index: int = 0) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + if isinstance(outpoint, OutPoint): + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + self._set_record(util, handle, tx_handle, + _PsbtRecordType.INPUT, index, key, value) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set input non witness utxo. + # @param[in] outpoint outpoint + # @param[in] transaction non witness utxo. + # @param[in] index input index + # @return void + def set_input_non_witness_utxo( + self, + outpoint: 'OutPoint', + transaction: 'Transaction', + index: int = 0) -> None: + self.set_input_record( + outpoint, + PsbtDefinition.PSBT_IN_NON_WITNESS_UTXO, + transaction, + index) + + ## + # @brief set input redeem script. + # @param[in] outpoint outpoint + # @param[in] redeem_script redeem script + # @param[in] index input index + # @return void + def set_input_redeem_script( + self, + outpoint: 'OutPoint', + redeem_script, + index: int = 0) -> None: + self.set_input_record( + outpoint, + PsbtDefinition.PSBT_IN_REDEEM_SCRIPT, + redeem_script, + index) + + ## + # @brief set input witness script. + # @param[in] outpoint outpoint + # @param[in] witness_script witness script + # @param[in] index input index + # @return void + def set_input_witness_script( + self, + outpoint: 'OutPoint', + witness_script, + index: int = 0) -> None: + self.set_input_record( + outpoint, + PsbtDefinition.PSBT_IN_WITNESS_SCRIPT, + witness_script, + index) + + ## + # @brief set input final scriptsig. + # @param[in] outpoint outpoint + # @param[in] scriptsig scriptsig + # @param[in] index input index + # @return void + def set_input_final_scriptsig( + self, + outpoint: 'OutPoint', + scriptsig, + index: int = 0) -> None: + self.set_input_record( + outpoint, PsbtDefinition.PSBT_IN_FINAL_SCRIPTSIG, scriptsig, index) + + ## + # @brief get input sighash type. + # @param[in] outpoint outpoint + # @return sighash type + def get_input_sighash_type(self, outpoint: 'OutPoint') -> 'SigHashType': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + sighashtype = util.call_func( + 'CfdGetPsbtSighashType', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + if sighashtype == 0: + raise CfdError(error_code=8, message='Error: not found.') + return SigHashType.get(sighashtype) + + ## + # @brief get input signature. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey + # @return signature + def get_input_signature( + self, + outpoint: 'OutPoint', + pubkey) -> 'SignParameter': + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + value = self._get_pubkey_record( + util, + handle, + tx_handle, + _PsbtRecordKind.INPUT_SIGNATURE, + index, + pubkey) + return SignParameter(value, related_pubkey=pk) + + ## + # @brief find input signature. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey + # @retval true find + # @retval false not found + def is_find_input_signature(self, outpoint: 'OutPoint', pubkey) -> bool: + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._is_find_pubkey( + util, + handle, + tx_handle, + _PsbtRecordKind.INPUT_SIGNATURE, + index, + pk) + + ## + # @brief get input signatures. + # @param[in] outpoint outpoint + # @return signature list + def get_input_signature_list( + self, outpoint: 'OutPoint') -> List['SignParameter']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + pk_list, _ = self._get_pubkey_list( + util, handle, tx_handle, + _PsbtRecordKind.INPUT_SIGNATURE, index) + sig_list = [] + for key in pk_list: + value = self._get_pubkey_record( + util, handle, tx_handle, _PsbtRecordKind.INPUT_SIGNATURE, + index, key) + sig_list.append(SignParameter( + value, related_pubkey=Pubkey(key))) + return sig_list + + ## + # @brief get input bip32 key. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey + # @return bip32 key + def get_input_bip32_data(self, outpoint: 'OutPoint', pubkey) -> 'KeyData': + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + fp, path = self._get_pubkey_bip32_data( + util, handle, tx_handle, _PsbtRecordKind.INPUT_BIP32, + index, pubkey) + return KeyData(pk, fp, path) + + ## + # @brief find input bip32 key. + # @param[in] outpoint outpoint + # @param[in] pubkey pubkey + # @retval true find + # @retval false not found + def is_find_input_bip32_data(self, outpoint: 'OutPoint', pubkey) -> bool: + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._is_find_pubkey( + util, + handle, + tx_handle, + _PsbtRecordKind.INPUT_BIP32, + index, + pk) + + ## + # @brief get input bip32 keys. + # @param[in] outpoint outpoint + # @return bip32 list + def get_input_bip32_list(self, outpoint: 'OutPoint') -> List['KeyData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._get_bip32_pubkey_list( + util, handle, tx_handle, _PsbtRecordKind.INPUT_BIP32, index) + + ## + # @brief get input utxo data. + # @param[in] outpoint outpoint + # @retval [0] utxo data + # @retval [1] locking script (or None) + # @retval [2] redeem script (or None) + # @retval [3] utxo transaction (or None) + def get_input_utxo_data( + self, + outpoint: 'OutPoint') -> Tuple['UtxoData', Optional['Script'], + Optional['Script'], + Optional['Transaction']]: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + amount, locking_script, redeem_script, descriptor, \ + full_tx = util.call_func( + 'CfdGetPsbtUtxoData', handle.get_handle(), + tx_handle.get_handle(), + str(outpoint.txid), outpoint.vout) + tx = Transaction(full_tx) if full_tx else None + script = Script(redeem_script) if redeem_script else None + scriptpubkey = Script(locking_script) if locking_script else None + utxo = UtxoData(outpoint, + amount=amount, descriptor=descriptor) + return utxo, scriptpubkey, script, tx + + ## + # @brief get input data by index. + # @param[in] index input index + # @retval [0] outpoint + # @retval [1] utxo amount + # @retval [2] utxo locking script (or None) + # @retval [3] redeem script (or None) + # @retval [4] descriptor + # @retval [5] utxo full transaction (or None) + def get_input_data_by_index( + self, index: int) -> Tuple[ + 'OutPoint', int, Optional['Script'], + Optional['Script'], str, Optional['Transaction']]: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + txid, vout, amount, locking_script, redeem_script,\ + descriptor, full_tx = util.call_func( + 'CfdGetPsbtUtxoDataByIndex', handle.get_handle(), + tx_handle.get_handle(), index) + tx = Transaction(full_tx) if full_tx else None + script = Script(redeem_script) if redeem_script else None + scriptpubkey = Script(locking_script) if locking_script else None + outpoint = OutPoint(txid, vout) + return outpoint, amount, scriptpubkey, script, descriptor, tx + + ## + # @brief get input final script witness. + # @param[in] outpoint outpoint + # @return script witness + def get_input_final_witness( + self, outpoint: 'OutPoint') -> List['ByteData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._get_bytedata_list( + util, handle, tx_handle, + _PsbtRecordKind.INPUT_FINAL_WITNESS, index) + + ## + # @brief get input unknown key list. + # @param[in] outpoint outpoint + # @return unknown key list + def get_input_unknown_keys(self, outpoint: 'OutPoint') -> List['ByteData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._get_bytedata_list( + util, handle, tx_handle, + _PsbtRecordKind.INPUT_UNKNOWN_KEYS, index) + + ## + # @brief get input record. + # @param[in] outpoint outpoint + # @param[in] key record key + # @return unknown key list + def get_input_record(self, outpoint: 'OutPoint', key) -> 'ByteData': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._get_record(util, handle, tx_handle, + _PsbtRecordType.INPUT, index, key) + + ## + # @brief find input record. + # @param[in] outpoint outpoint + # @param[in] key record key + # @retval true find + # @retval false not found + def is_find_input_record(self, outpoint: 'OutPoint', key) -> bool: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + index = util.call_func( + 'CfdGetPsbtTxInIndex', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + return self._is_find_record(util, handle, tx_handle, + _PsbtRecordType.INPUT, index, key) + + ## + # @brief get input redeem script. + # @param[in] outpoint outpoint + # @return redeem script + def get_input_redeem_script(self, outpoint: 'OutPoint') -> 'Script': + value = self.get_input_record( + outpoint, PsbtDefinition.PSBT_IN_REDEEM_SCRIPT) + return Script(value) + + ## + # @brief get input witness script. + # @param[in] outpoint outpoint + # @return witness script + def get_input_witness_script(self, outpoint: 'OutPoint') -> 'Script': + value = self.get_input_record( + outpoint, PsbtDefinition.PSBT_IN_WITNESS_SCRIPT) + return Script(value) + + ## + # @brief get input final scriptsig. + # @param[in] outpoint outpoint + # @return final scriptsig + def get_input_final_scriptsig(self, outpoint: 'OutPoint') -> 'Script': + value = self.get_input_record( + outpoint, PsbtDefinition.PSBT_IN_FINAL_SCRIPTSIG) + return Script(value) + + ## + # @brief clear input sign data. + # @param[in] outpoint outpoint + # @return void + def clear_input_sign_data(self, outpoint: 'OutPoint') -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdClearPsbtSignData', handle.get_handle(), + tx_handle.get_handle(), str( + outpoint.txid), outpoint.vout) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set output bip32 key data. + # @param[in] index output index + # @param[in] pubkey pubkey or descriptorPubkey + # @param[in] fingerprint fingerprint + # @param[in] bip32_path bip32 path + # @param[in] key_data keyData object + # @return void + def set_output_bip32_key( + self, + index: int, + pubkey=None, + fingerprint=None, + bip32_path: str = '', + key_data: Optional['KeyData'] = None) -> None: + if isinstance(key_data, KeyData): + pk = to_hex_string(key_data.pubkey) + fp = key_data.fingerprint if key_data.fingerprint else '' + path = key_data.bip32_path + elif pubkey is None: + raise CfdError(error_code=1, message='Error: pubkey is None.') + else: + pk = pubkey if isinstance(pubkey, str) else to_hex_string(pubkey) + fp = fingerprint if fingerprint else '' + path = bip32_path + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdSetPsbtTxOutBip32Pubkey', + handle.get_handle(), + tx_handle.get_handle(), + index, + pk, + to_hex_string(fp), + path) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set output redeem script (or witness script). + # @param[in] index output index + # @param[in] redeem_script redeem script + # @return void + def set_output_script(self, index: int, redeem_script) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + self._set_redeem_script( + util, + handle, + tx_handle, + _PsbtRecordType.OUTPUT, + index, + redeem_script) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set output record. + # @param[in] index output index + # @param[in] key record key + # @param[in] value record value + # @return void + def set_output_record(self, index: int, key, value) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + self._set_record(util, handle, tx_handle, + _PsbtRecordType.OUTPUT, index, key, value) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set output redeem script. + # @param[in] index output index + # @param[in] redeem_script redeem script + # @return void + def set_output_redeem_script(self, index: int, redeem_script) -> None: + self.set_output_record( + index, PsbtDefinition.PSBT_OUT_REDEEM_SCRIPT, redeem_script) + + ## + # @brief set output witness script. + # @param[in] index output index + # @param[in] witness_script witness script + # @return void + def set_output_witness_script(self, index: int, witness_script) -> None: + self.set_output_record( + index, PsbtDefinition.PSBT_OUT_WITNESS_SCRIPT, witness_script) + + ## + # @brief get output bip32 key data. + # @param[in] index output index + # @param[in] pubkey pubkey + # @return keyData object + def get_output_bip32_data(self, index: int, pubkey) -> 'KeyData': + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + fp, path = self._get_pubkey_bip32_data( + util, handle, tx_handle, + _PsbtRecordKind.OUTPUT_BIP32, index, pubkey) + return KeyData(pk, fp, path) + + ## + # @brief find output bip32 key data. + # @param[in] index output index + # @param[in] pubkey pubkey + # @retval true find + # @retval false not found + def is_find_output_bip32_data(self, index: int, pubkey) -> bool: + pk = pubkey if isinstance( + pubkey, Pubkey) else Pubkey(to_hex_string(pubkey)) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._is_find_pubkey( + util, + handle, + tx_handle, + _PsbtRecordKind.OUTPUT_BIP32, + index, + pk) + + ## + # @brief get output bip32 key list. + # @param[in] index output index + # @return keyData object list + def get_output_bip32_list(self, index: int) -> List['KeyData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_bip32_pubkey_list( + util, handle, tx_handle, _PsbtRecordKind.OUTPUT_BIP32, index) + + ## + # @brief get output unknown key list. + # @param[in] index output index + # @return keyData object list + def get_output_unknown_keys(self, index: int) -> List['ByteData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_bytedata_list( + util, handle, tx_handle, + _PsbtRecordKind.OUTPUT_UNKNOWN_KEYS, index) + + ## + # @brief get output record. + # @param[in] index output index + # @param[in] key record key + # @return record value + def get_output_record(self, index: int, key) -> 'ByteData': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_record(util, handle, tx_handle, + _PsbtRecordType.OUTPUT, index, key) + + ## + # @brief find output record. + # @param[in] index output index + # @param[in] key record key + # @retval true find + # @retval false not found + def is_find_output_record(self, index: int, key) -> bool: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._is_find_record(util, handle, tx_handle, + _PsbtRecordType.OUTPUT, index, key) + + ## + # @brief get output redeem script. + # @param[in] index output index + # @return redeem script + def get_output_redeem_script(self, index: int) -> 'Script': + value = self.get_output_record( + index, PsbtDefinition.PSBT_OUT_REDEEM_SCRIPT) + return Script(value) + + ## + # @brief get output witness script. + # @param[in] index output index + # @return witness script + def get_output_witness_script(self, index: int) -> 'Script': + value = self.get_output_record( + index, PsbtDefinition.PSBT_OUT_WITNESS_SCRIPT) + return Script(value) + + ## + # @brief set global xpub. + # @param[in] ext_pubkey ext pubkey or descriptorExtPubkey + # @param[in] fingerprint fingerprint + # @param[in] bip32_path bip32 path + # @param[in] key_data keyData object + # @return void + def set_global_xpub( + self, + ext_pubkey=None, + fingerprint=None, + bip32_path: str = '', + key_data: Optional['KeyData'] = None) -> None: + if isinstance(key_data, KeyData): + if key_data.ext_pubkey is None: + raise CfdError( + error_code=1, message='Error: ext_pubkey is None.') + pk = key_data.ext_pubkey + fp = key_data.fingerprint if key_data.fingerprint else '' + path = key_data.bip32_path + elif ext_pubkey is None: + raise CfdError(error_code=1, + message='Error: ext_pubkey is None.') + else: + pk = ext_pubkey + fp = fingerprint if fingerprint else '' + path = bip32_path + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + util.call_func( + 'CfdAddPsbtGlobalXpubkey', + handle.get_handle(), + tx_handle.get_handle(), + str(pk), + to_hex_string(fp), + path) + self._update_base64(util, handle, tx_handle) + + ## + # @brief set global record. + # @param[in] key record key + # @param[in] value record value + # @return void + def set_global_record(self, key, value) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + self._set_record(util, handle, tx_handle, + _PsbtRecordType.GLOBAL, 0, key, value) + self._update_base64(util, handle, tx_handle) + + ## + # @brief get global xpub. + # @param[in] xpub ext pubkey + # @return keyData object + def get_global_xpub(self, xpub) -> 'KeyData': + pk = xpub if isinstance(xpub, ExtPubkey) else ExtPubkey(xpub) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + fp, path = self._get_pubkey_bip32_data( + util, handle, tx_handle, _PsbtRecordKind.GLOBAL_XPUB, 0, pk) + return KeyData(pk, fp, path) + + ## + # @brief find global xpub. + # @param[in] xpub ext pubkey + # @retval true find + # @retval false not found + def is_find_global_xpub(self, xpub) -> bool: + pk = xpub if isinstance(xpub, ExtPubkey) else ExtPubkey(xpub) + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._is_find_pubkey( + util, handle, tx_handle, _PsbtRecordKind.GLOBAL_XPUB, 0, pk) + + ## + # @brief get global xpub list. + # @return keyData object list + def get_global_xpub_list(self) -> List['KeyData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_bip32_pubkey_list( + util, handle, tx_handle, _PsbtRecordKind.GLOBAL_XPUB, 0) + + ## + # @brief get global unknown list. + # @return unknown key list (contains global xpub keys) + def get_global_unknown_keys(self) -> List['ByteData']: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_bytedata_list( + util, handle, tx_handle, + _PsbtRecordKind.GLOBAL_UNKNOWN_KEYS, 0) + + ## + # @brief get global record. + # @param[in] key record key + # @return record value + def get_global_record(self, key) -> 'ByteData': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._get_record(util, handle, tx_handle, + _PsbtRecordType.GLOBAL, 0, key) + + ## + # @brief find global record. + # @param[in] key record key + # @retval true find + # @retval false not found + def is_find_global_record(self, key) -> bool: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tx_handle: + return self._is_find_record(util, handle, tx_handle, + _PsbtRecordType.GLOBAL, 0, key) + + ''' + ------------------------------------------------------- + internal functions + ------------------------------------------------------- + ''' + + ## + # @brief get pubkey record. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @param[in] pubkey pubkey + # @return value + def _get_pubkey_record( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int, pubkey) -> str: + key = str(pubkey) if isinstance( + pubkey, ExtPubkey) else to_hex_string(pubkey) + return util.call_func( + 'CfdGetPsbtPubkeyRecord', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index, key) + + ## + # @brief get pubkey bip32 data. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @param[in] pubkey pubkey or extpubkey + # @retval [0] fingerprint + # @retval [1] bip32 path + def _get_pubkey_bip32_data( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int, pubkey) -> Tuple['ByteData', str]: + key = str(pubkey) if isinstance( + pubkey, ExtPubkey) else to_hex_string(pubkey) + fp, path = util.call_func( + 'CfdGetPsbtBip32Data', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index, key) + return ByteData(fp), path + + ## + # @brief find pubkey record. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @param[in] pubkey pubkey or extpubkey + # @retval true find + # @retval false not found + def _is_find_pubkey( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int, pubkey) -> bool: + key = str(pubkey) if isinstance( + pubkey, ExtPubkey) else to_hex_string(pubkey) + return util.call_func( + 'CfdIsFindPsbtPubkeyRecord', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index, key) + + ## + # @brief get pubkey bip32 list. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @return keyData list + def _get_bip32_pubkey_list( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int) -> List['KeyData']: + num, work_handle = util.call_func( + 'CfdGetPsbtPubkeyList', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index) + with JobHandle(handle, work_handle, + 'CfdFreePsbtPubkeyList') as list_handle: + pubkeys = [] + for list_index in range(num): + pubkey, fingerprint, bip32_path = util.call_func( + 'CfdGetPsbtPubkeyListBip32Data', handle.get_handle(), + list_handle.get_handle(), list_index) + if len(pubkey) in [66, 130]: + pubkeys.append( + KeyData( + Pubkey(pubkey), + ByteData(fingerprint), + bip32_path)) + else: + pubkeys.append(KeyData(ExtPubkey(pubkey), + ByteData(fingerprint), bip32_path)) + return pubkeys + + ## + # @brief get pubkey list. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @retval [0] pubkey or extpubkey list + # @retval [1] hex list + def _get_pubkey_list( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int) -> Tuple[List[str], List['ByteData']]: + num, work_handle = util.call_func( + 'CfdGetPsbtPubkeyList', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index) + with JobHandle(handle, work_handle, + 'CfdFreePsbtPubkeyList') as list_handle: + pubkeys = [] + hex_pubkeys = [] + for list_index in range(num): + pubkey, pubkey_hex = util.call_func( + 'CfdGetPsbtPubkeyListData', handle.get_handle(), + list_handle.get_handle(), list_index) + pubkeys.append(pubkey) + hex_pubkeys.append(ByteData(pubkey_hex)) + return pubkeys, hex_pubkeys + + ## + # @brief get data list. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] kind record kind + # @param[in] index index + # @return data list + def _get_bytedata_list( + self, util, handle, psbt_handle, kind: '_PsbtRecordKind', + index: int) -> List['ByteData']: + num, work_handle = util.call_func( + 'CfdGetPsbtByteDataList', handle.get_handle(), + psbt_handle.get_handle(), kind.value, index) + with JobHandle(handle, work_handle, + 'CfdFreePsbtByteDataList') as list_handle: + data_list = [] + for list_index in range(num): + data = util.call_func( + 'CfdGetPsbtByteDataItem', handle.get_handle(), + list_handle.get_handle(), list_index) + data_list.append(ByteData(data)) + return data_list + + ## + # @brief set redeem script. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] type record type + # @param[in] index index + # @param[in] redeem_script redeem script + # @return void + def _set_redeem_script( + self, util, handle, psbt_handle, type: '_PsbtRecordType', + index: int, redeem_script) -> None: + util.call_func( + 'CfdSetPsbtRedeemScript', handle.get_handle(), + psbt_handle.get_handle(), type.value, index, + to_hex_string(redeem_script)) + + ## + # @brief set record. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] type record type + # @param[in] index index + # @param[in] key record key + # @param[in] value record value + # @return void + def _set_record( + self, util, handle, psbt_handle, type: '_PsbtRecordType', + index: int, key, value) -> None: + _key = key.value if isinstance(key, PsbtDefinition) else key + util.call_func( + 'CfdAddPsbtRecord', handle.get_handle(), psbt_handle.get_handle(), + type.value, index, to_hex_string(_key), to_hex_string(value)) + + ## + # @brief get record. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] type record type + # @param[in] index index + # @param[in] key record key + # @return record value + def _get_record(self, util, handle, psbt_handle, type: '_PsbtRecordType', + index: int, key) -> 'ByteData': + _key = key.value if isinstance(key, PsbtDefinition) else key + value = util.call_func( + 'CfdGetPsbtRecord', handle.get_handle(), psbt_handle.get_handle(), + type.value, index, to_hex_string(_key)) + return ByteData(value) + + ## + # @brief find record. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @param[in] type record type + # @param[in] index index + # @param[in] key record key + # @retval true find + # @retval false not found + def _is_find_record( + self, util, handle, psbt_handle, type: '_PsbtRecordType', + index: int, key) -> bool: + _key = key.value if isinstance(key, PsbtDefinition) else key + return util.call_func( + 'CfdIsFindPsbtRecord', handle.get_handle(), + psbt_handle.get_handle(), type.value, index, + to_hex_string(_key)) + + ## + # @brief get psbt handle. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] network network type + # @return psbt job handle + def _get_handle( + self, util, handle, + network: Optional['Network'] = None) -> 'JobHandle': + _network = Network.get(self.network) if not network else network + work_handle = util.call_func( + 'CfdCreatePsbtHandle', handle.get_handle(), + _network.value, self.base64, '', 0, 0) + return JobHandle(handle, work_handle, 'CfdFreePsbtHandle') + + ## + # @brief update psbt data. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @param[in] psbt_handle psbt handle + # @return void + def _update_base64(self, util, handle, psbt_handle) -> None: + self.base64, _ = util.call_func( + 'CfdGetPsbtData', handle.get_handle(), psbt_handle.get_handle()) + + +## +# @class _FundPsbtOpt +# @brief FundPsbt option class. +class _FundPsbtOpt(Enum): + ## + # effective fee rate + EFFECTIVE_FEE_RATE = 1 + ## + # dust fee rate + DUST_FEE_RATE = 2 + ## + # long term fee rate + LONG_TERM_FEE_RATE = 3 + ## + # minimum change threshold for knapsack + KNAPSACK_MIN_CHANGE = 4 + + +## +# @class _PsbtRecordType +# @brief PSBT record type class. +class _PsbtRecordType(Enum): + ## + # global + GLOBAL = 1 + ## + # input + INPUT = 2 + ## + # output + OUTPUT = 3 + + +## +# @class _PsbtRecordKind +# @brief PSBT record kind class. +class _PsbtRecordKind(Enum): + ## + # input signature + INPUT_SIGNATURE = 1 + ## + # input bip32 + INPUT_BIP32 = 2 + ## + # output bip32 + OUTPUT_BIP32 = 3 + ## + # global xpub + GLOBAL_XPUB = 4 + ## + # input final witness stack + INPUT_FINAL_WITNESS = 5 + ## + # input unknown key list + INPUT_UNKNOWN_KEYS = 6 + ## + # output unknown key list + OUTPUT_UNKNOWN_KEYS = 7 + ## + # global unknown key list + GLOBAL_UNKNOWN_KEYS = 8 + + +## +# All import target. +__all__ = [ + 'Psbt', + 'PsbtDefinition', + 'PsbtAppendInputData', + 'PsbtAppendOutputData', +] diff --git a/cfd/script.py b/cfd/script.py index a1222ed..2a635ab 100644 --- a/cfd/script.py +++ b/cfd/script.py @@ -3,7 +3,7 @@ # @file script.py # @brief bitcoin script function implements file. # @note Copyright 2020 CryptoGarage -from typing import List +from typing import List, Union from .util import CfdError, to_hex_string, get_util, JobHandle from .key import SignParameter, SigHashType from enum import Enum @@ -31,6 +31,12 @@ class HashType(Enum): ## # HashType: p2sh-p2wpkh P2SH_P2WPKH = 6 + ## + # HashType: taproot + TAPROOT = 7 + ## + # HashType: unknown + UNKNOWN = 255 ## # @brief get string. @@ -89,7 +95,7 @@ class Script: # @param[in] script_items asm strings (list or string) # @return script object @classmethod - def from_asm(cls, script_items: List[str]) -> 'Script': + def from_asm(cls, script_items: Union[List[str], str]) -> 'Script': _asm = script_items if isinstance(script_items, list): _asm = ' '.join(script_items) @@ -115,11 +121,11 @@ def create_multisig_scriptsig( _script = to_hex_string(redeem_script) util = get_util() with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeMultisigScriptSig', handle.get_handle()) with JobHandle( handle, - word_handle, + work_handle, 'CfdFreeMultisigScriptSigHandle') as script_handle: for param in sign_parameter_list: if isinstance(param, SignParameter) is False: @@ -163,19 +169,22 @@ def __str__(self) -> str: return self.hex ## - # @brief create multisig scriptsig. + # @brief parse script. # @param[in] script script # @return script asm @classmethod def _parse(cls, script): util = get_util() script_list = [] + if not script: + return '' + with util.create_handle() as handle: - word_handle, max_index = util.call_func( + work_handle, max_index = util.call_func( 'CfdParseScript', handle.get_handle(), script) with JobHandle( handle, - word_handle, + work_handle, 'CfdFreeScriptItemHandle') as script_handle: for i in range(max_index): item = util.call_func( diff --git a/cfd/taproot.py b/cfd/taproot.py new file mode 100644 index 0000000..d8a51b0 --- /dev/null +++ b/cfd/taproot.py @@ -0,0 +1,524 @@ +# -*- coding: utf-8 -*- +## +# @file taproot.py +# @brief bitcoin taproot function implements file. +# @note Copyright 2021 CryptoGarage +from typing import List, Optional, Tuple, Union +from .util import CfdError, CfdErrorCode, get_util, JobHandle, ByteData, \ + to_hex_string +from .key import SchnorrPubkey, Privkey +from .script import Script + + +## +# @brief tapscript leaf version. +TAPSCRIPT_LEAF_VERSION: int = 0xc0 + + +## +# @class TapBranch +# @brief TapBranch +class TapBranch: + ## + # @var branches + # script tree branches. + branches: List[Union['TapBranch', 'ByteData', 'Script']] + ## + # @var hash + # hash. + hash: 'ByteData' + ## + # @var tapscript + # tapscript. + tapscript: Optional['Script'] + ## + # @var tree_str + # tree serialize string. (cfd format) + tree_str: str + ## + # @var leaf_version + # leaf version. + leaf_version: int + ## + # @var taget_node_str + # target node route string. + taget_node_str: str + + ## + # @brief get tapbranch from string. + # @param[in] tree_str tree string. + # @return tapbranch object + @classmethod + def from_string(cls, tree_str: str) -> 'TapBranch': + result = TapBranch() + util = get_util() + with util.create_handle() as handle, TapBranch._get_handle( + util, handle) as tree_handle: + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), tree_str, '', 0, '') + result.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + branch_data = TapBranch._load_tree(handle, tree_handle) + result.tree_str = branch_data.tree_str + result.branches = branch_data.branches + result.hash = branch_data.hash + result.tapscript = branch_data.tapscript + result.leaf_version = branch_data.leaf_version + result.taget_node_str = '' + for branch in result.branches: + if isinstance(branch, TapBranch): + result.taget_node_str += to_hex_string( + branch.get_current_hash()) + else: + result.taget_node_str += to_hex_string(branch) + return result + + ## + # @brief constructor. + # @param[in] hash branch hash only + # @param[in] tapscript tapscript + # @param[in] tree_str scripttree string + # @param[in] leaf_version leaf version + def __init__(self, hash: Union['ByteData', str] = '', + tapscript: Optional['Script'] = None, + tree_str: str = '', + leaf_version: int = TAPSCRIPT_LEAF_VERSION): + self.branches = [] + if isinstance(hash, ByteData): + self.hash = hash + else: + self.hash = ByteData(hash) + if isinstance(tapscript, Script) and tapscript.hex: + self.tapscript = tapscript + else: + self.tapscript = None + self.taget_node_str = '' + self.tree_str = '' + self.leaf_version = leaf_version + if tree_str or self.tapscript or self.hash.hex: + if tree_str: + temp_tree_str = tree_str + elif self.tapscript and self.tapscript.hex: + temp_tree_str = f'tl({self.tapscript.hex})' + elif self.hash.hex: + temp_tree_str = self.hash.hex + else: + temp_tree_str = '' + self._load(temp_tree_str) + + ## + # @brief get string. + # @return tree or hash string. + def __str__(self) -> str: + return str(self.hash) if not self.tree_str else self.tree_str + + ## + # @brief get branch string. + # @return tree/branch string. + def as_str(self) -> str: + return self.tree_str + + ## + # @brief add branch. + # @param[in] branch branch + # @return void + def add_branch( + self, branch: Union['TapBranch', 'Script', 'ByteData']) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tree_handle: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + tapscript, self.leaf_version, self.taget_node_str) + branch_data = self._add_branch(handle, tree_handle, branch) + self.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + self.branches.append(branch_data) + if isinstance(branch_data, TapBranch): + self.taget_node_str += to_hex_string( + branch_data.get_current_hash()) + else: + self.taget_node_str += to_hex_string(branch_data) + + ## + # @brief add branch list. + # @param[in] branches branch list + # @return void + def add_branches( + self, branches: List[Union['TapBranch', 'Script', 'ByteData']], + ) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tree_handle: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + tapscript, self.leaf_version, self.taget_node_str) + for branch in branches: + branch_data = self._add_branch(handle, tree_handle, branch) + self.branches.append(branch_data) + if isinstance(branch_data, TapBranch): + self.taget_node_str += to_hex_string( + branch_data.get_current_hash()) + else: + self.taget_node_str += to_hex_string(branch_data) + self.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + + ## + # @brief has tapscript. + # @return True or False + def has_tapscript(self) -> bool: + return True if self.tapscript and self.tapscript.hex else False + + ## + # @brief get tapscript. + # @return script. + def get_tapscript(self) -> 'Script': + if not self.tapscript: + raise CfdError(CfdErrorCode.ILLEGAL_STATE, 'tapscript not found.') + return self.tapscript + + ## + # @brief get base hash. + # @return base hash. + def get_base_hash(self) -> 'ByteData': + return self.hash + + ## + # @brief get current branch hash. + # @return current branch hash. + def get_current_hash(self) -> 'ByteData': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tree_handle: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + tapscript, self.leaf_version, '') + count = util.call_func( + 'CfdGetTapBranchCount', handle.get_handle(), + tree_handle.get_handle()) + if count == 0: + return self.hash + + hash, _, _, _ = util.call_func( + 'CfdGetTapBranchData', handle.get_handle(), + tree_handle.get_handle(), count - 1, True) + return ByteData(hash) + + ## + # @brief get branch hash. + # @param[in] index target index from tapleaf. + # @return branch hash. + def get_branch_hash(self, index: int) -> 'ByteData': + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tree_handle: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + tapscript, self.leaf_version, '') + hash, _, _, _ = util.call_func( + 'CfdGetTapBranchData', handle.get_handle(), + tree_handle.get_handle(), index, True) + return ByteData(hash) + + ## + # @brief load tree info. + # @param[in] tree_str tree string + # @return void + def _load(self, tree_str: str) -> None: + util = get_util() + with util.create_handle() as handle, self._get_handle( + util, handle) as tree_handle: + if tree_str: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), tree_str, + tapscript, self.leaf_version, self.taget_node_str) + elif self.tapscript: + util.call_func( + 'CfdSetInitialTapLeaf', handle.get_handle(), + tree_handle.get_handle(), + self.tapscript.hex, self.leaf_version) + elif self.hash.hex: + util.call_func( + 'CfdSetInitialTapBranchByHash', handle.get_handle(), + tree_handle.get_handle(), self.hash.hex) + else: + return # do nothing + self.leaf_version, script, hash = util.call_func( + 'CfdGetBaseTapLeaf', handle.get_handle(), + tree_handle.get_handle()) + if self.leaf_version != 0: + self.tapscript = Script(script) + self.hash = ByteData(hash) + self.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + + ## + # @brief load tree info. + # @param[in] handle cfd handle + # @param[in] tree_handle script tree handle + # @return loaded tree/branch data. + @classmethod + def _load_tree(cls, handle, tree_handle) -> 'TapBranch': + util = get_util() + count = util.call_func( + 'CfdGetTapBranchCount', handle.get_handle(), + tree_handle.get_handle()) + branch = TapBranch() + for index in range(count): + _, work_handle = util.call_func( + 'CfdGetTapBranchHandle', handle.get_handle(), + tree_handle.get_handle(), index) + with JobHandle(handle, work_handle, + 'CfdFreeTaprootScriptTreeHandle') as br_hdl: + child = cls._load_tree(handle, br_hdl) + branch.branches.append(child) + branch.leaf_version, tapscript, hash = util.call_func( + 'CfdGetBaseTapLeaf', handle.get_handle(), + tree_handle.get_handle()) + if branch.leaf_version != 0: + branch.tapscript = Script(tapscript) + branch.hash = ByteData(hash) + branch.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + return branch + + ## + # @brief get scripttree handle. + # @param[in] util cfd util object + # @param[in] handle cfd handle + # @return scripttree job handle + @classmethod + def _get_handle(cls, util, handle) -> 'JobHandle': + work_handle = util.call_func( + 'CfdInitializeTaprootScriptTree', handle.get_handle()) + return JobHandle(handle, work_handle, 'CfdFreeTaprootScriptTreeHandle') + + ## + # @brief add branch internal. + # @param[in] handle cfd handle + # @param[in] tree_handle tree job handle + # @param[in] branch branch + # @return added branch data. + @classmethod + def _add_branch( + cls, handle, tree_handle, + branch: Union['TapBranch', 'ByteData', 'Script'], + ) -> Union['TapBranch', 'Script', 'ByteData']: + util = get_util() + branch_data = branch + hash = '' + if isinstance(branch, TapBranch): + if branch.tree_str: + util.call_func( + 'CfdAddTapBranchByScriptTreeString', + handle.get_handle(), tree_handle.get_handle(), + branch.tree_str) + else: + hash = str(branch.hash) + elif isinstance(branch, Script): + branch_data = TapBranch(tapscript=branch) + util.call_func( + 'CfdAddTapBranchByTapLeaf', handle.get_handle(), + tree_handle.get_handle(), branch.hex, TAPSCRIPT_LEAF_VERSION) + else: + hash = to_hex_string(branch) + + if hash: + util.call_func( + 'CfdAddTapBranchByHash', handle.get_handle(), + tree_handle.get_handle(), hash) + branch_data = ByteData(hash) + return branch_data + + +## +# @class TaprootScriptTree +# @brief TaprootScriptTree +class TaprootScriptTree(TapBranch): + ## + # @var internal_pubkey + # leaf internal_pubkey. + internal_pubkey: Optional['SchnorrPubkey'] + + ## + # @brief get script tree. + # @param[in] tapscript tapscript + # @param[in] branches append branch list. + # @param[in] internal_pubkey internal pubkey + # @return script tree object + @classmethod + def create( + cls, + tapscript: 'Script', + branches: List[Union['TapBranch', 'ByteData', 'Script']] = [], + internal_pubkey: Optional['SchnorrPubkey'] = None, + ) -> 'TaprootScriptTree': + result = TaprootScriptTree(tapscript) + result.add_branches(branches) + if isinstance(internal_pubkey, SchnorrPubkey): + result.internal_pubkey = internal_pubkey + return result + + ## + # @brief get script tree from control block. + # @param[in] control_block control block. + # @param[in] tapscript tapscript + # @return script tree object + @classmethod + def from_control_block( + cls, control_block, tapscript: 'Script') -> 'TaprootScriptTree': + result = TaprootScriptTree(Script('51')) # dummy + util = get_util() + with util.create_handle() as handle, TapBranch._get_handle( + util, handle) as tree_handle: + _internal_pubkey = util.call_func( + 'CfdSetTapScriptByWitnessStack', handle.get_handle(), + tree_handle.get_handle(), to_hex_string(control_block), + tapscript.hex) + branch_data = TapBranch._load_tree(handle, tree_handle) + result.tree_str = branch_data.tree_str + result.branches = branch_data.branches + result.tapscript = tapscript + result.internal_pubkey = SchnorrPubkey(_internal_pubkey) + for branch in result.branches: + if isinstance(branch, TapBranch): + result.taget_node_str += to_hex_string( + branch.get_current_hash()) + else: + result.taget_node_str += to_hex_string(branch) + return result + + ## + # @brief get script tree from string. + # @param[in] tree_str tree string. + # @param[in] tapscript tapscript + # @param[in] target_nodes target tapbranch hash list. + # @param[in] internal_pubkey internal pubkey + # @return script tree object + @classmethod + def from_string( + cls, tree_str: str, tapscript: 'Script', + target_nodes: List[Union['ByteData', str]] = [], + internal_pubkey: Optional['SchnorrPubkey'] = None, + ) -> 'TaprootScriptTree': + result = TaprootScriptTree(Script('51')) # dummy + util = get_util() + with util.create_handle() as handle, TapBranch._get_handle( + util, handle) as tree_handle: + target_nodes_str = '' + for node in target_nodes: + target_nodes_str += to_hex_string(node) + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), tree_str, + tapscript.hex, result.leaf_version, target_nodes_str) + result.tree_str = util.call_func( + 'CfdGetTaprootScriptTreeSrting', handle.get_handle(), + tree_handle.get_handle()) + branch_data = TapBranch._load_tree(handle, tree_handle) + result.tree_str = branch_data.tree_str + result.branches = branch_data.branches + result.hash = branch_data.hash + result.taget_node_str = target_nodes_str + if not target_nodes_str: + for branch in result.branches: + if isinstance(branch, TapBranch): + result.taget_node_str += to_hex_string( + branch.get_current_hash()) + else: + result.taget_node_str += to_hex_string(branch) + result.tapscript = branch_data.tapscript + if isinstance(internal_pubkey, SchnorrPubkey): + result.internal_pubkey = internal_pubkey + return result + + ## + # @brief constructor. + # @param[in] tapscript tapscript + def __init__(self, tapscript: 'Script'): + super().__init__('', tapscript) + self.internal_pubkey = None + + ## + # @brief get string. + # @return tree string. + def __str__(self) -> str: + if (not self.tapscript) and (not self.branches) and ( + self.internal_pubkey): + return 'tr(' + str(self.internal_pubkey) + ')' + if not self.internal_pubkey: + return self.tree_str + return 'tr(' + str(self.internal_pubkey) + ',' + self.tree_str + ')' + + ## + # @brief get taproot data. + # @param[in] internal_pubkey internal pubkey + # @retval [0] taproot schnorr pubkey. (for address) + # @retval [1] tapleaf hash. (for sighash) + # @retval [2] tapscript. + # @retval [3] control block. (for witness stack) + def get_taproot_data( + self, internal_pubkey: Optional['SchnorrPubkey'] = None, + ) -> Tuple['SchnorrPubkey', 'ByteData', 'Script', 'ByteData']: + pk = internal_pubkey if internal_pubkey else self.internal_pubkey + if not pk: + raise CfdError(CfdErrorCode.ILLEGAL_STATE, + 'internal pubkey not found.') + util = get_util() + with util.create_handle() as handle, TapBranch._get_handle( + util, handle) as tree_handle: + if not self.tapscript: + raise CfdError(CfdErrorCode.ILLEGAL_STATE, + 'tapscript not found.') + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + self.tapscript.hex, self.leaf_version, self.taget_node_str) + hash, tapleaf_hash, control_block = util.call_func( + 'CfdGetTaprootScriptTreeHash', handle.get_handle(), + tree_handle.get_handle(), to_hex_string(pk)) + return SchnorrPubkey(hash), ByteData(tapleaf_hash), \ + self.tapscript, ByteData(control_block) + + ## + # @brief get tweaked privkey. + # @param[in] internal_privkey internal privkey + # @return privkey. + def get_privkey(self, internal_privkey: 'Privkey') -> 'Privkey': + util = get_util() + with util.create_handle() as handle, TapBranch._get_handle( + util, handle) as tree_handle: + tapscript = self.tapscript.hex if self.tapscript else '' + util.call_func( + 'CfdSetScriptTreeFromString', handle.get_handle(), + tree_handle.get_handle(), self.tree_str, + tapscript, self.leaf_version, self.taget_node_str) + tweaked_privkey = util.call_func( + 'CfdGetTaprootTweakedPrivkey', handle.get_handle(), + tree_handle.get_handle(), to_hex_string(internal_privkey)) + return Privkey(hex=tweaked_privkey) + + +## +# All import target. +__all__ = [ + 'TapBranch', + 'TaprootScriptTree' +] diff --git a/cfd/transaction.py b/cfd/transaction.py index 986aeb7..e045256 100644 --- a/cfd/transaction.py +++ b/cfd/transaction.py @@ -3,7 +3,7 @@ # @file transaction.py # @brief transaction function implements file. # @note Copyright 2020 CryptoGarage -from typing import AnyStr, List, Optional, Tuple, Union +from typing import List, Optional, Tuple, Union import typing from .util import get_util, JobHandle, CfdError, to_hex_string,\ CfdErrorCode, ReverseByteData, ByteData @@ -11,11 +11,17 @@ from .key import Network, SigHashType, SignParameter, Privkey from .script import HashType, Script from .descriptor import Descriptor +from .taproot import TaprootScriptTree from enum import Enum import ctypes import copy +## +# @brief OP_CODESEPARATOR final position. +CODE_SEPARATOR_POSITION_FINAL: int = 0xffffffff + + ## # @class Txid # @brief Txid class. @@ -65,7 +71,7 @@ def __str__(self) -> str: # @brief equal method. # @param[in] other other object. # @return true or false. - def __eq__(self, other: 'OutPoint') -> bool: + def __eq__(self, other: object) -> bool: if not isinstance(other, OutPoint): return NotImplemented return (self.txid.hex == other.txid.hex) and ( @@ -84,7 +90,7 @@ def __lt__(self, other: 'OutPoint') -> bool: # @brief equal method. # @param[in] other other object. # @return true or false. - def __ne__(self, other: 'OutPoint') -> bool: + def __ne__(self, other: object) -> bool: return not self.__eq__(other) ## @@ -128,7 +134,7 @@ class UtxoData: ## # @var scriptsig_template # scriptsig template - scriptsig_template: Union['Script', 'ByteData', AnyStr] + scriptsig_template: Union['Script', 'ByteData', str] ## # @brief constructor. @@ -142,7 +148,7 @@ def __init__( self, outpoint: Optional['OutPoint'] = None, txid='', vout: int = 0, amount: int = 0, descriptor: Union[str, 'Descriptor'] = '', - scriptsig_template: Union['Script', 'ByteData', AnyStr] = ''): + scriptsig_template: Union['Script', 'ByteData', str] = ''): if isinstance(outpoint, OutPoint): self.outpoint = outpoint else: @@ -223,7 +229,7 @@ class TxIn: ## # @var witness_stack # witness stack - witness_stack: List[Union['Script', 'ByteData', AnyStr]] + witness_stack: List[Union['Script', 'ByteData', str]] ## # sequence disable. @@ -238,7 +244,10 @@ class TxIn: # @param[in] sequence sequence # @return sequence number. @classmethod - def get_sequence_number(cls, locktime: int = 0, sequence: int = SEQUENCE_DISABLE): + def get_sequence_number( + cls, + locktime: int = 0, + sequence: int = SEQUENCE_DISABLE): if sequence not in [-1, TxIn.SEQUENCE_DISABLE]: return sequence elif locktime == 0: @@ -362,6 +371,22 @@ def _update_tx_all(self): if self.enable_cache: self.get_tx_all() + ## + # @brief update transaction input. + # @param[in] outpoint outpoint + # @param[in] handle cfd handle + # @param[in] tx_handle tx handle + # @return void + def _update_txin_internal(self, handle, tx_handle, outpoint: 'OutPoint'): + pass + + ## + # @brief update transaction input cache. + # @param[in] outpoint OutPoint + # @return void + def _update_txin(self, outpoint: 'OutPoint'): + pass + ## # @brief get transaction input. # @param[in] handle cfd handle @@ -475,9 +500,9 @@ def add_multisig_sign( _script = to_hex_string(redeem_script) util = get_util() with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeMultisigSign', handle.get_handle()) - with JobHandle(handle, word_handle, + with JobHandle(handle, work_handle, 'CfdFreeMultisigSignHandle') as tx_handle: for sig in signature_list: _sig = to_hex_string(sig) @@ -528,7 +553,8 @@ def add_script_hash_sign( _hash_type = HashType.get(hash_type) _script = to_hex_string(redeem_script) util = get_util() - with util.create_handle() as handle: + with util.create_handle() as handle, self._get_handle( + handle, self.network) as tx_handle: clear_stack = True for sig in signature_list: _sig = sig @@ -540,20 +566,105 @@ def add_script_hash_sign( _sighashtype = SigHashType.get(sig.sighashtype) use_der_encode = sig.use_der_encode - self.hex = util.call_func( - 'CfdAddTxSign', handle.get_handle(), - self.network, self.hex, str(outpoint.txid), + util.call_func( + 'CfdAddTxSignByHandle', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), outpoint.vout, _hash_type.value, _sig, use_der_encode, _sighashtype.get_type(), _sighashtype.anyone_can_pay(), clear_stack) clear_stack = False + util.call_func( + 'CfdAddScriptHashLastSignByHandle', + handle.get_handle(), tx_handle.get_handle(), + str(outpoint.txid), outpoint.vout, _hash_type.value, _script) self.hex = util.call_func( - 'CfdAddScriptHashSign', - handle.get_handle(), self.network, self.hex, - str(outpoint.txid), outpoint.vout, _hash_type.value, - _script, False) - self._update_txin(outpoint) + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self._update_txin_internal(handle, tx_handle, outpoint) + + ## + # @brief add taproot sign. + # @param[in] outpoint outpoint + # @param[in] signature schnorr signature + # @param[in] sighashtype sighash type + # @param[in] annex annex + # @return void + def add_taproot_sign( + self, outpoint: 'OutPoint', + signature: Union['SignParameter', 'ByteData', str], + sighashtype: Union['SigHashType', str, int] = SigHashType.DEFAULT, + annex: Optional[Union['ByteData', str]] = None) -> None: + _signature = to_hex_string(signature) + _sighashtype = SigHashType.get(sighashtype) + util = get_util() + with util.create_handle() as handle, self._get_handle( + handle, self.network) as tx_handle: + if (len(_signature) == 64*2) and ( + _sighashtype != SigHashType.DEFAULT): + _signature = util.call_func( + 'CfdAddSighashTypeInSchnorrSignature', + handle.get_handle(), _signature, + _sighashtype.get_type(), _sighashtype.anyone_can_pay()) + + util.call_func( + 'CfdAddTaprootSignByHandle', + handle.get_handle(), tx_handle.get_handle(), + str(outpoint.txid), outpoint.vout, + _signature, '', '', to_hex_string(annex)) + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self._update_txin_internal(handle, tx_handle, outpoint) + + ## + # @brief add tapscript sign. + # @param[in] outpoint outpoint + # @param[in] signature_list signature list + # @param[in] tapscript tapscript + # @param[in] control_block control block + # @param[in] annex annex + # @return void + def add_tapscript_sign( + self, outpoint: 'OutPoint', + signature_list: List[Union['SignParameter', 'ByteData', str]], + tapscript: Optional[Union['Script', str]] = None, + control_block: Optional[Union['ByteData', str]] = None, + annex: Optional[Union['ByteData', str]] = None) -> None: + _script = to_hex_string(tapscript) + _control_block = to_hex_string(control_block) + util = get_util() + with util.create_handle() as handle, self._get_handle( + handle, self.network) as tx_handle: + clear_stack = True + for sig in signature_list: + _sig = to_hex_string(sig) + _sighashtype = SigHashType.ALL + if isinstance(sig, SignParameter): + _sighashtype = SigHashType.get(sig.sighashtype) + if (len(_sig) == 64*2) and ( + _sighashtype != SigHashType.DEFAULT): + _sig = util.call_func( + 'CfdAddSighashTypeInSchnorrSignature', + handle.get_handle(), _sig, + _sighashtype.get_type(), _sighashtype.anyone_can_pay()) + util.call_func( + 'CfdAddTxSignByHandle', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), + outpoint.vout, HashType.TAPROOT.value, _sig, + False, _sighashtype.get_type(), + _sighashtype.anyone_can_pay(), clear_stack) + clear_stack = False + + util.call_func( + 'CfdAddTaprootSignByHandle', + handle.get_handle(), tx_handle.get_handle(), + str(outpoint.txid), outpoint.vout, '', + _script, _control_block, to_hex_string(annex)) + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self._update_txin_internal(handle, tx_handle, outpoint) ## # @brief add sign. @@ -583,6 +694,22 @@ def add_sign( _sighashtype.anyone_can_pay(), clear_stack) self._update_txin(outpoint) + ## + # @brief get transaction handle. + # @param[in] handle cfd handle + # @param[in] network network type + # @return transaction job handle + def _get_handle( + self, handle, network: Optional[Union['Network', str, int]] = None, + ) -> 'JobHandle': + util = get_util() + _network = Network.get( + self.network) if not network else Network.get(network) + work_handle = util.call_func( + 'CfdInitializeTxDataHandle', handle.get_handle(), + _network.value, self.hex) + return JobHandle(handle, work_handle, 'CfdFreeTxDataHandle') + ## # @class Transaction @@ -629,6 +756,9 @@ class Transaction(_TransactionBase): # locktime locktime: int + ## + # default transaction version. + DEFAULT_VERSION: int = 2 ## # bitcoin network value. NETWORK = Network.MAINNET.value @@ -665,8 +795,13 @@ def parse_to_json(cls, hex: str, network=Network.MAINNET) -> str: # @param[in] enable_cache enable tx cache # @return transaction object @classmethod - def create(cls, version: int, locktime: int, txins: List['TxIn'], - txouts: List['TxOut'], enable_cache: bool = True) -> 'Transaction': + def create( + cls, + version: int = DEFAULT_VERSION, + locktime: int = 0, + txins: List['TxIn'] = [], + txouts: List['TxOut'] = [], + enable_cache: bool = True) -> 'Transaction': util = get_util() with util.create_handle() as handle: _tx_handle = util.call_func( @@ -733,27 +868,34 @@ def _update_info(self): ## # @brief update transaction input. # @param[in] outpoint outpoint + # @param[in] handle cfd handle + # @param[in] tx_handle tx handle # @return void - def _update_txin(self, outpoint): + def _update_txin_internal(self, handle, tx_handle, outpoint: 'OutPoint'): if self.enable_cache is False: return util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTxDataHandle', handle.get_handle(), - self.NETWORK, self.hex) - with JobHandle(handle, _tx_handle, - self.FREE_FUNC_NAME) as tx_handle: - self.txid, self.wtxid, self.size, self.vsize, self.weight,\ - self.version, self.locktime = util.call_func( - 'CfdGetTxInfoByHandle', handle.get_handle(), - tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - # update txin - txin, index = self._get_txin( - handle, tx_handle, outpoint=outpoint) - self.txin_list[index] = txin + self.txid, self.wtxid, self.size, self.vsize, self.weight,\ + self.version, self.locktime = util.call_func( + 'CfdGetTxInfoByHandle', handle.get_handle(), + tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + # update txin + txin, index = self._get_txin(handle, tx_handle, outpoint=outpoint) + self.txin_list[index] = txin + + ## + # @brief update transaction input. + # @param[in] outpoint outpoint + # @return void + def _update_txin(self, outpoint: 'OutPoint'): + if self.enable_cache is False: + return + util = get_util() + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + self._update_txin_internal(handle, tx_handle, outpoint) ## # @brief get transaction all data. @@ -784,21 +926,17 @@ def get_txout_list(handle, tx_handle): return txout_list util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTxDataHandle', handle.get_handle(), - self.NETWORK, self.hex) - with JobHandle(handle, _tx_handle, - self.FREE_FUNC_NAME) as tx_handle: - self.txid, self.wtxid, self.size, self.vsize, self.weight,\ - self.version, self.locktime = util.call_func( - 'CfdGetTxInfoByHandle', handle.get_handle(), - tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - self.txin_list = get_txin_list(handle, tx_handle) - self.txout_list = get_txout_list(handle, tx_handle) - return self.txin_list, self.txout_list + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + self.txid, self.wtxid, self.size, self.vsize, self.weight,\ + self.version, self.locktime = util.call_func( + 'CfdGetTxInfoByHandle', handle.get_handle(), + tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + self.txin_list = get_txin_list(handle, tx_handle) + self.txout_list = get_txout_list(handle, tx_handle) + return self.txin_list, self.txout_list ## # @brief add transaction input. @@ -831,36 +969,70 @@ def add_txout(self, amount: int, address='', locking_script='') -> None: # @return void def add(self, txins: List['TxIn'], txouts: List['TxOut']) -> None: util = get_util() - with util.create_handle() as handle: - _tx_handle = util.call_func( - 'CfdInitializeTransaction', handle.get_handle(), - self.NETWORK, 0, 0, self.hex) - with JobHandle( - handle, _tx_handle, self.FREE_FUNC_NAME) as tx_handle: - for txin in txins: - sec = TxIn.get_sequence_number( - self.locktime, txin.sequence) + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + for txin in txins: + sec = TxIn.get_sequence_number(self.locktime, txin.sequence) + util.call_func( + 'CfdAddTransactionInput', handle.get_handle(), + tx_handle.get_handle(), str(txin.outpoint.txid), + txin.outpoint.vout, sec) + for txout in txouts: + util.call_func( + 'CfdAddTransactionOutput', handle.get_handle(), + tx_handle.get_handle(), txout.amount, + str(txout.address), str(txout.locking_script), '') + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self.txid, self.wtxid, self.size, self.vsize, self.weight,\ + self.version, self.locktime = util.call_func( + 'CfdGetTxInfoByHandle', handle.get_handle(), + tx_handle.get_handle()) + self.txid = Txid(self.txid) + self.wtxid = Txid(self.wtxid) + self.txin_list += copy.deepcopy(txins) + self.txout_list += copy.deepcopy(txouts) + + ## + # @brief clear sign data. + # @param[in] outpoint outpoint + # @param[in] clear_witness_stack witness stack clear flag + # @param[in] clear_scriptsig scriptsig clear flag + # @return void + def clear_sign_data( + self, + outpoint: Optional['OutPoint'] = None, + clear_witness_stack: bool = True, + clear_scriptsig: bool = True) -> None: + outpoints = [] + if isinstance(outpoint, OutPoint): + outpoints = [outpoint] + else: + outpoints = [txin.outpoint for txin in self.txin_list] + util = get_util() + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + for target in outpoints: + if clear_witness_stack: util.call_func( - 'CfdAddTransactionInput', handle.get_handle(), - tx_handle.get_handle(), str(txin.outpoint.txid), - txin.outpoint.vout, sec) - for txout in txouts: + 'CfdClearWitnessStack', handle.get_handle(), + tx_handle.get_handle(), str(target.txid), + target.vout) + if clear_scriptsig: util.call_func( - 'CfdAddTransactionOutput', handle.get_handle(), - tx_handle.get_handle(), txout.amount, - str(txout.address), - str(txout.locking_script), '') - self.hex = util.call_func( - 'CfdFinalizeTransaction', handle.get_handle(), - tx_handle.get_handle()) - self.txid, self.wtxid, self.size, self.vsize, self.weight,\ - self.version, self.locktime = util.call_func( - 'CfdGetTxInfoByHandle', handle.get_handle(), - tx_handle.get_handle()) - self.txid = Txid(self.txid) - self.wtxid = Txid(self.wtxid) - self.txin_list += copy.deepcopy(txins) - self.txout_list += copy.deepcopy(txouts) + 'CfdUpdateTxInScriptSig', handle.get_handle(), + tx_handle.get_handle(), str(target.txid), + target.vout, '') + if clear_witness_stack or clear_scriptsig: + # update txin + txin, index = self._get_txin( + handle, tx_handle, outpoint=outpoint) + self.txin_list[index] = txin + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self._update_info() ## # @brief update transaction output amount. @@ -884,28 +1056,59 @@ def update_txout_amount(self, index: int, amount: int): # @param[in] pubkey pubkey # @param[in] redeem_script redeem script # @param[in] sighashtype sighash type + # @param[in] utxos utxo list (need if taproot) + # @param[in] tapleaf_hash tapleaf hash + # @param[in] annex annex bytes + # @param[in] codeseparator_pos tapscript codeseparator position # @return sighash def get_sighash( - self, - outpoint: 'OutPoint', - hash_type, - amount: int = 0, - pubkey='', - redeem_script='', - sighashtype=SigHashType.ALL) -> 'ByteData': + self, + outpoint: 'OutPoint', + hash_type, + amount: int = 0, + pubkey='', + redeem_script='', + sighashtype=SigHashType.ALL, + utxos: List['UtxoData'] = [], + tapleaf_hash: Optional[Union['ByteData', str]] = None, + annex: Optional[Union['ByteData', str]] = None, + codeseparator_pos: int = CODE_SEPARATOR_POSITION_FINAL, + ) -> 'ByteData': _hash_type = HashType.get(hash_type) _pubkey = to_hex_string(pubkey) _script = to_hex_string(redeem_script) _sighashtype = SigHashType.get(sighashtype) + _tapleaf_hash = to_hex_string(tapleaf_hash) util = get_util() - with util.create_handle() as handle: - sighash = util.call_func( - 'CfdCreateSighash', handle.get_handle(), - self.NETWORK, self.hex, str(outpoint.txid), - outpoint.vout, _hash_type.value, _pubkey, - _script, amount, _sighashtype.get_type(), - _sighashtype.anyone_can_pay()) - return ByteData(sighash) + if not utxos: + with util.create_handle() as handle: + sighash = util.call_func( + 'CfdCreateSighash', handle.get_handle(), + self.NETWORK, self.hex, str(outpoint.txid), + outpoint.vout, _hash_type.value, _pubkey, + _script, amount, _sighashtype.get_type(), + _sighashtype.anyone_can_pay()) + else: + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + for utxo in utxos: + util.call_func( + 'CfdSetTransactionUtxoData', handle.get_handle(), + tx_handle.get_handle(), str(utxo.outpoint.txid), + utxo.outpoint.vout, utxo.amount, '', + str(utxo.descriptor), '', '', + to_hex_string(utxo.scriptsig_template), False) + if (not _tapleaf_hash) and _script: + tree = TaprootScriptTree(Script(_script)) + _tapleaf_hash = tree.hash.hex + + sighash = util.call_func( + 'CfdCreateSighashByHandle', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), + outpoint.vout, _sighashtype.get_type(), + _sighashtype.anyone_can_pay(), _pubkey, _script, + _tapleaf_hash, codeseparator_pos, to_hex_string(annex)) + return ByteData(sighash) ## # @brief add sign with private key. @@ -915,6 +1118,9 @@ def get_sighash( # @param[in] amount amount # @param[in] sighashtype sighash type # @param[in] grind_r grind-R flag + # @param[in] utxos utxo list (need if taproot) + # @param[in] aux_rand random byte for taproot + # @param[in] annex annex bytes # @return void def sign_with_privkey( self, @@ -923,7 +1129,10 @@ def sign_with_privkey( privkey, amount: int = 0, sighashtype=SigHashType.ALL, - grind_r: bool = True) -> None: + grind_r: bool = True, + utxos: List['UtxoData'] = [], + aux_rand: Optional[Union['ByteData', str]] = None, + annex: Optional[Union['ByteData', str]] = None) -> None: _hash_type = HashType.get(hash_type) if isinstance(privkey, Privkey): _privkey = privkey @@ -934,32 +1143,76 @@ def sign_with_privkey( _pubkey = _privkey.pubkey _sighashtype = SigHashType.get(sighashtype) util = get_util() - with util.create_handle() as handle: - self.hex = util.call_func( - 'CfdAddSignWithPrivkeySimple', handle.get_handle(), - self.NETWORK, self.hex, str(outpoint.txid), - outpoint.vout, _hash_type.value, str(_pubkey), - str(_privkey), amount, _sighashtype.get_type(), - _sighashtype.anyone_can_pay(), grind_r) - self._update_txin(outpoint) + if not utxos: + with util.create_handle() as handle: + self.hex = util.call_func( + 'CfdAddSignWithPrivkeySimple', handle.get_handle(), + self.NETWORK, self.hex, str(outpoint.txid), + outpoint.vout, _hash_type.value, str(_pubkey), + str(_privkey), amount, _sighashtype.get_type(), + _sighashtype.anyone_can_pay(), grind_r) + self._update_txin(outpoint) + else: + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + for utxo in utxos: + util.call_func( + 'CfdSetTransactionUtxoData', handle.get_handle(), + tx_handle.get_handle(), str(utxo.outpoint.txid), + utxo.outpoint.vout, utxo.amount, '', + str(utxo.descriptor), '', '', + to_hex_string(utxo.scriptsig_template), False) + + util.call_func( + 'CfdAddSignWithPrivkeyByHandle', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), + outpoint.vout, str(_privkey), _sighashtype.get_type(), + _sighashtype.anyone_can_pay(), grind_r, + to_hex_string(aux_rand), to_hex_string(annex)) + self.hex = util.call_func( + 'CfdFinalizeTransaction', handle.get_handle(), + tx_handle.get_handle()) + self._update_txin_internal(handle, tx_handle, outpoint) ## # @brief verify sign. # @param[in] outpoint outpoint # @param[in] address address # @param[in] hash_type hash type - # @param[in] amount amount + # @param[in] amount amount (unused if taproot) + # @param[in] utxos tx all input utxo list. # @return void def verify_sign(self, outpoint: 'OutPoint', address, hash_type, - amount: int) -> None: + amount: int = 0, utxos: List['UtxoData'] = []) -> None: _hash_type = HashType.get(hash_type) util = get_util() - with util.create_handle() as handle: + if _hash_type in [HashType.P2SH_P2WPKH, HashType.P2SH_P2WSH]: + # p2sh-segwit call single type API. + with util.create_handle() as handle: + util.call_func( + 'CfdVerifyTxSign', handle.get_handle(), + self.NETWORK, self.hex, str(outpoint.txid), + outpoint.vout, str(address), _hash_type.value, + '', amount, '') + return + + with util.create_handle() as handle, super()._get_handle( + handle, self.network) as tx_handle: + if not utxos: + addr = address if isinstance( + address, Address) else AddressUtil.parse(address) + utxo = UtxoData(outpoint, amount=amount, + descriptor=f'raw({addr.locking_script})') + utxos = [utxo] + for utxo in utxos: + util.call_func( + 'CfdSetTransactionUtxoData', handle.get_handle(), + tx_handle.get_handle(), str(utxo.outpoint.txid), + utxo.outpoint.vout, utxo.amount, '', str(utxo.descriptor), + '', '', to_hex_string(utxo.scriptsig_template), False) util.call_func( - 'CfdVerifyTxSign', handle.get_handle(), - self.NETWORK, self.hex, str(outpoint.txid), - outpoint.vout, str(address), _hash_type.value, - '', amount, '') + 'CfdVerifyTxSignByHandle', handle.get_handle(), + tx_handle.get_handle(), str(outpoint.txid), outpoint.vout) ## # @brief verify signature. @@ -973,8 +1226,14 @@ def verify_sign(self, outpoint: 'OutPoint', address, hash_type, # @retval True signature valid. # @retval False signature invalid. def verify_signature( - self, outpoint: 'OutPoint', signature, hash_type, pubkey, - amount: int = 0, redeem_script='', sighashtype=SigHashType.ALL) -> bool: + self, + outpoint: 'OutPoint', + signature, + hash_type, + pubkey, + amount: int = 0, + redeem_script='', + sighashtype=SigHashType.ALL) -> bool: _signature = to_hex_string(signature) _pubkey = to_hex_string(pubkey) _script = to_hex_string(redeem_script) @@ -1009,22 +1268,28 @@ def verify_signature( # @retval [1] utxo fee. # @retval [2] total tx fee. @classmethod - def select_coins(cls, utxo_list: List['UtxoData'], tx_fee_amount: int, - target_amount: int, effective_fee_rate: float = 20.0, - long_term_fee_rate: float = 20.0, dust_fee_rate: float = 3.0, + def select_coins(cls, + utxo_list: List['UtxoData'], + tx_fee_amount: int, + target_amount: int, + effective_fee_rate: float = 20.0, + long_term_fee_rate: float = 20.0, + dust_fee_rate: float = 3.0, knapsack_min_change: int = -1, - ) -> Tuple[List['UtxoData'], int, int]: + ) -> Tuple[List['UtxoData'], + int, + int]: if (isinstance(utxo_list, list) is False) or ( len(utxo_list) == 0): raise CfdError( error_code=1, message='Error: Invalid utxo_list.') util = get_util() with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeCoinSelection', handle.get_handle(), len(utxo_list), 1, '', tx_fee_amount, effective_fee_rate, long_term_fee_rate, dust_fee_rate, knapsack_min_change) - with JobHandle(handle, word_handle, + with JobHandle(handle, work_handle, 'CfdFreeCoinSelectionHandle') as tx_handle: for index, utxo in enumerate(utxo_list): util.call_func( @@ -1110,11 +1375,12 @@ def estimate_fee(self, utxo_list: List['UtxoData'], fee_rate: float = 20.0, # @retval [0] total tx fee. # @retval [1] used reserved address. (None or reserved_address) def fund_raw_transaction( - self, txin_utxo_list: List['UtxoData'], utxo_list: List['UtxoData'], + self, txin_utxo_list: List['UtxoData'], + utxo_list: List['UtxoData'], reserved_address, target_amount: int = 0, effective_fee_rate: float = 20.0, long_term_fee_rate: float = 20.0, dust_fee_rate: float = -1.0, - knapsack_min_change: int = -1) -> Tuple[int, str]: + knapsack_min_change: int = -1) -> Tuple[int, Optional[str]]: util = get_util() def set_opt(handle, tx_handle, key, i_val=0, f_val=0, b_val=False): @@ -1135,10 +1401,10 @@ def set_opt(handle, tx_handle, key, i_val=0, f_val=0, b_val=False): network = temp_network.value with util.create_handle() as handle: - word_handle = util.call_func( + work_handle = util.call_func( 'CfdInitializeFundRawTx', handle.get_handle(), network, 1, '') - with JobHandle(handle, word_handle, + with JobHandle(handle, work_handle, 'CfdFreeFundRawTxHandle') as tx_handle: for utxo in txin_utxo_list: util.call_func( @@ -1184,7 +1450,7 @@ def set_opt(handle, tx_handle, key, i_val=0, f_val=0, b_val=False): self.hex = _new_hex self._update_tx_all() - return _tx_fee, used_addr + return int(_tx_fee), used_addr ## @@ -1220,5 +1486,6 @@ class _FundTxOpt(Enum): 'TxIn', 'TxOut', '_TransactionBase', - 'Transaction' + 'Transaction', + 'CODE_SEPARATOR_POSITION_FINAL' ] diff --git a/cfd/util.py b/cfd/util.py index 7c44a47..d319c9a 100644 --- a/cfd/util.py +++ b/cfd/util.py @@ -3,15 +3,15 @@ # @file util.py # @brief cfd utility file. # @note Copyright 2020 CryptoGarage -from ctypes import Union, c_int, c_void_p, c_char_p, c_int32, c_int64,\ - c_uint32, c_uint64, c_bool, c_double, c_ubyte, \ +from ctypes import c_int, c_void_p, c_char_p, c_int32, c_int64,\ + c_uint32, c_uint64, c_uint8, c_bool, c_double, c_ubyte, \ CDLL, byref, POINTER, ArgumentError from os.path import isfile, abspath from enum import Enum import platform import os import re -from typing import List +from typing import List, Union ################ # Public class # @@ -55,6 +55,9 @@ class CfdErrorCode(Enum): ## # CfdErrorCode: sign verification. SIGN_VERIFICATION = 7 + ## + # CfdErrorCode: not found. + NOT_FOUND = 8 ## @@ -76,10 +79,13 @@ class CfdError(Exception): # @param[in] message error message def __init__( self, - error_code: int = CfdErrorCode.UNKNOWN.value, + error_code: Union[int, 'CfdErrorCode'] = CfdErrorCode.UNKNOWN, message: str = '', ) -> None: - self.error_code = error_code + if isinstance(error_code, CfdErrorCode): + self.error_code = error_code.value + else: + self.error_code = int(error_code) self.message = message ## @@ -160,7 +166,7 @@ def __init__(self, data) -> None: self.hex = ''.join(new_list) elif isinstance(data, list): new_list = data[::-1] - self.hex = ''.join("%02x" % b for b in new_list) + self.hex = ''.join("%02x" % int(b) for b in new_list) else: self.hex = str(data).lower() if self.hex != '': @@ -199,7 +205,9 @@ def as_array(self) -> List[int]: # @param[in] value data # @return hex string. def to_hex_string(value) -> str: - if isinstance(value, bytes): + if value is None: + return '' + elif isinstance(value, bytes): return value.hex() elif isinstance(value, bytearray): return value.hex() @@ -252,6 +260,13 @@ class CIntP(object): pass +## +# @class CUint8P +# @brief uint8 pointer class. +class CUint8P(object): + pass + + ## # @class CUint32P # @brief uint32 pointer class. @@ -293,6 +308,9 @@ class CInt64P(object): # @brief int pointer. c_int_p = CIntP() ## +# @brief uint8 pointer. +c_uint8_p = CUint8P() +## # @brief uint32 pointer. c_uint32_p = CUint32P() ## @@ -447,6 +465,16 @@ class CfdUtil: ("CfdGetLastErrorMessage", c_int, [c_void_p, c_char_p_p]), # noqa: E501 ("CfdRequestExecuteJson", c_int, [c_void_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdSerializeByteData", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdEncryptAES", c_int, [c_void_p, c_char_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdDecryptAES", c_int, [c_void_p, c_char_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdEncodeBase64", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdDecodeBase64", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdEncodeBase58", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdDecodeBase58", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdRipemd160", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdSha256", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdHash160", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdHash256", c_int, [c_void_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 ("CfdCreateConfidentialAddress", c_int, [c_void_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdParseConfidentialAddress", c_int, [c_void_p, c_char_p, c_char_p_p, c_char_p_p, c_int_p]), # noqa: E501 ("CfdInitializeConfidentialTx", c_int, [c_void_p, c_uint32, c_uint32, c_char_p_p]), # noqa: E501 @@ -506,6 +534,8 @@ class CfdUtil: ("CfdCheckTweakAddFromSchnorrPubkey", c_int, [c_void_p, c_char_p, c_bool, c_char_p, c_char_p]), # noqa: E501 ("CfdSignSchnorr", c_int, [c_void_p, c_char_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdSignSchnorrWithNonce", c_int, [c_void_p, c_char_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdAddSighashTypeInSchnorrSignature", c_int, [c_void_p, c_char_p, c_int, c_bool, c_char_p_p]), # noqa: E501 + ("CfdGetSighashTypeFromSchnorrSignature", c_int, [c_void_p, c_char_p, c_int_p, c_bool_p]), # noqa: E501 ("CfdComputeSchnorrSigPoint", c_int, [c_void_p, c_char_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdVerifySchnorr", c_int, [c_void_p, c_char_p, c_char_p, c_char_p]), # noqa: E501 ("CfdSplitSchnorrSignature", c_int, [c_void_p, c_char_p, c_char_p_p, c_char_p_p]), # noqa: E501 @@ -517,6 +547,7 @@ class CfdUtil: ("CfdGetPrivkeyWif", c_int, [c_void_p, c_char_p, c_int, c_bool, c_char_p_p]), # noqa: E501 ("CfdParsePrivkeyWif", c_int, [c_void_p, c_char_p, c_char_p_p, c_int_p, c_bool_p]), # noqa: E501 ("CfdGetPubkeyFromPrivkey", c_int, [c_void_p, c_char_p, c_char_p, c_bool, c_char_p_p]), # noqa: E501 + ("CfdGetPubkeyFingerprint", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdCompressPubkey", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdUncompressPubkey", c_int, [c_void_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdInitializeCombinePubkey", c_int, [c_void_p, c_void_p_p]), # noqa: E501 @@ -548,6 +579,54 @@ class CfdUtil: ("CfdFinalizeTxSerializeForLedger", c_int, [c_void_p, c_void_p, c_int, c_char_p, c_bool, c_bool, c_char_p_p]), # noqa: E501 ("CfdFinalizeTxSerializeHashForLedger", c_int, [c_void_p, c_void_p, c_int, c_char_p, c_bool, c_bool, c_bool, c_char_p_p]), # noqa: E501 ("CfdFreeTxSerializeForLedger", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdCreatePsbtHandle", c_int, [c_void_p, c_int, c_char_p, c_char_p, c_uint32, c_uint32, c_void_p_p]), # noqa: E501 + ("CfdFreePsbtHandle", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdGetPsbtData", c_int, [c_void_p, c_void_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetPsbtGlobalData", c_int, [c_void_p, c_void_p, c_uint32_p, c_char_p_p, c_uint32_p, c_uint32_p]), # noqa: E501 + ("CfdJoinPsbt", c_int, [c_void_p, c_void_p, c_char_p]), # noqa: E501 + ("CfdSignPsbt", c_int, [c_void_p, c_void_p, c_char_p, c_bool]), # noqa: E501 + ("CfdCombinePsbt", c_int, [c_void_p, c_void_p, c_char_p]), # noqa: E501 + ("CfdFinalizePsbt", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdExtractPsbtTransaction", c_int, [c_void_p, c_void_p, c_char_p_p]), # noqa: E501 + ("CfdIsFinalizedPsbt", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdIsFinalizedPsbtInput", c_int, [c_void_p, c_void_p, c_char_p, c_uint32]), # noqa: E501 + ("CfdAddPsbtTxInWithPubkey", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_uint32, c_int64, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdAddPsbtTxInWithScript", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_uint32, c_int64, c_char_p, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetPsbtTxInUtxo", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int64, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetPsbtTxInBip32Pubkey", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetPsbtSignature", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetPsbtSighashType", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int]), # noqa: E501 + ("CfdSetPsbtFinalizeScript", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p]), # noqa: E501 + ("CfdClearPsbtSignData", c_int, [c_void_p, c_void_p, c_char_p, c_uint32]), # noqa: E501 + ("CfdGetPsbtSighashType", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int_p]), # noqa: E501 + ("CfdGetPsbtUtxoData", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int64_p, c_char_p_p, c_char_p_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetPsbtUtxoDataByIndex", c_int, [c_void_p, c_void_p, c_uint32, c_char_p_p, c_uint32_p, c_int64_p, c_char_p_p, c_char_p_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdAddPsbtTxOutWithPubkey", c_int, [c_void_p, c_void_p, c_int64, c_char_p, c_char_p, c_uint32_p]), # noqa: E501 + ("CfdAddPsbtTxOutWithScript", c_int, [c_void_p, c_void_p, c_int64, c_char_p, c_char_p, c_char_p, c_uint32_p]), # noqa: E501 + ("CfdSetPsbtTxOutBip32Pubkey", c_int, [c_void_p, c_void_p, c_uint32, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdGetPsbtTxInIndex", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_uint32_p]), # noqa: E501 + ("CfdGetPsbtPubkeyRecord", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdIsFindPsbtPubkeyRecord", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p]), # noqa: E501 + ("CfdGetPsbtBip32Data", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetPsbtPubkeyList", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_uint32_p, c_void_p_p]), # noqa: E501 + ("CfdGetPsbtPubkeyListData", c_int, [c_void_p, c_void_p, c_uint32, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetPsbtPubkeyListBip32Data", c_int, [c_void_p, c_void_p, c_uint32, c_char_p_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdFreePsbtPubkeyList", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdGetPsbtByteDataList", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_uint32_p, c_void_p_p]), # noqa: E501 + ("CfdGetPsbtByteDataItem", c_int, [c_void_p, c_void_p, c_uint32, c_char_p_p]), # noqa: E501 + ("CfdFreePsbtByteDataList", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdAddPsbtGlobalXpubkey", c_int, [c_void_p, c_void_p, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetPsbtRedeemScript", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p]), # noqa: E501 + ("CfdAddPsbtRecord", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p, c_char_p]), # noqa: E501 + ("CfdGetPsbtRecord", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdIsFindPsbtRecord", c_int, [c_void_p, c_void_p, c_int, c_uint32, c_char_p]), # noqa: E501 + ("CfdVerifyPsbtTxIn", c_int, [c_void_p, c_void_p, c_char_p, c_uint32]), # noqa: E501 + ("CfdInitializeFundPsbt", c_int, [c_void_p, c_void_p_p]), # noqa: E501 + ("CfdFundPsbtAddToUtxoList", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int64, c_char_p, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdSetOptionFundPsbt", c_int, [c_void_p, c_void_p, c_int, c_int64, c_double, c_bool]), # noqa: E501 + ("CfdFinalizeFundPsbt", c_int, [c_void_p, c_void_p, c_void_p, c_char_p, c_int64_p, c_uint32_p]), # noqa: E501 + ("CfdGetFundPsbtUsedUtxo", c_int, [c_void_p, c_void_p, c_uint32, c_uint32_p, c_char_p_p, c_uint32_p, c_int64_p, c_char_p_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdFreeFundPsbt", c_int, [c_void_p, c_void_p]), # noqa: E501 ("CfdParseScript", c_int, [c_void_p, c_char_p, c_void_p_p, c_uint32_p]), # noqa: E501 ("CfdGetScriptItem", c_int, [c_void_p, c_void_p, c_uint32, c_char_p_p]), # noqa: E501 ("CfdFreeScriptItemHandle", c_int, [c_void_p, c_void_p]), # noqa: E501 @@ -557,9 +636,36 @@ class CfdUtil: ("CfdAddMultisigScriptSigDataToDer", c_int, [c_void_p, c_void_p, c_char_p, c_int, c_bool, c_char_p]), # noqa: E501 ("CfdFinalizeMultisigScriptSig", c_int, [c_void_p, c_void_p, c_char_p, c_char_p_p]), # noqa: E501 ("CfdFreeMultisigScriptSigHandle", c_int, [c_void_p, c_void_p]), # noqa: E501 + ("CfdInitializeTaprootScriptTree", c_int, [c_void_p, c_void_p_p]), # noqa: E501 + ("CfdSetInitialTapLeaf", c_int, [c_void_p, c_void_p, c_char_p, c_uint8]), # noqa: E501 + ("CfdSetInitialTapBranchByHash", c_int, [c_void_p, c_void_p, c_char_p]), # noqa: E501 + ("CfdSetScriptTreeFromString", c_int, [c_void_p, c_void_p, c_char_p, c_char_p, c_uint8, c_char_p]), # noqa: E501 + ("CfdSetTapScriptByWitnessStack", c_int, [c_void_p, c_void_p, c_char_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdAddTapBranchByHash", c_int, [c_void_p, c_void_p, c_char_p]), # noqa: E501 + ("CfdAddTapBranchByScriptTree", c_int, [c_void_p, c_void_p, c_void_p]), # noqa: E501 + ("CfdAddTapBranchByScriptTreeString", c_int, [c_void_p, c_void_p, c_char_p]), # noqa: E501 + ("CfdAddTapBranchByTapLeaf", c_int, [c_void_p, c_void_p, c_char_p, c_uint8]), # noqa: E501 + ("CfdGetBaseTapLeaf", c_int, [c_void_p, c_void_p, c_uint8_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetTapBranchCount", c_int, [c_void_p, c_void_p, c_uint32_p]), # noqa: E501 + ("CfdGetTapBranchData", c_int, [c_void_p, c_void_p, c_uint8, c_bool, c_char_p_p, c_uint8_p, c_char_p_p, c_uint8_p]), # noqa: E501 + ("CfdGetTapBranchHandle", c_int, [c_void_p, c_void_p, c_uint8, c_char_p_p, c_void_p_p]), # noqa: E501 + ("CfdGetTaprootScriptTreeHash", c_int, [c_void_p, c_void_p, c_char_p, c_char_p_p, c_char_p_p, c_char_p_p]), # noqa: E501 + ("CfdGetTaprootTweakedPrivkey", c_int, [c_void_p, c_void_p, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdGetTaprootScriptTreeSrting", c_int, [c_void_p, c_void_p, c_char_p_p]), # noqa: E501 + ("CfdFreeTaprootScriptTreeHandle", c_int, [c_void_p, c_void_p]), # noqa: E501 ("CfdInitializeTransaction", c_int, [c_void_p, c_int, c_uint32, c_uint32, c_char_p, c_void_p_p]), # noqa: E501 ("CfdAddTransactionInput", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_uint32]), # noqa: E501 ("CfdAddTransactionOutput", c_int, [c_void_p, c_void_p, c_int64, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdClearWitnessStack", c_int, [c_void_p, c_void_p, c_char_p, c_uint32]), # noqa: E501 + ("CfdUpdateTxInScriptSig", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p]), # noqa: E501 + ("CfdSetTransactionUtxoData", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int64, c_char_p, c_char_p, c_char_p, c_char_p, c_char_p, c_bool]), # noqa: E501 + ("CfdCreateSighashByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int, c_bool, c_char_p, c_char_p, c_char_p, c_uint32, c_char_p, c_char_p_p]), # noqa: E501 + ("CfdAddSignWithPrivkeyByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p, c_int, c_bool, c_bool, c_char_p, c_char_p]), # noqa: E501 + ("CfdVerifyTxSignByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32]), # noqa: E501 + ("CfdAddTxSignByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int, c_char_p, c_bool, c_int, c_bool, c_bool]), # noqa: E501 + ("CfdAddTaprootSignByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_char_p, c_char_p, c_char_p, c_char_p]), # noqa: E501 + ("CfdAddPubkeyHashSignByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int, c_char_p, c_char_p, c_bool, c_int, c_bool]), # noqa: E501 + ("CfdAddScriptHashLastSignByHandle", c_int, [c_void_p, c_void_p, c_char_p, c_uint32, c_int, c_char_p]), # noqa: E501 ("CfdFinalizeTransaction", c_int, [c_void_p, c_void_p, c_char_p_p]), # noqa: E501 ("CfdFreeTransactionHandle", c_int, [c_void_p, c_void_p]), # noqa: E501 ("CfdUpdateTxOutAmount", c_int, [c_void_p, c_int, c_char_p, c_uint32, c_int64, c_char_p_p]), # noqa: E501 @@ -611,7 +717,7 @@ class CfdUtil: ## # @brief get instance. # @return utility instance. - @classmethod + @ classmethod def get_instance(cls): if not hasattr(cls, "_instance"): cls._instance = cls() @@ -775,6 +881,9 @@ def make_int_fn(fn): def make_uint32_fn(fn): return lambda *args: value_fn_wrapper(c_uint32(), fn, *args) + def make_uint8_fn(fn): + return lambda *args: value_fn_wrapper(c_uint8(), fn, *args) + def make_int32_fn(fn): return lambda *args: value_fn_wrapper(c_int32(), fn, *args) @@ -798,6 +907,8 @@ def make_input_str_fn(fn, pos): int32_pos = [i for (i, t) in enumerate(argtypes) if t == c_int32_p] uint32_pos = [i for (i, t) in enumerate( argtypes) if t == c_uint32_p] + uint8_pos = [i for (i, t) in enumerate( + argtypes) if t == c_uint8_p] int64_pos = [i for (i, t) in enumerate(argtypes) if t == c_int64_p] uint64_pos = [i for (i, t) in enumerate( argtypes) if t == c_uint64_p] @@ -814,6 +925,8 @@ def make_input_str_fn(fn, pos): argtypes[i] = POINTER(c_int32) elif isinstance(argtypes[i], CUint32P): argtypes[i] = POINTER(c_uint32) + elif isinstance(argtypes[i], CUint8P): + argtypes[i] = POINTER(c_uint8) elif isinstance(argtypes[i], CInt64P): argtypes[i] = POINTER(c_int64) elif isinstance(argtypes[i], CUint64P): @@ -835,6 +948,8 @@ def make_input_str_fn(fn, pos): fn = make_int32_fn(fn) elif len(uint32_pos) > 0 and i in uint32_pos: fn = make_uint32_fn(fn) + elif len(uint8_pos) > 0 and i in uint8_pos: + fn = make_uint8_fn(fn) elif len(int64_pos) > 0 and i in int64_pos: fn = make_int64_fn(fn) elif len(uint64_pos) > 0 and i in uint64_pos: diff --git a/cmake/CfdCommonOption.cmake b/cmake/CfdCommonOption.cmake index 73a61ce..848197f 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 63df95d..8dc2cad 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 c6beeab..b92ed3e 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 4ebcc93..23b8e9f 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -43,7 +43,7 @@ if(CFD_TARGET_VERSION) set(CFD_TARGET_TAG ${CFD_TARGET_VERSION}) message(STATUS "[external project local] cfd target=${CFD_TARGET_VERSION}") else() -set(CFD_TARGET_TAG v0.2.2) +set(CFD_TARGET_TAG v0.3.0) endif() if(CFD_TARGET_URL) set(CFD_TARGET_REP ${CFD_TARGET_URL}) diff --git a/integration_test/elements.conf b/integration_test/elements.conf index 32daff4..650d612 100644 --- a/integration_test/elements.conf +++ b/integration_test/elements.conf @@ -429,8 +429,8 @@ peginconfirmationdepth=1 [liquidregtest] enforce_pak=1 -port=18444 -rpcport=18445 +port=18446 +rpcport=18447 mainchainrpcport=18443 mainchainrpcuser=bitcoinrpc mainchainrpcpassword=password diff --git a/integration_test/tests/test_bitcoin.py b/integration_test/tests/test_bitcoin.py index f07b9e6..1ec35e8 100644 --- a/integration_test/tests/test_bitcoin.py +++ b/integration_test/tests/test_bitcoin.py @@ -1,12 +1,15 @@ +from cfd.taproot import TaprootScriptTree import unittest from helper import RpcWrapper, get_utxo -from cfd.address import AddressUtil -from cfd.key import SigHashType +from cfd.address import AddressUtil, Address +from cfd.key import SchnorrUtil, SigHashType, SchnorrPubkey, SignParameter from cfd.hdwallet import HDWallet -from cfd.script import HashType -from cfd.descriptor import parse_descriptor -from cfd.transaction import Transaction, TxIn, TxOut, UtxoData +from cfd.script import HashType, Script +from cfd.descriptor import parse_descriptor, Descriptor +from cfd.psbt import Psbt, PsbtAppendInputData, PsbtAppendOutputData +from cfd.transaction import OutPoint, Transaction, TxIn, TxOut, UtxoData from decimal import Decimal +from typing import List, Union import logging import time @@ -22,9 +25,9 @@ BTC_AMOUNT_BIT = 8 -def convert_bitcoin_utxos(test_obj, utxo_list): +def convert_bitcoin_utxos(test_obj, utxo_list) -> List['UtxoData']: # {"txid": "f3c8453e1bda1366bc859532e27a829c8ce623b766ae699a0377b168993c44b5", "vout": 0, "address": "bcrt1qyq7xhec45m75m5nvhzuh47vsj3as7tqf8t8vkr", "label": "test_fee", "scriptPubKey": "0014203c6be715a6fd4dd26cb8b97af990947b0f2c09", "amount": 50.0, "confirmations": 101, "spendable": false, "solvable": false, "safe": true} # noqa8 - utxos = [] + utxos: List['UtxoData'] = [] for utxo in utxo_list: desc = test_obj.desc_dic[utxo['address']] value = Decimal(str(utxo['amount'])) @@ -42,14 +45,17 @@ def search_utxos(test_obj, utxo_list, outpoint): test_obj.assertTrue(False, 'UTXO is empty. outpoint={}'.format(outpoint)) -def create_bitcoin_address(test_obj): +def create_bitcoin_address(test_obj: 'TestBitcoin'): + root_pk = test_obj.hdwallet.ext_privkey.get_extpubkey().pubkey + fp = root_pk.get_fingerprint() # fee address pk = str(test_obj.hdwallet.get_pubkey(path=FEE_PATH).pubkey) addr = AddressUtil.p2wpkh(pk, network=NETWORK) test_obj.path_dic[str(addr)] = FEE_PATH test_obj.addr_dic['fee'] = addr test_obj.desc_dic[str(addr)] = parse_descriptor( - 'wpkh({})'.format(str(pk)), network=NETWORK) + 'wpkh([{}{}]{})'.format(str(fp), FEE_PATH[1:], str(pk)), + network=NETWORK) print('set fee addr: ' + str(addr)) # wpkh main address @@ -59,7 +65,8 @@ def create_bitcoin_address(test_obj): test_obj.path_dic[str(addr)] = path test_obj.addr_dic['main'] = addr test_obj.desc_dic[str(addr)] = parse_descriptor( - 'wpkh({})'.format(str(pk)), network=NETWORK) + 'wpkh([{}{}]{})'.format(str(fp), path[1:], str(pk)), + network=NETWORK) print('set main addr: ' + str(addr)) # pkh address path = '{}/0/1'.format(ROOT_PATH) @@ -68,7 +75,8 @@ def create_bitcoin_address(test_obj): test_obj.path_dic[str(addr)] = path test_obj.addr_dic['p2pkh'] = addr test_obj.desc_dic[str(addr)] = parse_descriptor( - 'pkh({})'.format(str(pk)), network=NETWORK) + 'pkh([{}{}]{})'.format(str(fp), path[1:], str(pk)), + network=NETWORK) print('set p2pkh addr: ' + str(addr)) # wpkh address path = '{}/0/2'.format(ROOT_PATH) @@ -77,7 +85,8 @@ def create_bitcoin_address(test_obj): test_obj.path_dic[str(addr)] = path test_obj.addr_dic['p2wpkh'] = addr test_obj.desc_dic[str(addr)] = parse_descriptor( - 'wpkh({})'.format(str(pk)), network=NETWORK) + 'wpkh([{}{}]{})'.format(str(fp), path[1:], str(pk)), + network=NETWORK) print('set p2wpkh addr: ' + str(addr)) # p2sh-p2wpkh address path = '{}/0/3'.format(ROOT_PATH) @@ -86,7 +95,8 @@ def create_bitcoin_address(test_obj): test_obj.path_dic[str(addr)] = path test_obj.addr_dic['p2sh-p2wpkh'] = addr test_obj.desc_dic[str(addr)] = parse_descriptor( - 'sh(wpkh({}))'.format(str(pk)), network=NETWORK) + 'sh(wpkh([{}{}]{}))'.format(str(fp), path[1:], str(pk)), + network=NETWORK) print('set p2sh-p2wpkh addr: ' + str(addr)) # multisig_key @@ -121,7 +131,7 @@ def create_bitcoin_address(test_obj): print('set p2sh-p2wsh addr: ' + str(addr)) -def test_import_address(test_obj): +def test_import_address(test_obj: 'TestBitcoin'): btc_rpc = test_obj.conn.get_rpc() # fee address btc_rpc.importaddress(str(test_obj.addr_dic['fee']), 'test_fee', False) @@ -138,7 +148,7 @@ def test_import_address(test_obj): str(test_obj.addr_dic['p2sh-p2wsh']), 'test_sh_wsh', False) -def test_generate(test_obj): +def test_generate(test_obj: 'TestBitcoin'): # generatetoaddress -> fee addresss print(test_obj.addr_dic) btc_rpc = test_obj.conn.get_rpc() @@ -150,7 +160,7 @@ def test_generate(test_obj): print(resp) -def test_bitcoin_pkh(test_obj): +def test_bitcoin_pkh(test_obj: 'TestBitcoin'): btc_rpc = test_obj.conn.get_rpc() # create tx (output wpkh, p2sh-segwit, pkh) txouts = [ @@ -207,12 +217,12 @@ def test_bitcoin_pkh(test_obj): target_amount=0, effective_fee_rate=20.0, knapsack_min_change=1) # add sign - join_utxo_list = [] + join_utxo_list: List['UtxoData'] = [] join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = utxo_list join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list for txin in tx2.txin_list: utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) - path = test_obj.path_dic[str(utxo.descriptor.data.address)] + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) sk = test_obj.hdwallet.get_privkey(path=path).privkey tx2.sign_with_privkey(txin.outpoint, utxo.descriptor.data.hash_type, sk, amount=utxo.amount, @@ -227,7 +237,7 @@ def test_bitcoin_pkh(test_obj): print('UTXO: {}'.format(utxos)) -def test_bitcoin_multisig(test_obj): +def test_bitcoin_multisig(test_obj: 'TestBitcoin'): btc_rpc = test_obj.conn.get_rpc() # create tx (output multisig) txouts = [ @@ -303,13 +313,13 @@ def multisig_sign(tx_obj, utxo, path_list): utxo.outpoint, utxo.descriptor.data.hash_type, utxo.descriptor.data.redeem_script, signature_list) - join_utxo_list = [] + join_utxo_list: List['UtxoData'] = [] join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = utxo_list join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list for index, txin in enumerate(tx2.txin_list): utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) if not utxo.descriptor.data.redeem_script: - path = test_obj.path_dic[str(utxo.descriptor.data.address)] + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) sk = test_obj.hdwallet.get_privkey(path=path).privkey tx2.sign_with_privkey(txin.outpoint, utxo.descriptor.data.hash_type, @@ -328,7 +338,396 @@ def multisig_sign(tx_obj, utxo, path_list): print('UTXO: {}'.format(utxos)) +def test_psbt(test_obj: 'TestBitcoin'): + btc_rpc = test_obj.conn.get_rpc() + fee_addr = str(test_obj.addr_dic['fee']) + fee_sk = test_obj.hdwallet.get_privkey(path=FEE_PATH).privkey + main_addr = test_obj.addr_dic['main'] + utxos = get_utxo(btc_rpc, [str(fee_addr)]) # listunspent + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + txouts = [ + PsbtAppendOutputData( + 100000000, + address=test_obj.addr_dic['p2pkh'], + descriptor=test_obj.desc_dic[str(test_obj.addr_dic['p2pkh'])]), + PsbtAppendOutputData( + 100000000, + address=str(test_obj.addr_dic['p2wpkh']), + descriptor=test_obj.desc_dic[str(test_obj.addr_dic['p2wpkh'])]), + PsbtAppendOutputData( + 100000000, + address=str(test_obj.addr_dic['p2sh-p2wpkh']), + descriptor=test_obj.desc_dic[str( + test_obj.addr_dic['p2sh-p2wpkh'])], + ), + ] + psbt = Psbt.create(tx_version=2, network=NETWORK) + psbt.add(outputs=txouts) + psbt.fund( + utxo_list=utxo_list, + reserved_address_descriptor=test_obj.desc_dic[str(fee_addr)], + effective_fee_rate=2.0, long_term_fee_rate=2.0, knapsack_min_change=0) + psbt.sign(fee_sk) + # bitcoinrpc: finalize extract + ret = btc_rpc.finalizepsbt(str(psbt), True) + tx_hex = ret['hex'] if 'hex' in ret else '' + if not ret.get('complete', True): + raise AssertionError("finalizepsbt not complete.") + print(Transaction.parse_to_json(tx_hex, network=NETWORK)) + txid = btc_rpc.sendrawtransaction(tx_hex) + tx = Transaction(tx_hex) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + utxos = get_utxo(btc_rpc, [str(main_addr)]) + print('UTXO: {}'.format(utxos)) + + txid = tx.txid + txin_list = [] + key_list = [] + for index, _ in enumerate(txouts): + txout = tx.txout_list[index] + addr = txout.get_address(network=NETWORK) + desc = test_obj.desc_dic[str(addr)] + txin_list.append(PsbtAppendInputData( + outpoint=OutPoint(txid, index), + utxo=txout, descriptor=str(desc), + utxo_tx=tx_hex)) + path = str(test_obj.path_dic[str(addr)]) + key_list.append(test_obj.hdwallet.get_privkey(path=path).privkey) + txouts2 = [ + TxOut(300000000, str(test_obj.addr_dic['main'])), + ] + tx2 = Transaction.create(2, 0, [], txouts2) + psbt2 = Psbt.from_transaction(transaction=tx2, network=NETWORK) + psbt2.set_output_bip32_key(0, pubkey=str( + test_obj.desc_dic[str(txouts2[0].address)])) + psbt2.add(inputs=txin_list) + utxos = get_utxo(btc_rpc, [str(fee_addr)]) # listunspent + utxo_list2 = convert_bitcoin_utxos(test_obj, utxos) + psbt2.fund( + utxo_list=utxo_list2, + reserved_address_descriptor=test_obj.desc_dic[str(fee_addr)], + effective_fee_rate=2.0, long_term_fee_rate=2.0, knapsack_min_change=0) + psbt21 = Psbt(str(psbt2), network=NETWORK) + psbt22 = Psbt(str(psbt2), network=NETWORK) + psbt21.sign(fee_sk) + for key in key_list: + psbt22.sign(key) + # psbt2_str = btc_rpc.combinepsbt([str(psbt21), str(psbt22)]) + # psbt2 = Psbt(psbt2_str, network=NETWORK) + psbt2 = Psbt.combine_psbts([str(psbt21), psbt22]) + tx2 = psbt2.extract(True) + print(Transaction.parse_to_json(str(tx2), network=NETWORK)) + txid = btc_rpc.sendrawtransaction(str(tx2)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + utxos = get_utxo(btc_rpc, [str(main_addr)]) + print('UTXO: {}'.format(utxos)) + + +def test_taproot_schnorr(test_obj: 'TestBitcoin'): + btc_rpc = test_obj.conn.get_rpc() + main_addr = test_obj.addr_dic['main'] + main_pk, _ = SchnorrPubkey.from_pubkey(str(main_addr.pubkey)) + tr_addr = AddressUtil.taproot(main_pk, network=NETWORK) + main_path = str(test_obj.path_dic[str(main_addr)]) + main_sk = test_obj.hdwallet.get_privkey(path=main_path).privkey + + txouts = [ + TxOut(100000000, str(tr_addr)), + ] + tx = Transaction.create(2, 0, [], txouts) + # fundrawtransaction + fee_addr = str(test_obj.addr_dic['fee']) + fee_desc = test_obj.desc_dic[fee_addr] + fee_sk = test_obj.hdwallet.get_privkey(path=FEE_PATH).privkey + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx.fund_raw_transaction([], utxo_list, fee_addr, + target_amount=0, effective_fee_rate=2.0, + knapsack_min_change=0) + # add sign + for txin in tx.txin_list: + utxo = search_utxos(test_obj, utxo_list, txin.outpoint) + tx.sign_with_privkey(txin.outpoint, fee_desc.data.hash_type, fee_sk, + amount=utxo.amount, + sighashtype=SigHashType.ALL) + # broadcast + print(Transaction.parse_to_json(str(tx), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + # create tx (output wpkh only, input tx1-3) + txid = tx.txid + txin_list = [] + txin_utxo_list = [] + txin_list.append(TxIn(txid=txid, vout=0)) + desc = f'raw({str(tr_addr.locking_script)})' + txin_utxo_list.append(UtxoData( + txid=txid, vout=0, amount=txouts[0].amount, descriptor=desc)) + txouts2 = [ + TxOut(100000000, str(test_obj.addr_dic['main'])), + ] + tx2 = Transaction.create(2, 0, txin_list, txouts2) + main_addr = test_obj.addr_dic['main'] + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx2.fund_raw_transaction(txin_utxo_list, utxo_list, fee_addr, + target_amount=0, effective_fee_rate=20.0, + knapsack_min_change=1) + # add sign + join_utxo_list: List['UtxoData'] = [] + join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list + for txin in tx2.txin_list: + for utxo in utxo_list: + if utxo.outpoint == txin.outpoint: + join_utxo_list.append(utxo) + for index, txin in enumerate(tx2.txin_list): + utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) + if index == 0: + sk = main_sk + hash_type = main_addr.hash_type + else: + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) + sk = test_obj.hdwallet.get_privkey(path=path).privkey + hash_type = utxo.descriptor.data.hash_type + tx2.sign_with_privkey(txin.outpoint, hash_type, + sk, amount=utxo.amount, + sighashtype=SigHashType.ALL, + utxos=join_utxo_list) + # broadcast + print(Transaction.parse_to_json(str(tx2), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx2)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + utxos = get_utxo(btc_rpc, [str(main_addr)]) + print('UTXO: {}'.format(utxos)) + + +def test_taproot_tapscript(test_obj: 'TestBitcoin'): + btc_rpc = test_obj.conn.get_rpc() + main_addr = test_obj.addr_dic['main'] + main_pk, _ = SchnorrPubkey.from_pubkey(str(main_addr.pubkey)) + pkh_addr = test_obj.addr_dic['p2pkh'] + spk1, _ = SchnorrPubkey.from_pubkey(str(pkh_addr.pubkey)) + wpkh_addr = test_obj.addr_dic['p2wpkh'] + spk2, _ = SchnorrPubkey.from_pubkey(str(wpkh_addr.pubkey)) + main_path = str(test_obj.path_dic[str(main_addr)]) + main_sk = test_obj.hdwallet.get_privkey(path=main_path).privkey + pkh_path = str(test_obj.path_dic[str(pkh_addr)]) + sk1 = test_obj.hdwallet.get_privkey(path=pkh_path).privkey + # wpkh_path = str(test_obj.path_dic[str(wpkh_addr)]) + # sk2 = test_obj.hdwallet.get_privkey(path=wpkh_path).privkey + + script1 = Script.from_asm([str(spk1), 'OP_CHECKSIG']) + script2 = Script.from_asm([str(spk2), 'OP_CHECKSIG']) + op_true_script = Script('51') + op_true_sub_tree1 = TaprootScriptTree(op_true_script) + op_true_sub_tree1.add_branch(script1) + + script1_tree = TaprootScriptTree(script1) + script1_tree.add_branches([op_true_script, script2]) + script1_tree.internal_pubkey = main_pk + + op_true_tree = TaprootScriptTree(op_true_script) + op_true_tree.add_branches([script1, script2]) + op_true_tree.internal_pubkey = main_pk + + script2_tree = TaprootScriptTree(script2) + script2_tree.add_branch(op_true_sub_tree1) + script2_tree.internal_pubkey = main_pk + + tr_addr1 = AddressUtil.taproot(script1_tree, network=NETWORK) + tr_addr2 = AddressUtil.taproot(op_true_tree, network=NETWORK) + tr_addr3 = AddressUtil.taproot(script2_tree, network=NETWORK) + + txouts = [ + TxOut(100000, str(tr_addr1)), + TxOut(150000, str(tr_addr2)), + TxOut(200000, str(tr_addr3)), + ] + tx = Transaction.create(2, 0, [], txouts) + # fundrawtransaction + fee_addr = str(test_obj.addr_dic['fee']) + fee_desc = test_obj.desc_dic[fee_addr] + fee_sk = test_obj.hdwallet.get_privkey(path=FEE_PATH).privkey + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx.fund_raw_transaction([], utxo_list, fee_addr, + target_amount=0, effective_fee_rate=2.0, + knapsack_min_change=0) + # add sign + for txin in tx.txin_list: + utxo = search_utxos(test_obj, utxo_list, txin.outpoint) + tx.sign_with_privkey(txin.outpoint, fee_desc.data.hash_type, fee_sk, + amount=utxo.amount, + sighashtype=SigHashType.ALL) + # broadcast + print(Transaction.parse_to_json(str(tx), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + txid = tx.txid + utxo1 = UtxoData(txid=txid, vout=0, amount=txouts[0].amount, + descriptor=f'raw({str(tr_addr1.locking_script)})') + utxo2 = UtxoData(txid=txid, vout=1, amount=txouts[1].amount, + descriptor=f'raw({str(tr_addr2.locking_script)})') + utxo3 = UtxoData(txid=txid, vout=2, amount=txouts[2].amount, + descriptor=f'raw({str(tr_addr3.locking_script)})') + + # send tapscript script1 + txin_list = [] + txin_utxo_list = [] + txin_list.append(TxIn(txid=txid, vout=0)) + txin_utxo_list.append(utxo1) + txouts2 = [ + TxOut(txouts[0].amount, str(test_obj.addr_dic['main'])), + ] + tx2 = Transaction.create(2, 0, txin_list, txouts2) + main_addr = test_obj.addr_dic['main'] + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx2.fund_raw_transaction(txin_utxo_list, utxo_list, fee_addr, + target_amount=0, effective_fee_rate=2.0, + knapsack_min_change=0) + # add sign + join_utxo_list: List['UtxoData'] = [] + join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list + for txin in tx2.txin_list: + for utxo in utxo_list: + if utxo.outpoint == txin.outpoint: + join_utxo_list.append(utxo) + for index, txin in enumerate(tx2.txin_list): + utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) + if index == 0: + sk = sk1 + sighash = tx2.get_sighash( + txin.outpoint, HashType.TAPROOT, redeem_script=script1, + sighashtype=SigHashType.DEFAULT, utxos=join_utxo_list) + sig = SchnorrUtil.sign(sighash, sk1) + sign_param = SignParameter(sig, sighashtype=SigHashType.DEFAULT) + _, _, _, control_block = script1_tree.get_taproot_data() + tx2.add_tapscript_sign(txin.outpoint, [sign_param], + script1, control_block) + else: + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) + sk = test_obj.hdwallet.get_privkey(path=path).privkey + hash_type = utxo.descriptor.data.hash_type + tx2.sign_with_privkey(txin.outpoint, hash_type, + sk, amount=utxo.amount, + sighashtype=SigHashType.ALL, + utxos=join_utxo_list) + # broadcast + print(Transaction.parse_to_json(str(tx2), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx2)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + # send tapscript OP_TRUE + txin_list = [] + txin_utxo_list = [] + txin_list.append(TxIn(txid=txid, vout=1)) + txin_utxo_list.append(utxo2) + txouts2 = [ + TxOut(txouts[1].amount, str(test_obj.addr_dic['main'])), + ] + tx2 = Transaction.create(2, 0, txin_list, txouts2) + main_addr = test_obj.addr_dic['main'] + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx2.fund_raw_transaction(txin_utxo_list, utxo_list, fee_addr, + target_amount=0, effective_fee_rate=2.0, + knapsack_min_change=0) + # add sign + join_utxo_list = [] + join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list + for txin in tx2.txin_list: + for utxo in utxo_list: + if utxo.outpoint == txin.outpoint: + join_utxo_list.append(utxo) + for index, txin in enumerate(tx2.txin_list): + utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) + if index == 0: + _, _, _, control_block = op_true_tree.get_taproot_data() + tx2.add_tapscript_sign(txin.outpoint, [], + op_true_script, control_block) + else: + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) + sk = test_obj.hdwallet.get_privkey(path=path).privkey + hash_type = utxo.descriptor.data.hash_type + tx2.sign_with_privkey(txin.outpoint, hash_type, + sk, amount=utxo.amount, + sighashtype=SigHashType.ALL, + utxos=join_utxo_list) + # broadcast + print(Transaction.parse_to_json(str(tx2), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx2)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + # send tapscript internal_pubkey + txin_list = [] + txin_utxo_list = [] + txin_list.append(TxIn(txid=txid, vout=2)) + txin_utxo_list.append(utxo3) + txouts2 = [ + TxOut(txouts[2].amount, str(test_obj.addr_dic['main'])), + ] + tx2 = Transaction.create(2, 0, txin_list, txouts2) + main_addr = test_obj.addr_dic['main'] + utxos = get_utxo(btc_rpc, [fee_addr]) + utxo_list = convert_bitcoin_utxos(test_obj, utxos) + tx2.fund_raw_transaction(txin_utxo_list, utxo_list, fee_addr, + target_amount=0, effective_fee_rate=2.0, + knapsack_min_change=0) + # add sign + join_utxo_list = [] + join_utxo_list[len(join_utxo_list):len(join_utxo_list)] = txin_utxo_list + for txin in tx2.txin_list: + for utxo in utxo_list: + if utxo.outpoint == txin.outpoint: + join_utxo_list.append(utxo) + for index, txin in enumerate(tx2.txin_list): + utxo = search_utxos(test_obj, join_utxo_list, txin.outpoint) + if index == 0: + sk = script2_tree.get_privkey(main_sk) + hash_type = tr_addr3.hash_type + else: + path = str(test_obj.path_dic[str(utxo.descriptor.data.address)]) + sk = test_obj.hdwallet.get_privkey(path=path).privkey + hash_type = utxo.descriptor.data.hash_type + tx2.sign_with_privkey(txin.outpoint, hash_type, + sk, amount=utxo.amount, + sighashtype=SigHashType.ALL, + utxos=join_utxo_list) + # broadcast + print(Transaction.parse_to_json(str(tx2), network=NETWORK)) + btc_rpc.sendrawtransaction(str(tx2)) + # generate block + btc_rpc.generatetoaddress(2, fee_addr) + time.sleep(2) + + utxos = get_utxo(btc_rpc, [str(main_addr)]) + print('UTXO: {}'.format(utxos)) + + class TestBitcoin(unittest.TestCase): + hdwallet: 'HDWallet' + # addr_dic: dict[str, 'Address'] + # desc_dic: dict[str, 'Descriptor'] + # path_dic: dict[str, Union[str, List[str]]] + conn: 'RpcWrapper' + def setUp(self): logging.basicConfig() logging.getLogger("BitcoinRPC").setLevel(logging.DEBUG) @@ -351,6 +750,9 @@ def test_bitcoin(self): test_generate(self) test_bitcoin_pkh(self) test_bitcoin_multisig(self) + test_psbt(self) + test_taproot_schnorr(self) + test_taproot_tapscript(self) if __name__ == "__main__": diff --git a/integration_test/tests/test_elements.py b/integration_test/tests/test_elements.py index 45ec369..af1f53f 100644 --- a/integration_test/tests/test_elements.py +++ b/integration_test/tests/test_elements.py @@ -720,7 +720,10 @@ def setUp(self): self.btcConn = RpcWrapper( port=18443, rpc_user='bitcoinrpc', rpc_password='password') self.elmConn = RpcWrapper( - port=18445, rpc_user='elementsrpc', rpc_password='password') + port=18447, rpc_user='elementsrpc', rpc_password='password') + # init command + btc_rpc = self.btcConn.get_rpc() + btc_rpc.settxfee(0.00001) def test_elements(self): ''' diff --git a/setup.cfg b/setup.cfg index 550d126..a760311 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,7 @@ classifiers = Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 keywords = Bitcoin, cfd, libwally, secp256k1 project_urls = Source Code = https://github.com/p2pderivatives/cfd-python diff --git a/tests/data/address_test.json b/tests/data/address_test.json index 50560c8..edd1e36 100644 --- a/tests/data/address_test.json +++ b/tests/data/address_test.json @@ -191,6 +191,51 @@ "lockingScript": "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262" } }, + { + "case": "HashType(Taproot) mainnet", + "request": { + "keyData": { + "hex": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "network": "mainnet", + "hashType": "taproot" + }, + "expect": { + "address": "bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4", + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, + { + "case": "HashType(Taproot) testnet", + "request": { + "keyData": { + "hex": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "network": "testnet", + "hashType": "taproot" + }, + "expect": { + "address": "tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6", + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, + { + "case": "HashType(Taproot) regtest", + "request": { + "keyData": { + "hex": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "network": "regtest", + "hashType": "taproot" + }, + "expect": { + "address": "bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, { "case": "Elements HashType(P2PKH) liquidv1", "request": { @@ -418,7 +463,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: p2pk", - "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -536,7 +581,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: p2pk", - "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -789,6 +834,48 @@ "hash": "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262" } }, + { + "case": "HashType(Taproot) mainnet", + "request": { + "isElements": false, + "address": "bc1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naspp3kr4" + }, + "expect": { + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "network": "mainnet", + "hashType": "taproot", + "witnessVersion": 1, + "hash": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, + { + "case": "HashType(Taproot) testnet", + "request": { + "isElements": false, + "address": "tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6" + }, + "expect": { + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "network": "testnet", + "hashType": "taproot", + "witnessVersion": 1, + "hash": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, + { + "case": "HashType(Taproot) regtest", + "request": { + "isElements": false, + "address": "bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq" + }, + "expect": { + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "network": "regtest", + "hashType": "taproot", + "witnessVersion": 1, + "hash": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + } + }, { "case": "Elements HashType(P2PKH) liquidv1", "request": { @@ -1368,7 +1455,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: p2pk", - "message": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "message": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -1428,7 +1515,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: p2pk", - "message": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "message": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -1534,11 +1621,45 @@ "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4" ], "network": "testnet", + "hashType": "p2sh" + }, + "expect": { + "address": "2N1cnwQMYCDNkdzNNo8qQv1kuSBjb8aAYXy", + "redeemScript": "5f2102be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec521032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db21030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a1432102927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a2102ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e72102ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c12102b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f22103f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e6021022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc2102e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d2103c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c2102063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d821033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a5572103a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa2102818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c45fae" + } + }, + { + "case": "Testnet(pubkey max on witness)", + "request": { + "nrequired": 20, + "keys": [ + "02be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec5", + "032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db", + "030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a143", + "02927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a", + "02ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e7", + "02ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c1", + "02b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f2", + "03f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e60", + "022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc", + "02e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d", + "03c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c", + "02063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d8", + "033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a557", + "03a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa", + "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4", + "025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db", + "030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55", + "0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e", + "02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8", + "03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950" + ], + "network": "testnet", "hashType": "p2wsh" }, "expect": { - "address": "tb1quq2d256q5f3kdsehh920akwgw9up2vnn6vzzhcn54wc9ejtpkz5qfys3z4", - "witnessScript": "5f2102be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec521032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db21030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a1432102927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a2102ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e72102ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c12102b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f22103f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e6021022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc2102e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d2103c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c2102063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d821033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a5572103a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa2102818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c45fae" + "address": "tb1qmsnghn38jq4cn3c2cwexmt7qyhk5cqluanweztpe2f0qdc4ltyaswj6f0l", + "witnessScript": "01142102be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec521032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db21030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a1432102927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a2102ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e72102ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c12102b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f22103f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e6021022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc2102e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d2103c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c2102063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d821033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a5572103a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa2102818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c421025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db21030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55210267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e2102f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b82103968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb59500114ae" } }, { @@ -1761,7 +1882,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: bech", - "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -1836,7 +1957,7 @@ "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4" ], "network": "testnet", - "hashType": "p2wsh" + "hashType": "p2sh" }, "error": { "code": 1, @@ -1844,6 +1965,43 @@ "message": "CreateMultisigScript pubkeys array size is over." } }, + { + "case": "Error(count is over on witness)", + "request": { + "nrequired": 16, + "keys": [ + "0205ffcdde75f262d66ada3dd877c7471f8f8ee9ee24d917c3e18d01cee458bafe", + "02be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec5", + "032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db", + "030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a143", + "02927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a", + "02ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e7", + "02ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c1", + "02b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f2", + "03f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e60", + "022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc", + "02e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d", + "03c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c", + "02063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d8", + "033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a557", + "03a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa", + "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4", + "025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db", + "030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55", + "0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e", + "02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8", + "03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950" + ], + "network": "testnet", + "hashType": "p2wsh" + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "CreateMultisigScript pubkeys array size is over.", + "capi": "The number of pubkey has reached the upper limit." + } + }, { "case": "Error(wrong pubkey)", "request": { @@ -1935,7 +2093,7 @@ } }, { - "case": "Elements Error(Nonexisting address type)", + "case": "Elements Error(Non existing address type)", "request": { "isElements": true, "nrequired": 2, @@ -1950,7 +2108,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: bech", - "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." + "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -2029,7 +2187,7 @@ "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4" ], "network": "regtest", - "hashType": "p2wsh" + "hashType": "p2sh" }, "error": { "code": 1, @@ -2037,6 +2195,44 @@ "message": "CreateMultisigScript pubkeys array size is over." } }, + { + "case": "Elements Error(count is over on witness)", + "request": { + "isElements": true, + "nrequired": 16, + "keys": [ + "0205ffcdde75f262d66ada3dd877c7471f8f8ee9ee24d917c3e18d01cee458bafe", + "02be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec5", + "032f061438c62aa9a1685d7451a4bf1af8d0b8c132b0db4614147df19b687c01db", + "030dc96ba9b0dcce41a4b683164af15c045f0b169da1d1e234611a8cfc3195a143", + "02927b60e6bdbd728009e7e19feb4700a04f25328929730a609471b8e236ff050a", + "02ff43fd9fdb705d223951806f349dd2090edc4d971eb1c2a60c48cfb2af2862e7", + "02ce1316489880a77407f9637af4e806c5a7e731b45504d6f3fca506b207f8e3c1", + "02b12d700c4d851f773c55d17d9f59bf689a7cbdc01450c8679de9702fc77ac4f2", + "03f6d4cfd7688da7a130ea0f6bd7ecaa6e7ae868ae8614cd746c26b1cb9e808e60", + "022ac6940d159cd39b36cb4a2ec34fb2696e085be634ce1e7b5fcc118a6ac5e2cc", + "02e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d", + "03c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c", + "02063566b61b4754dc2956b3571bdce889decc23c789d6b58df0057808b20e66d8", + "033acbe038580c25da0c0c6e94c4dcbfa9c09f2f3bff59ae16aebfbd35a238a557", + "03a1423fc026f41f3f786db98a793802f77819e33692301ed24426e6dbad05aeaa", + "02818c3deec9c1f717cd6d97d2d9cf6cedfc9d97114fc6894ef71d4e1f69d859c4", + "025dffce0b5e131808a630d0d8769d22ead71fddf336836916c5906676e13394db", + "030023121bed4585fdfea023aee4c7f9731e3cfa6b2a8ec21a159615d2bad57e55", + "0267a49281bd9d6d366c39c62f2e95a2aab37638f2a4718891c542d0961962644e", + "02f48e8e2bcaeb16a6d781bb7a72f6250607bf21e32f08c48e37a9e4706e6d48b8", + "03968ac57888ddaa3b57caa39efd5d5382c24f3deed602775cd4895f7c7adb5950" + ], + "network": "regtest", + "hashType": "p2wsh" + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "CreateMultisigScript pubkeys array size is over.", + "capi": "The number of pubkey has reached the upper limit." + } + }, { "case": "Elements Error(wrong pubkey)", "request": { @@ -2108,6 +2304,673 @@ "address": "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs", "hashType": "p2pkh" } + }, + { + "case": "taproot regtest", + "request": { + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "network": "regtest" + }, + "expect": { + "address": "bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", + "hashType": "taproot" + } + } + ] + }, + { + "name": "Address.GetTapScriptTreeInfo", + "cases": [ + { + "case": "simple network(mainnet)", + "request": { + "network": "mainnet", + "isElements": false, + "internalPubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "tree": [ + { + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac" + }, + { + "branchHash": "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d" + }, + { + "branchHash": "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + } + ] + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "address": "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + }, + { + "case": "get privkey. network(testnet)", + "request": { + "network": "mainnet", + "isElements": false, + "internalPubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "internalPrivkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27", + "tree": [ + { + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac" + }, + { + "branchHash": "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d" + }, + { + "branchHash": "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + } + ] + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "tweakedPrivkey": "a7d17bee0b6313cf864a1ac6f203aafd74a40703ffc050f66517e4f83ff41a03", + "address": "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + }, + { + "case": "child branch.", + "request": { + "tree": [ + { + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac" + }, + { + "tapscript": "51" + } + ] + }, + "expect": { + "tapLeafHash": "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "topBranchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", + "nodes": [ + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675" + ], + "treeString": "{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)}" + } + }, + { + "case": "root branch. (use child branch)", + "request": { + "tree": [ + { + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac" + }, + { + "branchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + } + ] + }, + "expect": { + "tapLeafHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "topBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "nodes": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + ], + "treeString": "{af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4,tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + }, + { + "case": "root branch. (use child branch string)", + "request": { + "tree": [ + { + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac" + }, + { + "treeString": "{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)}" + } + ] + }, + "expect": { + "tapLeafHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "topBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "nodes": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + ], + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + } + ] + }, + { + "name": "Address.GetTapScriptTreeInfoByControlBlock", + "cases": [ + { + "case": "simple network(mainnet)", + "request": { + "network": "mainnet", + "isElements": false, + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "address": "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + }, + { + "case": "get privkey. network(testnet)", + "request": { + "network": "testnet", + "isElements": false, + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "internalPrivkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27" + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "tweakedPrivkey": "a7d17bee0b6313cf864a1ac6f203aafd74a40703ffc050f66517e4f83ff41a03", + "address": "tb1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqekgu0q", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + } + ] + }, + { + "name": "Address.GetTapScriptTreeFromString", + "cases": [ + { + "case": "simple network(mainnet)", + "request": { + "network": "mainnet", + "isElements": false, + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "internalPubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb" + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "address": "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + }, + { + "case": "get privkey. network(testnet)", + "request": { + "network": "mainnet", + "isElements": false, + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "internalPubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "internalPrivkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27" + }, + "expect": { + "tapLeafHash": "dfc43ba9fc5f8a9e1b6d6a50600c704bb9e41b741d9ed6de6559a53d2f38e513", + "topBranchHash": "09659d67a7a6fb82c1e382fd8f99f6bb43cbd80883845c6df7029f9589fc7af3", + "tweakedPubkey": "3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "tweakedPrivkey": "a7d17bee0b6313cf864a1ac6f203aafd74a40703ffc050f66517e4f83ff41a03", + "address": "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40", + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54", + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "nodes": [ + "4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d", + "dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + ], + "treeString": "{{4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6d,tl(201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac)},dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54}" + } + }, + { + "case": "child branch.", + "request": { + "treeString": "{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)}", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac" + }, + "expect": { + "tapLeafHash": "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "topBranchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", + "nodes": [ + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675" + ], + "treeString": "{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)}" + } + }, + { + "case": "root branch. (use child branch)", + "request": { + "treeString": "{af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4,tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac" + }, + "expect": { + "tapLeafHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "topBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "nodes": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + ], + "treeString": "{af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4,tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + }, + { + "case": "root branch. (use child branch string)", + "request": { + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac" + }, + "expect": { + "tapLeafHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "topBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "nodes": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + ], + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + }, + { + "case": "parse branch mode.", + "request": { + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + }, + "expect": { + "topBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + }, + { + "case": "parse branch mode. (single leaf)", + "request": { + "treeString": "tl(51)" + }, + "expect": { + "topBranchHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "treeString": "tl(51)" + } + }, + { + "case": "parse branch mode. (single branch hash)", + "request": { + "treeString": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + }, + "expect": { + "topBranchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "treeString": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4" + } + }, + { + "case": "tree search (contain multi script) 1", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}", + "tapscript": "51", + "nodes": [ + "06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df", + "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7c", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ] + }, + "expect": { + "tapLeafHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "topBranchHash": "0c1bebfc9a508bf4d5835d401d96d71b72f1873fd338aebfff06d7adbe0c0cc3", + "tapscript": "51", + "nodes": [ + "06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df", + "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7c", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ], + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}" + } + }, + { + "case": "tree search (contain multi script) 2", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}", + "tapscript": "51", + "nodes": [ + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ] + }, + "expect": { + "tapLeafHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "topBranchHash": "0c1bebfc9a508bf4d5835d401d96d71b72f1873fd338aebfff06d7adbe0c0cc3", + "tapscript": "51", + "nodes": [ + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ], + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}" + } + } + ] + }, + { + "name": "Address.GetTapBranchInfo", + "cases": [ + { + "case": "by tree search 1", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}", + "tapscript": "51", + "nodes": [ + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ], + "index": 0 + }, + "expect": { + "topBranchHash": "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "nodes": [ + "e82da59bb829eb21f7cb8eb9eb128626da9a9a31f3dfdeb29766faf14468e996" + ], + "treeString": "{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}" + } + }, + { + "case": "by tree search 2", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}", + "tapscript": "51", + "nodes": [ + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ], + "index": 1 + }, + "expect": { + "topBranchHash": "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "nodes": [ + "4b3bb79ea92e0b4f2bfa7e8c88d81133e347da393d72a37fe9cdcf1f5f56b5e0", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb" + ], + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + } + }, + { + "case": "by tree search 3", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}", + "tapscript": "51", + "nodes": [ + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ], + "index": 2 + }, + "expect": { + "topBranchHash": "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75", + "nodes": [], + "treeString": "tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)" + } + }, + { + "case": "by tapbranch", + "request": { + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}", + "index": 1 + }, + "expect": { + "topBranchHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "nodes": [], + "treeString": "tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)" + } + } + ] + }, + { + "name": "Address.AnalyzeTapScriptTree", + "cases": [ + { + "case": "multi branch tree", + "request": { + "treeString": "{{{tl(51),{tl(204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac),tl(2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac)}},{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),{tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac),tl(51)}},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}},tl(2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac)}" + }, + "expect": { + "branches": [ + { + "depth": 3, + "tapBranchHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "tapscript": "51", + "leafVersion": 192 + }, + { + "depth": 2, + "tapBranchHash": "32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7c", + "relatedBranchHash": [ + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086" + ] + }, + { + "depth": 4, + "tapBranchHash": "1aac269b1edaa45c69fb8d1a703a1bb69e90129cef7b7cfe9e676b28e6d1175d", + "tapscript": "204a7af8660f2b0bdb92d2ce8b88ab30feb916343228d2e7bd15da02e1f6a31d47ac", + "leafVersion": 192 + }, + { + "depth": 3, + "tapBranchHash": "aaf9ea4cbd2f4606a31a35d563fa371bc630d9d7bcc50f62d064a3d84e0e3086", + "relatedBranchHash": [ + "1aac269b1edaa45c69fb8d1a703a1bb69e90129cef7b7cfe9e676b28e6d1175d", + "e82da59bb829eb21f7cb8eb9eb128626da9a9a31f3dfdeb29766faf14468e996" + ] + }, + { + "depth": 4, + "tapBranchHash": "e82da59bb829eb21f7cb8eb9eb128626da9a9a31f3dfdeb29766faf14468e996", + "tapscript": "2000d134c42fd51c90fa82c6cfdaabd895474d979118525362c0cd236c857e29d9ac", + "leafVersion": 192 + }, + { + "depth": 1, + "tapBranchHash": "0ae6aed84487410216ab1a6af0c7fe38b190cb7bdd6c898c5b04482fdec43f94", + "relatedBranchHash": [ + "32a0a039ec1412be2803fd7b5f5444c03d498e5e8e107ee431a9597c7b5b3a7c", + "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e" + ] + }, + { + "depth": 4, + "tapBranchHash": "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", + "leafVersion": 192 + }, + { + "depth": 3, + "tapBranchHash": "7da36533760cede4c164d5c00eb1500a27dd86ca76914a9874112c43e0c1b945", + "relatedBranchHash": [ + "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "4b3bb79ea92e0b4f2bfa7e8c88d81133e347da393d72a37fe9cdcf1f5f56b5e0" + ] + }, + { + "depth": 5, + "tapBranchHash": "06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aac", + "leafVersion": 192 + }, + { + "depth": 4, + "tapBranchHash": "4b3bb79ea92e0b4f2bfa7e8c88d81133e347da393d72a37fe9cdcf1f5f56b5e0", + "relatedBranchHash": [ + "06b46c960d6824f0da5af71d9ecc55714de5b2d2da51be60bd12c77df20a20df", + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675" + ] + }, + { + "depth": 5, + "tapBranchHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "tapscript": "51", + "leafVersion": 192 + }, + { + "depth": 2, + "tapBranchHash": "aeeaab89d953f80ff117b3a94142c859f885c2d942ec13536f72dac0c961f27e", + "relatedBranchHash": [ + "7da36533760cede4c164d5c00eb1500a27dd86ca76914a9874112c43e0c1b945", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb" + ] + }, + { + "depth": 3, + "tapBranchHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "leafVersion": 192 + }, + { + "depth": 0, + "tapBranchHash": "0c1bebfc9a508bf4d5835d401d96d71b72f1873fd338aebfff06d7adbe0c0cc3", + "relatedBranchHash": [ + "0ae6aed84487410216ab1a6af0c7fe38b190cb7bdd6c898c5b04482fdec43f94", + "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75" + ] + }, + { + "depth": 1, + "tapBranchHash": "d7b0b8d070638ff4f0b7e7d2aa930c58ec2d39853fd04c29c4c6688fdcb2ae75", + "tapscript": "2008f8280d68e02e807ccffee141c4a6b7ac31d3c283ae0921892d95f691742c44ad20b0f8ce3e1df406514a773414b5d9e5779d8e68ce816e9db39b8e53255ac3b406ac", + "leafVersion": 192 + } + ] + } + }, + { + "case": "simple branch", + "request": { + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},tl(2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac)}" + }, + "expect": { + "branches": [ + { + "depth": 2, + "tapBranchHash": "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", + "leafVersion": 192 + }, + { + "depth": 1, + "tapBranchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "relatedBranchHash": [ + "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675" + ] + }, + { + "depth": 2, + "tapBranchHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "tapscript": "51", + "leafVersion": 192 + }, + { + "depth": 0, + "tapBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "relatedBranchHash": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb" + ] + }, + { + "depth": 1, + "tapBranchHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb", + "tapscript": "2057bf643684f6c5c75e1cdf45990036502a0d897394013210858cdabcbb95a05aad205bec1a08fa3443176edd0a08e2a64642f45e57543b62bffe43ec350edc33dc22ac", + "leafVersion": 192 + } + ] + } + }, + { + "case": "hash only branch", + "request": { + "treeString": "{{tl(20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac),tl(51)},e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb}" + }, + "expect": { + "branches": [ + { + "depth": 2, + "tapBranchHash": "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "tapscript": "20ac52f50b28cdd4d3bcb7f0d5cb533f232e4c4ef12fbf3e718420b84d4e3c3440ac", + "leafVersion": 192 + }, + { + "depth": 1, + "tapBranchHash": "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "relatedBranchHash": [ + "4691fbb1196f4675241c8958a7ab6378a63aa0cc008ed03d216fd038357f52fd", + "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675" + ] + }, + { + "depth": 2, + "tapBranchHash": "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675", + "tapscript": "51", + "leafVersion": 192 + }, + { + "depth": 0, + "tapBranchHash": "a625d1251a1100263fa9a77b81e9e6f46c2eb8d44b9f27b629875cc102efb0ec", + "relatedBranchHash": [ + "af151388d3bfbebcdc87e4a0b4a97bbfa378f2e5a909eb38a6978cb2a71f39c4", + "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb" + ] + }, + { + "depth": 1, + "tapBranchHash": "e47f58011f27e9046b8195d0ab6a2acbc68ce281437a8d5132dadf389b2a5ebb" + } + ] + } } ] } diff --git a/tests/data/bitcoin_coin_test.json b/tests/data/bitcoin_coin_test.json index 39a1a60..bb81d4d 100644 --- a/tests/data/bitcoin_coin_test.json +++ b/tests/data/bitcoin_coin_test.json @@ -42,7 +42,7 @@ ], "selectedAmount": 39062500, "feeAmount": 2860, - "utxoFeeAmount": 1360 + "utxoFeeAmount": 1380 } }, { @@ -66,7 +66,7 @@ ], "selectedAmount": 156250000, "feeAmount": 2860, - "utxoFeeAmount": 1360 + "utxoFeeAmount": 1380 } }, { @@ -90,7 +90,7 @@ ], "selectedAmount": 156250000, "feeAmount": 2860, - "utxoFeeAmount": 1360 + "utxoFeeAmount": 1380 } }, { @@ -118,7 +118,7 @@ ], "selectedAmount": 234375000, "feeAmount": 4220, - "utxoFeeAmount": 2720 + "utxoFeeAmount": 2760 } }, { @@ -146,7 +146,7 @@ ], "selectedAmount": 468750000, "feeAmount": 4220, - "utxoFeeAmount": 2720 + "utxoFeeAmount": 2760 } }, { @@ -170,7 +170,7 @@ ], "selectedAmount": 1250000000, "feeAmount": 2860, - "utxoFeeAmount": 1360 + "utxoFeeAmount": 1380 } }, { @@ -199,7 +199,7 @@ ], "selectedAmount": 468750000, "feeAmount": 3720, - "utxoFeeAmount": 2720 + "utxoFeeAmount": 2760 } }, { @@ -228,7 +228,7 @@ ], "selectedAmount": 100001090, "feeAmount": 1860, - "utxoFeeAmount": 360 + "utxoFeeAmount": 368 } }, { @@ -253,7 +253,7 @@ ], "selectedAmount": 155062500, "feeAmount": 1680, - "utxoFeeAmount": 180 + "utxoFeeAmount": 184 } }, { @@ -286,7 +286,7 @@ ], "selectedAmount": 115063590, "feeAmount": 1770, - "utxoFeeAmount": 270 + "utxoFeeAmount": 276 } }, { @@ -367,9 +367,9 @@ "tx": "02000000020a9a33750a810cd384ca5d93b09513f1eb5d93c669091b29eef710d2391ff7300000000000ffffffff0a9bf51e0ac499391efd9426e2c909901edd74a97d2378b49c8832c491ad1e9e0000000000ffffffff0200e1f505000000001600144352a1a6e86311f22274f7ebb2746de21b09b15dc00a7001000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c00000000" }, "expect": { - "feeAmount": 2520, - "txoutFeeAmount": 720, - "utxoFeeAmount": 1800 + "feeAmount": 2570, + "txoutFeeAmount": 740, + "utxoFeeAmount": 1830 } } ] @@ -394,11 +394,11 @@ } }, "expect": { - "hex": "02000000010af4768e14f820cb9063f55833b5999119e53390ecf4bf181842909b11d0974d0000000000ffffffff0380969800000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00093d00000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470ce47f19000000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", + "hex": "02000000010af4768e14f820cb9063f55833b5999119e53390ecf4bf181842909b11d0974d0000000000ffffffff0380969800000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00093d00000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c947f19000000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", "usedAddresses": [ "bc1q0r4elskfu8xlvvlvkergtzagv2ep8p9tpewghq" ], - "feeAmount": 3860 + "feeAmount": 3940 } }, { @@ -443,11 +443,11 @@ } }, "expect": { - "hex": "02000000030a9a33750a810cd384ca5d93b09513f1eb5d93c669091b29eef710d2391ff7300000000000ffffffff0a9bf51e0ac499391efd9426e2c909901edd74a97d2378b49c8832c491ad1e9e0000000000ffffffff0a503dbd4f8f2b064c70e048b21f93fe4584174478abf5f44747932cd21da87c0000000000ffffffff0300e1f505000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00e1f505000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0831b8040000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", + "hex": "02000000030a9a33750a810cd384ca5d93b09513f1eb5d93c669091b29eef710d2391ff7300000000000ffffffff0a9bf51e0ac499391efd9426e2c909901edd74a97d2378b49c8832c491ad1e9e0000000000ffffffff0a503dbd4f8f2b064c70e048b21f93fe4584174478abf5f44747932cd21da87c0000000000ffffffff0300e1f505000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00e1f505000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c9030b8040000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", "usedAddresses": [ "bc1q0r4elskfu8xlvvlvkergtzagv2ep8p9tpewghq" ], - "feeAmount": 7460 + "feeAmount": 7580 } }, { @@ -467,11 +467,11 @@ } }, "expect": { - "hex": "02000000010af4768e14f820cb9063f55833b5999119e53390ecf4bf181842909b11d0974d0000000000ffffffff0380969800000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00093d00000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470ce47f19000000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", + "hex": "02000000010af4768e14f820cb9063f55833b5999119e53390ecf4bf181842909b11d0974d0000000000ffffffff0380969800000000001600144352a1a6e86311f22274f7ebb2746de21b09b15d00093d00000000001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c947f19000000000016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", "usedAddresses": [ "bcrt1q0r4elskfu8xlvvlvkergtzagv2ep8p9tfkvkm6" ], - "feeAmount": 3860 + "feeAmount": 3940 } }, { diff --git a/tests/data/common_test.json b/tests/data/common_test.json index f22b22d..191ac9d 100644 --- a/tests/data/common_test.json +++ b/tests/data/common_test.json @@ -31,7 +31,8 @@ "error": { "code": 1, "type": "illegal_argument", - "message": "hex to byte convert error." + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." } } ] @@ -85,6 +86,210 @@ } ] }, + { + "name": "Base64.Encode", + "cases": [ + { + "case": "Normal", + "request": { + "hex": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252" + }, + "expect": { + "base64": "BIiyHgAAAAAAAAAAAGBJn4AbiW2DF5pDdK63giqurOqg2x+F7j6QTE3vvZaJA8vKqcmMh3oml30AglyVaiOOjd370yLM5PdLC1vWrOSn4jOiUg==" + } + }, + { + "case": "Error(invalid hex format)", + "request": { + "hex": "048", + "hasChecksum": false + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." + } + } + ] + }, + { + "name": "Base64.Decode", + "cases": [ + { + "case": "Normal", + "request": { + "base64": "BIiyHgAAAAAAAAAAAGBJn4AbiW2DF5pDdK63giqurOqg2x+F7j6QTE3vvZaJA8vKqcmMh3oml30AglyVaiOOjd370yLM5PdLC1vWrOSn4jOiUg==" + }, + "expect": { + "hex": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252" + } + }, + { + "case": "Error(invalid format)", + "request": { + "base64": "9XpNiB4DberdMn4jZfZTsXNXKKpgdQ6Q8mFg63kyDnHd8FfRfTXPxGN27bYXRSe1TqTfe4s6MyyZVqiGTWgNiimiSSumMnnsk54MNP990" + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "Decode base64 error." + } + } + ] + }, + { + "name": "Hash.Hash256", + "cases": [ + { + "case": "hasText=false", + "request": { + "message": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252", + "hasText": false + }, + "expect": { + "hex": "a0757d2dd44f4a3f50a188325b077997ea0a02f81b6b1c2b7b89033e5662318b" + } + }, + { + "case": "hasText=true", + "request": { + "message": "This asset is LBTC.", + "hasText": true + }, + "expect": { + "hex": "9fe2c589b0179696bb25f2aecc7787b263ac2142dccc84e0cb8696e7c022f4a3" + } + }, + { + "case": "Error(invalid format)", + "request": { + "message": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "hasText": false + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." + } + } + ] + }, + { + "name": "Hash.Hash160", + "cases": [ + { + "case": "hasText=false", + "request": { + "message": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252", + "hasText": false + }, + "expect": { + "hex": "f0a4b5617c5bf1953c7e40a0c21b1fd6943ee626" + } + }, + { + "case": "hasText=true", + "request": { + "message": "This asset is LBTC.", + "hasText": true + }, + "expect": { + "hex": "55a2fbb88cbd3c863885fda7b2f8628935b85464" + } + }, + { + "case": "Error(invalid format)", + "request": { + "message": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "hasText": false + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." + } + } + ] + }, + { + "name": "Hash.Sha256", + "cases": [ + { + "case": "hasText=false", + "request": { + "message": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252", + "hasText": false + }, + "expect": { + "hex": "b1fdce1ab81e434088df5d34af90122a35c152b9111fab7c3efb710ba122bd22" + } + }, + { + "case": "hasText=true", + "request": { + "message": "This asset is LBTC.", + "hasText": true + }, + "expect": { + "hex": "d3312069ae404156f1da33be2af14f40ee61a484b17256aad9893aff2d1de2eb" + } + }, + { + "case": "Error(invalid format)", + "request": { + "message": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "hasText": false + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." + } + } + ] + }, + { + "name": "Hash.Ripemd160", + "cases": [ + { + "case": "hasText=false", + "request": { + "message": "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7e233a252", + "hasText": false + }, + "expect": { + "hex": "f9d89ec4a5b362380771fc3a7c4193c246e973a2" + } + }, + { + "case": "hasText=true", + "request": { + "message": "This asset is LBTC.", + "hasText": true + }, + "expect": { + "hex": "8df6e6e9864825b00c190b603ae616d44dff2fbe" + } + }, + { + "case": "Error(invalid format)", + "request": { + "message": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "hasText": false + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "hex to byte convert error.", + "python": "Error: Invalid hex value." + } + } + ] + }, { "name": "AES.Encode", "cases": [ @@ -129,7 +334,10 @@ "code": 2, "type": "illegal_state", "message": "EncryptAes256Cbc error." - } + }, + "exclude": [ + "python" + ] }, { "case": "Error(invalid key)", diff --git a/tests/data/descriptor_test.json b/tests/data/descriptor_test.json index c746196..09abe83 100644 --- a/tests/data/descriptor_test.json +++ b/tests/data/descriptor_test.json @@ -460,7 +460,7 @@ "case": "pkh hdkey-derive mainnet", "request": { "isElements": false, - "descriptor": "pkh([d34db33f/44\"/0\"/0\"]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", + "descriptor": "pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", "network": "mainnet", "bip32DerivationPath": "0/44" }, @@ -937,7 +937,7 @@ "case": "Error(extpubkey hardened derive fail)", "request": { "isElements": false, - "descriptor": "pkh([d34db33f/44\"/0\"/0\"]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", + "descriptor": "pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", "network": "mainnet", "bip32DerivationPath": "0h/44" }, @@ -1121,7 +1121,7 @@ { "case": "invalid checksum.", "request": { - "descriptor": "sh(wpkh([ef57314e/0\"/0\"/4\"]03d3f817091de0bbe51e19b53303b12e463f664894d49cb5bf5bb19c88fbc54d8d))#eeerft8t" + "descriptor": "sh(wpkh([ef57314e/0'/0'/4']03d3f817091de0bbe51e19b53303b12e463f664894d49cb5bf5bb19c88fbc54d8d))#eeerft8t" }, "error": { "code": 1, diff --git a/tests/data/elements_coin_test.json b/tests/data/elements_coin_test.json index 14c1755..ae8cc2c 100644 --- a/tests/data/elements_coin_test.json +++ b/tests/data/elements_coin_test.json @@ -63,7 +63,7 @@ } ], "feeAmount": 3320, - "utxoFeeAmount": 1820 + "utxoFeeAmount": 1840 } }, { @@ -104,7 +104,7 @@ } ], "feeAmount": 5140, - "utxoFeeAmount": 3640 + "utxoFeeAmount": 3680 } }, { @@ -145,7 +145,7 @@ } ], "feeAmount": 1864, - "utxoFeeAmount": 364 + "utxoFeeAmount": 368 } }, { @@ -182,7 +182,7 @@ } ], "feeAmount": 1682, - "utxoFeeAmount": 182 + "utxoFeeAmount": 184 } }, { @@ -227,7 +227,7 @@ } ], "feeAmount": 1773, - "utxoFeeAmount": 273 + "utxoFeeAmount": 276 } }, { @@ -276,7 +276,7 @@ } ], "feeAmount": 5140, - "utxoFeeAmount": 3640 + "utxoFeeAmount": 3680 } }, { @@ -333,7 +333,7 @@ } ], "feeAmount": 2228, - "utxoFeeAmount": 728 + "utxoFeeAmount": 736 } }, { @@ -398,7 +398,7 @@ } ], "feeAmount": 2228, - "utxoFeeAmount": 728 + "utxoFeeAmount": 736 } }, { @@ -624,12 +624,57 @@ } }, "expect": { - "hex": "0200000000030f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c34600000bfa8774c5f753ce2f801a8106413b470af94edbff5b4242ed4c5a26d20e72b90000000000ffffffff040b0000000000000000000000000000000000000000000000000000000000000000000000ffffffff07010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0100000000000000000000000000000000000000000000000000000000000000aa01000000000000037300000100000000000000000000000000000000000000000000000000000000000000bb010000000001124c1e00160014a53be40113bb50f2b8b2d0bfea1e823e75632b5f0100000000000000000000000000000000000000000000000000000000000000aa0100000000004b57eb0016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", + "hex": "0200000000030f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c34600000bfa8774c5f753ce2f801a8106413b470af94edbff5b4242ed4c5a26d20e72b90000000000ffffffff040b0000000000000000000000000000000000000000000000000000000000000000000000ffffffff07010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0100000000000000000000000000000000000000000000000000000000000000aa01000000000000037200000100000000000000000000000000000000000000000000000000000000000000bb010000000001124c1e00160014a53be40113bb50f2b8b2d0bfea1e823e75632b5f0100000000000000000000000000000000000000000000000000000000000000aa0100000000004b57ec0016001478eb9fc2c9e1cdf633ecb646858ba862b21384ab00000000", "usedAddresses": [ "ex1q0r4elskfu8xlvvlvkergtzagv2ep8p9ttsns6q", "ex1q55a7gqgnhdg09w9j6zl7585z8e6kx26lxfq8xd" ], - "feeAmount": 883 + "feeAmount": 882 + } + }, + { + "case": "FundRawTransaction NotBaseAssetOnly", + "request": { + "utxoFile": "elements_utxo_3", + "selectUtxos": [ + { + "memo": "Issuance token UTXO for reissuance", + "txid": "57a15002d066ce52573d674df925c9bc0f1164849420705f2cfad8a68111230f", + "vout": 1, + "amount": 700000000, + "asset": "ed6927df918c89b5e3d8b5062acab2c749a3291bb7451d4267c7daaf1b52ad0b", + "descriptor": "wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))", + "isBlindIssuance": true, + "assetBlindFactor": "0b8954757234fd3ec9cf0dd6ef0a89d825ec56a9532e7da4b6cb90c51be3bbd8", + "blindFactor": "62e36e1f0fa4916b031648a6b6903083069fa587572a88b729250cde528cfd3b" + } + ], + "tx": "0200000000010f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c346000004010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c00000000", + "network": "liquidv1", + "targets": [ + { + "asset": "bb00000000000000000000000000000000000000000000000000000000000000", + "amount": 0, + "reservedAddress": "ex1q55a7gqgnhdg09w9j6zl7585z8e6kx26lxfq8xd" + } + ], + "feeInfo": { + "feeRate": 0.0, + "longTermFeeRate": 0.1, + "knapsackMinChange": -1, + "dustFeeRate": 3.0, + "feeAsset": "aa00000000000000000000000000000000000000000000000000000000000000", + "isBlindEstimateFee": true, + "exponent": 0, + "minimumBits": 52 + } + }, + "expect": { + "hex": "0200000000020f231181a6d8fa2c5f7020948464110fbcc925f94d673d5752ce66d00250a1570100008000ffffffffd8bbe31bc590cbb6a47d2e53a956ec25d8890aefd60dcfc93efd34727554890b0683fe0819a4f9770c8a7cd5824e82975c825e017aff8ba0d6a5eb4959cf9c6f010000000023c3460000040b0000000000000000000000000000000000000000000000000000000000000000000000ffffffff05010bad521bafdac767421d45b71b29a349c7b2ca2a06b5d8e3b5898c91df2769ed010000000029b92700001976a9146c22e209d36612e0d9d2a20b814d7d8648cc7a7788ac01cdb0ed311810e61036ac9255674101497850f5eee5e4320be07479c05473cbac010000000023c34600001976a9149bdcb18911fa9faad6632ca43b81739082b0a19588ac0100000000000000000000000000000000000000000000000000000000000000aa010000000000989680001600144352a1a6e86311f22274f7ebb2746de21b09b15d0100000000000000000000000000000000000000000000000000000000000000bb01000000000007a120001600148beaaac4654cf4ebd8e46ca5062b0e7fb3e7470c0100000000000000000000000000000000000000000000000000000000000000bb010000000001124c1e00160014a53be40113bb50f2b8b2d0bfea1e823e75632b5f00000000", + "usedAddresses": [ + "ex1q55a7gqgnhdg09w9j6zl7585z8e6kx26lxfq8xd" + ], + "feeAmount": 0 } }, { diff --git a/tests/data/elements_transaction_test.json b/tests/data/elements_transaction_test.json index 90685e3..c21f0e7 100644 --- a/tests/data/elements_transaction_test.json +++ b/tests/data/elements_transaction_test.json @@ -3842,7 +3842,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3864,7 +3864,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "22222222", "type": "binary", @@ -3887,7 +3887,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "22222222", "type": "binary", @@ -3910,7 +3910,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3944,7 +3944,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "", "type": "binary", @@ -3967,7 +3967,7 @@ "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "54b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c6717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb26", "type": "sign", @@ -3994,7 +3994,7 @@ "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "OP_0", "type": "auto" @@ -4027,7 +4027,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4052,7 +4052,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4077,7 +4077,7 @@ "txin": { "txid": "", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4102,7 +4102,7 @@ "txin": { "txid": "ea9d5a9e974af1d16730", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4127,7 +4127,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4d", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4151,7 +4151,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": -1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4178,7 +4178,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4202,7 +4202,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [] + "signParams": [] } }, "error": { @@ -4223,7 +4223,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "111", "type": "binary", @@ -4247,7 +4247,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -4272,7 +4272,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -4543,7 +4543,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "3044022054b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c60220717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb2601", "type": "binary", @@ -4576,7 +4576,7 @@ "txin": { "txid": "56eb4a177459bae6d310cd117dde5ff86e0a6572d44dcf5e25e611435fff9b31", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -4710,7 +4710,35 @@ }, "expect": { "success": false - } + }, + "exclude": [ + "json" + ] + }, + { + "case": "Fail HashType(P2WPKH) SigHashType(all) by AmountCommitment on json", + "request": { + "tx": "020000000101b7fc3ad65a21649fdf9a225a6165a2f945e895f2eac6b2bbc1d2f3681080f8030000000000ffffffff030b3584c0a40110d0a208aad09ca9be67dbe5fc7343b6287a8abb122439def1cc8c094d982039de127c6ffb3a012d7a54cb790baf1e923f48307eefd189aaa830dd4f03536ffcfc5365ae010225b8011f8b94e495574b4d0527350fef11fdbd93dbe21b17a914a3949e9a8b0b813db67c8fc5ad14194a297979cd870bcf3a513f93f3098858ba760efdcea670e9675930b210b9b7e5c33a87ebe3823d08e6b041a5120606213de33e800d38636a5a18277badf9f9393db822b15f3973aa03eaa4f0b5baacb04a6d8b37b5e70584550be4962178e20a9c0aec5faef855ccc617a914a3949e9a8b0b813db67c8fc5ad14194a297979cd870125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a0100000000000027100000000000000000000043010001033a6e5463aeaf8460ef48520746c9a162f4b715eb6d4f00cb975e3b7a01e57092012829e2e1e87787fe5e04512e0b68500f97ae8771a44c05a87d3646e9215bfd4d0b602300000000000000014d3500fbe175b8e442af9d8322a0c892e2bca2c25561799c01c07f66cbb936e9c90404338f40bcf348912cc25855cbd38621076efd088b36b1933baf9513aea1bbd522551c82a478a7fa34e1dcda8ef648761031332ad5388ccbc171f9475c9fde056908bdec0b0c408c41691c4bed297da4b1740bbf0f81eb2ccf0f112ba6ca2c6f1d6bf3c1cecc613aaac01153bfaf7906df82402dcc8d0867eecd212fee82827e1ab5fb7ab448b36bed57bc9edb153544a961ee5e22a33385336e77f2b67dd6274c4d0232635c3112f7cb9dbb758beeac7db0ca1945c3497d1187aee1106181b2b2bd8c5602b1460085d2ac1319d0df500169a9344528feed3304ad212678e79946ebcf915cbbb9be47b0e850e0022c058e95e6e18d73227d354910b6d2ef7435c8a5f6e1355c2e35173e7bef86924a14a0f3e747562af1898a8c65ac52ba31bd860e81090f3cc1da296557685c4529681b25066d4db6febed3cbc3252e87df247a96a6f8c44550ebaf0610b3ebfdc80c744d41b4cb80b17d3ffa95024fac8ec40cbad4a2e442479e334021ee50218dabfec1d68bfda180d7d509e0d2da389f589ee6024791c2557b4175aae469e7e11b2779d1a8433ffe99ac7db6cbc7a4296c1eba50636839b1a378ad27c9fca08859689d5f08103c45f8de0eedabe7987afa9dd3aeb970cb8276c22fd329f182b8200b1d1a4baa88f2a82e54b40ae07cb10bd7c9bad09e59b461ea0f955791ace19af8dd90cb7b55cd09d319abc75686d15606b88356cf1f6838f59d61d473d4d128821d7628ac3c9b9068e65acfbab69fcbe498ee115fcb1cb3133d51d66e4727a4c37a56b66b940d40f4aaa16be4fc7a8c58d1df13614905472354a996f463c0102503a5191c4677a9666f3190555022a1f447d0cc45f65a845dd8a0a9fd9835948fe4aee490ead426df02327ce189458c85d08aad605810e5b9372023992040213e0df83c1d775c248edd7095271f88d065c073a15b37c3455b23ae946fa4cb7e40a14dc8810614deacf8ccab5634bce2f6475c35867c0c71add1d2af013269bc0973d8a54a78b2f6234c9520ea490f188aa493d612f5bef278cfb9f48e525f7eb202f3603bfe5d978b1233447872dff46a21b1d19ec8a8f6a9c4dc1c4ee3ebb4124b4c2a9d7d28705eecb04b9c592cee292155c7204a5a3f781cbc970d9549f5afba68d2e1ddc4b08ee3af6085716c7af487c92a1abe79d7c9f5027cb03190c9f47e05db5896cd94b119c53de617533dd7bdce57e9f3ce69cf8f25b6bcfce6d8abdcf14e098551d8f72256c2c04a5fb835f5b9f5ba1c7a5989a1c93e181240cb175e34ecedb9f2ef23614054f44cbaacb732e5529f02a7622c2ffaf811d205ed2be83f723ff8ba4ff23dbca65aabad5196c44c37982bbd5a54f9b38e776b52b8e817ca2f2c246dcd7b68922f9b2b4e8792c4a00c02a81de950afa532ce77ded61182f8fb65a49d416ffd697a8bab26feff05c9e0e7bcf028fd4775566f5864c4cdd51e425756369d9d4478ca027907511cedab3dcd098f9c9a019714a6ce3b40f54fcfaa6e035c4c8e566bb050ef9b9f4ac995155f2fcda379f94a3cf2819e5d57221ebe63af2dbcf319942728ada876379a0d98991d25125168307e089bd2bc24843d0b58f958d75b37f097dced7d7579d94346127d7a2029c1966ccf7e80ede17511da539ed2e4cb7b65f644e906f9c57c5a17ec9375ab5b9b867d1ab68a00371cbb5fa209d6badae3728723e0dff89cd5f9bd38dec57321b43b32c747b0f645e02df0ce9d946a723243d53a85232c4f26853b2d788c7f5c6e9974da549af5cf23813eaf88a409198729f12f5b83cccff34b6153e0d3adb79a3ce11dde701807519db5df63939d4ed44abd34971bc14a477ed35a7596907753477aabdfc15265f2625787d8a152f9b5deabc5371682657b0c9bd4dc20ca95e388de3454ec80ded9b9b268ec55e2f0816be7e9b979a81685e2885bce1f0c873d6f268fda1a8d4fe8f736e95842fc9b05f84ed1d14a67566dfe931fa13ffff2672d9f9d30b13e62ecca4ff20f462be0626c0b93493446ff90d7ff1bddd867c8c6d9ccee603ab0fb0896544a20f2f9a5f5928e026bfd0ea93cc4c26abcf6159fa2c15245f2f3190cfc201316985a17b666e16230598a84e4bc31a6ae90491ea1f4550f96360778342c84c494c9faa073624396d8ca2e1ced3b545959040be0a5cb9d8bedd9584ba2db6f3bbf2dca734806076cb406432793f7b3ba7bc4ac35325e97d1f48d219c9e0e5a1555cb49cd97fdbfda3034ef08073a490624e2e1b76450e091d878c3caf9fe066680b96af0db14d9939c489dfd387b2e86b89ecd4bf1e6cc4c7a7f0e4533c6dcb510bec483faac1a9bc73cf2e1e595e8ffad98c7c400601b56840766f4d7934b2cbd6aa70bf6ebd18436d23f963b1a7711b3966415e50a0b0c67cb6de142e1a1ba5d7dfa77542c44bbbaa0e95ed0e702a506fdab23ee7aaa7843083266693dfb6c31be76e0bc3b33fa18b11e95888a18cdc4793ae7bc1a4686c546a52c9843fa19739522cdbbf874ec59d0c38b6a4b3aedbfbe72d82b8efba6deac3cbdb2f18ac25de8c8f095f1c921431e6a7c342fb17e8d3a5cb809521765de4adb001ae63d92e109317d090425dcdc197dfdbe7440b857824ef6128397653e20c33f995b4c782db036b420945b4c2c3d953ead32378939e9b74537e2dd26cd360bac1dbf752f66f1ddbb03eede3c76efc619bb06ebac2a0cfbb6c40f9a2762995e0911950c07eb7b5ca642234e0c0df99db32dfe99253003c052db72d4f744e423cc28620e1742363e5745ba759d15b62f77bb9c6534bcc13ee8ef1381219ac07219f8a75caa256ab7c3fffd0c40f93556541c929a1754c289235c4f80b68f0cd0043c9e0c2922e512a0730a80c2b4bf038ee9d5ece4e5dbc1f84258a81da43e4921278f1e4aeffa6cbfb8b2f83e9dc756811a00a44a48498b7f0f1b8ab9b1808914cb66b3fd41e6d4ff6579747b2174b4651c66382fd6000b6db2dec4763f61667115113469b73de1d911af384a36f7e4c960e10102e015b8d1250af9ea7981e79d84b49f714aa3210fb6e7f9b2a860d9df18c981f69b479bfd744a6c054df476919407bd227c58c76f244c8b8ea4265912e95b4285da68be013e3888b92d57b8689ad1ef5c6f0d3c0bed3cf25e86931e7c4bf14042d8ca9b7c323b2875b981eb9b5883c4e3bd75233cd9ae19568a3adede390bcfa787067596ab704c9b057d6a91b77c514de5c034a96cb31db3393ead266aab3eaebf2fbeffa63c4202c5febaa3f2b848878a5c2235619071c10f6146868e735971c8a53f59d3235b0e3b4a460473e7173f29d42982f69cef40207d43e2fd308a7ff6421044c8960c991f33a9d3334a8d63109251f672fd89f75ae1d9b32559b73c5a22e019f46a503f9dfb5bc82deb5103dde9e91f145c7507ad55b21e0ff8f96b5dcb6222dc8c5e428ddd9e9c7658a8ba8d318aa6180e75c260bb83e16898a68711be33c0ba8906e3233c8c1f31c9fc75857655ef26ba7b53952dab096a48ccf9258490a0b6e05304da43a43878eb0740fe1f952b9028d450b904ebca2d00d036bd38a435ffcf73d795397a84ec18f72a93cdddd4b8dfdac4c6c7877e16b480e819857c8e18920beb0ee09e6c3b2b6baadcffeac776591744394eb512d5814c1f68ff0c73e2bb3e2f4e0186282cd8da6b8e96e1d5bca47c98329608edaebe36c01895638a7c728cf871d2f19d5833234d2277d3acfd60ebd6c4add790eee3758bd840bc22962f1fa2227ae2853813cc36b5be46e37b246f6a9b0a21b6159a82b5f92800941e33df02188761bc730c2b284aa15dd4545ae0f4c4229a4d8c9b415a691a355e1858a26c7433195a42e55c020cbc0efcb51b560d561715c546091d655dd5cac04b4ce0986941229591f33f7c96e9acef7e878043c49a4a420de859ff20695196c37660511276e43705f73751d68f7ef1929b3ab8e32faabc3c83ac13466c543650220aa2669a3ed7fe34d6f6e6ddb44e390b7338a8a3056a8643010001ff410fe519866271fcc3d47b83120429858ebaa008dce79ae35b4455d7ec3f3045ffab1a938bbf628ff8c14f87f08943acca6bdeebfb06c18b58ec8f4d1d9c51fd4d0b60230000000000000001e20700490755f44beb25382ec7d71b25290f105ca685a456ebd1a7c560386ec9d03fbcb13ae429d8a04902b5daf4ef10010551a2848a31c42a43dc4037705a3ccd2e329f4ae6b02bebe80e58062b374e35d099147cdb36dcbf174ced965c6697a4187f68eb482706f30b24c4312a93a576f07398cd3c5a001645885aa6fdd659a1aeebfd51fe2fe0fc8f26ca9071ee7480de50a9c0637a9c551fedb0215045a888c40d4629109d8c93be540df83c991ac8c1ac9c3e2bb798fcea1c4a4791925c01d349e4ee5832b6e3d53a6d2dc2193b78d97b3b0dd951a846d48d83ceb067518e558214fa17837b747285b80aa92200c5340bdfb727e55d72c26dbc7816073a3654b084022e2d951463a0838f4683ec74184f18af1c0fddbebe292b6615c3c12b7bc381943f2a0968df482be55dd45ce19491349f2cb982935c092cd281f24e3ccffea71f3c776dc79cd4338fc93e393d92de1456166bc1019c1252932f43408338c3a84716dc842cea2a069b057a968d73892429ffd02225550bef7faa19e580cda00ec18d1d60d8e338a9cab95aaa857c579904eea03873c96b23bdd7b2d7aa39d402d5e81cf3585d390c990ba6d786ff2e8183c15e8ca4faeebefd7630b54179a6edea020f6de31ba0b8f36173ba0caf6eace25d9aaaf2ec1ec3cf6a87caef74787447017a2f661598ca5252baccedcf3b7cab0d1bf3d13d69651d92c0dda2baed6f979b5bf5b1f9e4b60592da53cad89bdc53111aafa7a9d8874e7d9f145e128162b709db2557456a1f06789ff8508bce78b47fcd6d63914753e3c5002b1a3a31cf5ac6e42ab6638499c0b964681e854e7284a9fad3a96e5c53ead3ea652e9d0af6e6b86fd920edb6187fe22a03f47fb617ab4232155fd249922d4893d2c786abb074bd399b210e4461c75169f13b0ddedc25c4def03f9a7e6e58e1a9575fe59556cefab31114c3b59fcfe80883920aede6ca6db4db539367894bf9c0a465a0448c6b2f370a3c41e1a9790dcad74918f41dcc7f568559401bc4a471102e43489a731328aad4cb8f9cb459298569a724048b2e879964143837eee75e8a4906fc3bfa22581589f3ca9f6b9958c46a30747e54b5c7fe66f510c77d658458f2311140287b9fc421a48e17073707087f37c1098e9d60b67bac01f1b5b2feef6c1902bbe5581a1b13836f555e63f6fefcc715f4b818acb499ac17e9a3633bf97e975ac49cbf76aaa1ef85411a3fb9062195e202d03a6aed9b652bcb380424032cbaadc3a8b808414836fe68f29b62d27a22abdb5a618191707b442e9dba525fb13698c1af1d00db5927e8178eb7e69ecd21b3199b3eb10ce7d9619428070fa17664b0b8134b45c6c532c74fb32b581a63f3af2d675a74ab467703f4dfede85c7f570fe65c21f71c709db4ae94e4b6fb92314aafb88f953dc8ed3b28da983910413fc9bdc78627c98b519bcaec8de7cf21abbe44deea9c3bd4ead89433bbffaf7a00f1712c09601df224c9635612f97df35812b64fe88a6055d7a971611b358ff7c65320a4eec533a007824fbfb1f0640f5634a0875189321a692fda6e4b39c2e339e3072e181dd228603c82d232f3e7370d344512ea0c0ae8428835974600bbb5870bb922de9f47be0c4fc2f3632f4ae44c7029589f3463ea4418e51c4038788942306cf7715ff8a09bd27413211db78d165fd0a698968f1f1b1023feccb850c99596933da86be7cea9590ec25d487739a25f1552a7f06f8265111dbd65b20241557234a6ddff88a42222e3622c2bb8c0020ed5e21cbce129b734df3cccb008386f78eab530f9625117a0d6d29a396e849564a0c74ec2198da0200dd80a5071fb23f282d0ef2c9bb0290bbad54f91fb3175d38b66cd7b729e370f64948fccdd69703c99196afef66b2187f7a8b9ad13a012cd344e43dc16b3cfc28a680a9764c150c93bf1db12ccb98414aaefa426ef91360acdad7cc13be34660751a8af8f11975ebfb646e8fe5293b51553dfe22d8be3c7d1ab1c4850a362ce11d12b2048a64e8b6398eb5e2078a14eebd532542917b337033b6e82b35a8630cccf170bd73f7e6868634409f1c3351fc83ad399afb3847fbaa7beb4c534bbdbd87df1cb49c9462901f46f00d9e4a1c02c8d817ed31a8e77cdc271ca8f05498951dba32abc0153637790377917ce9d872a4853c67ea639befdc9873ebeb66c30b803d9866c8118e5c7ced668fab3a80b2d4b68f8a387dd0efe3b493888cd02479c7186eea9b58f6b4b8dca92dea056ba78a81d35b29e2586a25385e3574ba4e28ce2702fe1d781b38aae3dd7ab7375014e631421fedfef38b00bc2e622422c3e5cfaf92180c20903e1ab4e983ef8fa20201f47674ce3f85f37691c611155367075ed9e131606105973b428d2e2f7badd32bb4d460735aa5d5774835d5b25369a3959bc71e60a53696c7473101c76a36fcf84192dd3f8ed934c7485033519d1704c78aecda9c8abf0dc118879779dbf4ca888dfd4dad2a6e4bb832e0283563a6057237122f325c72eef7dc89adc842b6f305ed9b2e7b24723754a1d3a26e149b90ba51cb0207d46ccd0a69bd63ad1e95f641069f69639bdccea03428601606bc85a5f2b747a3476e3449ff2aeed12214468353a5326e322354fb56c3f1dac4fa61584ddc38c5a54c5fb8ec332085f50ebfffde12c46e0283656fd954d121191dea53d19fe287e8aee935011fa7b3a240afdb24db95f3ae77c86462028f5b054ef8af36d3f7b4453ad50f470fec39f95901a57d3b3bb01e952269983475f1d13c5a5921a8709332fa9586a0fd8f82f87d06e66e9ec2272fd67a9cfcc1f48b7a1da9a3af716216b811f03fcf4a12c8e832c4e36bcb83f7cae5dd17a385cb9a679d9cc64be8d9f972ea022cd9625664180e98c3f292e62b56b5bea3b9845ce720482f5fb6931dfbd35aa3fc816a776a4553360ec18888b1d9d9acb07edeb5908955373fe6984abc8dbc6724cfec5a908fd606169d0c5ff914e0db3f1a566602dba4265c67f3233b1623b016306d8d3bb0d8c2f47e101fb626c42849c9cd83b5dd7d148ff4a488b6929e52048cf0a61d8e2d506e5d6e70c8beac188de2c14bf8f7c385461257886baf4bf3ad78e16c657e3de62e5cb42015a2cce88516887e1995018a51fa5a2a6688613dde7edf213266e5520b73336cc775ea542908f3f76e67d2792e73afb3429aeae188e39aceb3d17652740ae37fb3639c16888336bc9f1cfea546d3443214d153405e03216ae0ebb79b949ead937daf5587409cf53b6be2428c289dedd6194c2a42719660bc3ca3706f8ba4141734b6e2734ec0e90bb267fc59b92e91a38f554097193494073fb7559b6571994dc5381b5b4ca4443665cf4da2f41dbc737b554a4266c5a40bccc36f94585788eaceddbcc6a082cc1265811c7546f562743264fd8c6926b4f28a7136583057701b8386ba7821ffb12ba742d8a475765c58c1ab68d44adf9a45127a9d0a456152f2ec1317c3c7dda48944fa8595d0e86662c311e6970bb6e51589105def2032e775d5025553d3f44c89044272f870aa7b19a7f71ae0e5d184b6cb7d9e697ae8a56ac1d5619e0d9f1673072928e201975ec84417c83c0fd611cde590995d7d44ad1dbc09c98e178e48bc861b37e46c5257c8a66f6323a1a33958b14eb811ab6c8827dbf16955d01b5ce46bd1e1b19afd48a70310dfbc54d8bb6ed68ec4612c71145bff3a1833e1bb8c52962ec51219abbb58de0ba4c6ca3105fc2c181809102df98ba0e6c22ed21c6d5b30fa2432e8065d5b2b98b95800d6e5600aef541990321bf28be3cff705457c9c00d2a727352e92d102b15a9f7105457b27f93111bf4552ae1588e69e7656e2f1cb723c969c6e8a886564bee122eab57b145fbb2781dea3c099633a80141dfddefa16d93ed7becedff15f196dbd8adff8c47affc10d75aec5e9e03828e371787276193cae56253fc54eb9d1bf925152ad5f3b671f3944f9f61ab35f52b3790c655dc6f0f30ce33169b563f85057b1235fbd62c1d0f9ae9642c639c951bde2baf544117687ab8a3682206ab35b010000", + "isElements": true, + "txin": { + "txid": "03f8801068f3d2c1bbb2c6eaf295e845f9a265615a229adf9f64215ad63afcb7", + "vout": 0, + "signature": "52505e593132327ae41604e5ada810fa73b2951cf0ec7a6126b837eae60b0e491f3b18231af61311d2f98846bdea632acff87eee02f35e3880b92c14a0eb6418", + "pubkey": "03f942716865bb9b62678d99aa34de4632249d066d99de2b5a2e542e54908450d6", + "confidentialValueCommitment": "08b7057c10af7e696c1927584b006fbc3e7e914d4e7ac29f1876bf8d4a64276736", + "hashType": "p2wpkh", + "sighashType": "all", + "sighashAnyoneCanPay": false + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "Failed to VerifySignature. check fail." + }, + "exclude": [ + "capi" + ] } ] }, @@ -5705,7 +5733,10 @@ "code": 1, "type": "illegal_argument", "message": "" - } + }, + "exclude": [ + "json" + ] }, { "case": "with empty txins.txid", @@ -5776,7 +5807,10 @@ "code": 1, "type": "illegal_argument", "message": "" - } + }, + "exclude": [ + "json" + ] }, { "case": "with empty txins.peginwitness.asset", @@ -6010,7 +6044,10 @@ "code": 1, "type": "illegal_argument", "message": "" - } + }, + "exclude": [ + "json" + ] }, { "case": "with wrong txins.peginwitness.mainchainTxoutproof (deleted)", @@ -6049,7 +6086,10 @@ "code": 1, "type": "illegal_argument", "message": "" - } + }, + "exclude": [ + "json" + ] }, { "case": "with empty txouts.address", @@ -7663,7 +7703,9 @@ "asset": "02474456108a90b2040de29259cdaf64517f1d5583678692dbcfb077c4fd6afa", "assetamount": 500000000, "token": "ec7ddaab70ec4ef2d3d225dc929dff353ca8d9037299cc806731cae0cb790590", - "tokenamount": 1000000000 + "tokenamount": 1000000000, + "assetValueBlindFactor": "ec435d2f83fe3e0142b8a323bc837e0d585bf942fd8c50ca73c7853b1b82bd64", + "tokenValueBlindFactor": "63b4dde9e8fb336b970a1166d6511e4ae8d96b0d7407f0ab2ff1ccafb34718b0" } ] } @@ -7727,7 +7769,9 @@ "asset": "02474456108a90b2040de29259cdaf64517f1d5583678692dbcfb077c4fd6afa", "assetamount": 500000000, "token": "ec7ddaab70ec4ef2d3d225dc929dff353ca8d9037299cc806731cae0cb790590", - "tokenamount": 1000000000 + "tokenamount": 1000000000, + "assetValueBlindFactor": "ec435d2f83fe3e0142b8a323bc837e0d585bf942fd8c50ca73c7853b1b82bd64", + "tokenValueBlindFactor": "63b4dde9e8fb336b970a1166d6511e4ae8d96b0d7407f0ab2ff1ccafb34718b0" } ] } @@ -8466,7 +8510,7 @@ "asset": "186c7f955149a5274b39e24b6a50d1d6479f552f6522d91f3a97d771f1c18179", "blindFactor": "54a38ba4faceb46c3a0d0fb80d66332af987beadff36d2a5c5ac2110c0730eaf", "assetBlindFactor": "69c4e735c8e92765223ad29813ef4e3fd7873eda4ec05f27d137113705d37f3c", - "amount": 999838040 + "amount": 999860340 } ], "txouts": [ @@ -8526,7 +8570,7 @@ "asset": "186c7f955149a5274b39e24b6a50d1d6479f552f6522d91f3a97d771f1c18179", "blindFactor": "54a38ba4faceb46c3a0d0fb80d66332af987beadff36d2a5c5ac2110c0730eaf", "assetBlindFactor": "69c4e735c8e92765223ad29813ef4e3fd7873eda4ec05f27d137113705d37f3c", - "amount": 999838040 + "amount": 999860340 } ], "txoutConfidentialAddresses": [ diff --git a/tests/data/key_test.json b/tests/data/key_test.json index d5f9971..b562cef 100644 --- a/tests/data/key_test.json +++ b/tests/data/key_test.json @@ -1221,6 +1221,31 @@ } ] }, + { + "name": "Schnorr.TweakAddPrivkey", + "cases": [ + { + "case": "parity=false", + "request": { + "privkey": "4544c9f3dd84b9d5b32503e0dcd9ff60ab203bc9245361ebe6fe9556c7d0cc38", + "tweak": "df707f11dc54706101eafed0202a581f8f9b14e0a0dcf9411ecc50113ca06dcf" + }, + "expect": { + "privkey": "24b54905b9d92a36b51002b0fd045781800c73c315e7baf145f886db343af8c6" + } + }, + { + "case": "parity=true", + "request": { + "privkey": "56f1abb4261f68c5e76c248ed60b21b60bbff4f997db6503a1c9b78e8881a1b7", + "tweak": "4544c9f3dd84b9d5b32503e0dcd9ff60ab203bc9245361ebe6fe9556c7d0cc38" + }, + "expect": { + "privkey": "ee531e3fb765510fcbb8df5206cedda95a0f23b63bc09d2405073c550f856bc2" + } + } + ] + }, { "name": "Signature.EncodeByDer", "references": [ diff --git a/tests/data/psbt_test.json b/tests/data/psbt_test.json new file mode 100644 index 0000000..fbd9ddc --- /dev/null +++ b/tests/data/psbt_test.json @@ -0,0 +1,3073 @@ +[ + { + "name": "Psbt.DecodePsbt", + "cases": [ + { + "case": "Normal", + "request": { + "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", + "network": "mainnet", + "hasDetail": false, + "hasSimple": false + }, + "expect": { + "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": "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": [ + "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" + } + }, + "partial_signatures": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "signature": "30440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601" + } + ], + "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" + } + ] + }, + { + "non_witness_utxo": { + "txid": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "hash": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "version": 2, + "size": 191, + "vsize": 191, + "weight": 764, + "locktime": 0, + "vin": [ + { + "txid": "405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6", + "vout": 0, + "scriptSig": { + "asm": "3044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c01 03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "hex": "473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 499995000, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 8d20443a91969e3bca0e240cd0ffe4dc98c63de2 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "1DsCvxydk2JqbEj1EqL6mXcEvStsKQvKbx" + ] + } + } + ] + }, + "partial_signatures": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "signature": "3044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001" + } + ], + "bip32_derivs": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/1" + } + ] + } + ], + "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 + } + }, + { + "case": "Detail", + "request": { + "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", + "network": "mainnet", + "hasDetail": true, + "hasSimple": false + }, + "expect": { + "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" + ] + } + } + ] + }, + "tx_hex": "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + "version": 0, + "unknown": [], + "inputs": [ + { + "non_witness_utxo_hex": "02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000", + "non_witness_utxo": { + "txid": "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", + "hash": "9cb67d0ba945c8da2342429e19b12e11852e3a032144ca908996ff46f05dd906", + "version": 2, + "size": 246, + "vsize": 165, + "weight": 657, + "locktime": 0, + "vin": [ + { + "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": [ + "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" + } + }, + "partial_signatures": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "signature": "30440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601" + } + ], + "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", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + }, + { + "non_witness_utxo_hex": "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000", + "non_witness_utxo": { + "txid": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "hash": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "version": 2, + "size": 191, + "vsize": 191, + "weight": 764, + "locktime": 0, + "vin": [ + { + "txid": "405e51d745c9f534a71c9e4563521b832fedcbda65c6da2db502e8e236ead2c6", + "vout": 0, + "scriptSig": { + "asm": "3044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c01 03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "hex": "473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + }, + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 499995000, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 8d20443a91969e3bca0e240cd0ffe4dc98c63de2 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac", + "reqSigs": 1, + "type": "pubkeyhash", + "addresses": [ + "1DsCvxydk2JqbEj1EqL6mXcEvStsKQvKbx" + ] + } + } + ] + }, + "partial_signatures": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "signature": "3044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001" + } + ], + "bip32_derivs": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/1", + "descriptor": "[9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + } + ], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + } + ], + "fee": 399995000 + } + }, + { + "case": "Detail and Simple", + "request": { + "psbt": "cHNidP8BAP2GAQIAAAAH+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////PEJ8leyO15eW8T031SX76GhUXNkaHrDYn/kJgHMfOosHAAAAAP////8DgPD6AgAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5YDw+gIAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVX6kZgAAAAAABYAFCtuFro44oBQD4CyxXBu8C841ZCBAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCIHUR4dvDoKVJUHcXi7Nygne+xHLtv6hBF0SFUug+mYcmAiBbhUOZgPafTibMLQ0x4ztsORgr2TgrdXjSGS9W++fNJwEiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQEfgJaYAAAAAAAWABSL8J1gt+NPNIJ9jbyxx2OQqRbKgiICAidEz7JDbhVgQOwcj+hC2e+aGMtArUHzEnJTkMNfe9NrRzBEAiAWMAlPmBJeuMids3D4FaheSwTYp0EfmoKHt79XFujvwQIgA0ez55j8On/HZzxDZkVU6QpOjvhK9jC5t3uXRArr4WcBIgYCJ0TPskNuFWBA7ByP6ELZ75oYy0CtQfMSclOQw19702sYKnBHYCwAAIAAAACAAAAAgAEAAAACAAAAAAEBH4CWmAAAAAAAFgAU8dPuZ4KSJeuJLMqwHiL2p3fn4h4iAgLn6Nwjb6AkNpQI0s5NhQgEgmGrwpe4EWBNoIetcdE4VUcwRAIgFW2n6NOObTO5AFLZTMz2eOvsKVcfZoRM0d4TUNwsb2ICICrnDSpzucHS+d15HVHrbj6OmN+Ex4slnWL2qhTegTGXASIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgIDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20RHMEQCIA+O5JwYHIi6qsDnl5dtnaEaTezaph80fSrHpf3oYFHMAiAmE/NyMKdHkOnZaXl8DEd+7QfiGWDTlY3YT58qnsgjRAEiBgM9h0vxm2l89sY5ZZVHpYO4ZzAWSlttsBvyChTrm2rbRBgqcEdgLAAAgAAAAIAAAACAAQAAAAQAAAAAAQEfgJaYAAAAAAAWABQSt5VKde/Cog6G4y3C14ZH1nAHeSICAvsGFzDb3jyAa0oXqZ9FTIEoKuzubUb0qXWpTN/ToGUERzBEAiAROQcOrD/7+U+/N0Ydb4w3loRmvsQvdMCAC/RAfzZMtwIgBzMutywN5qqXXemgT/AETbn4HG/eta2bqFm0xM0w17YBIgYC+wYXMNvePIBrShepn0VMgSgq7O5tRvSpdalM39OgZQQYKnBHYCwAAIAAAACAAAAAgAEAAAAFAAAAAAEBH4Dw+gIAAAAAFgAUl4qQRg5EZxpS9JoJu1nMZ5S2PIkiAgPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7EcwRAIgO52xGqSl9m8A4TuPSS+gtRPaKjIH53Ofim0C2DF36rACIEY7ar0+P2cYkWxYOgYfodMZKSQw+EL44sgeQZjjCFDEASIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAABAR+AlpgAAAAAABYAFBzoeOOg2js0MIeX/ey3YiH4VBivIgIDwCMlwyj+1iKp2I+PUxjgUmGjw6lntyEdfWdlfiten75HMEQCIGh6HijcOAquKKktu6C9G5tPSM5jh4mquz554sbSZSWmAiBxYhJVPHQhP3uqJ/deeKeqYykFl+sZ563peckMW+/iFQEiBgPAIyXDKP7WIqnYj49TGOBSYaPDqWe3IR19Z2V+K16fvhgqcEdgLAAAgAAAAIAAAACAAQAAAAcAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgL38NfQAom3xaWBvDUnYEDDSN5I/EFAZ/MeomrZXABVDBgqcEdgLAAAgAAAAIAAAACAAQAAAGQAAAAA", + "network": "mainnet", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d5908100000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 962c4e08f336d3afbc3415c9d359ae1040470520", + "hex": "0014962c4e08f336d3afbc3415c9d359ae1040470520", + "type": "witness_v0_keyhash", + "address": "bc1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq5vd8ay" + } + }, + "partial_signatures": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "signature": "304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd2701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/1", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 8bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "hex": "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "type": "witness_v0_keyhash", + "address": "bc1q30cf6c9hud8nfqna3k7tr3mrjz53dj5zey6xnf" + } + }, + "partial_signatures": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "signature": "304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe16701" + } + ], + "bip32_derivs": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/2", + "descriptor": "[2a704760/44'/0'/0'/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "hex": "0014f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "type": "witness_v0_keyhash", + "address": "bc1q78f7ueuzjgj7hzfve2cpughk5am70cs7wk47xw" + } + }, + "partial_signatures": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "signature": "30440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de81319701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/3", + "descriptor": "[2a704760/44'/0'/0'/1/3]02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 19d65f8328b2206d9970785660ec0d34808fb075", + "hex": "001419d65f8328b2206d9970785660ec0d34808fb075", + "type": "witness_v0_keyhash", + "address": "bc1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4hk443v" + } + }, + "partial_signatures": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "signature": "304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec8234401" + } + ], + "bip32_derivs": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/4", + "descriptor": "[2a704760/44'/0'/0'/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 12b7954a75efc2a20e86e32dc2d78647d6700779", + "hex": "001412b7954a75efc2a20e86e32dc2d78647d6700779", + "type": "witness_v0_keyhash", + "address": "bc1qz2me2jn4alp2yr5xuvku94uxglt8qpmej3dnds" + } + }, + "partial_signatures": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "signature": "304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b601" + } + ], + "bip32_derivs": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/5", + "descriptor": "[2a704760/44'/0'/0'/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504" + } + ] + }, + { + "witness_utxo": { + "amount": 50000000, + "scriptPubKey": { + "asm": "0 978a90460e44671a52f49a09bb59cc6794b63c89", + "hex": "0014978a90460e44671a52f49a09bb59cc6794b63c89", + "type": "witness_v0_keyhash", + "address": "bc1qj79fq3swg3n355h5ngymkkwvv72tv0yfn760a3" + } + }, + "partial_signatures": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "signature": "304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c401" + } + ], + "bip32_derivs": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/1/1", + "descriptor": "[9d6b6d86/44'/0'/0'/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 1ce878e3a0da3b34308797fdecb76221f85418af", + "hex": "00141ce878e3a0da3b34308797fdecb76221f85418af", + "type": "witness_v0_keyhash", + "address": "bc1qrn583caqmgangvy8jl77edmzy8u9gx90snc5a5" + } + }, + "partial_signatures": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "signature": "30440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe21501" + } + ], + "bip32_derivs": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/7", + "descriptor": "[2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe" + } + ] + } + ], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/100", + "descriptor": "[2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c" + } + ] + } + ], + "fee": 1158 + } + }, + { + "case": "Detail and Simple (testnet)", + "request": { + "psbt": "cHNidP8BAP2GAQIAAAAH+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////PEJ8leyO15eW8T031SX76GhUXNkaHrDYn/kJgHMfOosHAAAAAP////8DgPD6AgAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5YDw+gIAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVX6kZgAAAAAABYAFCtuFro44oBQD4CyxXBu8C841ZCBAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCIHUR4dvDoKVJUHcXi7Nygne+xHLtv6hBF0SFUug+mYcmAiBbhUOZgPafTibMLQ0x4ztsORgr2TgrdXjSGS9W++fNJwEiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQEfgJaYAAAAAAAWABSL8J1gt+NPNIJ9jbyxx2OQqRbKgiICAidEz7JDbhVgQOwcj+hC2e+aGMtArUHzEnJTkMNfe9NrRzBEAiAWMAlPmBJeuMids3D4FaheSwTYp0EfmoKHt79XFujvwQIgA0ez55j8On/HZzxDZkVU6QpOjvhK9jC5t3uXRArr4WcBIgYCJ0TPskNuFWBA7ByP6ELZ75oYy0CtQfMSclOQw19702sYKnBHYCwAAIAAAACAAAAAgAEAAAACAAAAAAEBH4CWmAAAAAAAFgAU8dPuZ4KSJeuJLMqwHiL2p3fn4h4iAgLn6Nwjb6AkNpQI0s5NhQgEgmGrwpe4EWBNoIetcdE4VUcwRAIgFW2n6NOObTO5AFLZTMz2eOvsKVcfZoRM0d4TUNwsb2ICICrnDSpzucHS+d15HVHrbj6OmN+Ex4slnWL2qhTegTGXASIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgIDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20RHMEQCIA+O5JwYHIi6qsDnl5dtnaEaTezaph80fSrHpf3oYFHMAiAmE/NyMKdHkOnZaXl8DEd+7QfiGWDTlY3YT58qnsgjRAEiBgM9h0vxm2l89sY5ZZVHpYO4ZzAWSlttsBvyChTrm2rbRBgqcEdgLAAAgAAAAIAAAACAAQAAAAQAAAAAAQEfgJaYAAAAAAAWABQSt5VKde/Cog6G4y3C14ZH1nAHeSICAvsGFzDb3jyAa0oXqZ9FTIEoKuzubUb0qXWpTN/ToGUERzBEAiAROQcOrD/7+U+/N0Ydb4w3loRmvsQvdMCAC/RAfzZMtwIgBzMutywN5qqXXemgT/AETbn4HG/eta2bqFm0xM0w17YBIgYC+wYXMNvePIBrShepn0VMgSgq7O5tRvSpdalM39OgZQQYKnBHYCwAAIAAAACAAAAAgAEAAAAFAAAAAAEBH4Dw+gIAAAAAFgAUl4qQRg5EZxpS9JoJu1nMZ5S2PIkiAgPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7EcwRAIgO52xGqSl9m8A4TuPSS+gtRPaKjIH53Ofim0C2DF36rACIEY7ar0+P2cYkWxYOgYfodMZKSQw+EL44sgeQZjjCFDEASIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAABAR+AlpgAAAAAABYAFBzoeOOg2js0MIeX/ey3YiH4VBivIgIDwCMlwyj+1iKp2I+PUxjgUmGjw6lntyEdfWdlfiten75HMEQCIGh6HijcOAquKKktu6C9G5tPSM5jh4mquz554sbSZSWmAiBxYhJVPHQhP3uqJ/deeKeqYykFl+sZ563peckMW+/iFQEiBgPAIyXDKP7WIqnYj49TGOBSYaPDqWe3IR19Z2V+K16fvhgqcEdgLAAAgAAAAIAAAACAAQAAAAcAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgL38NfQAom3xaWBvDUnYEDDSN5I/EFAZ/MeomrZXABVDBgqcEdgLAAAgAAAAIAAAACAAQAAAGQAAAAA", + "network": "testnet", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d5908100000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 962c4e08f336d3afbc3415c9d359ae1040470520", + "hex": "0014962c4e08f336d3afbc3415c9d359ae1040470520", + "type": "witness_v0_keyhash", + "address": "tb1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq72k5xh" + } + }, + "partial_signatures": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "signature": "304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd2701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/1", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 8bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "hex": "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "type": "witness_v0_keyhash", + "address": "tb1q30cf6c9hud8nfqna3k7tr3mrjz53dj5znzp4g6" + } + }, + "partial_signatures": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "signature": "304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe16701" + } + ], + "bip32_derivs": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/2", + "descriptor": "[2a704760/44'/0'/0'/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "hex": "0014f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "type": "witness_v0_keyhash", + "address": "tb1q78f7ueuzjgj7hzfve2cpughk5am70cs7yswdaa" + } + }, + "partial_signatures": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "signature": "30440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de81319701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/3", + "descriptor": "[2a704760/44'/0'/0'/1/3]02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 19d65f8328b2206d9970785660ec0d34808fb075", + "hex": "001419d65f8328b2206d9970785660ec0d34808fb075", + "type": "witness_v0_keyhash", + "address": "tb1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4aswx2l" + } + }, + "partial_signatures": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "signature": "304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec8234401" + } + ], + "bip32_derivs": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/4", + "descriptor": "[2a704760/44'/0'/0'/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 12b7954a75efc2a20e86e32dc2d78647d6700779", + "hex": "001412b7954a75efc2a20e86e32dc2d78647d6700779", + "type": "witness_v0_keyhash", + "address": "tb1qz2me2jn4alp2yr5xuvku94uxglt8qpmechkqkr" + } + }, + "partial_signatures": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "signature": "304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b601" + } + ], + "bip32_derivs": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/5", + "descriptor": "[2a704760/44'/0'/0'/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504" + } + ] + }, + { + "witness_utxo": { + "amount": 50000000, + "scriptPubKey": { + "asm": "0 978a90460e44671a52f49a09bb59cc6794b63c89", + "hex": "0014978a90460e44671a52f49a09bb59cc6794b63c89", + "type": "witness_v0_keyhash", + "address": "tb1qj79fq3swg3n355h5ngymkkwvv72tv0yfecpuxz" + } + }, + "partial_signatures": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "signature": "304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c401" + } + ], + "bip32_derivs": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/1/1", + "descriptor": "[9d6b6d86/44'/0'/0'/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 1ce878e3a0da3b34308797fdecb76221f85418af", + "hex": "00141ce878e3a0da3b34308797fdecb76221f85418af", + "type": "witness_v0_keyhash", + "address": "tb1qrn583caqmgangvy8jl77edmzy8u9gx9064r8x8" + } + }, + "partial_signatures": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "signature": "30440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe21501" + } + ], + "bip32_derivs": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/7", + "descriptor": "[2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe" + } + ] + } + ], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/100", + "descriptor": "[2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c" + } + ] + } + ], + "fee": 1158 + } + }, + { + "case": "Detail and Simple (regtest)", + "request": { + "psbt": "cHNidP8BAP2GAQIAAAAH+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////PEJ8leyO15eW8T031SX76GhUXNkaHrDYn/kJgHMfOosHAAAAAP////8DgPD6AgAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5YDw+gIAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVX6kZgAAAAAABYAFCtuFro44oBQD4CyxXBu8C841ZCBAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCIHUR4dvDoKVJUHcXi7Nygne+xHLtv6hBF0SFUug+mYcmAiBbhUOZgPafTibMLQ0x4ztsORgr2TgrdXjSGS9W++fNJwEiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQEfgJaYAAAAAAAWABSL8J1gt+NPNIJ9jbyxx2OQqRbKgiICAidEz7JDbhVgQOwcj+hC2e+aGMtArUHzEnJTkMNfe9NrRzBEAiAWMAlPmBJeuMids3D4FaheSwTYp0EfmoKHt79XFujvwQIgA0ez55j8On/HZzxDZkVU6QpOjvhK9jC5t3uXRArr4WcBIgYCJ0TPskNuFWBA7ByP6ELZ75oYy0CtQfMSclOQw19702sYKnBHYCwAAIAAAACAAAAAgAEAAAACAAAAAAEBH4CWmAAAAAAAFgAU8dPuZ4KSJeuJLMqwHiL2p3fn4h4iAgLn6Nwjb6AkNpQI0s5NhQgEgmGrwpe4EWBNoIetcdE4VUcwRAIgFW2n6NOObTO5AFLZTMz2eOvsKVcfZoRM0d4TUNwsb2ICICrnDSpzucHS+d15HVHrbj6OmN+Ex4slnWL2qhTegTGXASIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgIDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20RHMEQCIA+O5JwYHIi6qsDnl5dtnaEaTezaph80fSrHpf3oYFHMAiAmE/NyMKdHkOnZaXl8DEd+7QfiGWDTlY3YT58qnsgjRAEiBgM9h0vxm2l89sY5ZZVHpYO4ZzAWSlttsBvyChTrm2rbRBgqcEdgLAAAgAAAAIAAAACAAQAAAAQAAAAAAQEfgJaYAAAAAAAWABQSt5VKde/Cog6G4y3C14ZH1nAHeSICAvsGFzDb3jyAa0oXqZ9FTIEoKuzubUb0qXWpTN/ToGUERzBEAiAROQcOrD/7+U+/N0Ydb4w3loRmvsQvdMCAC/RAfzZMtwIgBzMutywN5qqXXemgT/AETbn4HG/eta2bqFm0xM0w17YBIgYC+wYXMNvePIBrShepn0VMgSgq7O5tRvSpdalM39OgZQQYKnBHYCwAAIAAAACAAAAAgAEAAAAFAAAAAAEBH4Dw+gIAAAAAFgAUl4qQRg5EZxpS9JoJu1nMZ5S2PIkiAgPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7EcwRAIgO52xGqSl9m8A4TuPSS+gtRPaKjIH53Ofim0C2DF36rACIEY7ar0+P2cYkWxYOgYfodMZKSQw+EL44sgeQZjjCFDEASIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAABAR+AlpgAAAAAABYAFBzoeOOg2js0MIeX/ey3YiH4VBivIgIDwCMlwyj+1iKp2I+PUxjgUmGjw6lntyEdfWdlfiten75HMEQCIGh6HijcOAquKKktu6C9G5tPSM5jh4mquz554sbSZSWmAiBxYhJVPHQhP3uqJ/deeKeqYykFl+sZ563peckMW+/iFQEiBgPAIyXDKP7WIqnYj49TGOBSYaPDqWe3IR19Z2V+K16fvhgqcEdgLAAAgAAAAIAAAACAAQAAAAcAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgL38NfQAom3xaWBvDUnYEDDSN5I/EFAZ/MeomrZXABVDBgqcEdgLAAAgAAAAIAAAACAAQAAAGQAAAAA", + "network": "regtest", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d5908100000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 962c4e08f336d3afbc3415c9d359ae1040470520", + "hex": "0014962c4e08f336d3afbc3415c9d359ae1040470520", + "type": "witness_v0_keyhash", + "address": "bcrt1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfqur0e37" + } + }, + "partial_signatures": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "signature": "304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd2701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/1", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 8bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "hex": "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "type": "witness_v0_keyhash", + "address": "bcrt1q30cf6c9hud8nfqna3k7tr3mrjz53dj5z3tccln" + } + }, + "partial_signatures": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "signature": "304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe16701" + } + ], + "bip32_derivs": [ + { + "pubkey": "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/2", + "descriptor": "[2a704760/44'/0'/0'/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "hex": "0014f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "type": "witness_v0_keyhash", + "address": "bcrt1q78f7ueuzjgj7hzfve2cpughk5am70cs7xehq25" + } + }, + "partial_signatures": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "signature": "30440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de81319701" + } + ], + "bip32_derivs": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/3", + "descriptor": "[2a704760/44'/0'/0'/1/3]02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 19d65f8328b2206d9970785660ec0d34808fb075", + "hex": "001419d65f8328b2206d9970785660ec0d34808fb075", + "type": "witness_v0_keyhash", + "address": "bcrt1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4lehtak" + } + }, + "partial_signatures": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "signature": "304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec8234401" + } + ], + "bip32_derivs": [ + { + "pubkey": "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/4", + "descriptor": "[2a704760/44'/0'/0'/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 12b7954a75efc2a20e86e32dc2d78647d6700779", + "hex": "001412b7954a75efc2a20e86e32dc2d78647d6700779", + "type": "witness_v0_keyhash", + "address": "bcrt1qz2me2jn4alp2yr5xuvku94uxglt8qpme670dp2" + } + }, + "partial_signatures": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "signature": "304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b601" + } + ], + "bip32_derivs": [ + { + "pubkey": "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/5", + "descriptor": "[2a704760/44'/0'/0'/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504" + } + ] + }, + { + "witness_utxo": { + "amount": 50000000, + "scriptPubKey": { + "asm": "0 978a90460e44671a52f49a09bb59cc6794b63c89", + "hex": "0014978a90460e44671a52f49a09bb59cc6794b63c89", + "type": "witness_v0_keyhash", + "address": "bcrt1qj79fq3swg3n355h5ngymkkwvv72tv0yfm3c33t" + } + }, + "partial_signatures": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "signature": "304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c401" + } + ], + "bip32_derivs": [ + { + "pubkey": "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/1/1", + "descriptor": "[9d6b6d86/44'/0'/0'/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + } + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 1ce878e3a0da3b34308797fdecb76221f85418af", + "hex": "00141ce878e3a0da3b34308797fdecb76221f85418af", + "type": "witness_v0_keyhash", + "address": "bcrt1qrn583caqmgangvy8jl77edmzy8u9gx90cu623w" + } + }, + "partial_signatures": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "signature": "30440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe21501" + } + ], + "bip32_derivs": [ + { + "pubkey": "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/7", + "descriptor": "[2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe" + } + ] + } + ], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/100", + "descriptor": "[2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c" + } + ] + } + ], + "fee": 1158 + } + }, + { + "case": "Detail and Simple (hex string)", + "request": { + "psbt": "70736274ff0100fd86010200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d59081000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae104047052001086b0247304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd27012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca8201086b0247304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe1670121022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b0001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e01086b024730440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de813197012102e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d138550001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb07501086b0247304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec823440121033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb440001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d670077901086b0247304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b6012102fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a065040001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c8901086b0247304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c4012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec0001011f80969800000000001600141ce878e3a0da3b34308797fdecb76221f85418af01086b024730440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe215012103c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe00220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000220202f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c182a7047602c0000800000008000000080010000006400000000", + "network": "mainnet", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d5908100000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 962c4e08f336d3afbc3415c9d359ae1040470520", + "hex": "0014962c4e08f336d3afbc3415c9d359ae1040470520", + "type": "witness_v0_keyhash", + "address": "bc1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq5vd8ay" + } + }, + "final_scriptwitness": [ + "304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd2701", + "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 8bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "hex": "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82", + "type": "witness_v0_keyhash", + "address": "bc1q30cf6c9hud8nfqna3k7tr3mrjz53dj5zey6xnf" + } + }, + "final_scriptwitness": [ + "304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe16701", + "022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b" + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "hex": "0014f1d3ee67829225eb892ccab01e22f6a777e7e21e", + "type": "witness_v0_keyhash", + "address": "bc1q78f7ueuzjgj7hzfve2cpughk5am70cs7wk47xw" + } + }, + "final_scriptwitness": [ + "30440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de81319701", + "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855" + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 19d65f8328b2206d9970785660ec0d34808fb075", + "hex": "001419d65f8328b2206d9970785660ec0d34808fb075", + "type": "witness_v0_keyhash", + "address": "bc1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4hk443v" + } + }, + "final_scriptwitness": [ + "304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec8234401", + "033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44" + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 12b7954a75efc2a20e86e32dc2d78647d6700779", + "hex": "001412b7954a75efc2a20e86e32dc2d78647d6700779", + "type": "witness_v0_keyhash", + "address": "bc1qz2me2jn4alp2yr5xuvku94uxglt8qpmej3dnds" + } + }, + "final_scriptwitness": [ + "304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b601", + "02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504" + ] + }, + { + "witness_utxo": { + "amount": 50000000, + "scriptPubKey": { + "asm": "0 978a90460e44671a52f49a09bb59cc6794b63c89", + "hex": "0014978a90460e44671a52f49a09bb59cc6794b63c89", + "type": "witness_v0_keyhash", + "address": "bc1qj79fq3swg3n355h5ngymkkwvv72tv0yfn760a3" + } + }, + "final_scriptwitness": [ + "304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c401", + "03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec" + ] + }, + { + "witness_utxo": { + "amount": 10000000, + "scriptPubKey": { + "asm": "0 1ce878e3a0da3b34308797fdecb76221f85418af", + "hex": "00141ce878e3a0da3b34308797fdecb76221f85418af", + "type": "witness_v0_keyhash", + "address": "bc1qrn583caqmgangvy8jl77edmzy8u9gx90snc5a5" + } + }, + "final_scriptwitness": [ + "30440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe21501", + "03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe" + ] + } + ], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/100", + "descriptor": "[2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c" + } + ] + } + ], + "fee": 1158 + } + }, + { + "case": "non witness psbt", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 100000000, + "scriptPubKey": { + "asm": "OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL", + "hex": "a914509f5985f4e90a14fb90e39316fdb4f3ac97530787", + "type": "scripthash", + "address": "393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm" + } + }, + "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", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + }, + { + "non_witness_utxo_hex": "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000", + "bip32_derivs": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/1", + "descriptor": "[9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + } + ], + "outputs": [], + "fee": 599995000 + } + }, + { + "case": "final scriptsig", + "request": { + "psbt": "cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQcjIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBCNoEAEcwRAIgIGNZN+BRcNg9wyE62zpurmZxMAjkq8OLSRLFaA3sTwwCIDPAinGjB1ewhGPVMOwFjtdAGWj7ML72PUVJchoOSFuEAUcwRAIgXSdKeIfePvrehKSjScTDb1ibVWI7ECe32m2sicF4VjQCIGoDr+u7tgifHjf6yPmZpAFRYciSAUT9UxEtoFgEzUPMAUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSrgABACIAIDytBhneZ+YkenahAoE2NcBTRXxrpP3krB/9gUjXDkvMAQFHUiECkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnshAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCUq4iAgJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQhida22GLAAAgAAAAIAAAACAAAAAAAwAAAAiAgKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGexgqcEdgLAAAgAAAAIAAAACAAAAAAAwAAAAA", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000001692fd3c241d31618196bc36b6de4f6b889114e420e9bddf9cbbe531e5a544d540000000000ffffffff01104bcd1d000000002200203cad0619de67e6247a76a102813635c053457c6ba4fde4ac1ffd8148d70e4bcc00000000", + "version": 0, + "inputs": [ + { + "witness_utxo": { + "amount": 499996680, + "scriptPubKey": { + "asm": "OP_HASH160 945fb50391a70637c1ffc5ab7fb65308c2f23175 OP_EQUAL", + "hex": "a914945fb50391a70637c1ffc5ab7fb65308c2f2317587", + "type": "scripthash", + "address": "3FDYanAfKNajbBpKqdncDddUTS4yU4Wbqw" + } + }, + "final_scriptsig": { + "asm": "00209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9", + "hex": "2200209c4dacb25ebb8ada8bbb1addb869dea4d8170cc951f1d9694b2154e1583276c9" + }, + "final_scriptwitness": [ + "", + "3044022020635937e05170d83dc3213adb3a6eae66713008e4abc38b4912c5680dec4f0c022033c08a71a30757b08463d530ec058ed7401968fb30bef63d4549721a0e485b8401", + "304402205d274a7887de3efade84a4a349c4c36f589b55623b1027b7da6dac89c178563402206a03afebbbb6089f1e37fac8f999a4015161c8920144fd53112da05804cd43cc01", + "522103a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf32103f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b4752ae" + ] + } + ], + "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", + "descriptor": "[9d6b6d86/44'/0'/0'/0/12]02622c7974ec32de75afa3733b09a6c33d0dea514b29faaccfb0991774f4622242" + }, + { + "pubkey": "02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/12", + "descriptor": "[2a704760/44'/0'/0'/0/12]02906d399f6dbbecc898d4b8de3f497474c86a41f2cf36d71f95e5ca06b074867b" + } + ] + } + ], + "fee": 3320 + } + }, + { + "case": "not fill utxos", + "request": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAAAAAA=", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + "version": 0, + "inputs": [ + {}, + {} + ], + "outputs": [ + {}, + {} + ] + } + }, + { + "case": "global xpub field", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAABPAQSIsh4EpTqP8wAAACyDn7DWbxiH2xZ83FMKuY6HHYsBfryxmFaIdLbJhRY2TgPx52fAVVzgEFsqdtD4sZttM6FH+C91oFxMCVgMOWlP0wy3Zll4AAAAACwAAABPAQSIsh4Gkf5NKYAAACyyaggAhyPMjxmsCLzmNcCH1j1zi2PDPmIYbUPPOlgF8wLpFWYgtbKegnLobx2B+wfWxXxVfLwlIY394zq4zqBrewyuBdu3AAAAACwAAIAN/ANjZmQABmR1bW15MQQBAgMEDfwDY2ZkAAZkdW1teTIBAAAAAA==", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "02000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + "xpubs": [ + { + "xpub": { + "base58": "xpub6EkLrUTiaMiLbMAkbLN2BdH4hWkCQT7fLQf3Q6Ymx3gAqbuFeSKHfTMVDtjcsuRtEFqJbAsjYFZMrqeDLgRSsn4yuQygK44HWPrnA7gZC2C", + "hex": "0488b21e04a53a8ff30000002c839fb0d66f1887db167cdc530ab98e871d8b017ebcb198568874b6c98516364e03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd3" + }, + "master_fingerprint": "b7665978", + "path": "0/44", + "descriptorXpub": "[b7665978/0/44]03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd3" + }, + { + "xpub": { + "base58": "xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX", + "hex": "0488b21e0691fe4d298000002cb26a08008723cc8f19ac08bce635c087d63d738b63c33e62186d43cf3a5805f302e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b" + }, + "master_fingerprint": "ae05dbb7", + "path": "0/44'", + "descriptorXpub": "[ae05dbb7/0/44']02e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b" + } + ], + "version": 0, + "unknown": [ + { + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ], + "inputs": [], + "outputs": [ + {}, + {} + ] + } + }, + { + "case": "global unknown field", + "request": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAAAAAA==", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + "version": 0, + "unknown": [ + { + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ], + "inputs": [ + {}, + {} + ], + "outputs": [ + {}, + {} + ] + } + }, + { + "case": "input unknown field", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAA", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000", + "version": 0, + "inputs": [ + { + "non_witness_utxo_hex": "02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000", + "witness_utxo": { + "amount": 100000000, + "scriptPubKey": { + "asm": "OP_HASH160 509f5985f4e90a14fb90e39316fdb4f3ac975307 OP_EQUAL", + "hex": "a914509f5985f4e90a14fb90e39316fdb4f3ac97530787", + "type": "scripthash", + "address": "393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm" + } + }, + "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", + "descriptor": "[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ], + "unknown": [ + { + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + { + "non_witness_utxo_hex": "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000", + "bip32_derivs": [ + { + "pubkey": "02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/1", + "descriptor": "[9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + } + ], + "outputs": [], + "fee": 599995000 + } + }, + { + "case": "output unknown field", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", + "hasDetail": true, + "hasSimple": true + }, + "expect": { + "tx_hex": "02000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000", + "version": 0, + "inputs": [], + "outputs": [ + { + "bip32_derivs": [ + { + "pubkey": "03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/0/2", + "descriptor": "[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ], + "unknown": [ + { + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + { + "bip32_derivs": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2", + "descriptor": "[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + } + ] + } + }, + { + "case": "invalid network string", + "request": { + "psbt": "cHNidP8BAF4CAAAAAWkv08JB0xYYGWvDa23k9riJEU5CDpvd+cu+Ux5aVE1UAAAAAAD/////ARBLzR0AAAAAIgAgPK0GGd5n5iR6dqECgTY1wFNFfGuk/eSsH/2BSNcOS8wAAAAAAAEBIAhYzR0AAAAAF6kUlF+1A5GnBjfB/8Wrf7ZTCMLyMXWHAQMEAQAAAAEEIgAgnE2ssl67itqLuxrduGnepNgXDMlR8dlpSyFU4VgydskBBUdSIQOlEvX1nA55AfxHjtY1Pu929E+cssGFv7z38Lm7dnFr8yED9NRzYU6VSsT1UY5vfLMwe0+EdD4TftTuy19PtkPCO0dSriIGA6US9fWcDnkB/EeO1jU+73b0T5yywYW/vPfwubt2cWvzGCpwR2AsAACAAAAAgAAAAIAAAAAACwAAACIGA/TUc2FOlUrE9VGOb3yzMHtPhHQ+E37U7stfT7ZDwjtHGJ1rbYYsAACAAAAAgAAAAIAAAAAACwAAAAABACIAIDytBhneZ+YkenahAoE2NcBTRXxrpP3krB/9gUjXDkvMAQFHUiECkG05n2277MiY1LjeP0l0dMhqQfLPNtcfleXKBrB0hnshAmIseXTsMt51r6NzOwmmwz0N6lFLKfqsz7CZF3T0YiJCUq4iAgJiLHl07DLeda+jczsJpsM9DepRSyn6rM+wmRd09GIiQhida22GLAAAgAAAAIAAAACAAAAAAAwAAAAiAgKQbTmfbbvsyJjUuN4/SXR0yGpB8s821x+V5coGsHSGexgqcEdgLAAAgAAAAIAAAACAAAAAAAwAAAAA", + "network": "", + "hasDetail": false, + "hasSimple": false + }, + "error": { + "python": "Error: Invalid network type.", + "cfd": "Invalid network_type passed. network_type must be \"mainnet\" or \"testnet\" or \"regtest\"." + } + } + ] + }, + { + "name": "Psbt.CreatePsbt", + "cases": [ + { + "case": "empty input and output", + "request": { + "version": 2, + "locktime": 0, + "txins": [], + "txouts": [] + }, + "expect": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "hex": "70736274ff01000a0200000000000000000000" + } + }, + { + "case": "single input and empty output. (TxIn:1/TxOut:0)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0 + } + ], + "txouts": [] + }, + "expect": { + "psbt": "cHNidP8BADMCAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AAAAAAAAAA==", + "hex": "70736274ff01003302000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff00000000000000" + } + }, + { + "case": "empty input and single output. (TxIn:0/TxOut:1)", + "request": { + "version": 2, + "locktime": 0, + "txins": [], + "txouts": [ + { + "address": "tb1qckhylutuasz4a9jt2umqzv5087relfzpu5l03zkdl4xcar0598hszlxtpw", + "amount": 0 + } + ] + }, + "expect": { + "psbt": "cHNidP8BADUCAAAAAAEAAAAAAAAAACIAIMWuT/F87AVelktXNgEyjz+Hn6RB5T74is39TY6N9CnvAAAAAAAA", + "hex": "70736274ff0100350200000000010000000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef000000000000" + } + }, + { + "case": "single input and output. (TxIn:1/TxOut:1)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 1 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQEAAAAAAAAAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff0101000000000000002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "single input and output. (TxIn:1/TxOut:1, Maxmum Amount)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 2100000000000000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "multiple input and output. (TxIn:2/TxOut:3)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0 + }, + { + "txid": "d2911125d6ce9fac819206b5f262caafd76c4ce2f5893b4e54320c6c4cd3dd81", + "vout": 0 + }, + { + "txid": "d2911125d6ce9fac819206b5f262caafd76c4ce2f5893b4e54320c6c4cd3dd81", + "vout": 1 + } + ], + "txouts": [ + { + "address": "tb1qckhylutuasz4a9jt2umqzv5087relfzpu5l03zkdl4xcar0598hszlxtpw", + "amount": 2 + }, + { + "address": "tb1qafd8yzxdm77zphvnuy4l980tqzmgcptrs2jsy3rvn3d42jgf2nfq0a5y0a", + "amount": 21000000 + }, + { + "address": "tb1q7w0kyu46ddterr4sglzac38mgaf4dv8jfsf0egumry5yaqqq3fpq9d8w8f", + "amount": 123456789 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAP0GAQIAAAADTN6tpzfbl68zTw+k6HQy1gaHWe6mWjBn0fFKl55aneoAAAAAAP////+B3dNMbAwyVE47ifXiTGzXr8pi8rUGkoGsn87WJRGR0gAAAAAA/////4Hd00xsDDJUTjuJ9eJMbNevymLytQaSgayfztYlEZHSAQAAAAD/////AwIAAAAAAAAAIgAgxa5P8XzsBV6WS1c2ATKPP4efpEHlPviKzf1Njo30Ke9Ab0ABAAAAACIAIOpacgjN37wg3ZPhK/Kd6wC2jAVjgqUCRGycW1VJCVTSFc1bBwAAAAAiACDzn2JyumtXkY6wR8XcRPtHU1aw8kwS/KObGShOgACKQgAAAAAAAAAAAAAA", + "hex": "70736274ff0100fd060102000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030200000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef406f400100000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d215cd5b0700000000220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a420000000000000000000000" + } + }, + { + "case": "multiple input and output. (TxIn:2/TxOut:3, Maxmum Amount)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0 + }, + { + "txid": "d2911125d6ce9fac819206b5f262caafd76c4ce2f5893b4e54320c6c4cd3dd81", + "vout": 0 + }, + { + "txid": "d2911125d6ce9fac819206b5f262caafd76c4ce2f5893b4e54320c6c4cd3dd81", + "vout": 1 + } + ], + "txouts": [ + { + "address": "tb1qckhylutuasz4a9jt2umqzv5087relfzpu5l03zkdl4xcar0598hszlxtpw", + "amount": 10 + }, + { + "address": "tb1qafd8yzxdm77zphvnuy4l980tqzmgcptrs2jsy3rvn3d42jgf2nfq0a5y0a", + "amount": 20 + }, + { + "address": "tb1q7w0kyu46ddterr4sglzac38mgaf4dv8jfsf0egumry5yaqqq3fpq9d8w8f", + "amount": 2099999999999970 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAP0GAQIAAAADTN6tpzfbl68zTw+k6HQy1gaHWe6mWjBn0fFKl55aneoAAAAAAP////+B3dNMbAwyVE47ifXiTGzXr8pi8rUGkoGsn87WJRGR0gAAAAAA/////4Hd00xsDDJUTjuJ9eJMbNevymLytQaSgayfztYlEZHSAQAAAAD/////AwoAAAAAAAAAIgAgxa5P8XzsBV6WS1c2ATKPP4efpEHlPviKzf1Njo30Ke8UAAAAAAAAACIAIOpacgjN37wg3ZPhK/Kd6wC2jAVjgqUCRGycW1VJCVTS4j8HWvB1BwAiACDzn2JyumtXkY6wR8XcRPtHU1aw8kwS/KObGShOgACKQgAAAAAAAAAAAAAA", + "hex": "70736274ff0100fd060102000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030a00000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef1400000000000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d2e23f075af0750700220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a420000000000000000000000" + } + }, + { + "case": "default sequence on unused locktime. (TxIn:1/TxOut:1, Sequence default unused locktime)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0, + "sequence": 4294967295 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 2100000000000000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "default sequence on used locktime. (TxIn:1/TxOut:1, Sequence default used locktime)", + "request": { + "version": 2, + "locktime": 100, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0, + "sequence": 4294967295 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 2100000000000000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD+////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmJkAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000feffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000000000" + } + }, + { + "case": "set sequence value. (TxIn:1/TxOut:1, Set Sequence value)", + "request": { + "version": 2, + "locktime": 100, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0, + "sequence": 4294967293 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 2100000000000000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD9////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmJkAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000fdffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000000000" + } + }, + { + "case": "set sequence value2. (TxIn:1/TxOut:1, Set Sequence value2)", + "request": { + "version": 2, + "locktime": 0, + "txins": [ + { + "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", + "vout": 0, + "sequence": 100 + } + ], + "txouts": [ + { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "amount": 2100000000000000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAABkAAAAAQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea000000000064000000010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "legacy P2PKH. (TxIn:1/TxOut:1)", + "request": { + "version": 1, + "locktime": 0, + "txins": [ + { + "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", + "vout": 0, + "sequence": 4294967295 + } + ], + "txouts": [ + { + "address": "16XqcoS88Nsg523CYLM5Wdf7jvJFn8we7A", + "amount": 4999998000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAZxTyyphGFMKqjRbeZrrfk5QVd5BrFst0s5HQZYkxXtYAAAAAAD/////ATDqBSoBAAAAGXapFDytsQBA6ecAK72dBiD195wFYD/9iKwAAAAAAAAA", + "hex": "70736274ff01005501000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000000000" + } + }, + { + "case": "legacy P2SH. (TxIn:1/TxOut:1)", + "request": { + "version": 1, + "locktime": 0, + "txins": [ + { + "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "sequence": 4294967295 + } + ], + "txouts": [ + { + "address": "129bvPc6TkpM1z13bi6feNUV2DY7XHZrq6", + "amount": 4999998000 + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAeAHAF87leGKex+qddE7mJmfc4HzReOEmkjsHrJMaeSoAAAAAAD/////ATDqBSoBAAAAGXapFAyYmokUsn46hAKZAADAXQgfM3bFiKwAAAAAAAAA", + "hex": "70736274ff0100550100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000000000" + } + }, + { + "case": "invalid txid", + "request": { + "version": 1, + "locktime": 0, + "txins": [ + { + "txid": "a8694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "sequence": 4294967295 + } + ], + "txouts": [ + { + "address": "129bvPc6TkpM1z13bi6feNUV2DY7XHZrq6", + "amount": 4999998000 + } + ] + }, + "error": { + "cfd": "transaction data invalid.", + "python": "Error: Invalid txid." + } + } + ] + }, + { + "name": "Psbt.ConvertToPsbt", + "cases": [ + { + "case": "empty input and output", + "request": { + "tx": "02000000000000000000" + }, + "expect": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "hex": "70736274ff01000a0200000000000000000000" + } + }, + { + "case": "single input and empty output. (TxIn:1/TxOut:0)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff0000000000" + }, + "expect": { + "psbt": "cHNidP8BADMCAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AAAAAAAAAA==", + "hex": "70736274ff01003302000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff00000000000000" + } + }, + { + "case": "empty input and single output. (TxIn:0/TxOut:1)", + "request": { + "tx": "0200000000010000000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef00000000" + }, + "expect": { + "psbt": "cHNidP8BADUCAAAAAAEAAAAAAAAAACIAIMWuT/F87AVelktXNgEyjz+Hn6RB5T74is39TY6N9CnvAAAAAAAA", + "hex": "70736274ff0100350200000000010000000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef000000000000" + } + }, + { + "case": "single input and output. (TxIn:1/TxOut:1)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff0101000000000000002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQEAAAAAAAAAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff0101000000000000002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "single input and output. (TxIn:1/TxOut:1, Maxmum Amount)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "multiple input and output. (TxIn:2/TxOut:3)", + "request": { + "tx": "02000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030200000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef406f400100000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d215cd5b0700000000220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a4200000000" + }, + "expect": { + "psbt": "cHNidP8BAP0GAQIAAAADTN6tpzfbl68zTw+k6HQy1gaHWe6mWjBn0fFKl55aneoAAAAAAP////+B3dNMbAwyVE47ifXiTGzXr8pi8rUGkoGsn87WJRGR0gAAAAAA/////4Hd00xsDDJUTjuJ9eJMbNevymLytQaSgayfztYlEZHSAQAAAAD/////AwIAAAAAAAAAIgAgxa5P8XzsBV6WS1c2ATKPP4efpEHlPviKzf1Njo30Ke9Ab0ABAAAAACIAIOpacgjN37wg3ZPhK/Kd6wC2jAVjgqUCRGycW1VJCVTSFc1bBwAAAAAiACDzn2JyumtXkY6wR8XcRPtHU1aw8kwS/KObGShOgACKQgAAAAAAAAAAAAAA", + "hex": "70736274ff0100fd060102000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030200000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef406f400100000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d215cd5b0700000000220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a420000000000000000000000" + } + }, + { + "case": "multiple input and output. (TxIn:2/TxOut:3, Maxmum Amount)", + "request": { + "tx": "02000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030a00000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef1400000000000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d2e23f075af0750700220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a4200000000" + }, + "expect": { + "psbt": "cHNidP8BAP0GAQIAAAADTN6tpzfbl68zTw+k6HQy1gaHWe6mWjBn0fFKl55aneoAAAAAAP////+B3dNMbAwyVE47ifXiTGzXr8pi8rUGkoGsn87WJRGR0gAAAAAA/////4Hd00xsDDJUTjuJ9eJMbNevymLytQaSgayfztYlEZHSAQAAAAD/////AwoAAAAAAAAAIgAgxa5P8XzsBV6WS1c2ATKPP4efpEHlPviKzf1Njo30Ke8UAAAAAAAAACIAIOpacgjN37wg3ZPhK/Kd6wC2jAVjgqUCRGycW1VJCVTS4j8HWvB1BwAiACDzn2JyumtXkY6wR8XcRPtHU1aw8kwS/KObGShOgACKQgAAAAAAAAAAAAAA", + "hex": "70736274ff0100fd060102000000034cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20000000000ffffffff81ddd34c6c0c32544e3b89f5e24c6cd7afca62f2b5069281ac9fced6251191d20100000000ffffffff030a00000000000000220020c5ae4ff17cec055e964b573601328f3f879fa441e53ef88acdfd4d8e8df429ef1400000000000000220020ea5a7208cddfbc20dd93e12bf29deb00b68c056382a502446c9c5b55490954d2e23f075af0750700220020f39f6272ba6b57918eb047c5dc44fb475356b0f24c12fca39b19284e80008a420000000000000000000000" + } + }, + { + "case": "default sequence on unused locktime. (TxIn:1/TxOut:1, Sequence default unused locktime)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD/////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000ffffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "default sequence on used locktime. (TxIn:1/TxOut:1, Sequence default used locktime)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000feffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD+////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmJkAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000feffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000000000" + } + }, + { + "case": "set sequence value. (TxIn:1/TxOut:1, Set Sequence value)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000fdffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAAD9////AQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmJkAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea0000000000fdffffff010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326264000000000000" + } + }, + { + "case": "set sequence value2. (TxIn:1/TxOut:1, Set Sequence value2)", + "request": { + "tx": "02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea000000000064000000010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000" + }, + "expect": { + "psbt": "cHNidP8BAF4CAAAAAUzerac325evM08PpOh0MtYGh1nuplowZ9HxSpeeWp3qAAAAAABkAAAAAQBAB1rwdQcAIgAgGGMUPBTFFmgEvRkgM1baE2yYVnjNTSehuMYylgSQMmIAAAAAAAAA", + "hex": "70736274ff01005e02000000014cdeada737db97af334f0fa4e87432d6068759eea65a3067d1f14a979e5a9dea000000000064000000010040075af07507002200201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490326200000000000000" + } + }, + { + "case": "legacy P2PKH. (TxIn:1/TxOut:1)", + "request": { + "tx": "01000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000" + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAZxTyyphGFMKqjRbeZrrfk5QVd5BrFst0s5HQZYkxXtYAAAAAAD/////ATDqBSoBAAAAGXapFDytsQBA6ecAK72dBiD195wFYD/9iKwAAAAAAAAA", + "hex": "70736274ff01005501000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000000000" + } + }, + { + "case": "legacy P2SH. (TxIn:1/TxOut:1)", + "request": { + "tx": "0100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000" + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAeAHAF87leGKex+qddE7mJmfc4HzReOEmkjsHrJMaeSoAAAAAAD/////ATDqBSoBAAAAGXapFAyYmokUsn46hAKZAADAXQgfM3bFiKwAAAAAAAAA", + "hex": "70736274ff0100550100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000000000" + } + }, + { + "case": "signed legacy P2SH(permitSigData=true)", + "request": { + "tx": "0100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a800000000d900473044022054b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c60220717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb2601473044022054b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c60220717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb260147522102e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d2103c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c52aeffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000", + "permitSigData": true + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAeAHAF87leGKex+qddE7mJmfc4HzReOEmkjsHrJMaeSoAAAAAAD/////ATDqBSoBAAAAGXapFAyYmokUsn46hAKZAADAXQgfM3bFiKwAAAAAAAAA", + "hex": "70736274ff0100550100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000000000" + } + }, + { + "case": "signed P2SH-P2WPKH(permitSigData=true)", + "request": { + "tx": "010000000001019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b5800000000171600141462eca4b9b8d8df63550abd24d0cb64e8f2d746ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac024730440220773420c0ded41a55b1f1205cfb632f08f3f911a53e7338a0dac73ec6cbe3ca4702201907434d046185abedc5afddc2761a642bccc70af6d22b46394f1d04a8b242260121031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", + "permitSigData": true + }, + "expect": { + "psbt": "cHNidP8BAFUBAAAAAZxTyyphGFMKqjRbeZrrfk5QVd5BrFst0s5HQZYkxXtYAAAAAAD/////ATDqBSoBAAAAGXapFDytsQBA6ecAK72dBiD195wFYD/9iKwAAAAAAAAA", + "hex": "70736274ff01005501000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000000000" + } + }, + { + "case": "signed legacy P2SH(permitSigData=false)", + "request": { + "tx": "0100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a800000000d900473044022054b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c60220717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb2601473044022054b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c60220717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb260147522102e9662b666479ed7117aa76fb96f322a84408d0882707b301c7450098d439680d2103c0230a322f70675bef21097242ac70647798826588e47eca14e5715cef77008c52aeffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000", + "permitSigData": false + }, + "error": { + "message": "tx inputs must not have scriptSigs and witness stacks.", + "python": "Error: tx inputs must not have scriptSigs and witness stacks." + } + }, + { + "case": "signed P2SH-P2WPKH(permitSigData=false)", + "request": { + "tx": "010000000001019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b5800000000171600141462eca4b9b8d8df63550abd24d0cb64e8f2d746ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac024730440220773420c0ded41a55b1f1205cfb632f08f3f911a53e7338a0dac73ec6cbe3ca4702201907434d046185abedc5afddc2761a642bccc70af6d22b46394f1d04a8b242260121031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb00000000", + "permitSigData": false + }, + "error": { + "message": "tx inputs must not have scriptSigs and witness stacks.", + "python": "Error: tx inputs must not have scriptSigs and witness stacks." + } + } + ] + }, + { + "name": "Psbt.JoinPsbts", + "cases": [ + { + "case": "Join input and output", + "request": { + "psbts": [ + "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=" + ] + }, + "expect": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "Join tx only input and output", + "request": { + "psbts": [ + "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA==", + "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA=" + ] + }, + "expect": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAAAAAA=", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000000000000" + } + }, + { + "case": "duplicate input", + "request": { + "psbts": [ + "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=" + ] + }, + "expect": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + } + ] + }, + { + "name": "Psbt.CombinePsbt", + "cases": [ + { + "case": "normal", + "request": { + "psbts": [ + "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", + "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": { + "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", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787220202565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe4730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220202d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + } + ] + }, + { + "name": "Psbt.FinalizePsbtInput", + "cases": [ + { + "case": "P2WPKH x2", + "request": { + "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", + "inputs": [ + { + "txid": "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", + "vout": 1, + "finalScriptsig": "160014962c4e08f336d3afbc3415c9d359ae1040470520", + "final_scriptwitness": [ + "30440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601", + "02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + ] + }, + { + "txid": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "vout": 0, + "finalScriptsig": "473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + }, + "expect": { + "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=", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010717160014962c4e08f336d3afbc3415c9d359ae104047052001086b024730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb866012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac0000000001076a473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c700220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + } + ] + }, + { + "name": "Psbt.FinalizePsbt", + "cases": [ + { + "case": "P2WPKH", + "request": { + "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" + }, + "expect": { + "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=", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010717160014962c4e08f336d3afbc3415c9d359ae104047052001086b024730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb866012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac0000000001076a473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e10012102d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c700220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + } + ] + }, + { + "name": "Psbt.SignPsbt", + "cases": [ + { + "case": "P2WPKH input1", + "request": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "privkey": "KwNwembMPPQpgFfbhb5WPCgENwhnTaNQjf3a6cBuBiZ993Gu5gaR" + }, + "expect": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCICmYYqZ6m0VNbNpf7jSlLZCJv6AkRE2IAxw0qY4pi9vKAiBLR/z1B7gJVBCOA3VnP9vdCExfu5lPbJUXIPLrL2u4ZgEBAwQBAAAAAQQWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAABAL8CAAAAAcbS6jbi6AK1LdrGZdrL7S+DG1JjRZ4cpzT1yUXXUV5AAAAAAGpHMEQCIBG5bH0tDS6NyzcTjhisxGB1KWWt2cuHh99cNJ3w4q5mAiAuk68xtk9RZuVgWBlVXavsV755QwD62zcFLzHd3qmQXAEhA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rs/////wF4Uc0dAAAAABl2qRSNIEQ6kZaeO8oOJAzQ/+TcmMY94oisAAAAACIGAtn2iI8oWhWmoYgKIgLCsjCkLXfPYtpqSKykGYJizaPHGJ1rbYYsAACAAAAAgAAAAIAAAAAAAQAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787220202565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe4730440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "P2WPKH input2", + "request": { + "psbt": "cHNidP8BAJoCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8CAOH1BQAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5QDh9QUAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVUAAAAAAAEA9gIAAAAAAQHxmT/o5xiVQu5FBiWOFwIBviknA80nWssJ7OFmcv2EiwAAAAAXFgAUrJ74CyevHJ2VwdtddhMZMivEL8X/////AggEECQBAAAAFgAUCd4qBDHLs0RPwiytnZoP0JY5chAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwJHMEQCIB4H33IcMyJBno820H7q5HlZdboNnRljDKPNPcDUlnFyAiAVQo574GtlZ1AVOQUL15GjgPALvdvFCX6pe6e+QBcRSgEhAkrvQ7HVrHulAUmY1jzqxYOVnR/cZuommc2E7q+CooMGAAAAAAEBIADh9QUAAAAAF6kUUJ9ZhfTpChT7kOOTFv2086yXUweHAQMEAQAAAAEEFgAUlixOCPM206+8NBXJ01muEEBHBSAiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "privkey": "KyZrkrgAu5EhBbSbt6yMkuL5BexVtdTifCkZQL3HLBZ9wAEhhhMa" + }, + "expect": { + "psbt": "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==", + "hex": "70736274ff01009a0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220202d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7473044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "sign test1", + "request": { + "psbt": "cHNidP8BAJoCAAAAAonHK836UR/Xf9sUxOAteevfAUKhgnmTpQOEZScKiJY0AAAAAAD/////iccrzfpRH9d/2xTE4C15698BQqGCeZOlA4RlJwqIljQBAAAAAP////8CAOH1BQAAAAAWABTlAFRYlFfcUEnai61JHDrKiogZ9bZ9AQAAAAAAFgAURZ6ogiMODt3BGGc806JPGNl8HAkAAAAAAAEBKwDh9QUAAAAAIgAgKMluN4+an+0u5lHgrJHWTfv5f6tSHr9t0qnkuFnDSfMBAwQBAAAAAQVHUiEDSH8HDCIA6gPGSjrkB62Ch3iCtze1U7FTr9HdoXWEYJMhAh75zoF76LZsXxcVigYY74Yv8KXy3s8uUaFcJlPf5LYgUq4iBgIe+c6Be+i2bF8XFYoGGO+GL/Cl8t7PLlGhXCZT3+S2IAwAAAAAAQAAAAEAAAAiBgNIfwcMIgDqA8ZKOuQHrYKHeIK3N7VTsVOv0d2hdYRgkwwAAAAAAAAAAAEAAAAAAQEfkH8BAAAAAAAWABRFnqiCIw4O3cEYZzzTok8Y2XwcCSIGAszyNzrkZUZ0TSh3Y3u1lSNvpyyQT1Qmv3yeJc3F3JVvEAAAAAEBAAAAAAAAAAMAAAAAIgICHvnOgXvotmxfFxWKBhjvhi/wpfLezy5RoVwmU9/ktiAMAAAAAAEAAAABAAAAACICAszyNzrkZUZ0TSh3Y3u1lSNvpyyQT1Qmv3yeJc3F3JVvEAAAAAEBAAAAAAAAAAMAAAAA", + "privkey": "cRb1UKZrqEX49NHt7XKLG8DqTk19Gr4qT7993rC1ocpUoSuXSktw", + "hasGrindR": true + }, + "expect": { + "psbt": "cHNidP8BAJoCAAAAAonHK836UR/Xf9sUxOAteevfAUKhgnmTpQOEZScKiJY0AAAAAAD/////iccrzfpRH9d/2xTE4C15698BQqGCeZOlA4RlJwqIljQBAAAAAP////8CAOH1BQAAAAAWABTlAFRYlFfcUEnai61JHDrKiogZ9bZ9AQAAAAAAFgAURZ6ogiMODt3BGGc806JPGNl8HAkAAAAAAAEBKwDh9QUAAAAAIgAgKMluN4+an+0u5lHgrJHWTfv5f6tSHr9t0qnkuFnDSfMiAgIe+c6Be+i2bF8XFYoGGO+GL/Cl8t7PLlGhXCZT3+S2IEcwRAIgOQQaBdkZDHQz9Zkmn+AxK2DcELRr7XIF8xdrSh3ohY4CICVMkOyb1AbrcthRND9lNRYx5FNpWQDaEVjiM2G7wXC+AQEDBAEAAAABBUdSIQNIfwcMIgDqA8ZKOuQHrYKHeIK3N7VTsVOv0d2hdYRgkyECHvnOgXvotmxfFxWKBhjvhi/wpfLezy5RoVwmU9/ktiBSriIGAh75zoF76LZsXxcVigYY74Yv8KXy3s8uUaFcJlPf5LYgDAAAAAABAAAAAQAAACIGA0h/BwwiAOoDxko65Aetgod4grc3tVOxU6/R3aF1hGCTDAAAAAAAAAAAAQAAAAABAR+QfwEAAAAAABYAFEWeqIIjDg7dwRhnPNOiTxjZfBwJIgYCzPI3OuRlRnRNKHdje7WVI2+nLJBPVCa/fJ4lzcXclW8QAAAAAQEAAAAAAAAAAwAAAAAiAgIe+c6Be+i2bF8XFYoGGO+GL/Cl8t7PLlGhXCZT3+S2IAwAAAAAAQAAAAEAAAAAIgICzPI3OuRlRnRNKHdje7WVI2+nLJBPVCa/fJ4lzcXclW8QAAAAAQEAAAAAAAAAAwAAAAA=", + "hex": "70736274ff01009a020000000289c72bcdfa511fd77fdb14c4e02d79ebdf0142a1827993a5038465270a8896340000000000ffffffff89c72bcdfa511fd77fdb14c4e02d79ebdf0142a1827993a5038465270a8896340100000000ffffffff0200e1f50500000000160014e50054589457dc5049da8bad491c3aca8a8819f5b67d010000000000160014459ea882230e0eddc118673cd3a24f18d97c1c09000000000001012b00e1f5050000000022002028c96e378f9a9fed2ee651e0ac91d64dfbf97fab521ebf6dd2a9e4b859c349f32202021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b620473044022039041a05d9190c7433f599269fe0312b60dc10b46bed7205f3176b4a1de8858e0220254c90ec9bd406eb72d851343f65351631e453695900da1158e23361bbc170be0101030401000000010547522103487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda17584609321021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b62052ae2206021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b6200c000000000100000001000000220603487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda1758460930c0000000000000000010000000001011f907f010000000000160014459ea882230e0eddc118673cd3a24f18d97c1c09220602ccf2373ae46546744d2877637bb595236fa72c904f5426bf7c9e25cdc5dc956f1000000001010000000000000003000000002202021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b6200c00000000010000000100000000220202ccf2373ae46546744d2877637bb595236fa72c904f5426bf7c9e25cdc5dc956f100000000101000000000000000300000000" + } + } + ] + }, + { + "name": "Psbt.VerifyPsbtSign", + "cases": [ + { + "case": "Success All", + "request": { + "psbt": "70736274ff0100fd86010200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d59081000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae104047052001086b0247304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd27012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca8201086b0247304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe1670121022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b0001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e01086b024730440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de813197012102e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d138550001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb07501086b0247304402200f8ee49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec823440121033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb440001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d670077901086b0247304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b6012102fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a065040001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c8901086b0247304402203b9db11aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c4012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec0001011f80969800000000001600141ce878e3a0da3b34308797fdecb76221f85418af01086b024730440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe215012103c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe00220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000220202f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c182a7047602c0000800000008000000080010000006400000000" + }, + "expect": { + "success": true + } + }, + { + "case": "Fail", + "request": { + "psbt": "70736274ff0100fd86010200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d59081000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae104047052001086b0247304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd27012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca8201086b0247304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe1670121022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b0001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e01086b024730440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de813197012102e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d138550001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb07501086b0247304402200f8ef49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec823440121033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb440001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d670077901086b0247304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b6012102fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a065040001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c8901086b0247304402203b9db13aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c4012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec0001011f80969800000000001600141ce878e3a0da3b34308797fdecb76221f85418af01086b024730440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe215012103c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe00220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000220202f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c182a7047602c0000800000008000000080010000006400000000" + }, + "expect": { + "success": false, + "failTxins": [ + { + "txid": "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", + "vout": 4, + "reason": "Verify signature fail." + }, + { + "txid": "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", + "vout": 1, + "reason": "Verify signature fail." + } + ] + } + }, + { + "case": "Success (selected txin)", + "request": { + "psbt": "70736274ff0100fd86010200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555fa919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d59081000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae104047052001086b0247304402207511e1dbc3a0a5495077178bb3728277bec472edbfa84117448552e83e99872602205b85439980f69f4e26cc2d0d31e33b6c39182bd9382b7578d2192f56fbe7cd27012102565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe0001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca8201086b0247304402201630094f98125eb8c89db370f815a85e4b04d8a7411f9a8287b7bf5716e8efc102200347b3e798fc3a7fc7673c43664554e90a4e8ef84af630b9b77b97440aebe1670121022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b0001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e01086b024730440220156da7e8d38e6d33b90052d94cccf678ebec29571f66844cd1de1350dc2c6f6202202ae70d2a73b9c1d2f9dd791d51eb6e3e8e98df84c78b259d62f6aa14de813197012102e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d138550001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb07501086b0247304402200f8ef49c181c88baaac0e797976d9da11a4decdaa61f347d2ac7a5fde86051cc02202613f37230a74790e9d969797c0c477eed07e21960d3958dd84f9f2a9ec823440121033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb440001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d670077901086b0247304402201139070eac3ffbf94fbf37461d6f8c37968466bec42f74c0800bf4407f364cb7022007332eb72c0de6aa975de9a04ff0044db9f81c6fdeb5ad9ba859b4c4cd30d7b6012102fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a065040001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c8901086b0247304402203b9db13aa4a5f66f00e13b8f492fa0b513da2a3207e7739f8a6d02d83177eab00220463b6abd3e3f6718916c583a061fa1d319292430f842f8e2c81e4198e30850c4012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec0001011f80969800000000001600141ce878e3a0da3b34308797fdecb76221f85418af01086b024730440220687a1e28dc380aae28a92dbba0bd1b9b4f48ce638789aabb3e79e2c6d26525a60220716212553c74213f7baa27f75e78a7aa63290597eb19e7ade979c90c5befe215012103c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe00220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000220202f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c182a7047602c0000800000008000000080010000006400000000", + "outPointList": [ + { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1 + }, + { + "txid": "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", + "vout": 2 + }, + { + "txid": "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", + "vout": 3 + } + ] + }, + "expect": { + "success": true + } + }, + { + "case": "not finalized", + "request": { + "psbt": "cHNidP8BAP2GAQIAAAAH+DTafLXhg/yBX3hGInKEZg+koKWDviv7SlNb9CXlMd4BAAAAAP////9a72o7znYklRrluejAkI6P90ch7t96CZTVvx78fe6CgQIAAAAA/////6kt1klSeJ79FESTjpzlsQbnKACJxNxKeLPafHoradk/AwAAAAD//////Pcse7iIGUWVXkj1TmCBtRkBg2SE5cSKmSUGECnEBdEEAAAAAP////+gvhpNmiLIMtLSCjoITwENCamrQfs+Le29RusBBFePuQUAAAAA/////3GhQb9bZT4sQ3czBdaajJh2lERwyh4p5LA6CVPEvraVAQAAAAD/////PEJ8leyO15eW8T031SX76GhUXNkaHrDYn/kJgHMfOosHAAAAAP////8DgPD6AgAAAAAWABSzIr3c5jO4UaxzcKtFTws2egZU5YDw+gIAAAAAFgAUyrjFOm6PwCltHNORWjB9UcSRpVX6kZgAAAAAABYAFCtuFro44oBQD4CyxXBu8C841ZCBAAAAAAABAR+AlpgAAAAAABYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgICVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv5HMEQCIHUR4dvDoKVJUHcXi7Nygne+xHLtv6hBF0SFUug+mYcmAiBbhUOZgPafTibMLQ0x4ztsORgr2TgrdXjSGS9W++fNJwEiBgJWUkhGCzwYbezxPbBvByT9ULR8w+SJwwB2yDY9UDjO/hgqcEdgLAAAgAAAAIAAAACAAQAAAAEAAAAAAQEfgJaYAAAAAAAWABSL8J1gt+NPNIJ9jbyxx2OQqRbKgiICAidEz7JDbhVgQOwcj+hC2e+aGMtArUHzEnJTkMNfe9NrRzBEAiAWMAlPmBJeuMids3D4FaheSwTYp0EfmoKHt79XFujvwQIgA0ez55j8On/HZzxDZkVU6QpOjvhK9jC5t3uXRArr4WcBIgYCJ0TPskNuFWBA7ByP6ELZ75oYy0CtQfMSclOQw19702sYKnBHYCwAAIAAAACAAAAAgAEAAAACAAAAAAEBH4CWmAAAAAAAFgAU8dPuZ4KSJeuJLMqwHiL2p3fn4h4iAgLn6Nwjb6AkNpQI0s5NhQgEgmGrwpe4EWBNoIetcdE4VUcwRAIgFW2n6NOObTO5AFLZTMz2eOvsKVcfZoRM0d4TUNwsb2ICICrnDSpzucHS+d15HVHrbj6OmN+Ex4slnWL2qhTegTGXASIGAufo3CNvoCQ2lAjSzk2FCASCYavCl7gRYE2gh61x0ThVGCpwR2AsAACAAAAAgAAAAIABAAAAAwAAAAABAR+AlpgAAAAAABYAFBnWX4MosiBtmXB4VmDsDTSAj7B1IgIDPYdL8ZtpfPbGOWWVR6WDuGcwFkpbbbAb8goU65tq20RHMEQCIA+O5JwYHIi6qsDnl5dtnaEaTezaph80fSrHpf3oYFHMAiAmE/NyMKdHkOnZaXl8DEd+7QfiGWDTlY3YT58qnsgjRAEiBgM9h0vxm2l89sY5ZZVHpYO4ZzAWSlttsBvyChTrm2rbRBgqcEdgLAAAgAAAAIAAAACAAQAAAAQAAAAAAQEfgJaYAAAAAAAWABQSt5VKde/Cog6G4y3C14ZH1nAHeSICAvsGFzDb3jyAa0oXqZ9FTIEoKuzubUb0qXWpTN/ToGUERzBEAiAROQcOrD/7+U+/N0Ydb4w3loRmvsQvdMCAC/RAfzZMtwIgBzMutywN5qqXXemgT/AETbn4HG/eta2bqFm0xM0w17YBIgYC+wYXMNvePIBrShepn0VMgSgq7O5tRvSpdalM39OgZQQYKnBHYCwAAIAAAACAAAAAgAEAAAAFAAAAAAEBH4Dw+gIAAAAAFgAUl4qQRg5EZxpS9JoJu1nMZ5S2PIkiAgPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7EcwRAIgO52xGqSl9m8A4TuPSS+gtRPaKjIH53Ofim0C2DF36rACIEY7ar0+P2cYkWxYOgYfodMZKSQw+EL44sgeQZjjCFDEASIGA+PSRKOWfguHdl/ahsX/OIhfdJk5U7lYQ4iu8wsmr2rsGJ1rbYYsAACAAAAAgAAAAIABAAAAAQAAAAABAR+AlpgAAAAAABYAFBzoeOOg2js0MIeX/ey3YiH4VBivIgIDwCMlwyj+1iKp2I+PUxjgUmGjw6lntyEdfWdlfiten75HMEQCIGh6HijcOAquKKktu6C9G5tPSM5jh4mquz554sbSZSWmAiBxYhJVPHQhP3uqJ/deeKeqYykFl+sZ563peckMW+/iFQEiBgPAIyXDKP7WIqnYj49TGOBSYaPDqWe3IR19Z2V+K16fvhgqcEdgLAAAgAAAAIAAAACAAQAAAAcAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAAiAgL38NfQAom3xaWBvDUnYEDDSN5I/EFAZ/MeomrZXABVDBgqcEdgLAAAgAAAAIAAAACAAQAAAGQAAAAA", + "outPointList": [ + { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1 + }, + { + "txid": "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", + "vout": 2 + } + ] + }, + "expect": { + "success": false, + "failTxins": [ + { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1, + "reason": "psbt txin not finalized yet." + }, + { + "txid": "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", + "vout": 2, + "reason": "psbt txin not finalized yet." + } + ] + } + } + ] + }, + { + "name": "Psbt.AddPsbtData", + "cases": [ + { + "case": "add single input", + "request": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "inputs": [ + { + "txin": { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "address": "bc1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq5vd8ay" + }, + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BADMCAAAAAfg02ny14YP8gV94RiJyhGYPpKClg74r+0pTW/Ql5THeAQAAAAD/////AAAAAAAAAQEfgJaYAAAAAAAWABSWLE4I8zbTr7w0FcnTWa4QQEcFICIGAlZSSEYLPBht7PE9sG8HJP1QtHzD5InDAHbINj1QOM7+GCpwR2AsAACAAAAAgAAAAIABAAAAAQAAAAA=", + "hex": "70736274ff0100330200000001f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff00000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c0000800000008000000080010000000100000000" + } + }, + { + "case": "add single output", + "request": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "outputs": [ + { + "txout": { + "amount": 50000000, + "address": "bc1qkv3tmh8xxwu9rtrnwz452nctxeaqv489dcscvw" + }, + "output": { + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BACkCAAAAAAGA8PoCAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAAAAAAAiAgNHO/yMdwwbIgoueq5LrfbA1+rykCjVsp00OAErsonvgRgqcEdgLAAAgAAAAIAAAACAAAAAAAIAAAAA", + "hex": "70736274ff01002902000000000180f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e50000000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c0000800000008000000080000000000200000000" + } + }, + { + "case": "add multi input/output", + "request": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "inputs": [ + { + "txin": { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "address": "bc1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq5vd8ay" + }, + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ] + } + }, + { + "txin": { + "txid": "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", + "vout": 2, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "directLockingScript": "00148bf09d60b7e34f34827d8dbcb1c76390a916ca82" + }, + "bip32Derives": [ + { + "descriptor": "wpkh([2a704760/44h/0h/0h/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b)" + } + ] + } + }, + { + "txin": { + "txid": "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", + "vout": 3, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "address": "bc1q78f7ueuzjgj7hzfve2cpughk5am70cs7wk47xw" + }, + "bip32Derives": [ + { + "pubkey": "02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855", + "master_fingerprint": "2a704760", + "path": "44'/0'/0'/1/3" + } + ] + } + }, + { + "txin": { + "txid": "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", + "vout": 4, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "address": "bc1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4hk443v" + }, + "bip32Derives": [ + { + "descriptor": "wpkh([2a704760/44h/0h/0h/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44)" + } + ] + } + }, + { + "txin": { + "txid": "b98f570401eb46bded2d3efb41aba9090d014f083a0ad2d232c8229a4d1abea0", + "vout": 5, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 10000000, + "address": "bc1qz2me2jn4alp2yr5xuvku94uxglt8qpmej3dnds" + }, + "bip32Derives": [ + { + "descriptor": "wpkh([2a704760/44h/0h/0h/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504)" + } + ] + } + }, + { + "txin": { + "txid": "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", + "vout": 1, + "sequence": 4294967295 + }, + "input": { + "witnessUtxo": { + "amount": 50000000, + "address": "bc1qj79fq3swg3n355h5ngymkkwvv72tv0yfn760a3" + }, + "bip32Derives": [ + { + "descriptor": "wpkh([9d6b6d86/44h/0h/0h/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec)" + } + ] + } + } + ], + "outputs": [ + { + "txout": { + "amount": 50000000, + "address": "bc1qkv3tmh8xxwu9rtrnwz452nctxeaqv489dcscvw" + }, + "output": { + "bip32Derives": [ + { + "descriptor": "wpkh([2a704760/44h/0h/0h/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81)" + } + ] + } + }, + { + "txout": { + "amount": 50000000, + "directLockingScript": "0014cab8c53a6e8fc0296d1cd3915a307d51c491a555" + }, + "output": { + "bip32Derives": [ + { + "pubkey": "036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c", + "master_fingerprint": "9d6b6d86", + "path": "44'/0'/0'/0/2" + } + ] + } + } + ] + }, + "expect": { + "psbt": "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==", + "hex": "70736274ff0100fd3e010200000006f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff0280f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca822206022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b182a7047602c000080000000800000008001000000020000000001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e220602e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855182a7047602c000080000000800000008001000000030000000001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb0752206033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44182a7047602c000080000000800000008001000000040000000001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d6700779220602fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504182a7047602c000080000000800000008001000000050000000001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c89220603e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec189d6b6d862c0000800000008000000080010000000100000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "add output script", + "request": { + "psbt": "cHNidP8BAAoCAAAAAAAAAAAAAA==", + "outputs": [ + { + "txout": { + "address": "bcrt1q9rykudu0n2076thx28s2eywkfhaljlat2g0t7mwj48jtskwrf8es8aajgd", + "amount": 100000000 + }, + "output": { + "redeemScript": "522103487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda17584609321021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b62052ae", + "bip32Derives": [ + { + "pubkey": "03487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda175846093", + "master_fingerprint": "00000000", + "path": "0/1" + }, + { + "pubkey": "021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b620", + "master_fingerprint": "00000000", + "path": "1/1" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BADUCAAAAAAEA4fUFAAAAACIAICjJbjePmp/tLuZR4KyR1k37+X+rUh6/bdKp5LhZw0nzAAAAAAABAUdSIQNIfwcMIgDqA8ZKOuQHrYKHeIK3N7VTsVOv0d2hdYRgkyECHvnOgXvotmxfFxWKBhjvhi/wpfLezy5RoVwmU9/ktiBSriICAh75zoF76LZsXxcVigYY74Yv8KXy3s8uUaFcJlPf5LYgDAAAAAABAAAAAQAAACICA0h/BwwiAOoDxko65Aetgod4grc3tVOxU6/R3aF1hGCTDAAAAAAAAAAAAQAAAAA=", + "hex": "70736274ff01003502000000000100e1f5050000000022002028c96e378f9a9fed2ee651e0ac91d64dfbf97fab521ebf6dd2a9e4b859c349f30000000000010147522103487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda17584609321021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b62052ae2202021ef9ce817be8b66c5f17158a0618ef862ff0a5f2decf2e51a15c2653dfe4b6200c000000000100000001000000220203487f070c2200ea03c64a3ae407ad82877882b737b553b153afd1dda1758460930c00000000000000000100000000" + } + } + ] + }, + { + "name": "Psbt.SetPsbtData", + "cases": [ + { + "case": "set input", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA==", + "inputs": [ + { + "index": 0, + "input": { + "utxoFullTx": "02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000", + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ], + "sighash": "ALL" + } + }, + { + "index": 1, + "input": { + "utxoFullTx": "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000", + "bip32Derives": [ + { + "descriptor": "[9d6b6d86/44h/0h/0h/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "hex": "70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000" + } + }, + { + "case": "set input witness utxo", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAAAAA==", + "inputs": [ + { + "index": 0, + "input": { + "witnessUtxo": { + "amount": 100000000, + "address": "393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm" + }, + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe" + } + ], + "sighash": "ALL" + } + }, + { + "index": 1, + "input": { + "utxoFullTx": "0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000", + "bip32Derives": [ + { + "descriptor": "[9d6b6d86/44h/0h/0h/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "hex": "70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff00000000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000" + } + }, + { + "case": "set output", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA=", + "outputs": [ + { + "index": 0, + "output": { + "bip32Derives": [ + { + "descriptor": "[2a704760/44h/0h/0h/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81" + } + ] + } + }, + { + "index": 1, + "output": { + "bip32Derives": [ + { + "descriptor": "[9d6b6d86/44h/0h/0h/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c" + } + ] + } + } + ] + }, + "expect": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "hex": "70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a5550000000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "set global", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA=", + "global": { + "xpubs": [ + { + "descriptorXpub": "[b7665978/0/44]xpub6EkLrUTiaMiLbMAkbLN2BdH4hWkCQT7fLQf3Q6Ymx3gAqbuFeSKHfTMVDtjcsuRtEFqJbAsjYFZMrqeDLgRSsn4yuQygK44HWPrnA7gZC2C" + }, + { + "descriptorXpub": "[ae05dbb7/0/44']xpub6JNQxQDHv2vcUQiXjggbaGYZg3nmxX6ojMcJPSs4KfLSLnMBCg8VbJUh5n4to2SwLWXdSXnHBkUQx1fVnJ9oKYjPPYAQehjWRpx6ErQyykX" + } + ], + "unknown": [ + { + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + } + }, + "expect": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAABPAQSIsh4EpTqP8wAAACyDn7DWbxiH2xZ83FMKuY6HHYsBfryxmFaIdLbJhRY2TgPx52fAVVzgEFsqdtD4sZttM6FH+C91oFxMCVgMOWlP0wy3Zll4AAAAACwAAABPAQSIsh4Gkf5NKYAAACyyaggAhyPMjxmsCLzmNcCH1j1zi2PDPmIYbUPPOlgF8wLpFWYgtbKegnLobx2B+wfWxXxVfLwlIY394zq4zqBrewyuBdu3AAAAACwAAIAN/ANjZmQABmR1bW15MQQBAgMEDfwDY2ZkAAZkdW1teTIBAAAAAA==", + "hex": "70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000004f010488b21e04a53a8ff30000002c839fb0d66f1887db167cdc530ab98e871d8b017ebcb198568874b6c98516364e03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd30cb7665978000000002c0000004f010488b21e0691fe4d298000002cb26a08008723cc8f19ac08bce635c087d63d738b63c33e62186d43cf3a5805f302e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b0cae05dbb7000000002c0000800dfc03636664000664756d6d793104010203040dfc03636664000664756d6d79320100000000" + } + } + ] + }, + { + "name": "Psbt.SetPsbtRecord", + "cases": [ + { + "case": "input data", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "records": [ + { + "type": "input", + "index": 0, + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "type": "input", + "index": 0, + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAAQC/AgAAAAHG0uo24ugCtS3axmXay+0vgxtSY0WeHKc09clF11FeQAAAAABqRzBEAiARuWx9LQ0ujcs3E44YrMRgdSllrdnLh4ffXDSd8OKuZgIgLpOvMbZPUWblYFgZVV2r7Fe+eUMA+ts3BS8x3d6pkFwBIQPj0kSjln4Lh3Zf2obF/ziIX3SZOVO5WEOIrvMLJq9q7P////8BeFHNHQAAAAAZdqkUjSBEOpGWnjvKDiQM0P/k3JjGPeKIrAAAAAAiBgLZ9oiPKFoVpqGICiICwrIwpC13z2LaakispBmCYs2jxxida22GLAAAgAAAAIAAAACAAAAAAAEAAAAA", + "hex": "70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000dfc03636664000664756d6d793104010203040dfc03636664000664756d6d79320100000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c0000800000008000000080000000000100000000" + } + }, + { + "case": "input data (index=1)", + "request": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAAAA==", + "records": [ + { + "type": "input", + "index": 1, + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "type": "input", + "index": 1, + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + "expect": { + "psbt": "cHNidP8BAFwCAAAAAiZ//Xbq5rbBP/uGzquqJNQkhVICM8LDgF4K12RwlXjAAQAAAAD/////fSVGKkL/LLG6VAHliTJZ2zykvPXParlxXTUWPTHJ7MAAAAAAAP////8AAAAAAAABAPYCAAAAAAEB8Zk/6OcYlULuRQYljhcCAb4pJwPNJ1rLCezhZnL9hIsAAAAAFxYAFKye+AsnrxydlcHbXXYTGTIrxC/F/////wIIBBAkAQAAABYAFAneKgQxy7NET8IsrZ2aD9CWOXIQAOH1BQAAAAAXqRRQn1mF9OkKFPuQ45MW/bTzrJdTB4cCRzBEAiAeB99yHDMiQZ6PNtB+6uR5WXW6DZ0ZYwyjzT3A1JZxcgIgFUKOe+BrZWdQFTkFC9eRo4DwC73bxQl+qXunvkAXEUoBIQJK70Ox1ax7pQFJmNY86sWDlZ0f3GbqJpnNhO6vgqKDBgAAAAABASAA4fUFAAAAABepFFCfWYX06QoU+5Djkxb9tPOsl1MHhwEDBAEAAAABBBYAFJYsTgjzNtOvvDQVydNZrhBARwUgIgYCVlJIRgs8GG3s8T2wbwck/VC0fMPkicMAdsg2PVA4zv4YKnBHYCwAAIAAAACAAAAAgAEAAAABAAAAAAEAvwIAAAABxtLqNuLoArUt2sZl2svtL4MbUmNFnhynNPXJRddRXkAAAAAAakcwRAIgEblsfS0NLo3LNxOOGKzEYHUpZa3Zy4eH31w0nfDirmYCIC6TrzG2T1Fm5WBYGVVdq+xXvnlDAPrbNwUvMd3eqZBcASED49JEo5Z+C4d2X9qGxf84iF90mTlTuVhDiK7zCyavauz/////AXhRzR0AAAAAGXapFI0gRDqRlp47yg4kDND/5NyYxj3iiKwAAAAAIgYC2faIjyhaFaahiAoiAsKyMKQtd89i2mpIrKQZgmLNo8cYnWtthiwAAIAAAACAAAAAgAAAAAABAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAA", + "hex": "70736274ff01005c0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0000000000000100f602000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a283060000000001012000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac97530787010304010000000104160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c00008000000080000000800100000001000000000100bf0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000220602d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7189d6b6d862c000080000000800000008000000000010000000dfc03636664000664756d6d793104010203040dfc03636664000664756d6d7932010000" + } + }, + { + "case": "output data", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAAACICA2R0r/JjPDUYZVOftStiudb7nk4jV2Yo4fCgp5k0WOBsGJ1rbYYsAACAAAAAgAAAAIAAAAAAAgAAAAA=", + "records": [ + { + "type": "output", + "index": 0, + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "type": "output", + "index": 0, + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + "expect": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAIgIDRzv8jHcMGyIKLnquS632wNfq8pAo1bKdNDgBK7KJ74EYKnBHYCwAAIAAAACAAAAAgAAAAAACAAAADfwDY2ZkAAZkdW1teTEEAQIDBA38A2NmZAAGZHVtbXkyAQAAIgIDZHSv8mM8NRhlU5+1K2K51vueTiNXZijh8KCnmTRY4GwYnWtthiwAAIAAAACAAAAAgAAAAAACAAAAAA==", + "hex": "70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a5550000000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c000080000000800000008000000000020000000dfc03636664000664756d6d793104010203040dfc03636664000664756d6d79320100002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000" + } + }, + { + "case": "global data", + "request": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAAAAAAA=", + "records": [ + { + "type": "global", + "key": "010488b21e04a53a8ff30000002c839fb0d66f1887db167cdc530ab98e871d8b017ebcb198568874b6c98516364e03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd3", + "value": "b7665978000000002c000000" + }, + { + "type": "global", + "key": "010488b21e0691fe4d298000002cb26a08008723cc8f19ac08bce635c087d63d738b63c33e62186d43cf3a5805f302e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b", + "value": "ae05dbb7000000002c000080" + }, + { + "type": "global", + "key": "fc03636664000664756d6d7931", + "value": "01020304" + }, + { + "type": "global", + "key": "fc03636664000664756d6d7932", + "value": "00" + } + ] + }, + "expect": { + "psbt": "cHNidP8BAEgCAAAAAAIA4fUFAAAAABYAFLMivdzmM7hRrHNwq0VPCzZ6BlTlAOH1BQAAAAAWABTKuMU6bo/AKW0c05FaMH1RxJGlVQAAAABPAQSIsh4EpTqP8wAAACyDn7DWbxiH2xZ83FMKuY6HHYsBfryxmFaIdLbJhRY2TgPx52fAVVzgEFsqdtD4sZttM6FH+C91oFxMCVgMOWlP0wy3Zll4AAAAACwAAABPAQSIsh4Gkf5NKYAAACyyaggAhyPMjxmsCLzmNcCH1j1zi2PDPmIYbUPPOlgF8wLpFWYgtbKegnLobx2B+wfWxXxVfLwlIY394zq4zqBrewyuBdu3AAAAACwAAIAN/ANjZmQABmR1bW15MQQBAgMEDfwDY2ZkAAZkdW1teTIBAAAAAA==", + "hex": "70736274ff01004802000000000200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555000000004f010488b21e04a53a8ff30000002c839fb0d66f1887db167cdc530ab98e871d8b017ebcb198568874b6c98516364e03f1e767c0555ce0105b2a76d0f8b19b6d33a147f82f75a05c4c09580c39694fd30cb7665978000000002c0000004f010488b21e0691fe4d298000002cb26a08008723cc8f19ac08bce635c087d63d738b63c33e62186d43cf3a5805f302e9156620b5b29e8272e86f1d81fb07d6c57c557cbc25218dfde33ab8cea06b7b0cae05dbb7000000002c0000800dfc03636664000664756d6d793104010203040dfc03636664000664756d6d79320100000000" + } + } + ] + }, + { + "name": "Psbt.IsFinalizedPsbt", + "cases": [ + { + "case": "finalized", + "request": { + "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=" + }, + "expect": { + "success": true, + "finalizedAll": true + } + }, + { + "case": "not finalized", + "request": { + "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==" + }, + "expect": { + "success": false, + "finalizedAll": false, + "failInputs": [ + { + "txid": "544d545a1e53becbf9dd9b0e424e1189b8f6e46d6bc36b191816d341c2d32f69", + "vout": 0 + } + ] + } + }, + { + "case": "finalized (set target)", + "request": { + "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=", + "outPointList": [ + { + "txid": "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", + "vout": 1 + } + ] + }, + "expect": { + "success": true, + "finalizedAll": true + } + } + ] + }, + { + "name": "Psbt.GetPsbtUtxos", + "cases": [ + { + "case": "p2wpkh", + "request": { + "psbt": "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==" + }, + "expect": { + "utxos": [ + { + "txid": "de31e525f45b534afb2bbe83a5a0a40f6684722246785f81fc83e1b57cda34f8", + "vout": 1, + "address": "bc1qjckyuz8nxmf6l0p5zhyaxkdwzpqywpfq5vd8ay", + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe)" + }, + { + "txid": "8182ee7dfc1ebfd594097adfee2147f78f8e90c0e8b9e51a952476ce3b6aef5a", + "vout": 2, + "address": "bc1q30cf6c9hud8nfqna3k7tr3mrjz53dj5zey6xnf", + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/2]022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b)" + }, + { + "txid": "3fd9692b7a7cdab3784adcc4890028e706b1e59c8e934414fd9e785249d62da9", + "vout": 3, + "address": "bc1q78f7ueuzjgj7hzfve2cpughk5am70cs7wk47xw", + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/3]02e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855)" + }, + { + "txid": "d105c429100625998ac4e58464830119b581604ef5485e95451988b87b2cf7fc", + "vout": 4, + "address": "bc1qr8t9lqegkgsxmxts0ptxpmqdxjqglvr4hk443v", + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/4]033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44)" + }, + { + "txid": "b98f570401eb46bded2d3efb41aba9090d014f083a0ad2d232c8229a4d1abea0", + "vout": 5, + "address": "bc1qz2me2jn4alp2yr5xuvku94uxglt8qpmej3dnds", + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/5]02fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504)" + }, + { + "txid": "95b6bec453093ab0e4291eca70449476988c9ad6053377432c3e655bbf41a171", + "vout": 1, + "address": "bc1qj79fq3swg3n355h5ngymkkwvv72tv0yfn760a3", + "amount": 50000000, + "descriptor": "wpkh([9d6b6d86/44'/0'/0'/1/1]03e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec)" + } + ] + } + }, + { + "case": "p2wpkh & p2pkh", + "request": { + "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" + }, + "expect": { + "utxos": [ + { + "txid": "c078957064d70a5e80c3c23302528524d424aaabce86fb3fc1b6e6ea76fd7f26", + "vout": 1, + "address": "393JshdyfRGe4Z4zvjvYYFPSUbrFvJy5tm", + "amount": 100000000, + "descriptor": "sh(wpkh([2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe))" + }, + { + "txid": "c0ecc9313d16355d71b96acff5bca43cdb593289e50154bab12cff422a46257d", + "vout": 0, + "address": "1DsCvxydk2JqbEj1EqL6mXcEvStsKQvKbx", + "amount": 499995000, + "descriptor": "pkh([9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7)" + } + ] + } + }, + { + "case": "multisig", + "request": { + "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==" + }, + "expect": { + "utxos": [ + { + "txid": "544d545a1e53becbf9dd9b0e424e1189b8f6e46d6bc36b191816d341c2d32f69", + "vout": 0, + "address": "3FDYanAfKNajbBpKqdncDddUTS4yU4Wbqw", + "amount": 499996680, + "descriptor": "sh(wsh(multi(2,[2a704760/44'/0'/0'/0/11]03a512f5f59c0e7901fc478ed6353eef76f44f9cb2c185bfbcf7f0b9bb76716bf3,[9d6b6d86/44'/0'/0'/0/11]03f4d473614e954ac4f5518e6f7cb3307b4f84743e137ed4eecb5f4fb643c23b47)))" + } + ] + } + } + ] + }, + { + "name": "Psbt.FundPsbt", + "cases": [ + { + "case": "normal", + "request": { + "psbt": "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==", + "utxos": [ + { + "txid": "8b3a1f738009f99fd8b01e1ad95c5468e8fb25d5373df19697d78eec957c423c", + "vout": 7, + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/7]03c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe)" + }, + { + "txid": "0e5b0e16148da878ee8d9f0524ac0962a22dbbb66e2dc8a6237a1d4c8c4ea9cb", + "vout": 8, + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/8]02f505e0b5885959bd2f7e68dd73915940a5540e05c41a42f6c598ceb21bdc55f3)" + }, + { + "txid": "016b36a0a9df54d55f7b907aba8fdd3d90d797eee2bc46c5ed7dced41218e674", + "vout": 9, + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/9]02f7af7e1c3c8627e0e662ba04ec003879d9aeaeba5a02a1e24804ef4d7b497db4)" + }, + { + "txid": "dcc65ebfb8535f96f8a51e07d5c0a94f965000bd5765159a3cc27e4edd658c69", + "vout": 10, + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/10]03c04e76f9bc7d6433a6eb7c0c3c446213c5f361dae22929483f23718e1cee4ece)" + }, + { + "txid": "0230e7471501f9432f727583f2bc17c11f5c1132140319ba2d7a5a0145684c73", + "vout": 11, + "amount": 10000000, + "descriptor": "wpkh([2a704760/44'/0'/0'/1/11]03798ff13e5d17c6f8c1d69ff18d516613be2fa52149b3a8c60ef959d1075a5889)" + } + ], + "network": "mainnet", + "reservedDescriptor": "wpkh([2a704760/44'/0'/0'/1/100]02f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c)", + "feeInfo": { + "feeRate": 2.0, + "longTermFeeRate": 20.0, + "knapsackMinChange": 0, + "dustFeeRate": 3.0 + } + }, + "expect": { + "psbt": "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==", + "hex": "70736274ff0100fd86010200000007f834da7cb5e183fc815f7846227284660fa4a0a583be2bfb4a535bf425e531de0100000000ffffffff5aef6a3bce7624951ae5b9e8c0908e8ff74721eedf7a0994d5bf1efc7dee82810200000000ffffffffa92dd64952789efd1444938e9ce5b106e7280089c4dc4a78b3da7c7a2b69d93f0300000000fffffffffcf72c7bb8881945955e48f54e6081b51901836484e5c48a9925061029c405d10400000000ffffffffa0be1a4d9a22c832d2d20a3a084f010d09a9ab41fb3e2dedbd46eb0104578fb90500000000ffffffff71a141bf5b653e2c43773305d69a8c9876944470ca1e29e4b03a0953c4beb6950100000000ffffffff3c427c95ec8ed79796f13d37d525fbe868545cd91a1eb0d89ff90980731f3a8b0700000000ffffffff0380f0fa0200000000160014b322bddce633b851ac7370ab454f0b367a0654e580f0fa0200000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a555f2919800000000001600142b6e16ba38e280500f80b2c5706ef02f38d59081000000000001011f8096980000000000160014962c4e08f336d3afbc3415c9d359ae1040470520220602565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe182a7047602c000080000000800000008001000000010000000001011f80969800000000001600148bf09d60b7e34f34827d8dbcb1c76390a916ca822206022744cfb2436e156040ec1c8fe842d9ef9a18cb40ad41f312725390c35f7bd36b182a7047602c000080000000800000008001000000020000000001011f8096980000000000160014f1d3ee67829225eb892ccab01e22f6a777e7e21e220602e7e8dc236fa024369408d2ce4d8508048261abc297b811604da087ad71d13855182a7047602c000080000000800000008001000000030000000001011f809698000000000016001419d65f8328b2206d9970785660ec0d34808fb0752206033d874bf19b697cf6c639659547a583b86730164a5b6db01bf20a14eb9b6adb44182a7047602c000080000000800000008001000000040000000001011f809698000000000016001412b7954a75efc2a20e86e32dc2d78647d6700779220602fb061730dbde3c806b4a17a99f454c81282aecee6d46f4a975a94cdfd3a06504182a7047602c000080000000800000008001000000050000000001011f80f0fa0200000000160014978a90460e44671a52f49a09bb59cc6794b63c89220603e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aec189d6b6d862c000080000000800000008001000000010000000001011f80969800000000001600141ce878e3a0da3b34308797fdecb76221f85418af220603c02325c328fed622a9d88f8f5318e05261a3c3a967b7211d7d67657e2b5e9fbe182a7047602c0000800000008000000080010000000700000000220203473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81182a7047602c00008000000080000000800000000002000000002202036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c189d6b6d862c0000800000008000000080000000000200000000220202f7f0d7d00289b7c5a581bc35276040c348de48fc414067f31ea26ad95c00550c182a7047602c0000800000008000000080010000006400000000", + "usedAddresses": [ + "bc1q9dhpdw3cu2q9qruqktzhqmhs9uudtyyptgrq40" + ], + "feeAmount": 1166 + } + } + ] + } +] \ No newline at end of file diff --git a/tests/data/transaction_test.json b/tests/data/transaction_test.json index bd09be3..8f4daf0 100644 --- a/tests/data/transaction_test.json +++ b/tests/data/transaction_test.json @@ -1908,7 +1908,7 @@ "reqSigs": 1, "type": "witness_unknown", "addresses": [ - "bc1qwq3d396pvmz32t2w0erdhsmwqda8xvg5u6vv27f8yj5pel594p4qdpjcst" + "bc1zwq3d396pvmz32t2w0erdhsmwqda8xvg5u6vv27f8yj5pel594p4q0tt7xu" ] } } @@ -1972,6 +1972,141 @@ ] } }, + { + "case": "taproot input (TxIn:1 schnorr sign only)", + "request": { + "hex": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000" + }, + "expect": { + "txid": "25c8dc58303fd4f19f94c3572b123866dcc3adddcefb399897e25c226e52338b", + "hash": "8bfe5963390beca702f7b66438ac572ba0f88040a1c3fa634b97d1e3b65577ff", + "version": 2, + "size": 151, + "vsize": 100, + "weight": 397, + "locktime": 0, + "vin": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "scriptSig": { + "asm": "", + "hex": "" + }, + "txinwitness": [ + "61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f2087201" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 2499998000, + "n": 0, + "scriptPubKey": { + "asm": "0 164e985d0fc92c927a66c0cbaf78e6ea389629d5", + "hex": "0014164e985d0fc92c927a66c0cbaf78e6ea389629d5", + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": [ + "bc1qze8fshg0eykfy7nxcr96778xagufv2w4z2vce4" + ] + } + } + ] + } + }, + { + "case": "taproot input (TxIn:1 tapscript sign only)", + "request": { + "hex": "020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000" + }, + "expect": { + "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": [ + "bc1qze8fshg0eykfy7nxcr96778xagufv2w4z2vce4" + ] + } + } + ] + } + }, + { + "case": "taproot output (TxOut:1 segwit v1 address)", + "request": { + "hex": "02000000000101dd76bd7cb35e3055fa9e8b6c9d73edbf74cc86e50764ac6807eb322625dc6acd0000000000ffffffff0118f50295000000002251203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d20247304402201db912bc61dab1c6117b0aec2965ea1b2d1caa42a1372adc16c8cf673f1187d7022062667d8a976b197f7ba33299365eeb68c1e45fa2a255411672d89f7afab12cb20121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000" + }, + "expect": { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "hash": "dfc2c5fb69b191c85534248f546e3f58d2ddd08edaae5044f27edd431f42c125", + "version": 2, + "size": 203, + "vsize": 122, + "weight": 485, + "locktime": 0, + "vin": [ + { + "txid": "cd6adc252632eb0768ac6407e586cc74bfed739d6c8b9efa55305eb37cbd76dd", + "vout": 0, + "scriptSig": { + "asm": "", + "hex": "" + }, + "txinwitness": [ + "304402201db912bc61dab1c6117b0aec2965ea1b2d1caa42a1372adc16c8cf673f1187d7022062667d8a976b197f7ba33299365eeb68c1e45fa2a255411672d89f7afab12cb201", + "023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c44" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 2499999000, + "n": 0, + "scriptPubKey": { + "asm": "1 3dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "hex": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "reqSigs": 1, + "type": "witness_v1_taproot", + "addresses": [ + "bc1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fqw77n40" + ] + } + } + ] + } + }, { "case": "invalid hex string", "request": {}, @@ -2359,7 +2494,768 @@ "amount": 600000000, "hashType": "p2wpkh", "sighashType": "all" - } + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "Invalid Pubkey data." + } + }, + { + "case": "Error(pubkey is empty)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "p2wpkh", + "sighashType": "all" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "capi": "Failed to parameter. pubkey is null or empty.", + "cfd": "Invalid Pubkey data." + } + }, + { + "case": "Error(amount is invalid)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": -1, + "hashType": "p2wpkh", + "sighashType": "all" + } + }, + "error": { + "code": 3, + "type": "out_of_range", + "message": "Amount out of range." + } + }, + { + "case": "Error(hashType is invalid)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "aaa", + "sighashType": "all" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid hash type: aaa", + "json": "Invalid hashtype_str. hashtype_str must be \"p2pkh\" or \"p2sh\" or \"p2wpkh\" or \"p2wsh\" or \"p2sh-p2wpkh\" or \"p2sh-p2wsh\"." + } + }, + { + "case": "Error(sighashType is invalid)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "p2wpkh", + "sighashType": "aaaa" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid sighash type.", + "json": "Invalid sighashType. sighashType must be \"all, none, single\"." + } + }, + { + "case": "Error(txHex is not hex string)", + "request": { + "tx": "zzzz0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000", + "txin": { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "keyData": { + "hex": "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac", + "type": "redeem_script" + }, + "amount": 4900000000, + "hashType": "p2wsh", + "sighashType": "single" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid hex value.", + "cfd": "hex to byte convert error." + } + }, + { + "case": "Error(txid is not hex string)", + "request": { + "tx": "0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000", + "txin": { + "txid": "zz25690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "keyData": { + "hex": "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac", + "type": "redeem_script" + }, + "amount": 4900000000, + "hashType": "p2wsh", + "sighashType": "single" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid hex value.", + "cfd": "hex to byte convert error." + } + }, + { + "case": "Error(keyData: (hex is not hex string)", + "request": { + "tx": "0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000", + "txin": { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "keyData": { + "hex": "zz026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac", + "type": "redeem_script" + }, + "amount": 4900000000, + "hashType": "p2wsh", + "sighashType": "single" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid hex value.", + "cfd": "hex to byte convert error." + } + }, + { + "case": "Error(pubkeyHex is not hex string)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee63zz", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "p2wpkh", + "sighashType": "all" + } + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid hex value.", + "cfd": "hex to byte convert error." + } + } + ] + }, + { + "name": "Transaction.GetSighash", + "references": [ + "https://techmedia-think.hatenablog.com/entry/2016/07/08/153449" + ], + "cases": [ + { + "case": "HashType(P2WPKH) SigHashType(all)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 600000000, + "lockingScript": "00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1" + } + ] + }, + "expect": { + "sighash": "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670" + } + }, + { + "case": "HashType(P2WSH) SigHashType(single)", + "request": { + "tx": "0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000", + "txin": { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "keyData": { + "hex": "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac", + "type": "redeem_script" + }, + "hashType": "p2wsh", + "sighashType": "single" + }, + "utxos": [ + { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "amount": 4900000000, + "lockingScript": "00205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0" + } + ] + }, + "expect": { + "sighash": "82dde6e4f1e94d02c2b7ad03d2115d691f48d064e9d52f58194a6637e4194391" + } + }, + { + "case": "HashType(P2WSH) SigHashType(none)", + "request": { + "tx": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "txin": { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "keyData": { + "hex": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "type": "redeem_script" + }, + "hashType": "p2wsh", + "sighashType": "none" + }, + "utxos": [ + { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "amount": 987654321, + "lockingScript": "0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54" + } + ] + }, + "expect": { + "sighash": "e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36" + } + }, + { + "case": "HashType(P2WSH) SigHashType(all|anyonecanpay)", + "request": { + "tx": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "txin": { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "keyData": { + "hex": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "type": "redeem_script" + }, + "hashType": "p2wsh", + "sighashType": "all", + "sighashAnyoneCanPay": true + }, + "utxos": [ + { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "amount": 987654321, + "lockingScript": "0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54" + } + ] + }, + "expect": { + "sighash": "2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e" + } + }, + { + "case": "HashType(P2WSH) SigHashType(none|anyonecanpay)", + "request": { + "tx": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "txin": { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "keyData": { + "hex": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "type": "redeem_script" + }, + "hashType": "p2wsh", + "sighashType": "none", + "sighashAnyoneCanPay": true + }, + "utxos": [ + { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "amount": 987654321, + "lockingScript": "0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54" + } + ] + }, + "expect": { + "sighash": "781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a" + } + }, + { + "case": "HashType(P2WSH) SigHashType(single|anyonecanpay)", + "request": { + "tx": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "txin": { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "keyData": { + "hex": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "type": "redeem_script" + }, + "hashType": "p2wsh", + "sighashType": "single", + "sighashAnyoneCanPay": true + }, + "utxos": [ + { + "txid": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", + "vout": 1, + "amount": 987654321, + "lockingScript": "0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54" + } + ] + }, + "expect": { + "sighash": "511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b" + } + }, + { + "case": "HashType(P2PKH) SigHashType(all)", + "request": { + "tx": "01000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000", + "txin": { + "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", + "vout": 0, + "keyData": { + "hex": "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "hashType": "p2pkh", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", + "vout": 0, + "amount": 4999998000, + "lockingScript": "76a9141462eca4b9b8d8df63550abd24d0cb64e8f2d74688ac" + } + ] + }, + "expect": { + "sighash": "f66fdcfbe73820d26162111873d76062bb3e1b23bc9eaf6ab8a3b333f4bc5242" + } + }, + { + "case": "HashType(P2SH) SigHashType(all)", + "request": { + "tx": "0100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000", + "txin": { + "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "keyData": { + "hex": "2102ddeda4a5b67955c32247c28379cf3461c872e34f96ec94ddd61c66bbcfda1906ac", + "type": "redeem_script" + }, + "hashType": "p2sh", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "expect": { + "sighash": "45a0abd384fa552e98d1a35f47b2990146bb1a430ea4191569b7324cae3c9da3" + } + }, + { + "case": "HashType(P2SH-P2WPKH) SigHashType(all)", + "request": { + "tx": "01000000019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000000ffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac00000000", + "txin": { + "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", + "vout": 0, + "keyData": { + "hex": "031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "hashType": "p2sh-p2wpkh", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", + "vout": 0, + "amount": 4999998000, + "descriptor": "sh(wpkh(031777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb))" + } + ] + }, + "expect": { + "sighash": "3717db19cd23a81a3800b1ca448d818d9963f9558e9476bbdcf6827d574ab15a" + } + }, + { + "case": "HashType(P2SH-P2WSH) SigHashType(all)", + "request": { + "tx": "0100000001e007005f3b95e18a7b1faa75d13b98999f7381f345e3849a48ec1eb24c69e4a80000000000ffffffff0130ea052a010000001976a9140c989a8914b27e3a8402990000c05d081f3376c588ac00000000", + "txin": { + "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "keyData": { + "hex": "2102ddeda4a5b67955c32247c28379cf3461c872e34f96ec94ddd61c66bbcfda1906ac", + "type": "redeem_script" + }, + "hashType": "p2sh-p2wsh", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", + "vout": 0, + "amount": 4999998000, + "lockingScript": "a914d1e7b42208cce9d54766f2bf270d73be22894c6387" + } + ] + }, + "expect": { + "sighash": "830cbe9845f555337c6b17334ce375030c3bf7f709e862d468586771047791f5" + } + }, + { + "case": "HashType(Taproot-schnorr) SigHashType(all)", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "keyData": { + "hex": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "hashType": "taproot", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "descriptor": "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)", + "amount": 2499999000 + } + ] + }, + "expect": { + "sighash": "e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02" + } + }, + { + "case": "HashType(Taproot-schnorr) SigHashType(all) annex", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "keyData": { + "hex": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "type": "pubkey" + }, + "hashType": "taproot", + "sighashType": "all", + "annex": "5002ffff" + }, + "utxos": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "descriptor": "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)", + "amount": 2499999000 + } + ] + }, + "expect": { + "sighash": "2c6e49c6ecf07fd4998e885b396457378c55ea3c58ae718979473d4e152a41ad" + } + }, + { + "case": "HashType(Taproot-tapscript) SigHashType(all)", + "request": { + "tx": "02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "keyData": { + "hex": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "type": "redeem_script" + }, + "hashType": "taproot", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "descriptor": "raw(51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2)", + "amount": 2499999000 + } + ] + }, + "expect": { + "sighash": "80e53eaee13048aee9c6c13fa5a8529aad7fe2c362bfc16f1e2affc71f591d36" + } + }, + { + "case": "HashType(Taproot-tapscript) SigHashType(all) annex", + "request": { + "tx": "02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "keyData": { + "hex": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "type": "redeem_script" + }, + "hashType": "taproot", + "sighashType": "all", + "annex": "5004eeeeeeee" + }, + "utxos": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "descriptor": "raw(51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2)", + "amount": 2499999000 + } + ] + }, + "expect": { + "sighash": "f0d0559b30e6caafc9b254e57feb2207b7bfb762e6c34baea43c55595865566c" + } + }, + { + "case": "Error(Txid is not found)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 122500000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "Txid is not found." + } + }, + { + "case": "Error(Txid is empty)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 122500000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid txid.", + "cfd": "Txid size Invalid." + } + }, + { + "case": "Error(Txid is invalid)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "ef51e1", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 122500000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "error": { + "code": 1, + "type": "illegal_argument", + "python": "Error: Invalid txid.", + "cfd": "Txid size Invalid." + } + }, + { + "case": "Error(TxHex is empty)", + "request": { + "tx": "", + "txin": { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 122500000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "error": { + "code": 1, + "type": "illegal_argument", + "capi": "Failed to parameter. pointer is null.", + "cfd": "transaction data invalid." + } + }, + { + "case": "Error(TxHex is invalid)", + "request": { + "tx": "010000000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] + }, + "error": { + "code": 1, + "type": "illegal_argument", + "message": "transaction data invalid." + } + }, + { + "case": "Error(pubkey is invalid)", + "request": { + "tx": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "txin": { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "keyData": { + "hex": "025476c2e83188368da1ff3e292e7aca", + "type": "pubkey" + }, + "amount": 600000000, + "hashType": "p2wpkh", + "sighashType": "all" + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2381,13 +3277,20 @@ "amount": 600000000, "hashType": "p2wpkh", "sighashType": "all" - } + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, "type": "illegal_argument", - "capi": "Failed to parameter. pubkey is null or empty.", - "cfd": "Invalid Pubkey data." + "message": "Invalid Pubkey data." } }, { @@ -2401,10 +3304,17 @@ "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", "type": "pubkey" }, - "amount": -1, "hashType": "p2wpkh", "sighashType": "all" - } + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": -1, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 3, @@ -2423,16 +3333,23 @@ "hex": "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357", "type": "pubkey" }, - "amount": 600000000, "hashType": "aaa", "sighashType": "all" - } + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 600000000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: aaa", - "json": "Invalid hashtype_str. hashtype_str must be \"p2pkh\" or \"p2sh\" or \"p2wpkh\" or \"p2wsh\" or \"p2sh-p2wpkh\" or \"p2sh-p2wsh\"." + "json": "Invalid address_type passed. address_type must be \"p2pkh\", \"p2sh\", \"p2wpkh\", \"p2wsh\", \"taproot\", \"p2sh-p2wpkh\", or \"p2sh-p2wsh\"." } }, { @@ -2449,7 +3366,15 @@ "amount": 600000000, "hashType": "p2wpkh", "sighashType": "aaaa" - } + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2472,7 +3397,15 @@ "amount": 4900000000, "hashType": "p2wsh", "sighashType": "single" - } + }, + "utxos": [ + { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2495,7 +3428,15 @@ "amount": 4900000000, "hashType": "p2wsh", "sighashType": "single" - } + }, + "utxos": [ + { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2518,7 +3459,15 @@ "amount": 4900000000, "hashType": "p2wsh", "sighashType": "single" - } + }, + "utxos": [ + { + "txid": "f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", + "vout": 0, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2541,7 +3490,15 @@ "amount": 600000000, "hashType": "p2wpkh", "sighashType": "all" - } + }, + "utxos": [ + { + "txid": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", + "vout": 1, + "amount": 4999998000, + "lockingScript": "a914d8de653e7763cc37305a00fc79a491ab70e2e5cb87" + } + ] }, "error": { "code": 1, @@ -2606,6 +3563,56 @@ "hex": "020000000001019c53cb2a6118530aaa345b799aeb7e4e5055de41ac5b2dd2ce47419624c57b580000000017160014eb3c0d55b7098a4aef4a18ee1eebcb1ed924a82bffffffff0130ea052a010000001976a9143cadb10040e9e7002bbd9d0620f5f79c05603ffd88ac0247304402200aecb70a0dfff96a088c6cd60dc5905e8117f5e68e9009de0e45a87fe5f6bdc402204188555d295daaf309716ca2372bb9d9cc778105fe87de50f5a9ccc352acf314012103f942716865bb9b62678d99aa34de4632249d066d99de2b5a2e542e54908450d600000000" } }, + { + "case": "taproot-schnorr by descriptor", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "sighashType": "all", + "privkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27", + "hashType": "taproot", + "amount": 2499999000 + }, + "utxos": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "descriptor": "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)", + "amount": 2499999000 + } + ] + }, + "expect": { + "hex": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000" + } + }, + { + "case": "taproot-schnorr by lockingScript", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "sighashType": "all", + "privkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27", + "hashType": "taproot", + "amount": 2499999000 + }, + "utxos": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "amount": 2499999000 + } + ] + }, + "expect": { + "hex": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000" + } + }, { "case": "empty hex string", "request": { @@ -2796,6 +3803,26 @@ "type": "illegal_argument", "message": "Invalid address_type. address_type must be \"p2wpkh\" or \"p2sh-p2wpkh\" or \"p2pkh\"." } + }, + { + "case": "utxo empty with taproot-schnorr", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "sighashType": "all", + "privkey": "305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27", + "hashType": "taproot", + "amount": 2499999000 + } + }, + "error": { + "code": 2, + "type": "illegal_state", + "message": "Utxo is not found. CreateSignatureHashByTaproot fail.", + "capi": "unsupport witness version on ECDSA." + } } ] }, @@ -3489,7 +4516,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3510,7 +4537,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "22222222", "type": "binary", @@ -3532,7 +4559,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "22222222", "type": "binary", @@ -3554,7 +4581,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3587,7 +4614,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "", "type": "binary", @@ -3608,7 +4635,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3629,7 +4656,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "sign", @@ -3650,7 +4677,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", "type": "pubkey", @@ -3671,7 +4698,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", "type": "binary", @@ -3692,7 +4719,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -3714,7 +4741,7 @@ "txid": "587bc524964147ced22d5bac41de55504e7eeb9a795b34aa0a5318612acb539c", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "773420c0ded41a55b1f1205cfb632f08f3f911a53e7338a0dac73ec6cbe3ca471907434d046185abedc5afddc2761a642bccc70af6d22b46394f1d04a8b24226", "type": "sign", @@ -3740,7 +4767,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "54b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c6717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb26", "type": "sign", @@ -3766,7 +4793,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "54b6116e20efb6da997e4fabe8e0af10ecb7b9d5009050795c21475ca4c6f3c6717787bc18eb2064b49a18cb8dec8b3bbaeb68ea71c58fa73bb1ec7e19d2cb26", "type": "auto", @@ -3792,7 +4819,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "OP_TRUE", "type": "op_code" @@ -3821,7 +4848,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "OP_0", "type": "auto" @@ -3853,7 +4880,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3877,7 +4904,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3901,7 +4928,7 @@ "txin": { "txid": "", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3925,7 +4952,7 @@ "txin": { "txid": "ea9d5a9e974af1d16730", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3949,7 +4976,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4d", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3972,7 +4999,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": -1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -3996,7 +5023,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -4019,7 +5046,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [] + "signParams": [] } }, "error": { @@ -4038,7 +5065,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "111", "type": "binary", @@ -4061,7 +5088,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -4085,7 +5112,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -4110,7 +5137,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "OP_TRUE", "type": "op_code" @@ -5819,7 +6846,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -5842,7 +6869,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -5875,7 +6902,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -5899,7 +6926,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -5925,7 +6952,7 @@ "txin": { "txid": "", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -5951,7 +6978,7 @@ "txin": { "txid": "ea9d5a9e974af1d16730", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -5977,7 +7004,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4d", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -6002,7 +7029,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": -1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -6028,7 +7055,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 1, - "signParam": [ + "signParams": [ { "hex": "11111111", "type": "binary", @@ -6053,7 +7080,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "111", "type": "binary", @@ -6078,7 +7105,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -6104,7 +7131,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -6131,7 +7158,7 @@ "txid": "a8e4694cb21eec489a84e345f381739f99983bd175aa1f7b8ae1953b5f0007e0", "vout": 0, "isWitness": false, - "signParam": [ + "signParams": [ { "hex": "OP_TRUE", "type": "auto" @@ -6166,7 +7193,7 @@ "txin": { "txid": "ea9d5a9e974af1d167305aa6ee598706d63274e8a40f4f33af97db37a7adde4c", "vout": 0, - "signParam": [ + "signParams": [ { "hex": "47ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb", "type": "sign", @@ -6265,6 +7292,51 @@ ] } }, + { + "case": "HashType(taproot-schnorr)", + "request": { + "tx": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000", + "isElements": false, + "txins": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "address": "", + "amount": 2499999000, + "descriptor": "raw(51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb)" + } + ] + }, + "expect": { + "success": true, + "failTxins": [] + } + }, + { + "case": "HashType(taproot-tapscript)", + "request": { + "tx": "020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000", + "isElements": false, + "txins": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "address": "bcrt1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fq50z666", + "amount": 2499999000 + } + ] + }, + "expect": { + "success": false, + "failTxins": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "reason": "The script analysis of tapscript is not supported." + } + ] + } + }, { "case": "empty txid", "request": { @@ -6286,6 +7358,31 @@ "python": "Error: Invalid txid.", "cfd": "Txid size Invalid." } + }, + { + "case": "Invalid tapscript control", + "request": { + "tx": "020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7f001a1d2d5400000000", + "isElements": false, + "txins": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "address": "bcrt1p8hh955u8526hjqhn5m5a5pmhymgecmxgerrmqj70tgvhk25mq8fq50z666", + "amount": 2499999000 + } + ] + }, + "expect": { + "success": false, + "failTxins": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "reason": "Unmatch locking script." + } + ] + } } ] }, @@ -6370,6 +7467,61 @@ "success": true } }, + { + "case": "HashType(Taproot-schnorr) SigHashType(all)", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "signature": "61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f20872", + "pubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "amount": 2499999000, + "hashType": "taproot", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "lockingScript": "51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "amount": 2499999000 + } + ] + }, + "expect": { + "success": true + } + }, + { + "case": "HashType(Taproot-tapscript) SigHashType(all)", + "request": { + "tx": "02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "txin": { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "signature": "f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee9", + "pubkey": "1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + "redeemScript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "amount": 2499999000, + "hashType": "taproot", + "sighashType": "all", + "sighashAnyoneCanPay": false + }, + "utxos": [ + { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "lockingScript": "51203dee5a5387a2b57902f3a6e9da077726d19c6cc8c8c7b04bcf5a197b2a9b01d2", + "amount": 2499999000 + } + ] + }, + "expect": { + "success": true + } + }, { "case": "Error(Txid is empty)", "request": { @@ -6532,7 +7684,7 @@ "code": 1, "type": "illegal_argument", "python": "Error: Invalid hash type: p2dummy", - "json": "Invalid hashtype_str. hashtype_str must be \"p2pkh\" or \"p2sh\" or \"p2wpkh\" or \"p2wsh\"." + "json": "Invalid hashtype_str. hashtype_str must be \"p2pkh\" or \"p2sh\" or \"p2wpkh\" or \"p2wsh\" or \"taproot\"." } }, { @@ -6640,5 +7792,70 @@ } } ] + }, + { + "name": "Transaction.AddTaprootSchnorrSign", + "cases": [ + { + "case": "SigHash(all)", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "isElements": false, + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "signature": "61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f20872", + "sighashType": "all" + } + }, + "expect": { + "hex": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014161f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f208720100000000" + } + }, + { + "case": "SigHash(default)", + "request": { + "tx": "020000000116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "isElements": false, + "txin": { + "txid": "2fea883042440d030ca5929814ead927075a8f52fef5f4720fa3cec2e475d916", + "vout": 0, + "signature": "61f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f20872", + "sighashType": "default" + } + }, + "expect": { + "hex": "0200000000010116d975e4c2cea30f72f4f5fe528f5a0727d9ea149892a50c030d44423088ea2f0000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d5014061f75636003a870b7a1685abae84eedf8c9527227ac70183c376f7b3a35b07ebcbea14749e58ce1a87565b035b2f3963baa5ae3ede95e89fd607ab7849f2087200000000" + } + } + ] + }, + { + "name": "Transaction.AddTapscriptSign", + "cases": [ + { + "case": "SigHash(all)", + "request": { + "tx": "02000000015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d500000000", + "isElements": false, + "txin": { + "txid": "195d6c18afaa6c33dcc2da86c6c0cd3f93ec2b44e4c8e9be00c7000eafa1805b", + "vout": 0, + "signParams": [ + { + "hex": "f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee901", + "type": "sign", + "sighashType": "all" + } + ], + "tapscript": "201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac", + "controlBlock": "c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54" + } + }, + "expect": { + "hex": "020000000001015b80a1af0e00c700bee9c8e4442bec933fcdc0c686dac2dc336caaaf186c5d190000000000ffffffff0130f1029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50341f5aa6b260f9df687786cd3813ba83b476e195041bccea800f2571212f4aae9848a538b6175a4f8ea291d38e351ea7f612a3d700dca63cd3aff05d315c5698ee90122201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfbac61c01777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb4d18084bb47027f47d428b2ed67e1ccace5520fdc36f308e272394e288d53b6ddc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d5400000000" + } + } + ] } ] \ No newline at end of file diff --git a/tests/test_address.py b/tests/test_address.py index 8eac958..7f694be 100644 --- a/tests/test_address.py +++ b/tests/test_address.py @@ -1,10 +1,11 @@ from unittest import TestCase from tests.util import load_json_file, exec_test,\ - assert_equal, assert_error, assert_match + assert_equal, assert_error, assert_match, assert_message from cfd.address import AddressUtil -from cfd.key import Network -from cfd.script import HashType -from cfd.util import CfdError +from cfd.key import Network, SchnorrPubkey, Privkey +from cfd.script import HashType, Script +from cfd.taproot import TapBranch, TaprootScriptTree +from cfd.util import CfdError, ByteData def test_address_func(obj, name, case, req, exp, error): @@ -35,6 +36,9 @@ def test_address_func(obj, name, case, req, exp, error): elif _hash_type == HashType.P2SH_P2WSH: resp = AddressUtil.p2sh_p2wsh( req['keyData']['hex'], network=_network) + elif _hash_type == HashType.TAPROOT: + resp = AddressUtil.taproot( + req['keyData']['hex'], network=_network) elif name == 'Address.GetInfo': resp = AddressUtil.parse(req['address']) elif name == 'Address.MultisigAddresses': @@ -49,11 +53,195 @@ def test_address_func(obj, name, case, req, exp, error): resp = AddressUtil.from_locking_script( req['lockingScript'], network=_network) + elif name == 'Address.GetTapScriptTreeInfo': + resp = {} + nodes = [] + for node in req['tree'][1:]: + if 'tapscript' in node: + nodes.append(Script(node['tapscript'])) + elif 'treeString' in node: + nodes.append(TapBranch(tree_str=node['treeString'])) + else: + nodes.append(ByteData(node['branchHash'])) + pk = None if 'internalPubkey' not in req else SchnorrPubkey( + req['internalPubkey']) + if 'tapscript' in req['tree'][0]: + tree = TaprootScriptTree.create( + Script(req['tree'][0]['tapscript']), nodes, pk) + if 'internalPubkey' not in req: + tapleaf_hash = tree.get_base_hash() + resp = { + 'tapLeafHash': tapleaf_hash, + 'tapscript': tree.tapscript, + } + else: + tap_data = tree.get_taproot_data() + addr = AddressUtil.taproot(tree, network=_network) + resp = { + 'tapLeafHash': tap_data[1], + 'tapscript': tap_data[2], + 'tweakedPubkey': tap_data[0], + 'controlBlock': tap_data[3], + 'address': addr.address, + 'lockingScript': addr.locking_script, + } + if 'internalPrivkey' in req: + tweak_privkey = tree.get_privkey( + Privkey(hex=req['internalPrivkey'])) + resp['tweakedPrivkey'] = tweak_privkey + nodes = [] + for node in tree.branches: + if isinstance(node, TapBranch): + nodes.append(node.get_current_hash()) + else: + nodes.append(str(node)) + resp['nodes'] = nodes + elif 'treeString' in node: + tree = TapBranch(tree_str=node['treeString']) + else: + tree = TapBranch(ByteData(node['branchHash'])) + resp['topBranchHash'] = tree.get_current_hash() + resp['treeString'] = tree.as_str() + + elif name == 'Address.GetTapScriptTreeInfoByControlBlock': + tree = TaprootScriptTree.from_control_block( + ByteData(req['controlBlock']), + Script(req['tapscript'])) + tap_data = tree.get_taproot_data() + addr = AddressUtil.taproot(tree, network=_network) + resp = { + 'tapLeafHash': tap_data[1], + 'tweakedPubkey': tap_data[0], + 'controlBlock': tap_data[3], + 'tapscript': tap_data[2], + 'address': addr.address, + 'lockingScript': addr.locking_script, + } + resp['topBranchHash'] = tree.get_current_hash() + resp['treeString'] = tree.as_str() + nodes = [] + for node in tree.branches: + if isinstance(node, TapBranch): + nodes.append(node.get_current_hash()) + else: + nodes.append(str(node)) + resp['nodes'] = nodes + if 'internalPrivkey' in req: + tweak_privkey = tree.get_privkey( + Privkey(hex=req['internalPrivkey'])) + resp['tweakedPrivkey'] = tweak_privkey + + elif name == 'Address.GetTapScriptTreeFromString': + resp = {} + if 'tapscript' in req: + nodes = [ByteData(node) for node in req.get('nodes', [])] + pk = None if 'internalPubkey' not in req else SchnorrPubkey( + req['internalPubkey']) + tree = TaprootScriptTree.from_string( + req['treeString'], Script(req['tapscript']), nodes, pk) + if pk is not None: + tap_data = tree.get_taproot_data() + addr = AddressUtil.taproot(tree, network=_network) + resp = { + 'tweakedPubkey': tap_data[0], + 'controlBlock': tap_data[3], + 'address': addr.address, + 'lockingScript': addr.locking_script, + } + if 'internalPrivkey' in req: + tweak_privkey = tree.get_privkey( + Privkey(hex=req['internalPrivkey'])) + resp['tweakedPrivkey'] = tweak_privkey + resp['tapLeafHash'] = tree.get_base_hash() + resp['tapscript'] = tree.tapscript + nodes = [] + for node in tree.branches: + if isinstance(node, TapBranch): + nodes.append(node.get_current_hash()) + else: + nodes.append(str(node)) + resp['nodes'] = nodes + else: + tree = TapBranch(tree_str=req['treeString']) + resp['topBranchHash'] = tree.get_current_hash() + resp['treeString'] = tree.as_str() + + elif name == 'Address.GetTapBranchInfo': + resp = {} + nodes = [ByteData(node) for node in req.get('nodes', [])] + tree = TaprootScriptTree.from_string( + req['treeString'], Script(req.get('tapscript', '')), nodes) + branch = tree.branches[req.get('index', 0)] + resp['tapLeafHash'] = branch.get_base_hash() + nodes = [] + for node in branch.branches: + if isinstance(node, TapBranch): + nodes.append(node.get_current_hash()) + else: + nodes.append(str(node)) + resp['nodes'] = nodes + resp['topBranchHash'] = branch.get_current_hash() + resp['treeString'] = branch.as_str() + + elif name == 'Address.AnalyzeTapScriptTree': + resp = [] + + def collect_branch(branch): + count = len(branch.branches) + for index, child in enumerate(branch.branches): + collect_branch(child) + br = TapBranch(hash=branch.get_branch_hash( + count - index - 1)) + resp.append(br) + + if not branch.branches: + resp.append(branch) + elif branch.has_tapscript(): + br = TapBranch(tapscript=branch.tapscript) + resp.append(br) + else: + br = TapBranch(hash=branch.get_base_hash()) + resp.append(br) + + tree = TapBranch.from_string(req['treeString']) + collect_branch(tree) + else: raise Exception('unknown name: ' + name) assert_error(obj, name, case, error) - if isinstance(resp, list): + if name == 'Address.AnalyzeTapScriptTree': + exp_dict = {} + for exp_data in exp['branches']: + exp_dict[exp_data['tapBranchHash']] = exp_data + assert_match(obj, name, case, len(exp['branches']), + len(resp), 'list length') + ret_dict = {} + for ret_data in resp: + hash_val = str(ret_data.get_current_hash()) + exp_data = exp_dict.get(hash_val, None) + if exp_data is None: + assert_message(obj, name, case, + f'hash {hash_val} not found.' + + str(exp_dict)) + elif 'tapscript' in exp_data: + assert_equal(obj, name, case, exp_data, + str(ret_data.tapscript), 'tapscript') + ret_dict[hash_val] = ret_data + assert_match(obj, name, case, len(exp_dict), + len(ret_dict), 'hash list length') + + elif isinstance(resp, dict): + for key, val in resp.items(): + if isinstance(val, list): + assert_match(obj, name, case, len(exp[key]), + len(val), f'{key}:Len') + for index, list_val in enumerate(val): + assert_match(obj, name, case, str(exp[key][index]), + str(list_val), f'{key}:{index}') + else: + assert_equal(obj, name, case, exp, val, key) + elif isinstance(resp, list): assert_match(obj, name, case, len(exp['addresses']), len(resp), 'addressLen') if 'pubkeys' in exp: @@ -110,3 +298,16 @@ def setUp(self): def test_address(self): exec_test(self, 'Address', test_address_func) + + def test_scripttree(self): + tree = TaprootScriptTree.create(Script('51')) + tree.add_branch(Script('51')) + tree.add_branch(Script('51')) + pk = '0000000000000000000000000000000000000000000000000000000000000001' + tweakedPk, _, _, ctrlBlock = tree.get_taproot_data(SchnorrPubkey(pk)) + self.assertEqual( + 'e3f3b67db1123a90fa960119099ae04c18b0f6e1f437157739222cd233b21212', + tweakedPk.hex) + self.assertEqual( + 'c10000000000000000000000000000000000000000000000000000000000000001a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675', # noqa: E501 + ctrlBlock.hex) diff --git a/tests/test_common.py b/tests/test_common.py index 8d91733..015d2db 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -1,5 +1,65 @@ from unittest import TestCase +from tests.util import load_json_file,\ + exec_test, assert_equal, assert_error from cfd.util import ByteData, CfdError +from cfd.crypto import CryptoUtil, HashUtil + + +def test_crypto_func(obj, name, case, req, exp, error): + try: + if name == 'Base58.Encode': + resp = CryptoUtil.encode_base58(req['hex'], req['hasChecksum']) + elif name == 'Base58.Decode': + resp = CryptoUtil.decode_base58(req['data'], req['hasChecksum']) + elif name == 'Base64.Encode': + resp = CryptoUtil.encode_base64(req['hex']) + elif name == 'Base64.Decode': + resp = CryptoUtil.decode_base64(req['base64']) + elif name == 'AES.Encode': + resp = CryptoUtil.encrypto_aes( + req['key'], req['data'], req.get('iv', None)) + elif name == 'AES.Decode': + resp = CryptoUtil.decrypto_aes( + req['key'], req['data'], req.get('iv', None)) + elif name == 'Hash.Hash256': + resp = HashUtil.hash256(req['message'], req['hasText']) + elif name == 'Hash.Hash160': + resp = HashUtil.hash160(req['message'], req['hasText']) + elif name == 'Hash.Sha256': + resp = HashUtil.sha256(req['message'], req['hasText']) + elif name == 'Hash.Ripemd160': + resp = HashUtil.ripemd160(req['message'], req['hasText']) + else: + raise Exception('unknown name: ' + name) + assert_error(obj, name, case, error) + + assert_equal(obj, name, case, exp, str(resp), 'hex') + assert_equal(obj, name, case, exp, str(resp), 'base64') + assert_equal(obj, name, case, exp, str(resp), 'data') + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + assert_equal(obj, name, case, exp, err.message) + return True + + +class TestCrypto(TestCase): + def setUp(self): + self.test_list = load_json_file('common_test.json') + + def test_base58(self): + exec_test(self, 'Base58', test_crypto_func) + + def test_base64(self): + exec_test(self, 'Base64', test_crypto_func) + + def test_hash(self): + exec_test(self, 'Hash', test_crypto_func) + + def test_aes(self): + exec_test(self, 'AES', test_crypto_func) class TestByteData(TestCase): diff --git a/tests/test_confidential_transaction.py b/tests/test_confidential_transaction.py index f9676d9..efa9ab0 100644 --- a/tests/test_confidential_transaction.py +++ b/tests/test_confidential_transaction.py @@ -130,7 +130,7 @@ def get_tx(): hash_type = HashType.P2SH if txin.get('isWitness', True): hash_type = HashType.P2WSH - for param in txin.get('signParam', []): + for param in txin.get('signParams', []): _sighashtype = SigHashType.get( param.get('sighashType', 'all'), param.get('sighashAnyoneCanPay', False)) @@ -182,7 +182,7 @@ def get_tx(): elif name == 'ConfidentialTransaction.AddScriptHashSign': resp, txin = get_tx() signature_list = [] - for param in txin.get('signParam', []): + for param in txin.get('signParams', []): _sighashtype = SigHashType.get( param.get('sighashType', 'all'), param.get('sighashAnyoneCanPay', False)) @@ -265,6 +265,8 @@ def get_tx(): elif name == 'ConfidentialTransaction.VerifySignature': assert_equal(obj, name, case, exp, resp, 'success') else: + if str(resp) != exp['hex']: + print(str(resp)) assert_equal(obj, name, case, exp, str(resp), 'hex') except CfdError as err: @@ -548,7 +550,9 @@ def test_ct_transaction_func4(obj, name, case, req, exp, error): txout_list = resp['req_output'] tx = typing.cast('ConfidentialTransaction', resp['tx']) blinder_list = typing.cast( - typing.List[typing.Union['BlindData', 'IssuanceAssetBlindData', 'IssuanceTokenBlindData']], resp['blinder_list']) + typing.List[typing.Union[ + 'BlindData', 'IssuanceAssetBlindData', + 'IssuanceTokenBlindData']], resp['blinder_list']) blinding_keys = exp.get('blindingKeys', []) issuance_list = exp.get('issuanceList', []) txout_index_list = [] @@ -831,6 +835,8 @@ def test_elements_tx_func(obj, name, case, req, exp, error): assert_equal(obj, name, case, exp, txout_fee, 'txoutFeeAmount') assert_equal(obj, name, case, exp, utxo_fee, 'utxoFeeAmount') elif name == 'Elements.FundTransaction': + if resp['hex'] != exp['hex']: + print(resp['hex']) assert_equal(obj, name, case, exp, resp['hex'], 'hex') assert_equal(obj, name, case, exp, resp['feeAmount'], 'feeAmount') exp_addr_list = exp['usedAddresses'] @@ -974,4 +980,7 @@ def test_parse_unblind_tx(self): str(tx.txout_list[0].get_address())) self.assertEqual( 'VTpz4UGuFrPeMdFvW6dzq1vH3ZumciG6jmGnCUidgqsY5RHRxbGfLjndgUjzECCzQnNwAGoP8ohYdHXv', # noqa: E501 - str(tx.txout_list[0].get_address(is_confidential=True))) + str(tx.txout_list[0].get_confidential_address())) + self.assertEqual( + None, + tx.txout_list[1].get_confidential_address()) diff --git a/tests/test_key.py b/tests/test_key.py index 34e0af5..7f0fde4 100644 --- a/tests/test_key.py +++ b/tests/test_key.py @@ -219,6 +219,11 @@ def test_schnorr_func(obj, name, case, req, exp, error): req['message'], req['nonce'], req['pubkey'], is_message_hashed=req['isHashed']) + elif name == 'Schnorr.TweakAddPrivkey': + resp = '' + _pk, _parity, privkey = SchnorrPubkey.add_tweak_from_privkey( + req['privkey'], req['tweak']) + else: raise Exception('unknown name: ' + name) assert_error(obj, name, case, error) diff --git a/tests/test_psbt.py b/tests/test_psbt.py new file mode 100644 index 0000000..01a3e6e --- /dev/null +++ b/tests/test_psbt.py @@ -0,0 +1,670 @@ +from unittest import TestCase +from tests.util import load_json_file,\ + exec_test, assert_equal, assert_error, assert_message, assert_match +from cfd.util import ByteData, CfdError +from cfd.address import AddressUtil +from cfd.hdwallet import KeyData, ExtPubkey +from cfd.key import Network, Pubkey, SigHashType +from cfd.psbt import Psbt +from cfd.script import Script +from cfd.transaction import Transaction, OutPoint, TxOut, UtxoData, TxIn + + +def test_decode_psbt_func(obj, name, case, req, exp, error): + try: + if name != 'Psbt.DecodePsbt': + raise Exception('unknown name: ' + name) + + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + assert_error(obj, name, case, error) + + assert_equal(obj, name, case, exp, str(psbt.get_tx()), 'tx_hex') + if 'version' in exp: + _, ver, _, _ = psbt.get_global_data() + assert_equal(obj, name, case, exp, ver, 'version') + xpubkeys = psbt.get_global_xpub_list() + if 'xpubs' in exp: + assert_match(obj, name, case, len(exp['xpubs']), len(xpubkeys), + 'global:xpubs:num') + for xpub_index, xpub_data in enumerate(exp.get('xpubs', [])): + assert_match(obj, name, case, xpub_data['xpub']['base58'], str( + xpubkeys[xpub_index].ext_pubkey), + f'global:xpubs{xpub_index}:xpub') + assert_match(obj, name, case, + xpub_data['master_fingerprint'], str( + xpubkeys[xpub_index].fingerprint), + f'global:xpubs{xpub_index}:master_fingerprint') + assert_match(obj, name, case, xpub_data['path'], str( + xpubkeys[xpub_index].bip32_path), + f'global:xpubs{xpub_index}:path') + if 'unknown' in exp: + unknown_keys = psbt.get_global_unknown_keys() + key_len = len(unknown_keys) + if req.get('hasDetail', False): + key_len = key_len - len(xpubkeys) + assert_match(obj, name, case, len(exp['unknown']), key_len, + 'global:unknown:num') + for unknown_data in exp.get('unknown', []): + key = unknown_data['key'] + value = psbt.get_global_record(key) + assert_match(obj, name, case, unknown_data['value'], str( + value), f'global:unknown:{key}') + + in_num, out_num = psbt.get_tx_count() + assert_match(obj, name, case, len(exp['inputs']), in_num, 'num:inputs') + assert_match(obj, name, case, len( + exp['outputs']), out_num, 'num:outputs') + + for index in range(in_num): + exp_input = exp['inputs'][index] + outpoint = psbt.get_input_outpoint(index) + if ('witness_utxo' in exp_input) or ( + 'non_witness_utxo_hex' in exp_input): + utxo, locking_script, _, full_tx = psbt.get_input_utxo_data( + outpoint) + if 'witness_utxo' in exp_input: + assert_match( + obj, + name, + case, + exp_input['witness_utxo']['amount'], + utxo.amount, + f'input{index}:witness_utxo:amount') + assert_match( + obj, + name, + case, + exp_input['witness_utxo']['scriptPubKey']['hex'], + str(locking_script), + f'input{index}:witness_utxo:scriptPubKey:hex') + if 'non_witness_utxo_hex' in exp_input: + assert_match( + obj, name, case, exp_input['non_witness_utxo_hex'], + str(full_tx), f'input{index}:non_witness_utxo_hex') + if 'sighash' in exp_input: + sighash = psbt.get_input_sighash_type(outpoint) + assert_match( + obj, name, case, exp_input['sighash'].lower(), str( + sighash), f'input{index}:sighash') + if 'final_scriptsig' in exp_input: + final_scriptsig = psbt.get_input_final_scriptsig(outpoint) + assert_match( + obj, + name, + case, + exp_input['final_scriptsig']['hex'], + str(final_scriptsig), + f'input{index}:final_scriptsig:hex') + if 'final_scriptsig' in exp_input: + final_scriptsig = psbt.get_input_final_scriptsig(outpoint) + assert_match( + obj, + name, + case, + exp_input['final_scriptsig']['hex'], + str(final_scriptsig), + f'input{index}:final_scriptsig:hex') + if 'final_scriptwitness' in exp_input: + witness = psbt.get_input_final_witness(outpoint) + assert_match(obj, name, case, len( + exp_input['final_scriptwitness']), len(witness), + f'input{index}:final_scriptwitness:num') + for wit_index, stack in enumerate( + exp_input.get('final_scriptwitness', [])): + assert_match( + obj, name, case, stack, str(witness[wit_index]), + f'input{index}:final_scriptwitness{wit_index}') + if 'redeem_script' in exp_input: + redeem_script = psbt.get_input_redeem_script(outpoint) + assert_match( + obj, + name, + case, + exp_input['redeem_script']['hex'], + str(redeem_script), + f'input{index}:redeem_script:hex') + if 'witness_script' in exp_input: + witness_script = psbt.get_input_witness_script(outpoint) + assert_match( + obj, + name, + case, + exp_input['witness_script']['hex'], + str(witness_script), + f'input{index}:witness_script:hex') + if 'partial_signatures' in exp_input: + sigs = psbt.get_input_signature_list(outpoint) + assert_match(obj, name, case, len( + exp_input['partial_signatures']), len(sigs), + f'input{index}:partial_signatures:num') + for sig_index, sig_data in enumerate( + exp_input.get('partial_signatures', [])): + assert_match( + obj, name, case, sig_data['pubkey'], str( + sigs[sig_index].related_pubkey), + f'input{index}:partial_signatures{sig_index}:pubkey') + assert_match(obj, name, case, sig_data['signature'], str( + sigs[sig_index].hex), + f'input{index}:partial_signatures{sig_index}:sig') + if 'bip32_derivs' in exp_input: + pubkeys = psbt.get_input_bip32_list(outpoint) + assert_match(obj, name, case, len( + exp_input['bip32_derivs']), len(pubkeys), + f'input{index}:bip32_derivs:num') + for key_index, key_data in enumerate( + exp_input.get('bip32_derivs', [])): + assert_match(obj, name, case, key_data['pubkey'], str( + pubkeys[key_index].pubkey), + f'input{index}:bip32_derivs{key_index}:pubkey') + assert_match( + obj, name, case, + key_data['master_fingerprint'], str( + pubkeys[key_index].fingerprint), + f'input{index}:bip32_derivs{key_index}:fingerprint') + assert_match(obj, name, case, key_data['path'], + pubkeys[key_index].bip32_path, + f'input{index}:bip32_derivs{key_index}:path') + if 'unknown' in exp_input: + unknown_keys = psbt.get_input_unknown_keys(outpoint) + assert_match(obj, name, case, len(exp_input['unknown']), len( + unknown_keys), f'input{index}:unknown:num') + for unknown_data in exp_input.get('unknown', []): + key = unknown_data['key'] + value = psbt.get_input_record(outpoint, key) + assert_match(obj, name, case, unknown_data['value'], str( + value), f'input{index}:unknown:{key}') + + for index in range(out_num): + exp_output = exp['outputs'][index] + if 'redeem_script' in exp_output: + redeem_script = psbt.get_output_redeem_script(index) + assert_match( + obj, + name, + case, + exp_output['redeem_script']['hex'], + str(redeem_script), + f'output{index}:redeem_script:hex') + if 'witness_script' in exp_output: + witness_script = psbt.get_output_witness_script(index) + assert_match( + obj, + name, + case, + exp_output['witness_script']['hex'], + str(witness_script), + f'output{index}:witness_script:hex') + if 'bip32_derivs' in exp_output: + pubkeys = psbt.get_output_bip32_list(index) + assert_match(obj, name, case, len( + exp_output['bip32_derivs']), len(pubkeys), + f'output{index}:bip32_derivs:num') + for key_index, key_data in enumerate( + exp_output.get('bip32_derivs', [])): + assert_match(obj, name, case, key_data['pubkey'], str( + pubkeys[key_index].pubkey), + f'output{index}:bip32_derivs{key_index}:pubkey') + assert_match( + obj, name, case, + key_data['master_fingerprint'], str( + pubkeys[key_index].fingerprint), + f'output{index}:bip32_derivs{key_index}:fingerprint') + assert_match(obj, name, case, key_data['path'], + pubkeys[key_index].bip32_path, + f'output{index}:bip32_derivs{key_index}:path') + if 'unknown' in exp_output: + unknown_keys = psbt.get_output_unknown_keys(index) + assert_match(obj, name, case, len(exp_output['unknown']), len( + unknown_keys), f'output{index}:unknown:num') + for unknown_data in exp_output.get('unknown', []): + key = unknown_data['key'] + value = psbt.get_output_record(index, key) + assert_match(obj, name, case, unknown_data['value'], str( + value), f'output{index}:unknown:{key}') + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + assert_equal(obj, name, case, exp, err.message) + return True + + +def test_verify_psbt_func(obj, name, case, req, exp, error): + try: + error = False if exp.get('success', True) else True + if name == 'Psbt.VerifyPsbtSign': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + outpoints = req.get('outPointList', []) + if outpoints: + for txin in outpoints: + psbt.verify(OutPoint(txin['txid'], txin['vout'])) + else: + psbt.verify() + else: + raise Exception('unknown name: ' + name) + assert_error(obj, name, case, error) + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + for fail_data in exp.get('failTxins', []): + if fail_data['reason'] in err.message: + return True + assert_message(obj, name, case, err.message) + return True + + +def test_check_finalized_psbt_func(obj, name, case, req, exp, error): + try: + resp = {} + if name == 'Psbt.IsFinalizedPsbt': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + success = True + fail_inputs = [] + outpoints = req.get('outPointList', psbt.get_tx().txin_list) + for txin in outpoints: + if isinstance(txin, TxIn): + outpoint = txin.outpoint + else: + outpoint = OutPoint(txin['txid'], txin['vout']) + if not psbt.is_finalized_input(outpoint): + success = False + fail_inputs.append(outpoint) + finalized_all = psbt.is_finalized() + resp = { + 'success': success, + 'finalizedAll': finalized_all, + 'failInputs': fail_inputs, + } + else: + raise Exception('unknown name: ' + name) + assert_error(obj, name, case, error) + + assert_equal(obj, name, case, exp, resp['success'], 'success') + assert_equal(obj, name, case, exp, + resp['finalizedAll'], 'finalizedAll') + exp_fail_inputs = exp.get('failInputs', []) + assert_match(obj, name, case, len(exp_fail_inputs), + len(resp['failInputs']), 'failInputs') + if len(exp_fail_inputs) == len(resp['failInputs']): + for txin in exp_fail_inputs: + outpoint = OutPoint(txin['txid'], txin['vout']) + if outpoint not in resp['failInputs']: + assert_message(obj, name, case, + f'not found in failInputs: {str(outpoint)}') + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + assert_equal(obj, name, case, exp, err.message) + return True + + +def test_get_utxos_psbt_func(obj, name, case, req, exp, error): + try: + resp = {} + if name == 'Psbt.GetPsbtUtxos': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + resp = [] + in_count, _ = psbt.get_tx_count() + for index in range(in_count): + outpoint, amount, _, _, desc, _ = psbt.get_input_data_by_index( + index) + resp.append(UtxoData(outpoint, amount=amount, descriptor=desc)) + else: + raise Exception('unknown name: ' + name) + assert_error(obj, name, case, error) + + exp_utxos = exp.get('utxos', []) + assert_match(obj, name, case, len(exp_utxos), len(resp), 'utxos') + if len(exp_utxos) == len(resp): + for index, exp_utxo in enumerate(exp_utxos): + utxo: 'UtxoData' = resp[index] + assert_equal(obj, name, case, exp_utxo, + str(utxo.outpoint.txid), 'txid') + assert_equal(obj, name, case, exp_utxo, + utxo.outpoint.vout, 'vout') + assert_equal(obj, name, case, exp_utxo, utxo.amount, 'amount') + assert_equal(obj, name, case, exp_utxo, + str(utxo.descriptor), 'descriptor') + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + assert_equal(obj, name, case, exp, err.message) + return True + + +def test_psbt_func(obj, name, case, req, exp, error): + try: + fee_amount = None + if name == 'Psbt.CreatePsbt': + resp = Psbt.create( + req['version'], + req['locktime'], + network=req.get( + 'network', + Network.MAINNET)) + for txin in req.get('txins', []): + sequence = txin.get('sequence', TxIn.SEQUENCE_DISABLE) + if (sequence == TxIn.SEQUENCE_DISABLE) and ( + req['locktime'] != 0): + sequence = TxIn.SEQUENCE_FINAL + resp.add_input(OutPoint(txin['txid'], txin['vout']), + sequence=sequence) + for txout in req.get('txouts', []): + resp.add_output(txout['amount'], address=txout['address']) + elif name == 'Psbt.ConvertToPsbt': + tx = Transaction(req['tx']) + resp = Psbt.from_transaction( + tx, + permit_sig_data=req.get('permitSigData', False), + network=req.get('network', Network.MAINNET)) + elif name == 'Psbt.JoinPsbts': + resp = Psbt.join_psbts(req['psbts'], network=req.get( + 'network', Network.MAINNET)) + elif name == 'Psbt.CombinePsbt': + resp = Psbt.combine_psbts(req['psbts'], network=req.get( + 'network', Network.MAINNET)) + elif name == 'Psbt.FinalizePsbtInput': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + for input in req.get('inputs', []): + scripts = [] + outpoint = OutPoint(input['txid'], input['vout']) + if 'final_scriptwitness' in input: + for stack in input['final_scriptwitness']: + try: + scripts.append(Script(stack)) + except BaseException: + scripts.append(Script.from_asm([stack])) + psbt.set_input_finalize(outpoint, scripts) + if 'finalScriptsig' in input: + if 'final_scriptwitness' in input: + psbt.set_input_final_scriptsig( + outpoint, input['finalScriptsig']) + else: + psbt.set_input_finalize( + outpoint, Script(input['finalScriptsig'])) + psbt.clear_input_sign_data(outpoint) + resp = psbt + elif name == 'Psbt.FinalizePsbt': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + psbt.finalize() + resp = psbt + elif name == 'Psbt.SignPsbt': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + psbt.sign(privkey=req['privkey'], + has_grind_r=req.get('hasGrindR', True)) + resp = psbt + elif name == 'Psbt.AddPsbtData': + net_type = Network.get(req.get('network', Network.MAINNET)) + psbt = Psbt(req['psbt'], network=net_type) + for input_data in req.get('inputs', []): + txin = input_data['txin'] + input = input_data['input'] + utxo = TxOut(0) + if 'witnessUtxo' in input: + addr = '' + if 'address' in input['witnessUtxo']: + addr = AddressUtil.parse( + input['witnessUtxo']['address']) + utxo = TxOut( + input['witnessUtxo']['amount'], + address=addr, + locking_script=input['witnessUtxo'].get( + 'directLockingScript', + '')) + script = '' if 'redeemScript' not in input else Script( + input['redeemScript']) + tx = '' if 'utxoFullTx' not in input else Transaction( + input['utxoFullTx']) + outpoint = OutPoint(txin['txid'], txin['vout']) + psbt.add_input( + outpoint, + utxo=utxo, + redeem_script=script, + utxo_tx=tx, + sequence=txin.get( + 'sequence', + 4294967295)) + for bip32_data in input.get('bip32Derives', []): + if 'descriptor' in bip32_data: + psbt.set_input_bip32_key( + outpoint, pubkey=bip32_data['descriptor']) + else: + psbt.set_input_bip32_key( + outpoint, + key_data=KeyData(Pubkey(bip32_data['pubkey']), + fingerprint=ByteData( + bip32_data['master_fingerprint']), + bip32_path=bip32_data['path'])) + _, index = psbt.get_tx_count() + for output_data in req.get('outputs', []): + txout = output_data['txout'] + output = output_data['output'] + addr = '' + if 'address' in txout: + addr = AddressUtil.parse( + txout['address']) + script = '' if 'redeemScript' not in output else Script( + output['redeemScript']) + psbt.add_output( + txout['amount'], + address=addr, + locking_script=txout.get( + 'directLockingScript', + ''), + redeem_script=script) + for bip32_data in output.get('bip32Derives', []): + if 'descriptor' in bip32_data: + psbt.set_output_bip32_key( + index, pubkey=bip32_data['descriptor']) + else: + psbt.set_output_bip32_key( + index, key_data=KeyData( + Pubkey( + bip32_data['pubkey']), + fingerprint=ByteData( + bip32_data['master_fingerprint']), + bip32_path=bip32_data['path'])) + index += 1 + resp = psbt + elif name == 'Psbt.SetPsbtData': + net_type = Network.get(req.get('network', Network.MAINNET)) + psbt = Psbt(req['psbt'], network=net_type) + for input_data in req.get('inputs', []): + input = input_data['input'] + outpoint = psbt.get_input_outpoint(input_data.get('index', 0)) + full_tx = input.get('utxoFullTx', '') + utxo = None + if 'witnessUtxo' in input: + addr = '' + if 'address' in input['witnessUtxo']: + addr = AddressUtil.parse( + input['witnessUtxo']['address']) + utxo = TxOut( + input['witnessUtxo']['amount'], + addr, + input['witnessUtxo'].get( + 'directLockingScript', + '')) + if 'redeemScript' in input: + psbt.set_input_script(outpoint, input['redeemScript']) + if full_tx or (utxo is not None): + utxo = TxOut(0) if utxo is None else utxo + psbt.set_input_utxo(outpoint, utxo, full_tx) + for bip32_data in input.get('bip32Derives', []): + if 'descriptor' in bip32_data: + psbt.set_input_bip32_key( + outpoint, pubkey=bip32_data['descriptor']) + else: + psbt.set_input_bip32_key( + outpoint, + key_data=KeyData(Pubkey(bip32_data['pubkey']), + fingerprint=ByteData( + bip32_data['master_fingerprint']), + bip32_path=bip32_data['path'])) + if 'sighash' in input: + psbt.set_input_sighash_type( + outpoint, SigHashType.get(input['sighash'])) + for sig_data in input.get('partialSignature', []): + psbt.set_input_signature( + outpoint, sig_data['pubkey'], sig_data['signature']) + for record in input.get('unknown', []): + psbt.set_input_record( + outpoint, record['key'], record['value']) + for output_data in req.get('outputs', []): + output = output_data['output'] + index = output_data.get('index', 0) + if 'redeemScript' in output: + psbt.set_output_script(index, output['redeemScript']) + for bip32_data in output.get('bip32Derives', []): + if 'descriptor' in bip32_data: + psbt.set_output_bip32_key( + index, pubkey=bip32_data['descriptor']) + else: + psbt.set_output_bip32_key( + index, key_data=KeyData( + Pubkey( + bip32_data['pubkey']), + fingerprint=ByteData( + bip32_data['master_fingerprint']), + bip32_path=bip32_data['path'])) + for record in output.get('unknown', []): + psbt.set_output_record( + index, record['key'], record['value']) + if 'global' in req: + global_data = req['global'] + for xpub_data in global_data.get('xpubs', []): + if 'descriptorXpub' in xpub_data: + psbt.set_global_xpub( + ext_pubkey=xpub_data['descriptorXpub']) + else: + psbt.set_global_xpub( + key_data=KeyData( + ExtPubkey( + xpub_data['xpub']), + fingerprint=ByteData( + xpub_data['master_fingerprint']), + bip32_path=xpub_data['path'])) + for record in global_data.get('unknown', []): + psbt.set_global_record(record['key'], record['value']) + resp = psbt + elif name == 'Psbt.SetPsbtRecord': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + for record in req['records']: + if record['type'] == 'input': + psbt.set_input_record(None, record['key'], record['value'], + record.get('index', 0)) + elif record['type'] == 'output': + psbt.set_output_record(record.get( + 'index', 0), record['key'], record['value']) + elif record['type'] == 'global': + psbt.set_global_record(record['key'], record['value']) + resp = psbt + elif name == 'Psbt.FundPsbt': + psbt = Psbt(req['psbt'], network=req.get( + 'network', Network.MAINNET)) + utxos = [] + desc = req['reservedDescriptor'] + fee_rate = req['feeInfo']['feeRate'] + long_term_fee_rate = req['feeInfo']['longTermFeeRate'] + knapsack_min_change = req['feeInfo']['knapsackMinChange'] + dust_fee_rate = req['feeInfo']['dustFeeRate'] + for utxo in req.get('utxos', []): + utxos.append( + UtxoData( + OutPoint( + utxo['txid'], + utxo['vout']), + amount=utxo['amount'], + descriptor=utxo['descriptor'])) + fee_amount = psbt.fund(utxos, desc, fee_rate, long_term_fee_rate, + dust_fee_rate, knapsack_min_change) + resp = psbt + else: + raise Exception('unknown name: ' + name) + assert_error(obj, name, case, error) + + assert_equal(obj, name, case, exp, str(resp), 'psbt') + if isinstance(resp, Psbt) and ('hex' in exp): + assert_equal(obj, name, case, exp, str(resp.get_bytes()), 'hex') + if fee_amount: + assert_equal(obj, name, case, exp, fee_amount, 'feeAmount') + + except CfdError as err: + if not error: + print('{}:{} req={}'.format(name, case, req)) + raise err + assert_equal(obj, name, case, exp, err.message) + return True + + +class TestPsbt(TestCase): + def setUp(self): + self.test_list = load_json_file('psbt_test.json') + + def test_psbt_decode(self): + exec_test(self, 'Psbt.DecodePsbt', test_decode_psbt_func) + + def test_psbt_create(self): + exec_test(self, 'Psbt.CreatePsbt', test_psbt_func) + + def test_psbt_convert(self): + exec_test(self, 'Psbt.ConvertToPsbt', test_psbt_func) + + def test_psbt_join(self): + exec_test(self, 'Psbt.JoinPsbts', test_psbt_func) + + def test_psbt_combine(self): + exec_test(self, 'Psbt.CombinePsbt', test_psbt_func) + + def test_psbt_finalize_input(self): + exec_test(self, 'Psbt.FinalizePsbtInput', test_psbt_func) + + def test_psbt_finalize(self): + exec_test(self, 'Psbt.FinalizePsbt', test_psbt_func) + + def test_psbt_sign(self): + exec_test(self, 'Psbt.SignPsbt', test_psbt_func) + + def test_psbt_verify(self): + exec_test(self, 'Psbt.VerifyPsbtSign', test_verify_psbt_func) + + def test_psbt_add(self): + exec_test(self, 'Psbt.AddPsbtData', test_psbt_func) + + def test_psbt_set_data(self): + exec_test(self, 'Psbt.SetPsbtData', test_psbt_func) + + def test_psbt_set_record(self): + exec_test(self, 'Psbt.SetPsbtRecord', test_psbt_func) + + def test_psbt_is_finalized(self): + exec_test(self, 'Psbt.IsFinalizedPsbt', test_check_finalized_psbt_func) + + def test_psbt_get_utxos(self): + exec_test(self, 'Psbt.GetPsbtUtxos', test_get_utxos_psbt_func) + + def test_psbt_fund(self): + exec_test(self, 'Psbt.FundPsbt', test_psbt_func) + + def test_decodepsbt(self): + psbt_str = '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' # noqa: E501 + json_str = Psbt.parse_to_json(psbt_str, has_detail=True) + exp_str = """\ +{"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"]}}]},"tx_hex":"0200000002267ffd76eae6b6c13ffb86ceabaa24d42485520233c2c3805e0ad764709578c00100000000ffffffff7d25462a42ff2cb1ba5401e5893259db3ca4bcf5cf6ab9715d35163d31c9ecc00000000000ffffffff0200e1f50500000000160014b322bddce633b851ac7370ab454f0b367a0654e500e1f50500000000160014cab8c53a6e8fc0296d1cd3915a307d51c491a55500000000","version":0,"unknown":[],"inputs":[{"non_witness_utxo_hex":"02000000000101f1993fe8e7189542ee4506258e170201be292703cd275acb09ece16672fd848b0000000017160014ac9ef80b27af1c9d95c1db5d761319322bc42fc5ffffffff02080410240100000016001409de2a0431cbb3444fc22cad9d9a0fd09639721000e1f5050000000017a914509f5985f4e90a14fb90e39316fdb4f3ac975307870247304402201e07df721c3322419e8f36d07eeae4795975ba0d9d19630ca3cd3dc0d4967172022015428e7be06b6567501539050bd791a380f00bbddbc5097ea97ba7be4017114a0121024aef43b1d5ac7ba5014998d63ceac583959d1fdc66ea2699cd84eeaf82a2830600000000","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"}},"partial_signatures":[{"pubkey":"02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe","signature":"30440220299862a67a9b454d6cda5fee34a52d9089bfa024444d88031c34a98e298bdbca02204b47fcf507b80954108e0375673fdbdd084c5fbb994f6c951720f2eb2f6bb86601"}],"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","descriptor":"[2a704760/44'/0'/0'/1/1]02565248460b3c186decf13db06f0724fd50b47cc3e489c30076c8363d5038cefe"}]},{"non_witness_utxo_hex":"0200000001c6d2ea36e2e802b52ddac665dacbed2f831b5263459e1ca734f5c945d7515e40000000006a473044022011b96c7d2d0d2e8dcb37138e18acc460752965add9cb8787df5c349df0e2ae6602202e93af31b64f5166e5605819555dabec57be794300fadb37052f31dddea9905c012103e3d244a3967e0b87765fda86c5ff38885f74993953b9584388aef30b26af6aecffffffff017851cd1d000000001976a9148d20443a91969e3bca0e240cd0ffe4dc98c63de288ac00000000","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"]}}]},"partial_signatures":[{"pubkey":"02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7","signature":"3044022066ec956e96783cd6122c9c07732a3ae9931b69abdaff1f362c28ed5fa96728d302206a16d4ae46fed48d3d0492737613bd9b51fe3e2453b4446e447c65bf2c967e1001"}],"bip32_derivs":[{"pubkey":"02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7","master_fingerprint":"9d6b6d86","path":"44'/0'/0'/0/1","descriptor":"[9d6b6d86/44'/0'/0'/0/1]02d9f6888f285a15a6a1880a2202c2b230a42d77cf62da6a48aca4198262cda3c7"}]}],"outputs":[{"bip32_derivs":[{"pubkey":"03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81","master_fingerprint":"2a704760","path":"44'/0'/0'/0/2","descriptor":"[2a704760/44'/0'/0'/0/2]03473bfc8c770c1b220a2e7aae4badf6c0d7eaf29028d5b29d3438012bb289ef81"}]},{"bip32_derivs":[{"pubkey":"036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c","master_fingerprint":"9d6b6d86","path":"44'/0'/0'/0/2","descriptor":"[9d6b6d86/44'/0'/0'/0/2]036474aff2633c351865539fb52b62b9d6fb9e4e23576628e1f0a0a7993458e06c"}]}],"fee":399995000}\ +""" # noqa: E501 + self.assertEqual(exp_str, json_str, 'Fail: decodepsbt') diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 5997722..f45cfe5 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -6,11 +6,30 @@ from cfd.address import AddressUtil from cfd.descriptor import parse_descriptor from cfd.script import HashType -from cfd.key import Network, SigHashType, SignParameter -from cfd.transaction import OutPoint, TxIn, TxOut, Transaction, Txid, UtxoData +from cfd.key import Network, SigHashType, SignParameter, SchnorrUtil +from cfd.transaction import OutPoint, TxIn, TxOut, Transaction, Txid, \ + UtxoData, CODE_SEPARATOR_POSITION_FINAL import json +def load_utxo_list(request): + result = [] + utxos = request.get('utxos', []) + for utxo in utxos: + desc = utxo.get('descriptor', '') + if not desc: + if 'address' in utxo: + addr = utxo['address'] + desc = f'addr({addr})' + if 'lockingScript' in utxo: + script = utxo['lockingScript'] + desc = f'raw({script})' + data = UtxoData(txid=utxo['txid'], vout=utxo['vout'], + amount=utxo['amount'], descriptor=desc) + result.append(data) + return result + + def test_transaction_func1(obj, name, case, req, exp, error): try: resp = None @@ -74,6 +93,7 @@ def test_transaction_func2(obj, name, case, req, exp, error): if 'txin' in req: txin = req['txin'] if name == 'Transaction.SignWithPrivkey': + utxos = load_utxo_list(req) _sighashtype = SigHashType.get( txin.get('sighashType', 'all'), txin.get('sighashAnyoneCanPay', False)) @@ -82,12 +102,16 @@ def test_transaction_func2(obj, name, case, req, exp, error): txin['hashType'], txin['privkey'], amount=txin.get('amount', 0), - sighashtype=_sighashtype) + sighashtype=_sighashtype, + grind_r=txin.get('isGrindR', True), + utxos=utxos, + aux_rand=txin.get('auxRand', None), + annex=txin.get('annex', None)) elif name == 'Transaction.AddSign': hash_type = HashType.P2SH if txin.get('isWitness', True): hash_type = HashType.P2WSH - for param in txin.get('signParam', []): + for param in txin.get('signParams', []): _sighashtype = SigHashType.get( param.get('sighashType', 'all'), param.get('sighashAnyoneCanPay', False)) @@ -136,7 +160,7 @@ def test_transaction_func2(obj, name, case, req, exp, error): elif name == 'Transaction.AddScriptHashSign': signature_list = [] - for param in txin.get('signParam', []): + for param in txin.get('signParams', []): _sighashtype = SigHashType.get( param.get('sighashType', 'all'), param.get('sighashAnyoneCanPay', False)) @@ -157,8 +181,40 @@ def test_transaction_func2(obj, name, case, req, exp, error): if 'multisig p2wsh' == case: print(str(resp)) + elif name == 'Transaction.AddTaprootSchnorrSign': + _sighashtype = SigHashType.get( + txin.get('sighashType', 'default'), + txin.get('sighashAnyoneCanPay', False)) + resp.add_taproot_sign( + OutPoint(txin['txid'], txin['vout']), + signature=txin['signature'], + sighashtype=_sighashtype, + annex=txin.get('annex', None)) + + elif name == 'Transaction.AddTapscriptSign': + signature_list = [] + for param in txin.get('signParams', []): + _sighashtype = SigHashType.get( + param.get('sighashType', 'default'), + param.get('sighashAnyoneCanPay', False)) + try: + sign = SignParameter( + param['hex'], + sighashtype=_sighashtype, use_der_encode=False) + signature_list.append(sign) + except CfdError: + signature_list.append(param['hex']) + + resp.add_tapscript_sign( + OutPoint(txin['txid'], txin['vout']), + signature_list=signature_list, + tapscript=txin['tapscript'], + control_block=txin['controlBlock'], + annex=txin.get('annex', None)) + elif name == 'Transaction.VerifySign': err_list = [] + utxos = load_utxo_list(req) for txin in req.get('txins', []): hash_type = HashType.P2WPKH addr = txin.get('address', '') @@ -174,7 +230,7 @@ def test_transaction_func2(obj, name, case, req, exp, error): try: resp.verify_sign( OutPoint(txin['txid'], txin['vout']), - addr, hash_type, txin.get('amount', 0)) + addr, hash_type, txin.get('amount', 0), utxos) except CfdError as err: _dict = {'txid': txin['txid'], 'vout': txin['vout']} _dict['reason'] = err.message @@ -184,14 +240,38 @@ def test_transaction_func2(obj, name, case, req, exp, error): resp = {'success': success, 'failTxins': err_list} elif name == 'Transaction.VerifySignature': - resp = resp.verify_signature( - OutPoint(txin['txid'], txin['vout']), - signature=txin.get('signature', ''), - hash_type=txin['hashType'], - amount=txin.get('amount', 0), - pubkey=txin['pubkey'], - redeem_script=txin.get('redeemScript', ''), - sighashtype=txin.get('sighashType', 'all')) + if txin['hashType'] == 'taproot': + utxos = load_utxo_list(req) + txin = req['txin'] + script = txin.get('redeemScript', '') + pubkey = txin['pubkey'] if not script else '' + _sighashtype = SigHashType.get( + txin.get('sighashType', 'all'), + txin.get('sighashAnyoneCanPay', False)) + sighash = resp.get_sighash( + OutPoint(txin['txid'], txin['vout']), + txin['hashType'], + amount=txin.get('amount', 0), + pubkey=pubkey, + redeem_script=script, + sighashtype=_sighashtype, + utxos=utxos, + tapleaf_hash=txin.get('', ''), + annex=txin.get('annex', None), + codeseparator_pos=txin.get('codeSeparatorPosition', + CODE_SEPARATOR_POSITION_FINAL)) + resp = SchnorrUtil.verify(txin.get('signature', ''), sighash, + txin['pubkey'], + is_message_hashed=True) + else: + resp = resp.verify_signature( + OutPoint(txin['txid'], txin['vout']), + signature=txin.get('signature', ''), + hash_type=txin['hashType'], + amount=txin.get('amount', 0), + pubkey=txin['pubkey'], + redeem_script=txin.get('redeemScript', ''), + sighashtype=txin.get('sighashType', 'all')) else: return False @@ -229,8 +309,9 @@ def test_transaction_func3(obj, name, case, req, exp, error): if name == 'Transaction.Decode': resp = Transaction.parse_to_json( req.get('hex', ''), req.get('network', 'mainnet')) - elif name == 'Transaction.CreateSighash': + elif name in ['Transaction.CreateSighash', 'Transaction.GetSighash']: resp = Transaction.from_hex(req['tx']) + utxos = load_utxo_list(req) txin = req['txin'] key_data = txin['keyData'] pubkey = key_data['hex'] if key_data['type'] == 'pubkey' else '' @@ -244,7 +325,13 @@ def test_transaction_func3(obj, name, case, req, exp, error): amount=txin.get('amount', 0), pubkey=pubkey, redeem_script=script, - sighashtype=_sighashtype) + sighashtype=_sighashtype, + utxos=utxos, + tapleaf_hash=txin.get('', ''), + annex=txin.get('annex', None), + codeseparator_pos=txin.get('codeSeparatorPosition', + CODE_SEPARATOR_POSITION_FINAL)) + elif name == 'Transaction.GetWitnessStackNum': resp = Transaction.from_hex(req['tx']) txin = req['txin'] @@ -341,6 +428,8 @@ def test_bitcoin_tx_func(obj, name, case, req, exp, error): assert_equal(obj, name, case, exp, txout_fee, 'txoutFeeAmount') assert_equal(obj, name, case, exp, utxo_fee, 'utxoFeeAmount') elif name == 'Bitcoin.FundTransaction': + if resp['hex'] != exp['hex']: + print(resp['hex']) assert_equal(obj, name, case, exp, resp['hex'], 'hex') assert_equal(obj, name, case, exp, resp['feeAmount'], 'feeAmount') exp_addr_list = exp['usedAddresses'] @@ -501,12 +590,6 @@ def test_create_raw_transaction(self): "0200000000010201000000000000000000000000000000000000000000000000000000000000000200000000ffffffff01000000000000000000000000000000000000000000000000000000000000000300000000ffffffff0310270000000000001600148b756cbd98f4f55e985f80437a619d47f0732a941027000000000000160014c0a3dd0b7c1b3281be91112e16ce931dbac2a97950c3000000000000160014ad3abd3c325e40e20d89aa054dd980b97494f16c02473044022034db802aad655cd9be589075fc8ef325b6ffb8c24e5b27eb87bde8ad38f5fd7a0220364c916c8e8fc0adf714d7148cd1c6dc6f3e67d55471e57233b1870c65ec2727012103782f0ea892d7000e5f0f82b6ff283382a76500137a542bb0a616530094a8f54c0000000000", # noqa: E501 tx.hex) - addr11 = AddressUtil.p2wpkh(pubkey1, Network.REGTEST) - try: - tx.verify_sign( - outpoint=outpoint1, - address=addr11, - hash_type=addr11.hash_type, - amount=50000) - except Exception as err: - self.assertIsNone(err) + def test_empty_input(self): + txout = TxOut(1000) + self.assertEqual('', str(txout.locking_script)) diff --git a/tests/util.py b/tests/util.py index fcca5cb..cf0e8c7 100644 --- a/tests/util.py +++ b/tests/util.py @@ -74,7 +74,12 @@ def assert_equal(test_obj, test_name, case, expect, value, err_msg, _value, 'Fail: {}:{}'.format(test_name, case)) elif param_name in expect: - if isinstance(expect[param_name], str) and (not isinstance(_value, str)): + if isinstance( + expect[param_name], + str) and ( + not isinstance( + _value, + str)): _value = str(_value) fail_param_name = log_name if log_name else param_name test_obj.assertEqual( diff --git a/tools/function_converter.py b/tools/function_converter.py index 659e6bd..ca19861 100644 --- a/tools/function_converter.py +++ b/tools/function_converter.py @@ -73,7 +73,11 @@ def parse_func(func_str): ['uint64_t', 'c_uint64'], ['uint64_t*', 'c_uint64_p'], ['unsigned char', 'c_ubyte'], + ['uint8_t', 'c_uint8'], + ['uint8_t*', 'c_uint8_p'], ['double', 'c_double'], + ['CfdPsbtRecordType', 'c_int'], + ['CfdPsbtRecordKind', 'c_int'], ]