diff --git a/.github/workflows/check_pre-merge_develop.yml b/.github/workflows/check_pre-merge_develop.yml index 491fb787..b4355b17 100644 --- a/.github/workflows/check_pre-merge_develop.yml +++ b/.github/workflows/check_pre-merge_develop.yml @@ -61,14 +61,12 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] shared: [on, off] elements: [on, off] exclude: - os: ubuntu-18.04 elements: off - - os: ubuntu-16.04 - elements: off steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/check_pre-merge_master.yml b/.github/workflows/check_pre-merge_master.yml index 1c64ca0b..53fb3588 100644 --- a/.github/workflows/check_pre-merge_master.yml +++ b/.github/workflows/check_pre-merge_master.yml @@ -63,14 +63,12 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] shared: [on, off] elements: [on, off] exclude: - os: ubuntu-18.04 elements: off - - os: ubuntu-16.04 - elements: off steps: - uses: actions/checkout@v2 diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6eeefdea..12bbcd91 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -55,7 +55,7 @@ if(LIBWALLY_TARGET_VERSION) set(LIBWALLY_TARGET_TAG ${LIBWALLY_TARGET_VERSION}) message(STATUS "[external project local] libwally-core target=${LIBWALLY_TARGET_VERSION}") else() -set(LIBWALLY_TARGET_TAG refs/tags/cfd-0.3.7) +set(LIBWALLY_TARGET_TAG refs/tags/cfd-0.3.8) endif() if(LIBWALLY_TARGET_URL) set(LIBWALLY_TARGET_REP ${LIBWALLY_TARGET_URL}) diff --git a/include/cfdcore/cfdcore_address.h b/include/cfdcore/cfdcore_address.h index c7d28813..4cc26db4 100644 --- a/include/cfdcore/cfdcore_address.h +++ b/include/cfdcore/cfdcore_address.h @@ -531,6 +531,34 @@ class CFD_CORE_EXPORT Address { */ Script GetLockingScript() const; + /** + * @brief Get the pegout address. + * @param[in] type network type + * @param[in] locking_script pegout locking script + * @return address + */ + static Address GetPegoutAddress(NetType type, const Script& locking_script); + /** + * @brief Get the pegout address. + * @param[in] type network type + * @param[in] locking_script pegout locking script + * @param[in] network_parameter network prefix parameter + * @return address + */ + static Address GetPegoutAddress( + NetType type, const Script& locking_script, + const AddressFormatData& network_parameter); + /** + * @brief Get the pegout address. + * @param[in] type network type + * @param[in] locking_script pegout locking script + * @param[in] network_parameters network prefix list + * @return address + */ + static Address GetPegoutAddress( + NetType type, const Script& locking_script, + const std::vector& network_parameters); + private: /** * @brief calculate P2SH Address diff --git a/include/cfdcore/cfdcore_block.h b/include/cfdcore/cfdcore_block.h new file mode 100644 index 00000000..2fe6f5b2 --- /dev/null +++ b/include/cfdcore/cfdcore_block.h @@ -0,0 +1,154 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_block.h + * + * @brief The block related class definition. + */ +#ifndef CFD_CORE_INCLUDE_CFDCORE_CFDCORE_BLOCK_H_ +#define CFD_CORE_INCLUDE_CFDCORE_CFDCORE_BLOCK_H_ + +#include +#include + +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_coin.h" +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_transaction.h" + +namespace cfd { +namespace core { + +/** + * @brief block header. + */ +struct BlockHeader { + uint32_t version = 0; //!< version + BlockHash prev_block_hash; //!< previous block hash + BlockHash merkle_root_hash; //!< merkle root hash + uint32_t time = 0; //!< time + uint32_t bits = 0; //!< bits + uint32_t nonce = 0; //!< nonce +}; + +/** + * @brief block data class. + */ +class CFD_CORE_EXPORT Block { + public: + /** + * @brief default constructor + */ + Block(); + /** + * @brief constructor + * @param[in] hex hex string + */ + explicit Block(const std::string& hex); + /** + * @brief constructor + * @param[in] data byte data + */ + explicit Block(const ByteData& data); + /** + * @brief destructor. + */ + virtual ~Block() { + // do nothing + } + /** + * @brief copy constructor. + * @param[in] object object + */ + Block(const Block& object); + /** + * @brief copy constructor. + * @param[in] object object + * @return object + */ + Block& operator=(const Block& object); + /** + * @brief Get a hex string. + * @return hex string + */ + std::string GetHex() const; + /** + * @brief Get a ByteData object. + * @return ByteData object. + */ + ByteData GetData() const; + /** + * @brief check valid data. + * @retval true valid. + * @retval false invalid. + */ + bool IsValid() const; + /** + * @brief Get a BlockHash. + * @return block hash. + */ + BlockHash GetBlockHash() const; + + /** + * @brief get txoutproof. + * @param[in] txid target txid + * @return txoutproof. + */ + ByteData GetTxOutProof(const Txid& txid) const; + /** + * @brief get txoutproof. + * @param[in] txids target txid list + * @return txoutproof. + */ + ByteData GetTxOutProof(const std::vector& txids) const; + + /** + * @brief get txid. + * @param[in] index tx index + * @return txid. + */ + Txid GetTxid(uint32_t index) const; + /** + * @brief get txid list. + * @return txid list. + */ + std::vector GetTxids() const; + /** + * @brief exist txid. + * @param[in] txid txid + * @retval true exist + * @retval false not exist + */ + bool ExistTxid(const Txid& txid) const; + /** + * @brief Get the transaction. + * @param[in] txid txid + * @return transaction + */ + Transaction GetTransaction(const Txid& txid) const; + /** + * @brief Get the transaction count. + * @return transaction count + */ + uint32_t GetTransactionCount() const; + /** + * @brief get block header. + * @return block header. + */ + BlockHeader GetBlockHeader() const; + /** + * @brief Serialize block header. + * @return Serialized block header. + */ + ByteData SerializeBlockHeader() const; + + private: + ByteData data_; ///< byte data + BlockHeader header_; ///< block header + std::vector txs_; ///< transaction data list + std::vector txids_; ///< transaction id list +}; + +} // namespace core +} // namespace cfd + +#endif // CFD_CORE_INCLUDE_CFDCORE_CFDCORE_BLOCK_H_ diff --git a/include/cfdcore/cfdcore_bytedata.h b/include/cfdcore/cfdcore_bytedata.h index 35b30ae6..89e5a50e 100644 --- a/include/cfdcore/cfdcore_bytedata.h +++ b/include/cfdcore/cfdcore_bytedata.h @@ -181,6 +181,21 @@ class CFD_CORE_EXPORT ByteData { return result.Concat(args...); } + /** + * @brief Split a byte data. + * @param[in] split_size_list split size list. + * @return split byte data list. + */ + std::vector SplitData( + const std::vector& split_size_list) const; + + /** + * @brief Split a byte data. + * @param[in] size_from_top size from top. + * @return split byte data list. + */ + ByteData SplitData(uint32_t size_from_top) const; + /** * @brief Push to back. * @param[in] back_insert_data back insert data. @@ -565,6 +580,13 @@ class CFD_CORE_EXPORT Serializer { static constexpr uint8_t kViTag64 = 255; //!< VarInt64 static constexpr uint8_t kViMax8 = 252; //!< VarInt8 + /** + * @brief check big endian. + * @retval true big endian. + * @retval false little endian. + */ + static bool IsBigEndian(); + /** * @brief get variable integer size. * @param[in] value value @@ -667,6 +689,12 @@ class CFD_CORE_EXPORT Serializer { */ void AddDirectNumber(int64_t number); + /** + * @brief add direct number. + * @param[in] number value + */ + void AddDirectBigEndianNumber(uint32_t number); + /** * @brief add direct byte array. * @param[in] buffer buffer @@ -772,6 +800,11 @@ class CFD_CORE_EXPORT Deserializer { * @return uint8 */ uint8_t ReadUint8(); + /** + * @brief read uint32 from big endian. + * @return uint32 + */ + uint32_t ReadUint32FromBigEndian(); /** * @brief read variable integer. @@ -808,6 +841,13 @@ class CFD_CORE_EXPORT Deserializer { */ uint32_t GetReadSize(); + /** + * @brief Check EOF. + * @retval true already eof. + * @retval false not eof. + */ + bool HasEof(); + protected: std::vector buffer_; //!< buffer uint32_t offset_; //!< offset diff --git a/include/cfdcore/cfdcore_descriptor.h b/include/cfdcore/cfdcore_descriptor.h index deb7e13b..78ff1672 100644 --- a/include/cfdcore/cfdcore_descriptor.h +++ b/include/cfdcore/cfdcore_descriptor.h @@ -411,6 +411,19 @@ class CFD_CORE_EXPORT DescriptorScriptReference { explicit DescriptorScriptReference( const Address& address_script, const std::vector& address_prefixes); + /** + * @brief constructor. + * @param[in] locking_script locking script + * @param[in] script_type script type + * @param[in] key_list key(pubkey, extprivkey, extpubkey) list + * @param[in] tapbranch taproot tapbranch + * @param[in] address_prefixes address prefix list + */ + explicit DescriptorScriptReference( + const Script& locking_script, DescriptorScriptType script_type, + const std::vector& key_list, + const TapBranch& tapbranch, + const std::vector& address_prefixes); /** * @brief constructor. * @param[in] locking_script locking script @@ -525,6 +538,17 @@ class CFD_CORE_EXPORT DescriptorScriptReference { std::vector GetKeyList() const; // taproot api + /** + * @brief exist taproot tapbranch. + * @retval true exist + * @retval false not exist + */ + bool HasTapBranch() const; + /** + * @brief getting taproot tapbranch. + * @return tapbranch + */ + TapBranch GetTapBranch() const; /** * @brief exist taproot script tree. * @retval true exist @@ -550,6 +574,8 @@ class CFD_CORE_EXPORT DescriptorScriptReference { Script redeem_script_; //!< redeem script Address address_script_; //!< address script data uint32_t req_num_; //!< multisig require signature number + TapBranch tapbranch_; //!< taproot branch + bool is_tapbranch_; //!< exist tapbranch TaprootScriptTree script_tree_; //!< taproot script tree //! child script std::shared_ptr child_script_ = nullptr; @@ -658,20 +684,12 @@ class CFD_CORE_EXPORT DescriptorNode { * @return pubkey */ Pubkey GetPubkey(std::vector* array_argument) const; - /** - * @brief get schnorr pubkey. - * @param[in] array_argument argument array. - * @return schnorr pubkey - */ - SchnorrPubkey GetSchnorrPubkey( - std::vector* array_argument) const; /** * @brief get script tree. * @param[in] array_argument argument array. * @return TapBranch */ - TaprootScriptTree GetScriptTree( - std::vector* array_argument) const; + TapBranch GetTapBranch(std::vector* array_argument) const; /** * @brief get key reference object. * @param[in] array_argument argument diff --git a/include/cfdcore/cfdcore_elements_transaction.h b/include/cfdcore/cfdcore_elements_transaction.h index d568fab1..297be76c 100644 --- a/include/cfdcore/cfdcore_elements_transaction.h +++ b/include/cfdcore/cfdcore_elements_transaction.h @@ -367,7 +367,7 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { * @param[in] addr_type address type * @param[in] redeem_script redeem script * @param[in] pegin_btc_tx_size pegin bitcoin transaction size - * @param[in] fedpeg_script fedpeg script + * @param[in] claim_script claim script * @param[in] is_issuance issuance transaction * @param[in] is_blind blind transaction (for issuance/reissuance) * @param[out] witness_area_size witness area size @@ -380,24 +380,25 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { * 0 to 64. Number of bits of the value to keep private. 0 is auto. * @param[in,out] rangeproof_size rangeproof size. * 0 is calclate from exponent and minimum bits. not 0 is using value. + * @param[in] pegin_txoutproof_size pegin txoutproof size. * @return TxIn size. */ static uint32_t EstimateTxInSize( AddressType addr_type, Script redeem_script = Script(), - uint32_t pegin_btc_tx_size = 0, Script fedpeg_script = Script(), + uint32_t pegin_btc_tx_size = 0, Script claim_script = Script(), bool is_issuance = false, bool is_blind = false, uint32_t* witness_area_size = nullptr, uint32_t* no_witness_area_size = nullptr, bool is_reissuance = false, const Script* scriptsig_template = nullptr, int exponent = 0, int minimum_bits = kDefaultBlindMinimumBits, - uint32_t* rangeproof_size = nullptr); + uint32_t* rangeproof_size = nullptr, uint32_t pegin_txoutproof_size = 0); /** * @brief estimate txin's virtual size direct. * @param[in] addr_type address type * @param[in] redeem_script redeem script * @param[in] pegin_btc_tx_size pegin bitcoin transaction size - * @param[in] fedpeg_script fedpeg script + * @param[in] claim_script claim script * @param[in] is_issuance issuance transaction * @param[in] is_blind blind transaction (for issuance/reissuance) * @param[in] is_reissuance reissuance transaction @@ -408,15 +409,16 @@ class CFD_CORE_EXPORT ConfidentialTxIn : public AbstractTxIn { * 0 to 64. Number of bits of the value to keep private. 0 is auto. * @param[in,out] rangeproof_size rangeproof size. * 0 is calclate from exponent and minimum bits. not 0 is using value. + * @param[in] pegin_txoutproof_size pegin txoutproof size. * @return TxIn virtual size. */ static uint32_t EstimateTxInVsize( AddressType addr_type, Script redeem_script = Script(), - uint32_t pegin_btc_tx_size = 0, Script fedpeg_script = Script(), + uint32_t pegin_btc_tx_size = 0, Script claim_script = Script(), bool is_issuance = false, bool is_blind = false, bool is_reissuance = false, const Script* scriptsig_template = nullptr, int exponent = 0, int minimum_bits = kDefaultBlindMinimumBits, - uint32_t* rangeproof_size = nullptr); + uint32_t* rangeproof_size = nullptr, uint32_t pegin_txoutproof_size = 0); /** * @brief constructor. @@ -651,7 +653,7 @@ class CFD_CORE_EXPORT ConfidentialTxInReference * -1 to 18. -1 is public value. 0 is most private. * @param[in] minimum_bits rangeproof blinding bits. * 0 to 64. Number of bits of the value to keep private. 0 is auto. - * @param[in] fedpeg_script fedpeg script + * @param[in] claim_script fedpeg script * @param[in] scriptsig_template scriptsig template * @param[out] witness_area_size witness area size * @param[out] no_witness_area_size no witness area size @@ -661,7 +663,7 @@ class CFD_CORE_EXPORT ConfidentialTxInReference AddressType addr_type, Script redeem_script = Script(), bool is_blind = false, int exponent = 0, int minimum_bits = kDefaultBlindMinimumBits, - Script fedpeg_script = Script(), + Script claim_script = Script(), const Script* scriptsig_template = nullptr, uint32_t* witness_area_size = nullptr, uint32_t* no_witness_area_size = nullptr) const; @@ -675,7 +677,7 @@ class CFD_CORE_EXPORT ConfidentialTxInReference * -1 to 18. -1 is public value. 0 is most private. * @param[in] minimum_bits rangeproof blinding bits. * 0 to 64. Number of bits of the value to keep private. 0 is auto. - * @param[in] fedpeg_script fedpeg script + * @param[in] claim_script fedpeg script * @param[in] scriptsig_template scriptsig template * @return TxIn virtual size. */ @@ -683,7 +685,7 @@ class CFD_CORE_EXPORT ConfidentialTxInReference AddressType addr_type, Script redeem_script = Script(), bool is_blind = false, int exponent = 0, int minimum_bits = kDefaultBlindMinimumBits, - Script fedpeg_script = Script(), + Script claim_script = Script(), const Script* scriptsig_template = nullptr) const; private: @@ -707,6 +709,16 @@ struct RangeProofInfo { uint64_t max_value; //!< the maximum value that commit could have }; +/** + * @brief Unblind output information structure + */ +struct UnblindParameter { + ConfidentialAssetId asset; //!< confidential asset + BlindFactor abf; //!< asset blind factor + BlindFactor vbf; //!< value blind factor + ConfidentialValue value; //!< unblinded value +}; + /** * @brief Class that holds TxOut information of Confidential Transaction */ @@ -848,6 +860,13 @@ class CFD_CORE_EXPORT ConfidentialTxOut : public AbstractTxOut { */ ByteData256 GetWitnessHash() const; + /** + * @brief Get unblind data. + * @param[in] blinding_key blinding key + * @return unblind parameter + */ + UnblindParameter Unblind(const Privkey& blinding_key) const; + /** * @brief Create ConfidentialTxOut object for the destroy amount. * @param[in] asset destroy asset. @@ -985,16 +1004,6 @@ struct IssuanceParameter { ConfidentialAssetId token; //!< token asset }; -/** - * @brief Unblind output information structure - */ -struct UnblindParameter { - ConfidentialAssetId asset; //!< confidential asset - BlindFactor abf; //!< asset blind factor - BlindFactor vbf; //!< value blind factor - ConfidentialValue value; //!< unblinded value -}; - /** * @brief Information structure for Blind */ @@ -1129,6 +1138,12 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { * @param[in] index txin index */ void RemoveTxIn(uint32_t index); + /** + * @brief Set the sequence number. + * @param[in] tx_in_index TxIn index + * @param[in] sequence sequence + */ + void SetTxInSequence(uint32_t tx_in_index, uint32_t sequence); /** * @brief Set unlocking script. * @param[in] tx_in_index TxIn index @@ -1639,6 +1654,33 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { NetType elements_net_type = NetType::kLiquidV1, Address* descriptor_derive_address = nullptr); + /** + * @brief Get pegout address from Descriptor information. + * @param[in] bitcoin_descriptor descriptor + * @param[in] bip32_counter bip32 counter + * @param[in] net_type network type. + * @param[in] elements_net_type elements network type. + * @return descriptor derive address + */ + static Address GetPegoutAddressFromDescriptor( + const std::string& bitcoin_descriptor, uint32_t bip32_counter, + NetType net_type, NetType elements_net_type); + + /** + * @brief Unblind processing is applied to blinded data + * @param[in] nonce nonce + * @param[in] blinding_key blinding private key + * @param[in] rangeproof asset amount rangeproof + * @param[in] value_commitment blind value commitement + * @param[in] extra unblind need data + * @param[in] asset confidential asset id + * @return UnblindParameter structure output when unblinded + */ + static UnblindParameter CalculateUnblindData( + const ConfidentialNonce& nonce, const Privkey& blinding_key, + const ByteData& rangeproof, const ConfidentialValue& value_commitment, + const Script& extra, const ConfidentialAssetId& asset); + protected: std::vector vin_; ///< TxIn array std::vector vout_; ///< TxOut array @@ -1731,21 +1773,6 @@ class CFD_CORE_EXPORT ConfidentialTransaction : public AbstractTransaction { const void* buffer, size_t buffer_size, size_t explicit_size, uint8_t* address); - /** - * @brief Unblind processing is applied to blinded data - * @param[in] nonce nonce - * @param[in] blinding_key blinding private key - * @param[in] rangeproof asset amount rangeproof - * @param[in] value_commitment blind value commitement - * @param[in] extra unblind need data - * @param[in] asset confidential asset id - * @return UnblindParameter structure output when unblinded - */ - static UnblindParameter CalculateUnblindData( - const ConfidentialNonce& nonce, const Privkey& blinding_key, - const ByteData& rangeproof, const ConfidentialValue& value_commitment, - const Script& extra, const ConfidentialAssetId& asset); - /** * @brief Unblind processing is applied to the blinded Issue data * @param[in] blinding_key blinding private key diff --git a/include/cfdcore/cfdcore_script.h b/include/cfdcore/cfdcore_script.h index 4827ace4..d3e9884e 100644 --- a/include/cfdcore/cfdcore_script.h +++ b/include/cfdcore/cfdcore_script.h @@ -544,11 +544,12 @@ class CFD_CORE_EXPORT ScriptOperator { /** * @brief Check if it is OP_SUCCESSxx. - * @param[in] op_code OP Code + * @param[in] op_code OP Code + * @param[in] is_elements elements mode flag * @retval true OP_SUCCESSxx * @retval false other */ - static bool IsOpSuccess(ScriptType op_code); + static bool IsOpSuccess(ScriptType op_code, bool is_elements = false); /** * @brief get data type. diff --git a/include/cfdcore/cfdcore_taproot.h b/include/cfdcore/cfdcore_taproot.h index 0b1ba41b..cb74bda5 100644 --- a/include/cfdcore/cfdcore_taproot.h +++ b/include/cfdcore/cfdcore_taproot.h @@ -109,6 +109,29 @@ class CFD_CORE_EXPORT TapBranch { */ virtual std::vector GetNodeList() const; + /** + * @brief Get tweak. + * @param[in] internal_pubkey internal pubkey + * @return tweak. + */ + ByteData256 GetTapTweak(const SchnorrPubkey& internal_pubkey) const; + /** + * @brief Get a tweaked pubkey. + * @param[in] internal_pubkey internal pubkey + * @param[out] parity parity flag. + * @return tweaked schnorr pubkey. + */ + SchnorrPubkey GetTweakedPubkey( + const SchnorrPubkey& internal_pubkey, bool* parity = nullptr) const; + /** + * @brief Get a tweaked privkey. + * @param[in] internal_privkey internal privkey + * @param[out] parity parity flag. + * @return tweaked privkey. + */ + Privkey GetTweakedPrivkey( + const Privkey& internal_privkey, bool* parity = nullptr) const; + /** * @brief find tapscript in this branch. * @param[in] tapscript tapscript @@ -229,29 +252,10 @@ class CFD_CORE_EXPORT TaprootScriptTree : public TapBranch { * @return tapleaf hash. */ ByteData256 GetTapLeafHash() const; - /** - * @brief Get tweak. - * @param[in] internal_pubkey internal pubkey - * @return tweak. - */ - ByteData256 GetTapTweak(const SchnorrPubkey& internal_pubkey) const; - /** - * @brief Get a tweaked pubkey. - * @param[in] internal_pubkey internal pubkey - * @param[out] parity parity flag. - * @return tweaked schnorr pubkey. - */ - SchnorrPubkey GetTweakedPubkey( - const SchnorrPubkey& internal_pubkey, bool* parity = nullptr) const; - /** - * @brief Get a tweaked privkey. - * @param[in] internal_privkey internal privkey - * @param[out] parity parity flag. - * @return tweaked privkey. - */ - Privkey GetTweakedPrivkey( - const Privkey& internal_privkey, bool* parity = nullptr) const; + using TapBranch::GetTapTweak; + using TapBranch::GetTweakedPrivkey; + using TapBranch::GetTweakedPubkey; /** * @brief Get a node list. @@ -303,8 +307,7 @@ class CFD_CORE_EXPORT TaprootUtil { * @return tapscript control data. */ static ByteData CreateTapScriptControl( - const SchnorrPubkey& internal_pubkey, - const TaprootScriptTree& merkle_tree, + const SchnorrPubkey& internal_pubkey, const TapBranch& merkle_tree, SchnorrPubkey* witness_program = nullptr, Script* locking_script = nullptr); diff --git a/include/cfdcore/cfdcore_transaction.h b/include/cfdcore/cfdcore_transaction.h index 756773f4..33466f49 100644 --- a/include/cfdcore/cfdcore_transaction.h +++ b/include/cfdcore/cfdcore_transaction.h @@ -289,6 +289,12 @@ class CFD_CORE_EXPORT Transaction : public AbstractTransaction { * @param[in] index index */ void RemoveTxIn(uint32_t index); + /** + * @brief Set the sequence number. + * @param[in] tx_in_index TxIn index + * @param[in] sequence sequence + */ + void SetTxInSequence(uint32_t tx_in_index, uint32_t sequence); /** * @brief Set the unlocking script. * @param[in] tx_in_index TxIn index diff --git a/include/cfdcore/cfdcore_transaction_common.h b/include/cfdcore/cfdcore_transaction_common.h index 5a51e8f6..71ae4283 100644 --- a/include/cfdcore/cfdcore_transaction_common.h +++ b/include/cfdcore/cfdcore_transaction_common.h @@ -144,6 +144,12 @@ class CFD_CORE_EXPORT OutPoint { * @retval false equals */ bool operator!=(const OutPoint& object) const; + /** + * @brief Compare object. + * @param[in] object compare target. + * @return compare value (0 is match) + */ + int Compare(const OutPoint& object) const; private: Txid txid_; //!< txid @@ -241,6 +247,11 @@ class CFD_CORE_EXPORT AbstractTxIn { * @return sequence番号 */ uint32_t GetSequence() const; + /** + * @brief Set a sequence number. + * @param[in] sequence sequence number + */ + void SetSequence(uint32_t sequence); /** * @brief Get a script witness. * @return ScriptWitness @@ -584,6 +595,12 @@ class CFD_CORE_EXPORT AbstractTransaction { * @param[in] index index */ void RemoveTxIn(uint32_t index); + /** + * @brief Set the sequence number. + * @param[in] tx_in_index TxIn index + * @param[in] sequence sequence + */ + void SetTxInSequence(uint32_t tx_in_index, uint32_t sequence); /** * @brief Set the unlocking script. * @param[in] tx_in_index index diff --git a/src/Makefile.srclist b/src/Makefile.srclist index 455149be..73fc3e7b 100644 --- a/src/Makefile.srclist +++ b/src/Makefile.srclist @@ -10,6 +10,7 @@ CFDCORE_SOURCES = \ cfdcore_util.cpp \ cfdcore_wally_util.cpp \ cfdcore_script.cpp \ + cfdcore_block.cpp \ cfdcore_descriptor.cpp \ cfdcore_transaction_common.cpp \ cfdcore_transaction.cpp \ diff --git a/src/cfdcore_address.cpp b/src/cfdcore_address.cpp index 8b946675..354f86ac 100644 --- a/src/cfdcore_address.cpp +++ b/src/cfdcore_address.cpp @@ -1223,5 +1223,48 @@ Script Address::GetLockingScript() const { return locking_script; } +Address Address::GetPegoutAddress(NetType type, const Script& locking_script) { + return GetPegoutAddress(type, locking_script, GetBitcoinAddressFormatList()); +} + +Address Address::GetPegoutAddress( + NetType type, const Script& locking_script, + const AddressFormatData& network_parameter) { + const std::vector params = {network_parameter}; + return GetPegoutAddress(type, locking_script, params); +} + +Address Address::GetPegoutAddress( + NetType type, const Script& locking_script, + const std::vector& network_parameters) { + auto list = locking_script.GetElementList(); + if ((!locking_script.IsPegoutScript()) || (list.size() <= 2)) { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid pegout script. This script have not a pegout address."); + } + + Script pegout_locking_script = Script(list[2].GetBinaryData()); + auto items = pegout_locking_script.GetElementList(); + if (pegout_locking_script.IsP2wpkhScript()) { + ByteData hash(items[1].GetBinaryData()); + return Address(type, WitnessVersion::kVersion0, hash, network_parameters); + } else if (pegout_locking_script.IsTaprootScript()) { + ByteData hash(items[1].GetBinaryData()); + return Address(type, WitnessVersion::kVersion1, hash, network_parameters); + } else if (pegout_locking_script.IsP2pkhScript()) { + ByteData160 hash(items[2].GetBinaryData()); + return Address(type, AddressType::kP2pkhAddress, hash, network_parameters); + } else if (pegout_locking_script.IsP2shScript()) { + ByteData160 hash(items[1].GetBinaryData()); + return Address(type, AddressType::kP2shAddress, hash, network_parameters); + } else { + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Invalid pegout script. This script is unsupported by pegout " + "address."); + } +} + } // namespace core } // namespace cfd diff --git a/src/cfdcore_block.cpp b/src/cfdcore_block.cpp new file mode 100644 index 00000000..e005c775 --- /dev/null +++ b/src/cfdcore_block.cpp @@ -0,0 +1,250 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_block.cpp + * + * @brief Classes related to block. + * + * @see https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.cpp + */ +#include "cfdcore/cfdcore_block.h" + +#include +#include + +#include "cfdcore/cfdcore_exception.h" +#include "cfdcore/cfdcore_logger.h" +#include "cfdcore/cfdcore_transaction.h" +#include "cfdcore/cfdcore_util.h" +#include "cfdcore_block_internal.h" // NOLINT + +namespace cfd { +namespace core { + +using logger::warn; + +// ----------------------------------------------------------------------------- +// Internal file functions +// ----------------------------------------------------------------------------- +/** + * @brief calculate tree width + * @param[in] transaction_count transaction count + * @param[in] height height + * @return tree width + */ +static uint64_t CalcTreeWidth(uint64_t transaction_count, uint64_t height) { + uint64_t u64_1{1}; + return (transaction_count + (u64_1 << height) - 1) >> height; +} + +/** + * @brief Convert bits to bytes. + * @param[in] bits bits. + * @return byte data + */ +static ByteData BitsToBytes(const std::vector& bits) { + std::vector ret((bits.size() + 7) / 8); + for (size_t p = 0; p < bits.size(); p++) { + ret[p / 8] |= bits[p] << (p % 8); + } + return ByteData(ret); +} + +// ----------------------------------------------------------------------------- +// Block +// ----------------------------------------------------------------------------- +Block::Block() : data_() { + // do nothing +} + +Block::Block(const ByteData& data) : data_(data) { + Deserializer dec(data); + header_.version = dec.ReadUint32(); + header_.prev_block_hash = BlockHash(dec.ReadBuffer(32)); + header_.merkle_root_hash = BlockHash(dec.ReadBuffer(32)); + header_.time = dec.ReadUint32(); + header_.bits = dec.ReadUint32(); + header_.nonce = dec.ReadUint32(); + uint64_t tx_count = dec.ReadVariableInt(); + size_t read_size = data.GetDataSize() - dec.GetReadSize(); + auto txs = ByteData(dec.ReadBuffer(static_cast(read_size))); + for (uint64_t index = 0; index < tx_count; ++index) { + Transaction tx(txs); + auto tx_data = tx.GetData(); + uint32_t cur_size = static_cast(tx_data.GetDataSize()); + uint32_t unread_size = static_cast(txs.GetDataSize()) - cur_size; + if (unread_size != 0) { + auto arr = txs.SplitData(std::vector{cur_size, unread_size}); + txs = arr[1]; + } + txs_.emplace_back(tx_data); + txids_.emplace_back(tx.GetTxid()); + } +} + +Block::Block(const std::string& hex) : Block(ByteData(hex)) {} + +Block::Block(const Block& object) { + data_ = object.data_; + header_ = object.header_; + txs_ = object.txs_; + txids_ = object.txids_; +} + +Block& Block::operator=(const Block& object) { + if (this != &object) { + data_ = object.data_; + header_ = object.header_; + txs_ = object.txs_; + txids_ = object.txids_; + } + return *this; +} + +std::string Block::GetHex() const { return data_.GetHex(); } + +ByteData Block::GetData() const { return data_; } + +BlockHash Block::GetBlockHash() const { + return BlockHash(HashUtil::Sha256D(SerializeBlockHeader())); +} + +Txid Block::GetTxid(uint32_t index) const { + if (static_cast(txids_.size()) <= index) { + throw CfdException( + CfdError::kCfdOutOfRangeError, + "The index is outside the scope of the txid list."); + } + return txids_[index]; +} + +std::vector Block::GetTxids() const { return txids_; } + +bool Block::ExistTxid(const Txid& txid) const { + for (const auto& temp_txid : txids_) { + if (txid.Equals(temp_txid)) return true; + } + return false; +} + +Transaction Block::GetTransaction(const Txid& txid) const { + for (size_t index = 0; index < txids_.size(); ++index) { + if (txid.Equals(txids_[index])) { + return Transaction(txs_[index]); + } + } + throw CfdException( + CfdError::kCfdIllegalArgumentError, "target txid not found."); +} + +uint32_t Block::GetTransactionCount() const { + return static_cast(txids_.size()); +} + +BlockHeader Block::GetBlockHeader() const { return header_; } + +ByteData Block::SerializeBlockHeader() const { + Serializer obj; + obj.AddDirectNumber(header_.version); + obj.AddDirectBytes(header_.prev_block_hash.GetData()); + obj.AddDirectBytes(header_.merkle_root_hash.GetData()); + obj.AddDirectNumber(header_.time); + obj.AddDirectNumber(header_.bits); + obj.AddDirectNumber(header_.nonce); + return obj.Output(); +} + +bool Block::IsValid() const { return !data_.IsEmpty(); } + +ByteData Block::GetTxOutProof(const Txid& txid) const { + return GetTxOutProof(std::vector{txid}); +} + +ByteData Block::GetTxOutProof(const std::vector& txids) const { + MerkleBlock merkle_block(*this, txids); + Serializer obj; + obj.AddDirectBytes(SerializeBlockHeader()); + obj.AddDirectBytes(merkle_block.Serialize()); + return obj.Output(); +} + +// ----------------------------------------------------------------------------- +// MerkleBlock +// ----------------------------------------------------------------------------- +MerkleBlock::MerkleBlock(const Block& block, const std::vector& txids) { + std::vector target_indexes; + auto txid_list = block.GetTxids(); + target_indexes.reserve(txid_list.size()); + for (const auto& txid : txid_list) { + bool is_find = false; + for (const auto& target_txid : txids) { + if (target_txid.Equals(txid)) { + is_find = true; + break; + } + } + target_indexes.push_back(is_find); + } + + transaction_count = static_cast(txid_list.size()); + bits_.clear(); + txids_.clear(); + + uint64_t height = 0; + while (CalcTreeWidth(transaction_count, height) > 1) ++height; + + TraverseAndBuild(height, 0, txid_list, target_indexes); +} + +ByteData MerkleBlock::Serialize() const { + Serializer obj; + obj.AddDirectNumber(static_cast(transaction_count)); + obj.AddVariableInt(txids_.size()); + for (const auto& txid : txids_) { + obj.AddDirectBytes(txid.GetData()); + } + auto bits = BitsToBytes(bits_); + obj.AddVariableBuffer(bits); + return obj.Output(); +} + +void MerkleBlock::TraverseAndBuild( + uint64_t height, uint64_t pos, const std::vector& txids, + const std::vector matches) { + bool has_parent_of_match = false; + for (uint64_t index = pos << height; + (index < ((pos + 1) << height)) && (index < transaction_count); + ++index) { + if (matches[index]) { + has_parent_of_match = true; + break; + } + } + bits_.push_back(has_parent_of_match); + if ((height == 0) || (!has_parent_of_match)) { + // if at height 0, or nothing interesting below, store hash and stop + txids_.push_back(CalculateHash(height, pos, txids)); + } else { + TraverseAndBuild(height - 1, pos * 2, txids, matches); + if ((pos * 2 + 1) < CalcTreeWidth(transaction_count, height - 1)) { + TraverseAndBuild(height - 1, pos * 2 + 1, txids, matches); + } + } +} + +Txid MerkleBlock::CalculateHash( + uint64_t height, uint64_t pos, const std::vector& txids) { + if (height == 0) return txids[pos]; + + Txid left = CalculateHash(height - 1, pos * 2, txids); + Txid right; + if ((pos * 2 + 1) < CalcTreeWidth(transaction_count, height - 1)) { + right = CalculateHash(height - 1, pos * 2 + 1, txids); + } else { + right = left; + } + ByteData data = left.GetData().Concat(right.GetData()); + return Txid(HashUtil::Sha256D(data)); +} + +} // namespace core +} // namespace cfd diff --git a/src/cfdcore_block_internal.h b/src/cfdcore_block_internal.h new file mode 100644 index 00000000..26a41c5f --- /dev/null +++ b/src/cfdcore_block_internal.h @@ -0,0 +1,68 @@ +// Copyright 2021 CryptoGarage +/** + * @file cfdcore_block_internal.h + * + * @brief The block related class definition. + */ +#ifndef CFD_CORE_SRC_CFDCORE_BLOCK_INTERNAL_H_ +#define CFD_CORE_SRC_CFDCORE_BLOCK_INTERNAL_H_ + +#include +#include + +#include "cfdcore/cfdcore_block.h" +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_coin.h" +#include "cfdcore/cfdcore_common.h" + +namespace cfd { +namespace core { + +/** + * @brief Calc merkle block class. + */ +class CFD_CORE_EXPORT MerkleBlock { + public: + /** + * @brief constructor. + * @param[in] block block object. + * @param[in] txids target txid list. + */ + MerkleBlock(const Block& block, const std::vector& txids); + + /** + * @brief get serialize data. + * @return serialized data. + */ + ByteData Serialize() const; + + private: + uint64_t transaction_count; //!< total number of transactions + std::vector bits_; //!< node-is-parent-of-matched-txid bits + std::vector txids_; //!< transaction id list + + /** + * @brief Traverse and build. + * @param[in] height height + * @param[in] pos position + * @param[in] txids txid list + * @param[in] matches target match list + */ + void TraverseAndBuild( + uint64_t height, uint64_t pos, const std::vector& txids, + const std::vector matches); + + /** + * @brief calculate hash. + * @param[in] height height + * @param[in] pos position + * @param[in] txids txid list + * @return hash (txid) + */ + Txid CalculateHash( + uint64_t height, uint64_t pos, const std::vector& txids); +}; + +} // namespace core +} // namespace cfd +#endif // CFD_CORE_SRC_CFDCORE_BLOCK_INTERNAL_H_ diff --git a/src/cfdcore_bytedata.cpp b/src/cfdcore_bytedata.cpp index 8bac8c46..a43012dd 100644 --- a/src/cfdcore_bytedata.cpp +++ b/src/cfdcore_bytedata.cpp @@ -19,6 +19,32 @@ namespace core { using logger::warn; +////////////////////////////////// +/// Internal function +////////////////////////////////// +/** + * @brief check big endian. + * @retval true big endian. + * @retval false little endian. + */ +static bool IsBigEndian() { + static const uint32_t k32bitValue = 0x04030201; + static bool* is_big_endian = nullptr; + static bool kTrue = true; + static bool kFalse = false; + if (is_big_endian == nullptr) { + uint8_t buf[4]; + memcpy(buf, &k32bitValue, sizeof(buf)); + if (buf[0] == 1) { + // little + is_big_endian = &kFalse; + } else { + is_big_endian = &kTrue; + } + } + return *is_big_endian; +} + ////////////////////////////////// /// ByteData ////////////////////////////////// @@ -97,6 +123,30 @@ bool ByteData::IsLarge(const ByteData& source, const ByteData& destination) { return source.data_ < destination.data_; } +std::vector ByteData::SplitData( + const std::vector& split_size_list) const { + std::vector result; + uint32_t offset = 0; + uint32_t max = static_cast(data_.size()); + for (uint32_t size : split_size_list) { + if (size == 0) { + result.emplace_back(ByteData()); + } else if ((offset + size) > max) { + throw CfdException( + kCfdIllegalArgumentError, "total size is maximum over."); + } else { + result.emplace_back(&data_[offset], size); + offset += size; + } + } + return result; +} + +ByteData ByteData::SplitData(uint32_t size_from_top) const { + auto ret = SplitData(std::vector{size_from_top}); + return ret[0]; +} + void ByteData::Push(const ByteData& back_insert_data) { if (back_insert_data.IsEmpty()) return; const std::vector& insert_bytes = back_insert_data.data_; @@ -280,6 +330,8 @@ Serializer& Serializer::operator=(const Serializer& object) { return *this; } +bool Serializer::IsBigEndian() { return cfd::core::IsBigEndian(); } + void Serializer::CheckNeedSize(uint32_t need_size) { size_t size = buffer_.size() - static_cast(offset_); if (size < need_size) { @@ -416,6 +468,23 @@ void Serializer::AddDirectNumber(int64_t number) { offset_ += sizeof(number); } +void Serializer::AddDirectBigEndianNumber(uint32_t number) { + CheckNeedSize(sizeof(number)); + uint8_t* buf = &buffer_.data()[offset_]; + if (IsBigEndian()) { + memcpy(buf, &number, sizeof(number)); + } else { + uint8_t tmp_buf[4] = { + static_cast((number & 0xff000000) >> 24), + static_cast((number & 0x00ff0000) >> 16), + static_cast((number & 0x0000ff00) >> 8), + static_cast(number & 0x000000ff), + }; + memcpy(buf, tmp_buf, sizeof(tmp_buf)); + } + offset_ += sizeof(number); +} + Serializer& Serializer::operator<<(const ByteData& buffer) { AddDirectBytes(buffer); return *this; @@ -495,6 +564,23 @@ uint8_t Deserializer::ReadUint8() { return result; } +uint32_t Deserializer::ReadUint32FromBigEndian() { + uint32_t result = 0; + CheckReadSize(sizeof(result)); + if (IsBigEndian()) { + memcpy(&result, &buffer_.data()[offset_], sizeof(result)); + } else { + uint8_t tmp_buf[4]; + memcpy(tmp_buf, &buffer_.data()[offset_], sizeof(tmp_buf)); + result = static_cast(tmp_buf[3] & 0x000000ff) + + static_cast((tmp_buf[2] << 8) & 0x0000ff00) + + static_cast((tmp_buf[1] << 16) & 0x00ff0000) + + static_cast((tmp_buf[0] << 24) & 0xff000000); + } + offset_ += sizeof(result); + return result; +} + uint64_t Deserializer::ReadVariableInt() { CheckReadSize(1); const uint8_t* buf = buffer_.data() + offset_; @@ -564,6 +650,8 @@ ByteData Deserializer::ReadVariableData() { uint32_t Deserializer::GetReadSize() { return offset_; } +bool Deserializer::HasEof() { return (buffer_.size() <= offset_); } + void Deserializer::CheckReadSize(uint64_t size) { if (size > std::numeric_limits::max()) { warn(CFD_LOG_SOURCE, "It exceeds the handling size."); diff --git a/src/cfdcore_descriptor.cpp b/src/cfdcore_descriptor.cpp index 0f49c266..6ff78544 100644 --- a/src/cfdcore_descriptor.cpp +++ b/src/cfdcore_descriptor.cpp @@ -442,7 +442,8 @@ DescriptorKeyType DescriptorKeyReference::GetKeyType() const { // ----------------------------------------------------------------------------- DescriptorScriptReference::DescriptorScriptReference() : script_type_(DescriptorScriptType::kDescriptorScriptNull), - is_script_(false) { + is_script_(false), + is_tapbranch_(false) { // do nothing } @@ -452,6 +453,7 @@ DescriptorScriptReference::DescriptorScriptReference( : script_type_(script_type), locking_script_(locking_script), is_script_(false), + is_tapbranch_(false), addr_prefixes_(address_prefixes) { if ((script_type != DescriptorScriptType::kDescriptorScriptRaw) && (script_type != DescriptorScriptType::kDescriptorScriptMiniscript)) { @@ -470,6 +472,7 @@ DescriptorScriptReference::DescriptorScriptReference( : script_type_(script_type), locking_script_(locking_script), is_script_(true), + is_tapbranch_(false), addr_prefixes_(address_prefixes) { redeem_script_ = child_script.locking_script_; child_script_ = std::make_shared(child_script); @@ -484,6 +487,7 @@ DescriptorScriptReference::DescriptorScriptReference( locking_script_(locking_script), is_script_(false), req_num_(req_sig_num), + is_tapbranch_(false), keys_(key_list), addr_prefixes_(address_prefixes) { // do nothing @@ -496,6 +500,22 @@ DescriptorScriptReference::DescriptorScriptReference( locking_script_(address_script.GetLockingScript()), is_script_(false), address_script_(address_script), + is_tapbranch_(false), + addr_prefixes_(address_prefixes) { + // do nothing +} + +DescriptorScriptReference::DescriptorScriptReference( + const Script& locking_script, DescriptorScriptType script_type, + const std::vector& key_list, + const TapBranch& tapbranch, + const std::vector& address_prefixes) + : script_type_(script_type), + locking_script_(locking_script), + is_script_(false), + tapbranch_(tapbranch), + is_tapbranch_(true), + keys_(key_list), addr_prefixes_(address_prefixes) { // do nothing } @@ -508,6 +528,7 @@ DescriptorScriptReference::DescriptorScriptReference( : script_type_(script_type), locking_script_(locking_script), is_script_(false), + is_tapbranch_(false), script_tree_(script_tree), keys_(key_list), addr_prefixes_(address_prefixes) { @@ -524,6 +545,8 @@ DescriptorScriptReference::DescriptorScriptReference( child_script_ = object.child_script_; keys_ = object.keys_; req_num_ = object.req_num_; + tapbranch_ = object.tapbranch_; + is_tapbranch_ = object.is_tapbranch_; script_tree_ = object.script_tree_; addr_prefixes_ = object.addr_prefixes_; } @@ -539,6 +562,8 @@ DescriptorScriptReference& DescriptorScriptReference::operator=( child_script_ = object.child_script_; keys_ = object.keys_; req_num_ = object.req_num_; + tapbranch_ = object.tapbranch_; + is_tapbranch_ = object.is_tapbranch_; script_tree_ = object.script_tree_; addr_prefixes_ = object.addr_prefixes_; } @@ -570,14 +595,19 @@ Address DescriptorScriptReference::GenerateAddress(NetType net_type) const { locking_script_.IsTaprootScript() || locking_script_.IsP2wshScript()) { auto hash = locking_script_.GetElementList()[1].GetBinaryData(); - return Address(net_type, locking_script_.GetWitnessVersion(), hash); + return Address( + net_type, locking_script_.GetWitnessVersion(), hash, + addr_prefixes_); } else if (locking_script_.IsP2shScript()) { auto hash = locking_script_.GetElementList()[1].GetBinaryData(); - return Address(net_type, AddressType::kP2shAddress, ByteData160(hash)); + return Address( + net_type, AddressType::kP2shAddress, ByteData160(hash), + addr_prefixes_); } else if (locking_script_.IsP2pkhScript()) { auto hash = locking_script_.GetElementList()[2].GetBinaryData(); return Address( - net_type, AddressType::kP2pkhAddress, ByteData160(hash)); + net_type, AddressType::kP2pkhAddress, ByteData160(hash), + addr_prefixes_); } warn(CFD_LOG_SOURCE, "raw type descriptor is not support."); throw CfdException( @@ -767,6 +797,12 @@ std::vector DescriptorScriptReference::GetKeyList() return keys_; } +bool DescriptorScriptReference::HasTapBranch() const { return is_tapbranch_; } + +TapBranch DescriptorScriptReference::GetTapBranch() const { + return tapbranch_; +} + bool DescriptorScriptReference::HasScriptTree() const { return !script_tree_.GetScript().IsEmpty(); } @@ -898,13 +934,15 @@ void DescriptorNode::AnalyzeChild( child_node_.push_back(node); offset = idx + 1; } else if (name_ == "tr") { - DescriptorNode node(addr_prefixes_); - node.value_ = descriptor.substr(offset, idx - offset); - node.node_type_ = DescriptorNodeType::kDescriptorTypeKey; - node.depth_ = depth + 1; - node.parent_kind_ = parent_kind_; - child_node_.push_back(node); - offset = idx + 1; + if (child_node_.empty()) { + DescriptorNode node(addr_prefixes_); + node.value_ = descriptor.substr(offset, idx - offset); + node.node_type_ = DescriptorNodeType::kDescriptorTypeKey; + node.depth_ = depth + 1; + node.parent_kind_ = parent_kind_; + child_node_.push_back(node); + offset = idx + 1; + } } else { // ignore for miniscript // warn(CFD_LOG_SOURCE, "Illegal command."); @@ -1160,6 +1198,7 @@ void DescriptorNode::AnalyzeKey() { CfdError::kCfdIllegalArgumentError, "Failed to taproot key. taproot is xonly pubkey only."); } + key_info_ = pubkey.GetHex(); } else if ( (parent_kind_ == "tr") && (bytes.GetDataSize() == SchnorrPubkey::kSchnorrPubkeySize)) { @@ -1167,12 +1206,10 @@ void DescriptorNode::AnalyzeKey() { schnorr_pubkey = SchnorrPubkey(bytes); pubkey = schnorr_pubkey.CreatePubkey(); key_type_ = DescriptorKeyType::kDescriptorKeySchnorr; + key_info_ = schnorr_pubkey.GetHex(); } else { is_wif = true; } - if (!is_wif) { - key_info_ = pubkey.GetHex(); - } } catch (const CfdException& except) { std::string errmsg(except.what()); if (errmsg.find("hex to byte convert error.") != std::string::npos) { @@ -1370,7 +1407,8 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { CFD_LOG_SOURCE, "Failed to taproot node num. size={}", child_node_.size()); throw CfdException( - CfdError::kCfdIllegalArgumentError, "Failed to taproot node num."); + CfdError::kCfdIllegalArgumentError, + "Failed to taproot node num." + child_node_[0].value_); } child_node_[0].node_type_ = DescriptorNodeType::kDescriptorTypeKey; child_node_[0].parent_kind_ = "tr"; @@ -1393,12 +1431,7 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { ++index) { temp_args.push_back("0"); } - if (child_node_[1].GetScriptTree(&temp_args).ToString().empty()) { - warn(CFD_LOG_SOURCE, "Failed to analyze tapscript tree."); - throw CfdException( - CfdError::kCfdIllegalArgumentError, - "Failed to analyze tapscript tree."); - } + child_node_[1].GetTapBranch(&temp_args); // check } } else if (child_node_.size() != 1) { warn( @@ -1446,6 +1479,7 @@ void DescriptorNode::AnalyzeAll(const std::string& parent_name) { CfdError::kCfdIllegalArgumentError, "Failed to taproot. pkh is unsupported."); } + child_node_[0].parent_kind_ = parent_kind_; child_node_[0].AnalyzeAll(name_); if ((name_ == "wpkh") || (name_ == "wsh")) { @@ -1473,8 +1507,35 @@ void DescriptorNode::AnalyzeScriptTree() { std::string temp_name; for (size_t idx = 0; idx < desc.size(); ++idx) { const char& str = desc[idx]; - if ((str == ' ') || (str == ',') || (str == '{') || (str == '}')) { + if ((str == ' ') || (str == '{')) { if (script_depth == 0) ++offset; + } else if ((str == ',') || (str == '}')) { + if (script_depth == 0) { + tapscript = desc.substr(offset, idx - offset); + if (tapscript.length() >= (kByteData256Length * 2)) { + offset = idx + 1; + DescriptorNode node(addr_prefixes_); + node.name_ = temp_name; + node.node_type_ = DescriptorNodeType::kDescriptorTypeKey; + node.value_ = tapscript; + node.depth_ = 1; + node.parent_kind_ = "tr"; + if (!temp_name.empty()) { + node.AnalyzeChild(tapscript, 2); + } + node.AnalyzeAll("tr"); + tree_node_.emplace(tapscript, node); + child_node_.emplace_back(node); + ++tapleaf_count; + tapscript = ""; + temp_name = ""; + info( + CFD_LOG_SOURCE, "HashTarget script_depth={}, child.value={}", + script_depth, node.value_); + } else { + ++offset; + } + } } else if (str == '(') { if (script_depth == 0) { temp_name = desc.substr(offset, idx - offset); @@ -1512,10 +1573,32 @@ void DescriptorNode::AnalyzeScriptTree() { } } if (tree_node_.empty()) { - warn(CFD_LOG_SOURCE, "Failed to taproot. empty script."); - throw CfdException( - CfdError::kCfdIllegalArgumentError, - "Failed to taproot. empty script."); + if (value_.length() >= (kByteData256Length * 2)) { + tapscript = value_; + DescriptorNode node(addr_prefixes_); + node.name_ = temp_name; + node.node_type_ = DescriptorNodeType::kDescriptorTypeKey; + node.value_ = tapscript; + node.depth_ = 1; + node.parent_kind_ = "tr"; + if (!temp_name.empty()) { + node.AnalyzeChild(tapscript, 2); + } + node.AnalyzeAll("tr"); + tree_node_.emplace(tapscript, node); + child_node_.emplace_back(node); + ++tapleaf_count; + tapscript = ""; + temp_name = ""; + info( + CFD_LOG_SOURCE, "LastTarget script_depth={}, child.value={}", + script_depth, node.value_); + } else { + warn(CFD_LOG_SOURCE, "Failed to taproot. empty script."); + throw CfdException( + CfdError::kCfdIllegalArgumentError, + "Failed to taproot. empty script."); + } } } @@ -1645,17 +1728,21 @@ std::vector DescriptorNode::GetReferences( child_node_[0].GetKeyReferences(array_argument); SchnorrPubkey pubkey = ref.GetSchnorrPubkey(); keys.push_back(ref); - if (child_node_.size() < 2) { - locking_script = - ScriptUtil::CreateTaprootLockingScript(pubkey.GetByteData256()); - result.emplace_back( - locking_script, script_type_, keys, addr_prefixes_); - } else { - auto tree = child_node_[1].GetScriptTree(array_argument); + TapBranch branch; + if (child_node_.size() >= 2) { + branch = child_node_[1].GetTapBranch(array_argument); + } + if (branch.HasTapLeaf()) { + TaprootScriptTree tree(branch); TaprootUtil::CreateTapScriptControl( pubkey, tree, nullptr, &locking_script); result.emplace_back( locking_script, script_type_, keys, tree, addr_prefixes_); + } else { + TaprootUtil::CreateTapScriptControl( + pubkey, branch, nullptr, &locking_script); + result.emplace_back( + locking_script, script_type_, keys, branch, addr_prefixes_); } } else { std::vector keys; @@ -1696,7 +1783,12 @@ std::vector DescriptorNode::GetReferences( script_type_ == DescriptorScriptType::kDescriptorScriptWpkh) { locking_script = ScriptUtil::CreateP2wpkhLockingScript(pubkey); } else if (script_type_ == DescriptorScriptType::kDescriptorScriptPk) { - build << pubkey << ScriptOperator::OP_CHECKSIG; + if (parent_kind_ == "tr") { + build << SchnorrPubkey::FromPubkey(pubkey).GetData() + << ScriptOperator::OP_CHECKSIG; + } else { + build << pubkey << ScriptOperator::OP_CHECKSIG; + } locking_script = build.Build(); } result.emplace_back( @@ -1716,13 +1808,7 @@ Pubkey DescriptorNode::GetPubkey( return ref.GetPubkey(); } -SchnorrPubkey DescriptorNode::GetSchnorrPubkey( - std::vector* array_argument) const { - DescriptorKeyReference ref = GetKeyReferences(array_argument); - return ref.GetSchnorrPubkey(); -} - -TaprootScriptTree DescriptorNode::GetScriptTree( +TapBranch DescriptorNode::GetTapBranch( std::vector* array_argument) const { static auto sort_func = [](const std::string& src, const std::string& dest) { return src.length() > dest.length(); @@ -1734,32 +1820,39 @@ TaprootScriptTree DescriptorNode::GetScriptTree( std::sort(key_list.begin(), key_list.end(), sort_func); std::string desc = value_; + std::string target; Script first_script; for (const auto& script_str : key_list) { const auto& node_ref = tree_node_.at(script_str); - const auto obj = node_ref.GetReference(array_argument); - Script script = - obj.HasRedeemScript() ? obj.GetRedeemScript() : obj.GetLockingScript(); - if (first_script.IsEmpty()) first_script = script; - const auto tapscript = "tl(" + script.GetHex() + ")"; - auto offset = desc.find(script_str); - while (offset != std::string::npos) { - desc = desc.replace(offset, script_str.length(), tapscript); - offset = desc.find(script_str); + if (node_ref.node_type_ == DescriptorNodeType::kDescriptorTypeKey) { + auto ref = node_ref.GetKeyReferences(array_argument); + target = ref.GetSchnorrPubkey().GetHex(); + } else { + const auto obj = node_ref.GetReference(array_argument); + Script script = obj.HasRedeemScript() ? obj.GetRedeemScript() + : obj.GetLockingScript(); + if (first_script.IsEmpty()) first_script = script; + target = "tl(" + script.GetHex() + ")"; + } + if (script_str != target) { + auto offset = desc.find(script_str); + while (offset != std::string::npos) { + desc = desc.replace(offset, script_str.length(), target); + offset = desc.find(script_str); + } } } TapBranch tree; - if ((!desc.empty()) && (desc[0] != '{')) { - tree = TapBranch::FromString(desc); + if (desc.empty() || (desc == "{}")) { + // do nothing } else { tree = TapBranch::FromString(desc); - if (!tree.HasTapLeaf()) { + if ((!tree.HasTapLeaf()) && (!first_script.IsEmpty())) { tree = tree.ChangeTapLeaf(first_script); } } - - return TaprootScriptTree(tree); + return tree; } DescriptorKeyReference DescriptorNode::GetKeyReferences( diff --git a/src/cfdcore_elements_transaction.cpp b/src/cfdcore_elements_transaction.cpp index 848d9af0..830de44f 100644 --- a/src/cfdcore_elements_transaction.cpp +++ b/src/cfdcore_elements_transaction.cpp @@ -45,8 +45,8 @@ static constexpr uint32_t kTransactionVersionNoWitness = 0x40000000; static constexpr const uint32_t kIssuanceAppendSize = 82; /// blind issuance's append size: entity,hash,amount(33),key(33) static constexpr const uint32_t kIssuanceBlindSize = 130; -/// pegin size: btc(9),asset(33),block(33),fedpeg(-),txSize(3),txoutproof(152) -static constexpr const uint32_t kPeginWitnessSize = 230; +/// pegin size: btc(9),asset(33),block(33),claim(1),txSize(-),txoutproof(-) +static constexpr const uint32_t kPeginWitnessSize = 76; /// Size of asset at unblind static constexpr size_t kAssetSize = ASSET_TAG_LEN; @@ -75,6 +75,23 @@ static constexpr uint8_t kTxInFeaturePegin = WALLY_TX_IS_PEGIN; static const ByteData256 kEmptyByteData256; // @formatter:on +/** + * @brief Get serialized size. + * @param[in] size base buffer size. + * @return serialized size. + */ +static uint32_t GetSerializedSize(uint32_t size) { + if (size < 76) { + return 1 + size; + } else if (size < 256) { + return 2 + size; + } else if (size < 65536) { + return 3 + size; + } else { + return 5 + size; + } +} + /** * @brief rangeProofなどを生成する。 * @param[in] value amount @@ -823,10 +840,10 @@ ByteData256 ConfidentialTxIn::GetWitnessHash() const { uint32_t ConfidentialTxIn::EstimateTxInSize( AddressType addr_type, Script redeem_script, uint32_t pegin_btc_tx_size, - Script fedpeg_script, bool is_issuance, bool is_blind, + Script claim_script, bool is_issuance, bool is_blind, uint32_t *witness_area_size, uint32_t *no_witness_area_size, bool is_reissuance, const Script *scriptsig_template, int exponent, - int minimum_bits, uint32_t *rangeproof_size) { + int minimum_bits, uint32_t *rangeproof_size, uint32_t txoutproof_size) { // TODO(k-matsuzawa): Set amount upper limit and calculate maximum size static constexpr const int64_t kIssuanceAmount = kMaxAmount; uint32_t witness_size = 0; @@ -849,10 +866,11 @@ uint32_t ConfidentialTxIn::EstimateTxInSize( } if (pegin_btc_tx_size != 0) { - witness_size += pegin_btc_tx_size + kPeginWitnessSize; - if (!fedpeg_script.IsEmpty()) { + witness_size += GetSerializedSize(pegin_btc_tx_size) + + GetSerializedSize(txoutproof_size) + kPeginWitnessSize; + if (!claim_script.IsEmpty()) { witness_size += - static_cast(fedpeg_script.GetData().GetSerializeSize()); + static_cast(claim_script.GetData().GetSerializeSize()); } } witness_size += 1; // pegin witness num @@ -888,15 +906,16 @@ uint32_t ConfidentialTxIn::EstimateTxInSize( uint32_t ConfidentialTxIn::EstimateTxInVsize( AddressType addr_type, Script redeem_script, uint32_t pegin_btc_tx_size, - Script fedpeg_script, bool is_issuance, bool is_blind, bool is_reissuance, + Script claim_script, bool is_issuance, bool is_blind, bool is_reissuance, const Script *scriptsig_template, int exponent, int minimum_bits, - uint32_t *rangeproof_size) { + uint32_t *rangeproof_size, uint32_t txoutproof_size) { uint32_t witness_size = 0; uint32_t no_witness_size = 0; ConfidentialTxIn::EstimateTxInSize( - addr_type, redeem_script, pegin_btc_tx_size, fedpeg_script, is_issuance, + addr_type, redeem_script, pegin_btc_tx_size, claim_script, is_issuance, is_blind, &witness_size, &no_witness_size, is_reissuance, - scriptsig_template, exponent, minimum_bits, rangeproof_size); + scriptsig_template, exponent, minimum_bits, rangeproof_size, + txoutproof_size); return AbstractTransaction::GetVsizeFromSize(no_witness_size, witness_size); } @@ -923,7 +942,7 @@ ConfidentialTxInReference::ConfidentialTxInReference() uint32_t ConfidentialTxInReference::EstimateTxInSize( AddressType addr_type, Script redeem_script, bool is_blind, int exponent, - int minimum_bits, Script fedpeg_script, const Script *scriptsig_template, + int minimum_bits, Script claim_script, const Script *scriptsig_template, uint32_t *witness_area_size, uint32_t *no_witness_area_size) const { uint32_t witness_size = 0; uint32_t size = 0; @@ -941,10 +960,17 @@ uint32_t ConfidentialTxInReference::EstimateTxInSize( if ((!pegin_witness_.IsEmpty()) && (pegin_witness_.GetWitnessNum() > 5)) { uint32_t pegin_btc_tx_size = static_cast(pegin_witness_.GetWitness()[4].GetDataSize()); - witness_size += pegin_btc_tx_size + kPeginWitnessSize; - if (!fedpeg_script.IsEmpty()) + uint32_t txoutproof_size = + static_cast(pegin_witness_.GetWitness()[5].GetDataSize()); + witness_size += GetSerializedSize(pegin_btc_tx_size) + + GetSerializedSize(txoutproof_size) + kPeginWitnessSize; + if (!claim_script.IsEmpty()) { witness_size += - static_cast(fedpeg_script.GetData().GetSerializeSize()); + static_cast(claim_script.GetData().GetSerializeSize()); + } else { + witness_size += static_cast( + pegin_witness_.GetWitness()[3].GetDataSize()); + } } witness_size += 1; // pegin witness num @@ -973,13 +999,13 @@ uint32_t ConfidentialTxInReference::EstimateTxInSize( uint32_t ConfidentialTxInReference::EstimateTxInVsize( AddressType addr_type, Script redeem_script, bool is_blind, int exponent, - int minimum_bits, Script fedpeg_script, + int minimum_bits, Script claim_script, const Script *scriptsig_template) const { uint32_t witness_size = 0; uint32_t no_witness_size = 0; EstimateTxInSize( - addr_type, redeem_script, is_blind, exponent, minimum_bits, - fedpeg_script, scriptsig_template, &witness_size, &no_witness_size); + addr_type, redeem_script, is_blind, exponent, minimum_bits, claim_script, + scriptsig_template, &witness_size, &no_witness_size); return AbstractTransaction::GetVsizeFromSize(no_witness_size, witness_size); } @@ -1117,6 +1143,17 @@ const RangeProofInfo ConfidentialTxOut::DecodeRangeProofInfo( return range_proof_info; } +UnblindParameter ConfidentialTxOut::Unblind( + const Privkey &blinding_key) const { + if (!confidential_value_.HasBlinding()) { + throw CfdException( + CfdError::kCfdIllegalStateError, "This output is not blinded."); + } + return ConfidentialTransaction::CalculateUnblindData( + nonce_, blinding_key, range_proof_, confidential_value_, locking_script_, + asset_); +} + // ----------------------------------------------------------------------------- // ConfidentialTxOutReference // ----------------------------------------------------------------------------- @@ -1174,7 +1211,7 @@ uint32_t ConfidentialTxOutReference::GetSerializeSize( work_proof_size = 4 + static_cast(range_proof_.GetDataSize()); } else { int64_t amount = confidential_value_.GetAmount().GetSatoshiValue(); - if (amount == 0) amount = kMaxAmount; + if (amount == 0) amount = (nonce_.IsEmpty()) ? kMaxAmount : 1; work_proof_size = 4 + CalculateRangeProofSize(amount, exponent, minimum_bits); if (rangeproof_size != nullptr) *rangeproof_size = work_proof_size; @@ -1519,6 +1556,12 @@ void ConfidentialTransaction::RemoveTxIn(uint32_t index) { vin_.erase(ite); } +void ConfidentialTransaction::SetTxInSequence( + uint32_t tx_in_index, uint32_t sequence) { + AbstractTransaction::SetTxInSequence(tx_in_index, sequence); + vin_[tx_in_index].SetSequence(sequence); +} + void ConfidentialTransaction::SetUnlockingScript( uint32_t tx_in_index, const Script &unlocking_script) { AbstractTransaction::SetUnlockingScript(tx_in_index, unlocking_script); @@ -3192,6 +3235,19 @@ PegoutKeyData ConfidentialTransaction::GetPegoutPubkeyData( return result; } +Address ConfidentialTransaction::GetPegoutAddressFromDescriptor( + const std::string &bitcoin_descriptor, uint32_t bip32_counter, + NetType net_type, NetType elements_net_type) { + auto prefix = (net_type == NetType::kMainnet) ? ByteData("0488b21e") + : ByteData("043587cf"); + ExtPubkey base_ext_pubkey; + Address result; + GenerateExtPubkeyFromDescriptor( + bitcoin_descriptor, bip32_counter, prefix, net_type, elements_net_type, + &base_ext_pubkey, &result); + return result; +} + ExtPubkey ConfidentialTransaction::GenerateExtPubkeyFromDescriptor( const std::string &bitcoin_descriptor, uint32_t bip32_counter, const ByteData &prefix, NetType net_type, NetType elements_net_type, @@ -3287,9 +3343,9 @@ ExtPubkey ConfidentialTransaction::GenerateExtPubkeyFromDescriptor( // If it is the same as base, add a default path. if (child_xpub.ToString() == base_ext_pubkey->ToString()) { std::string xpub_str = base_ext_pubkey->ToString() + "/0/*"; - if (script_ref.GetAddressType() == AddressType::kP2shP2wpkhAddress) { + if (derive_script.GetAddressType() == AddressType::kP2shP2wpkhAddress) { xpub_str = "sh(wpkh(" + xpub_str + "))"; - } else if (script_ref.GetAddressType() == AddressType::kP2wpkhAddress) { + } else if (derive_script.GetAddressType() == AddressType::kP2wpkhAddress) { xpub_str = "wpkh(" + xpub_str + ")"; } else { xpub_str = "pkh(" + xpub_str + ")"; diff --git a/src/cfdcore_logger.cpp b/src/cfdcore_logger.cpp index b89f78bd..10eecf8a 100644 --- a/src/cfdcore_logger.cpp +++ b/src/cfdcore_logger.cpp @@ -193,7 +193,7 @@ void cfd::core::logger::CfdLogger::SetLogger(void* function_address) { bool cfd::core::logger::CfdLogger::IsEnableLogLevel(CfdLogLevel level) { if (log_level_ == kCfdLogLevelOff) return false; - if (is_initialized_ && is_alive_ && (level >= log_level_)) return true; + if (is_initialized_ && is_alive_ && (level <= log_level_)) return true; return false; } diff --git a/src/cfdcore_script.cpp b/src/cfdcore_script.cpp index a656e675..bbafafb1 100644 --- a/src/cfdcore_script.cpp +++ b/src/cfdcore_script.cpp @@ -470,15 +470,23 @@ ScriptOperator ScriptOperator::Get(const std::string& message) { return ite->second; } -bool ScriptOperator::IsOpSuccess(ScriptType op_code) { +bool ScriptOperator::IsOpSuccess(ScriptType op_code, bool is_elements) { if ((op_code == kOpSuccess80) || (op_code == kOpSuccess98) || ((op_code >= kOpSuccess126) && (op_code <= kOpSuccess129)) || ((op_code >= kOpSuccess131) && (op_code <= kOpSuccess134)) || ((op_code >= kOpSuccess137) && (op_code <= kOpSuccess138)) || ((op_code >= kOpSuccess141) && (op_code <= kOpSuccess142)) || ((op_code >= kOpSuccess149) && (op_code <= kOpSuccess153)) || - ((op_code >= kOpSuccess187) && (op_code <= kOpSuccess254))) { + ((op_code >= kOpSuccess187) && (op_code <= kOpSuccess191)) || + ((op_code >= kOpSuccess195) && (op_code <= kOpSuccess249)) || + (op_code == kOpSuccess252)) { return true; + } else if (!is_elements) { + if (((op_code >= kOpSuccess192) && (op_code <= kOpSuccess194)) || + ((op_code >= kOpSuccess250) && (op_code <= kOpSuccess251)) || + ((op_code >= kOpSuccess253) && (op_code <= kOpSuccess254))) { + return true; + } } return false; } @@ -829,6 +837,7 @@ void Script::SetStackData(const ByteData& bytedata) { bool is_collect_buffer = false; uint32_t collect_buffer_size = 0; std::vector collect_buffer; + std::vector top_collect_buffer(2); uint32_t offset = 0; while (offset < buffer.size()) { uint8_t view_data = buffer[offset]; @@ -940,17 +949,31 @@ void Script::SetStackData(const ByteData& bytedata) { if (is_collect_buffer) { collect_buffer.clear(); collect_buffer.resize(collect_buffer_size); + auto tmp_collect_buffer_size = collect_buffer_size; if ((offset + collect_buffer_size) > buffer.size()) { - warn(CFD_LOG_SOURCE, "buffer is incorrect size."); - throw InvalidScriptException("buffer is incorrect size."); + if ((script_stack_.size() >= 2) && + (top_collect_buffer[0].GetDataSize() == 3) && + (top_collect_buffer[1].GetDataSize() == 4)) { + // (push past end) If script is coinbase scriptsig, length is low. + tmp_collect_buffer_size = + static_cast(buffer.size()) - offset; + warn(CFD_LOG_SOURCE, "This script is coinbase scriptsig?"); + } else { + warn(CFD_LOG_SOURCE, "buffer is incorrect size."); + throw InvalidScriptException("buffer is incorrect size."); + } } // OK collect_buffer.assign( buffer.begin() + offset, - buffer.begin() + offset + collect_buffer_size); + buffer.begin() + offset + tmp_collect_buffer_size); - if (collect_buffer_size <= kMaxScriptNumSize) { + if (top_collect_buffer.size() > script_stack_.size()) { + // for coinbase script check + top_collect_buffer[script_stack_.size()] = ByteData(collect_buffer); + } + if (tmp_collect_buffer_size <= kMaxScriptNumSize) { ScriptElement script_element = ScriptElement(ConvertToNumber(collect_buffer), true); script_stack_.push_back(script_element); @@ -959,7 +982,7 @@ void Script::SetStackData(const ByteData& bytedata) { ScriptElement script_element = ScriptElement(byte_array); script_stack_.push_back(script_element); } - offset += collect_buffer_size; + offset += tmp_collect_buffer_size; is_collect_buffer = false; } else { ++offset; diff --git a/src/cfdcore_taproot.cpp b/src/cfdcore_taproot.cpp index 6b16855a..f6304c63 100644 --- a/src/cfdcore_taproot.cpp +++ b/src/cfdcore_taproot.cpp @@ -150,6 +150,8 @@ std::string TapBranch::ToString() const { ver_str = "," + ByteData(leaf_version_).GetHex(); } buf = "tl(" + script_.GetHex() + ver_str + ")"; + } else if (branch_list_.empty() && root_commitment_.IsEmpty()) { + return ""; } else { buf = root_commitment_.GetHex(); } @@ -410,6 +412,36 @@ TapBranch TapBranch::FromString(const std::string& text) { return result; } +ByteData256 TapBranch::GetTapTweak( + const SchnorrPubkey& internal_pubkey) const { + ByteData256 hash = GetCurrentBranchHash(); + static auto kTaggedHash = HashUtil::Sha256("TapTweak"); + auto hasher = HashUtil(HashUtil::kSha256) + << kTaggedHash << kTaggedHash << internal_pubkey.GetData(); + if (!hash.IsEmpty()) hasher << hash; + return hasher.Output256(); +} + +SchnorrPubkey TapBranch::GetTweakedPubkey( + const SchnorrPubkey& internal_pubkey, bool* parity) const { + ByteData256 hash = GetTapTweak(internal_pubkey); + return internal_pubkey.CreateTweakAdd(hash, parity); +} + +Privkey TapBranch::GetTweakedPrivkey( + const Privkey& internal_privkey, bool* parity) const { + bool is_parity = false; + auto internal_pubkey = + SchnorrPubkey::FromPrivkey(internal_privkey, &is_parity); + Privkey privkey = internal_privkey; + if (is_parity) privkey = internal_privkey.CreateNegate(); + + ByteData256 hash = GetTapTweak(internal_pubkey); + internal_pubkey.CreateTweakAdd(hash, &is_parity); + if (parity != nullptr) *parity = is_parity; + return privkey.CreateTweakAdd(hash); +} + // ---------------------------------------------------------------------------- // TaprootScriptTree // ---------------------------------------------------------------------------- @@ -504,36 +536,6 @@ bool TaprootScriptTree::IsValid() const { return !script_.IsEmpty(); } ByteData256 TaprootScriptTree::GetTapLeafHash() const { return GetBaseHash(); } -ByteData256 TaprootScriptTree::GetTapTweak( - const SchnorrPubkey& internal_pubkey) const { - ByteData256 hash = GetCurrentBranchHash(); - static auto kTaggedHash = HashUtil::Sha256("TapTweak"); - auto hasher = HashUtil(HashUtil::kSha256) - << kTaggedHash << kTaggedHash << internal_pubkey.GetData() - << hash; - return hasher.Output256(); -} - -SchnorrPubkey TaprootScriptTree::GetTweakedPubkey( - const SchnorrPubkey& internal_pubkey, bool* parity) const { - ByteData256 hash = GetTapTweak(internal_pubkey); - return internal_pubkey.CreateTweakAdd(hash, parity); -} - -Privkey TaprootScriptTree::GetTweakedPrivkey( - const Privkey& internal_privkey, bool* parity) const { - bool is_parity = false; - auto internal_pubkey = - SchnorrPubkey::FromPrivkey(internal_privkey, &is_parity); - Privkey privkey = internal_privkey; - if (is_parity) privkey = internal_privkey.CreateNegate(); - - ByteData256 hash = GetTapTweak(internal_pubkey); - internal_pubkey.CreateTweakAdd(hash, &is_parity); - if (parity != nullptr) *parity = is_parity; - return privkey.CreateTweakAdd(hash); -} - std::vector TaprootScriptTree::GetNodeList() const { return nodes_; } @@ -570,12 +572,13 @@ bool TaprootUtil::IsValidLeafVersion(uint8_t leaf_version) { } ByteData TaprootUtil::CreateTapScriptControl( - const SchnorrPubkey& internal_pubkey, const TaprootScriptTree& merkle_tree, + const SchnorrPubkey& internal_pubkey, const TapBranch& merkle_tree, SchnorrPubkey* witness_program, Script* locking_script) { bool parity = false; auto pubkey_data = merkle_tree.GetTweakedPubkey(internal_pubkey, &parity).GetByteData256(); uint8_t top = merkle_tree.GetLeafVersion(); + if (top == 0) top = TaprootScriptTree::kTapScriptLeafVersion; if (parity) top |= 0x01; Serializer builder; builder.AddDirectByte(top); diff --git a/src/cfdcore_transaction.cpp b/src/cfdcore_transaction.cpp index b57852cf..40d598e3 100644 --- a/src/cfdcore_transaction.cpp +++ b/src/cfdcore_transaction.cpp @@ -588,6 +588,12 @@ void Transaction::RemoveTxIn(uint32_t index) { CallbackStateChange(kStateChangeRemoveTxIn); } +void Transaction::SetTxInSequence(uint32_t tx_in_index, uint32_t sequence) { + AbstractTransaction::SetTxInSequence(tx_in_index, sequence); + vin_[tx_in_index].SetSequence(sequence); + CallbackStateChange(kStateChangeUpdateTxIn); +} + void Transaction::SetUnlockingScript( uint32_t tx_in_index, const Script &unlocking_script) { AbstractTransaction::SetUnlockingScript(tx_in_index, unlocking_script); diff --git a/src/cfdcore_transaction_common.cpp b/src/cfdcore_transaction_common.cpp index e8498ac1..9b9db9a3 100644 --- a/src/cfdcore_transaction_common.cpp +++ b/src/cfdcore_transaction_common.cpp @@ -110,6 +110,8 @@ void AbstractTxIn::SetUnlockingScript(const Script &unlocking_script) { uint32_t AbstractTxIn::GetSequence() const { return sequence_; } +void AbstractTxIn::SetSequence(uint32_t sequence) { sequence_ = sequence; } + ScriptWitness AbstractTxIn::GetScriptWitness() const { return script_witness_; } @@ -267,22 +269,36 @@ bool OutPoint::operator!=(const OutPoint &object) const { return !(*this == object); } +int OutPoint::Compare(const OutPoint &object) const { + if (vout_ < object.vout_) + return 1; + else if (vout_ > object.vout_) + return -1; + return std::strncmp( + txid_.GetHex().c_str(), object.txid_.GetHex().c_str(), + kByteData256Length * 2); +} + bool operator<(const OutPoint &source, const OutPoint &dest) { - if (source.GetVout() < dest.GetVout()) { - return true; - } - if (source.GetTxid().GetData().GetBytes() < - dest.GetTxid().GetData().GetBytes()) { + int comp = source.Compare(dest); + if (comp == 0) { + return false; + } else if (comp > 0) { return true; + } else { + return false; } - return false; } bool operator<=(const OutPoint &source, const OutPoint &dest) { - if (source == dest) { + int comp = source.Compare(dest); + if (comp == 0) { return true; + } else if (comp > 0) { + return true; + } else { + return false; } - return (source < dest); } bool operator>=(const OutPoint &source, const OutPoint &dest) { @@ -290,10 +306,7 @@ bool operator>=(const OutPoint &source, const OutPoint &dest) { } bool operator>(const OutPoint &source, const OutPoint &dest) { - if (source == dest) { - return false; - } - return !(source < dest); + return !(source <= dest); } // ----------------------------------------------------------------------------- @@ -367,6 +380,20 @@ void AbstractTransaction::RemoveTxIn(uint32_t index) { } } +void AbstractTransaction::SetTxInSequence( + uint32_t tx_in_index, uint32_t sequence) { + CheckTxInIndex(tx_in_index, __LINE__, __FUNCTION__); + + struct wally_tx *tx_pointer = + static_cast(wally_tx_pointer_); + if ((tx_pointer == nullptr) || + (static_cast(tx_pointer->num_inputs) <= tx_in_index)) { + warn(CFD_LOG_SOURCE, "wally invalid state."); + throw CfdException(kCfdIllegalStateError, "wally invalid state error."); + } + tx_pointer->inputs[tx_in_index].sequence = sequence; +} + void AbstractTransaction::SetUnlockingScript( uint32_t tx_in_index, const Script &unlocking_script) { CheckTxInIndex(tx_in_index, __LINE__, __FUNCTION__); diff --git a/test/Makefile.srclist b/test/Makefile.srclist index a54d1d21..2e3b9e43 100644 --- a/test/Makefile.srclist +++ b/test/Makefile.srclist @@ -47,6 +47,7 @@ TEST_CFDCORE_SOURCES= \ test_signatureutil.cpp \ test_randomnumberutil.cpp \ test_sighashtype.cpp \ + test_block.cpp \ test_schnorrsig.cpp \ test_ecdsa_adaptor.cpp \ test_taproot_merkletree.cpp \ diff --git a/test/test_address.cpp b/test/test_address.cpp index 3757c289..06e93123 100644 --- a/test/test_address.cpp +++ b/test/test_address.cpp @@ -196,14 +196,15 @@ TEST(Address, TaprootAddressTest) { EXPECT_STREQ("51201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", address.GetLockingScript().GetHex().c_str()); + auto formats = cfd::core::GetBitcoinAddressFormatList(); EXPECT_NO_THROW((address = Address(NetType::kTestnet, - WitnessVersion::kVersion1, pubkey))); + WitnessVersion::kVersion1, pubkey, formats[1]))); EXPECT_STREQ("tb1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8naskf8ee6", address.GetAddress().c_str()); EXPECT_EQ(NetType::kTestnet, address.GetNetType()); EXPECT_NO_THROW((address = Address(NetType::kRegtest, - WitnessVersion::kVersion1, pubkey))); + WitnessVersion::kVersion1, pubkey, formats))); EXPECT_STREQ("bcrt1pzamhq9jglfxaj0r5ahvatr8uc77u973s5tm04yytdltsey5r8nasmsdlvq", address.GetAddress().c_str()); EXPECT_EQ(NetType::kRegtest, address.GetNetType()); @@ -235,14 +236,15 @@ TEST(Address, TaprootScriptAddressTest) { EXPECT_STREQ("512088de1a59b38939f58fb4f8c5ffc3d56390d43e9e91c7b1d67f91e070f3108799", address.GetLockingScript().GetHex().c_str()); + auto formats = cfd::core::GetBitcoinAddressFormatList(); EXPECT_NO_THROW((address = Address(NetType::kTestnet, - WitnessVersion::kVersion1, tree, pubkey))); + WitnessVersion::kVersion1, tree, pubkey, formats[1]))); EXPECT_STREQ("tb1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vs7rjr8c", address.GetAddress().c_str()); EXPECT_EQ(NetType::kTestnet, address.GetNetType()); EXPECT_NO_THROW((address = Address(NetType::kRegtest, - WitnessVersion::kVersion1, tree, pubkey))); + WitnessVersion::kVersion1, tree, pubkey, formats))); EXPECT_STREQ("bcrt1p3r0p5kdn3yultra5lrzlls74vwgdg057j8rmr4nlj8s8pucss7vsn6c9jz", address.GetAddress().c_str()); EXPECT_EQ(NetType::kRegtest, address.GetNetType()); @@ -1020,4 +1022,14 @@ TEST(Address, ElementsInvalidAddressTest) { EXPECT_THROW(Address(ElementsNetType::kNetTypeNum, ElementsAddressType::kP2pkhAddress, hash, GetElementsAddressFormatList()), CfdException)<< "net"; } +TEST(Address, PegoutAddressTest) { + Script pegout_script("6a2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f17a914a722b257cabc3b8e7d46f8fb293f893f368219da872103700dcb030588ed828d85f645b48971de0d31e8c0244da46710d18681627f5a4a4101044e949dcf8ac2daac82a3e4999ee28e2711661793570c4daab34cd38d76a425d6bfe102f3fea8be12109925fad32c78b65afea4de1d17a826e7375d0e2d0066"); + Address addr1 = Address::GetPegoutAddress(NetType::kRegtest, pegout_script); + EXPECT_EQ("2N8UxQ5u9YXYFn6Ukj5KGXCMDUZTixKTXHo", addr1.GetAddress()); + + Address addr2 = Address::GetPegoutAddress(NetType::kMainnet, pegout_script, + GetBitcoinAddressFormatList()[0]); + EXPECT_EQ("3GvkLLy7w52uaJrD3whPuFMxGDFZDDWg13", addr2.GetAddress()); +} + #endif // CFD_DISABLE_ELEMENTS diff --git a/test/test_block.cpp b/test/test_block.cpp new file mode 100644 index 00000000..d9f7e0a8 --- /dev/null +++ b/test/test_block.cpp @@ -0,0 +1,140 @@ +#include "gtest/gtest.h" +#include + +#include "cfdcore/cfdcore_common.h" +#include "cfdcore/cfdcore_bytedata.h" +#include "cfdcore/cfdcore_script.h" + +#include "cfdcore/cfdcore_block.h" +#include "cfdcore/cfdcore_transaction_common.h" + +using cfd::core::ByteData; +using cfd::core::Block; +using cfd::core::BlockHash; +using cfd::core::Txid; +using cfd::core::Script; + +TEST(Block, GetTxoutProof1) { + struct CfdTestTxoutProofVector { + std::string block_hash; + std::string block_hex; + std::string txid; + std::string exp_txoutproof; + }; + std::vector test_vector = { + { + "53fd7b794cf751a148b2be637df6c7daf663f1be509cb35294bd69400fdc694e", + "00000030957958949bad814d1666ed0d4a005c8aed6b7fd56df5d12c81d584c71e5fae2dfe391f9150dcfb06d54d4eb6621672590bf46bed6893da825c076b841794cec5414e2660ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d5000101ffffffff0200f9029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", + "c5ce9417846b075c82da9368ed6bf40b59721662b64e4dd506fbdc50911f39fe", + "00000030957958949bad814d1666ed0d4a005c8aed6b7fd56df5d12c81d584c71e5fae2dfe391f9150dcfb06d54d4eb6621672590bf46bed6893da825c076b841794cec5414e2660ffff7f20000000000100000001fe391f9150dcfb06d54d4eb6621672590bf46bed6893da825c076b841794cec50101" + /* + { + "hash": "53fd7b794cf751a148b2be637df6c7daf663f1be509cb35294bd69400fdc694e", + "strippedsize": 215, + "size": 251, + "weight": 896, + "height": 213, + "version": 805306368, + "versionHex": "30000000", + "merkleroot": "c5ce9417846b075c82da9368ed6bf40b59721662b64e4dd506fbdc50911f39fe", + "tx": [ + "c5ce9417846b075c82da9368ed6bf40b59721662b64e4dd506fbdc50911f39fe" + ], + "time": 1613123137, + "mediantime": 1613123136, + "nonce": 0, + "bits": "207fffff", + "difficulty": 4.656542373906925e-010, + "chainwork": "00000000000000000000000000000000000000000000000000000000000001ac", + "nTx": 1, + "previousblockhash": "2dae5f1ec784d5812cd1f56dd57f6bed8a5c004a0ded66164d81ad9b94587995", + "nextblockhash": "6dad89ad7924f5baa5369144b20311303ab4b951df89f4f7ccc6cca2a7109b5b" + } + */ + }, { + /* + { + "hash": "0ae1754425645e84cf354366150db602bd1ca38a5b64b6d7949ffc7f5480ab41", + "confirmations": 2, + "strippedsize": 2475, + "size": 4691, + "weight": 12116, + "height": 631, + "version": 536870912, + "versionHex": "20000000", + "merkleroot": "03af1d58df59023834b82f0e7c0debe4411b520bfa7cabaa65fa46b743deb5d8", + "tx": [ + "7f5fb624f5cdce391362aa6befea307c4e778e008e799b40ca7119046f26ab31", + "b4bcb584d0ee9c1e687c69ad0497b2686f7d47529affc0f1df8210b2a074c40c", + "7af0cb6d0a0ded748790daa5e20b079e30cc82d90a267cad982328ed11409c17", + "8d0b1863957eaa5b9c82a07c4e8b78801e496a8af4ed11450186fb1e7bdbfa29", + "b42e9550b5129b34152950843ca09b0674a51ef4d273688366b216db7da16442", + "d4ebf5a67e891b059c6aa67dd06c0ac3e129bf959919e2077c6519d6d460b347", + "5edd72b9fef5225167c11862063c8cd955e648e01470b9784693d3868eaadf49", + "d6f11f1fa8efb17911c1918ec1f2964d20b6bd5ddcefc60acc751094344f2b5e", + "cb3f209415bd73c709740fa0742ba960679cf22e86f691d11eb08e4a85cef95f", + "f4be3e47478145959d2d0978bf1900db2521be4d4f2964b277c35b754133bc7d", + "9c9a3d9783dd9ac6c14c0ee487fa94f2e53053a7c96d10c37f0289edcdeb2b7e", + "a5ab7f31660deb709d4ab2a70f4ce16a7cb02a16b03e843a39aba43115d3217f", + "dc11069c2643ff09717a290e7dc0e38863316ec68b24fbb7d47d4e670f74777f", + "4b244572aaec7a7b92431f7371b42547aca705b7ede430081be6374e8a672282", + "4ae603bfb1689c29b1e5feb2cbd2f1ebb950df3ed4b25b6ad98f2f56da8cac93", + "18b54774739e59b7bb0ec6a7196000c0f8fe42b441502636bdb5adde40f9e8a8", + "587b9d5224a54fb3427cd99dc276b8acab4d4322c1b5681408d74a2927cd62ac", + "98bdb3d84051b02a8bb147bb34d2e34c5b32339aebcccff696429c04538a45df", + "695eddd38e01b5f67f93d3dcbdca033e1d8fd3feaefbdbcc2a2bd1326a6b7be4", + "2bc841beb4de23e39e9674f96afc7a8b3c6db60c6d3c645c06a747eeb5135ae8", + "be37763a766b5aa48d31a44a2c34ff1355e55e7f0efae58de9594d4eae3ca8ed" + ], + "time": 1622375957, + "mediantime": 1622375362, + "nonce": 1, + "bits": "207fffff", + "difficulty": 4.656542373906925e-010, + "chainwork": "00000000000000000000000000000000000000000000000000000000000004f0", + "nTx": 21, + "previousblockhash": "42960b23c8af44e5c2b2a372ef44f069898f1f085adabe72420f03ccf7e187d9", + "nextblockhash": "7f181ea901d54ff50ee20be014a45151fe11406b8cb046574e7ce78e929352b1" + } + */ + "0ae1754425645e84cf354366150db602bd1ca38a5b64b6d7949ffc7f5480ab41", + "00000020d987e1f7cc030f4272beda5a081f8f8969f044ef72a3b2c2e544afc8230b9642d8b5de43b746fa65aaab7cfa0b521b41e4eb0d7c0e2fb834380259df581daf03157eb360ffff7f200100000015020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff050277020101ffffffff02246aa01200000000160014aef2e2877c45ada6b9eaef2bdb9131630ae12dca0000000000000000266a24aa21a9ed2af9d1c54b61988d37ce9bcde367fba7f3c5910cecaf8be1b6b443e937e39cec0120000000000000000000000000000000000000000000000000000000000000000000000000020000000001013d067178968e8e7469a61a82c365508ee3615bed93ed421ddefe19da412171080000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f2500000000160014a9b62806472c7b70c9f753bf41573e1506534dc40247304402205de2d7b0acf8e6027fcb1e197745ca8d23c1a4a7f4dc449762265e2aab2022f202204d35e5866f31595616b44f77e218fbddbfed702b830eb4c2aac087c5c1ad0648012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001019421ca1de8781f070262511333197ab44f1629dbb704bd923f48bae4fa67a9f90000000000feffffff0213373f250000000016001455d6fbae5d95d2b03e5210abbe5a15ddbdb62c47a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee0247304402206f590ce48f6f7a93821c116c3a4a9659d9739d8a9bc62fcae12e34cf0e4b26500220361867ad6dcd30af0bec09a9571a8888452d2812275201f4878de296a78e8627012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001018e4212da7a883762b80efdf512263fd8d412771515b8b81cf992c01314fc93210000000000feffffff0213373f2500000000160014563423d5881cddbb72a1d6fea1af5c56bbea6a3fa0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee024730440220185d48c45b2e83f56249bfda4e2f0e1a211165c3c1175bd358cc7e4024e4764202201835811415ee2e5efbf42445b0113f54ae87b99e77e59252a69960759fdd8cf7012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d46302000002000000000101f0d8cef892eb52f81ac0e86bce4df8eeb9d0c1336cb62a069626c173e65321f80000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f2500000000160014e53cad733f173a80f1402d16123321535454048e0247304402207bbea0204f17fb98370ada2158666b4554d1354409efbd326835413041cd24410220323a16ee7b50f298446db61cd197cc44dd27fd90d45adb1a4fd780574300be7b012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d47602000002000000000101106a9ee2f6bbc4e58cbff2b1c99a53cdddec1a153f8a699350c00969500097210000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f2500000000160014f0321c988998afdb2b8788ce862402110792be8c0247304402207f6e3931036bb80b127b3d25ecf92bf5507cf057ec5af7c3a70b37e0c74575eb02205da9e647d92c86b2e17533050edd95e7eb887981e6145ade05728e0447a357bf012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d47602000002000000000101ea826992d745371bd6d762692369b94b2ac047df92d8b83cf9f46f6c9dbb13450000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f2500000000160014008e9141e1a0fd29384d41585598d2d1dc90ad380247304402202aeae79aa171eb28d21ee22fff4b58794d77e1ceda4c0bdd02fe87b4b6260c17022067184229e67f0ac2729e30206700fb50f0691e248690b6b103788552ebfb8b37012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001011723ce44d3f606a6fbd3de7e5204ad22f6b48154e19e0e37fdb547c84e57bdfe0000000000feffffff0213373f2500000000160014b30e6003f0a61a3c594678af24bfe170894c15cca0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee0247304402202876093a9dec94f9ce1fb3b7c487a8cf38b09a9f3e438c2a1e56d2ff8e2287df02204bba8b7936cccad0302d4f0e39da4b27779a2e8a5ba5a506fe00907544224633012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001016de1c5853fd550cb2fdd67f25b32496bb182404e2f127b5e82e48c587622eabc0000000000feffffff0213373f250000000016001496864cef7241cc0c24e8914f4ec75e34da0a70a6a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee0247304402207dde78431c3e5ede2a90af0b3e3d32fadb5712e5ec04e3fac2dc6137d747187902202fc5603f63ce4498ad9062ed35b37ca5fc9a19fc90a9ef40ddec55b30820862c012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001013e22c22ad4afedfac4642f54ebc1fb93f94f3af1c7cedd78fb17242975e00e650000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f250000000016001485fe6084fcec5323e4647ccb6781f2975ce78a690247304402202d4676c01c0f5f39d98f3e0435101c283baff3f2168bb20fccc99ef514abc77c02205703439a4ca4c289f9ab36e31ba88b81d5503ad705a30b036b628e505ef2b07f012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001018e637219b2c53a395f6b61b85ff720e7380161b31473556d470878ccc9077fe80000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f2500000000160014d9a72d531686342f1f81a447f9643d22ec7bd0ab02473044022010df9caa2ae04bf2b04cce039d859e8fe9f04add799ce02ce4f5848a48eebb9802204e11ae33d32a7c99af8dcc85797b5dc0b7a864c5f9848be41604740fac2bdb89012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d4760200000200000000010124c74f712076cb581a3ee4ae502e094487139ca3da724edb9c0acfd905bd8d340000000000feffffff0213373f250000000016001465eab055d88f1ac853fe1f790740ce24c8623e4ea0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee02473044022053373da5b4b0583d7ef8e743b0fad5688d5a81c82dd33fb6a614a0cf7234431a0220192e5802c1bf8d2e05bca416e3e42b1fb7947169333c40a33bfda2bf1170237d012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d4760200000200000000010145df9bc5da442ff71110c0872dc58939138aba50f2e12bdf13ef7e75ab2f893c0000000000feffffff0213373f250000000016001427880ac035c111a8e8f71e4ae6a5bb4df79518c4a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee0247304402200a13fd5ae3a2dd9bb210316fea853cc8ad9ada202a58c91f53c61a0741b63833022012a15e4bb30938b31da9606d03e2692e12ce87afe816df19ca20934dfadb7641012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d4760200000200000000010140b280318583b4e346fab2a2a126201799ddaffa169d3ac375b9434b3937a0750000000000feffffff0213373f2500000000160014e11569e65a6a7bbe75ca0322070eb0745201b99ca0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee024730440220655e813dcd37ee11f44d82eddc1540225866e3cc730b768c6c5f9b80cd1c447502200e538e03ed2a3c4f5da9396e884890556e42bf5d25ae49eb1b6c8a0101096fb0012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001012fb0c024a0ae79e6217dc8889c6af40853b2b230a0e9f79d765c8b5525e0e2320000000000feffffff0213373f25000000001600147f44238db9775e0e738fb949724a7ffb66f4ac47a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee02473044022032778f2c844abedb675ef947bdc7a11271800b5a11c98be5c0c9fdc6b2037f7302206acf28de27d327dc5a9d93998edad27f7509aa8479a1a2be1cb28f0eee28bf6a012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d47602000002000000000101892c26005096da187a5de107320c08a02e458be0184af915edbacaf5ad898c160000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f250000000016001460524bb60b4dc68abe9ba195376d572dd6cc20fa0247304402206b43d3fcceff2ee92380f6e112b99907b94df56c9ed2ab7df4fa750fd5010ed70220526807188cb56ff1944e19db3910b06d944c8772a76fd91b83cca791c740da14012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d4760200000200000000010128877cbe42170fca382e506d745a333a1e1eed9ed5c3597cf904cd441f7fef050000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f250000000016001462bf5d506758c4075c1484ed199c12b1d070dcc9024730440220514defa364f4cf78355fb64a79f0be487a0515f2c383cca2c7572b862f389b8b022040fb39e0cef457968c22c7a778b3225bf6d95e661c6bfaeea40dc6c9ae7fd9d5012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001019b34c725b2b7de36389cec07b79109d482dab48e1641c2ba143fe1e0ab80f86d0000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f250000000016001437586a889710f537d70d511e41f45ade16ae96b80247304402205e1560130977bf5584e1938c34daa77e3dd0393eff6cfb18fa3d485a622a9f1d02200d55678c7c274442b0aa99d0a047094051b125ff74e8582be20d52dbb1825889012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d476020000020000000001017883189114213697b8adc8069c8bafc80d9972c9fc1949a478c57bde58daacbb0000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f25000000001600144b69012485912f125d6121323d1b2c55e24b3a8f0247304402203bf784686661951078c64dce1410677127d92015d018a1233a9be6351f26ad2302203b6287e552aed0abcf8740cc464d5843dbd3712e58b33dfa2be6947ba88e7b8f012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d4760200000200000000010180143a2ecddd4d3b32eb7c1e6378a054a2a02b278811a5ffbddf4db4e17f0ad70000000000feffffff0213373f2500000000160014fe956a004e01b6cbe82bf6a351f9370a60917abba0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee024730440220769b4f0bc75725b686fcc131af5b31722eeb0e7835bdeb812e16982183045d66022070a2f04e7619e75a9abd62170a1dbffceb11e398119c559bdb66cf49d91d7b43012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d47602000002000000000101f33b3ce193ef8a4450c6b4db2184538c0e5c5c5406e549478d8afe10b3d7e1760000000000feffffff02a0860100000000001600143df45aa3e4c76b1f2f4693675770ba4e3db2acee13373f25000000001600147235fdc337715c994998751dbfb0f3a38e87594e02473044022071323b810bed75c508337a442268503d70ac598f8db2fd0af57fb7ff6b913450022075157811bc930b2a7e2dc2cf5d6b075ca99e3e81bef2992111e43b8aebe9a0ac012102b30fc0ddd4de67700667921a0c73f9e773d473c4827abe8c63b5060725b745d434020000", + "cb3f209415bd73c709740fa0742ba960679cf22e86f691d11eb08e4a85cef95f", + "00000020d987e1f7cc030f4272beda5a081f8f8969f044ef72a3b2c2e544afc8230b9642d8b5de43b746fa65aaab7cfa0b521b41e4eb0d7c0e2fb834380259df581daf03157eb360ffff7f20010000001500000006774b1a7f9e060f94a1c7bd9d8109e233014e4e74a09a5b85a42add1837c18de15ff9ce854a8eb01ed191f6862ef29c6760a92b74a00f7409c773bd1594203fcb7dbc3341755bc377b264294f4dbe2125db0019bf78092d9d95458147473ebef40b484381159b8168441d718d2855f98b076d7f319e08fc129fc6684a10364d6cab62141fa7cf9455e1db2b83d9746dcb4151f71cbde29b8074d9c280cf2329858c705d27704a43f47e3a3066b9253a3d7380cb20874e67178450d2ab376f06ee027b00" + } + }; + + for (const auto& test_data : test_vector) { + SCOPED_TRACE("test_txid:" + test_data.txid); + + Block block(test_data.block_hex); + EXPECT_EQ(test_data.block_hash, block.GetBlockHash().GetHex()); + auto proof = block.GetTxOutProof(Txid(test_data.txid)); + EXPECT_EQ(test_data.exp_txoutproof, proof.GetHex()); + } +} + +TEST(Block, GetTransaction) { + std::string block_hex = "00000030957958949bad814d1666ed0d4a005c8aed6b7fd56df5d12c81d584c71e5fae2dfe391f9150dcfb06d54d4eb6621672590bf46bed6893da825c076b841794cec5414e2660ffff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d5000101ffffffff0200f9029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"; + Block block(block_hex); + Txid txid("c5ce9417846b075c82da9368ed6bf40b59721662b64e4dd506fbdc50911f39fe"); + EXPECT_TRUE(block.ExistTxid(txid)); + EXPECT_FALSE(block.ExistTxid(Txid( + "695eddd38e01b5f67f93d3dcbdca033e1d8fd3feaefbdbcc2a2bd1326a6b7be4"))); + EXPECT_EQ( + "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502d5000101ffffffff0200f9029500000000160014164e985d0fc92c927a66c0cbaf78e6ea389629d50000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", + block.GetTransaction(txid).GetHex() + ); + EXPECT_EQ(1, block.GetTransactionCount()); + EXPECT_EQ(txid.GetHex(), block.GetTxid(0).GetHex()); + + Block block2; + EXPECT_FALSE(block2.IsValid()); + block2 = block; + EXPECT_TRUE(block2.IsValid()); + EXPECT_EQ(block_hex, block2.GetHex()); + Block block3(block); + EXPECT_EQ(block_hex, block3.GetData().GetHex()); + EXPECT_EQ(block.GetBlockHeader().prev_block_hash.GetHex(), + block3.GetBlockHeader().prev_block_hash.GetHex()); +} diff --git a/test/test_bytedata.cpp b/test/test_bytedata.cpp index ea8323cd..7e7dc310 100644 --- a/test/test_bytedata.cpp +++ b/test/test_bytedata.cpp @@ -237,3 +237,17 @@ TEST(ByteData, Push) { EXPECT_NO_THROW(data.Push(data3)); EXPECT_STREQ(data.GetHex().c_str(), "0011223344444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555"); } + +TEST(ByteData, SplitData) { + ByteData data("00112233444444444444444444444444444444444444444455555555"); + auto sub_data = data.SplitData(4); + EXPECT_EQ("00112233", sub_data.GetHex()); + auto list = data.SplitData(std::vector{4, 20}); + EXPECT_EQ(2, list.size()); + if (list.size() == 2) { + EXPECT_EQ("00112233", list[0].GetHex()); + EXPECT_EQ("4444444444444444444444444444444444444444", list[1].GetHex()); + EXPECT_TRUE(sub_data == list[0]); + EXPECT_FALSE(sub_data == list[1]); + } +} diff --git a/test/test_bytedata160.cpp b/test/test_bytedata160.cpp index 92c100ae..6fcf03cd 100644 --- a/test/test_bytedata160.cpp +++ b/test/test_bytedata160.cpp @@ -5,12 +5,6 @@ #include "cfdcore/cfdcore_bytedata.h" #include "cfdcore/cfdcore_exception.h" -// https://qiita.com/yohm/items/477bac065f4b772127c7 - -// The main function are using gtest's main(). - -// TEST(test_suite_name, test_name) - using cfd::core::ByteData; using cfd::core::ByteData160; using cfd::core::ByteData256; @@ -63,6 +57,8 @@ TEST(ByteData160, BytesConstructor) { EXPECT_STREQ(byte_data.GetHex().c_str(), "0102030405060708090001020304050607080900"); EXPECT_TRUE(is_equals); + auto copy_data = byte_data; + EXPECT_TRUE(copy_data == byte_data); } TEST(ByteData160, ByteDataConstructor) { diff --git a/test/test_descriptor.cpp b/test/test_descriptor.cpp index ed420682..8098c954 100644 --- a/test/test_descriptor.cpp +++ b/test/test_descriptor.cpp @@ -36,6 +36,7 @@ using cfd::core::AddressType; using cfd::core::Privkey; using cfd::core::Pubkey; using cfd::core::SchnorrPubkey; +using cfd::core::TapBranch; using cfd::core::TaprootScriptTree; TEST(Descriptor, Parse_pk) { @@ -882,6 +883,29 @@ TEST(Descriptor, ParseElements_addr) { EXPECT_STREQ(locking_script.ToString().c_str(), "0 c62982ba62f90e2929b8830cc3c6dc0c38fe7766d178f217f0dbbd0bf2705201"); } + +TEST(Descriptor, ParseElements_raw) { + std::string descriptor = "raw(0020ef8110fa7ddefb3e2d02b2c1b1480389b4bc93f606281570cfc20dba18066aee)#2xu4jtw0"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + + EXPECT_NO_THROW(desc = Descriptor::ParseElements(descriptor)); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString()); + EXPECT_STREQ(desc_str.c_str(), descriptor.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), "0 ef8110fa7ddefb3e2d02b2c1b1480389b4bc93f606281570cfc20dba18066aee"); + + std::vector script_list; + std::vector arg_list1; + EXPECT_NO_THROW(script_list = desc.GetReferenceAll(&arg_list1)); + EXPECT_EQ(script_list.size(), 1); + if (script_list.size() == 1) { + EXPECT_TRUE(script_list[0].HasAddress()); + EXPECT_STREQ(script_list[0].GenerateAddress(NetType::kLiquidV1).GetAddress().c_str(), + "ex1qa7q3p7nammanutgzktqmzjqr3x6teylkqc5p2ux0cgxm5xqxdthqn90k5w"); + } +} #endif // CFD_DISABLE_ELEMENTS TEST(Descriptor, xpriv_derive_hardened) { @@ -998,6 +1022,7 @@ TEST(Descriptor, Parse_Taproot_pubkey) { DescriptorScriptReference script_ref; SchnorrPubkey pubkey; NetType nettype = NetType::kRegtest; + DescriptorNode node; try { desc = Descriptor::Parse(descriptor1); @@ -1006,6 +1031,7 @@ TEST(Descriptor, Parse_Taproot_pubkey) { } EXPECT_NO_THROW(script_ref = desc.GetReference()); + EXPECT_NO_THROW(node = desc.GetNode()); EXPECT_TRUE(script_ref.HasKey()); EXPECT_TRUE(script_ref.HasAddress()); EXPECT_EQ(1, script_ref.GetKeyNum()); @@ -1018,11 +1044,11 @@ TEST(Descriptor, Parse_Taproot_pubkey) { EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), - "bcrt1paag57xhtzja2dnzh4vex37ejnjj5p3yy2nmlgem3a4e3ud962gdqqctzwn"); + "bcrt1pvv8jm84ye0xr7p9h8l2k58rm287nryk73cnw0nvfxyjfqqpn60gssz7u5f"); EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); EXPECT_STREQ(locking_script.ToString().c_str(), - "1 ef514f1aeb14baa6cc57ab3268fb329ca540c48454f7f46771ed731e34ba521a"); + "1 630f2d9ea4cbcc3f04b73fd56a1c7b51fd3192de8e26e7cd893124900033d3d1"); EXPECT_STREQ(pubkey.GetHex().c_str(), pubkey_hex.c_str()); @@ -1065,11 +1091,11 @@ TEST(Descriptor, Parse_Taproot_xpubkey) { EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), - "bc1p33h4j4kre3e9r4yrl35rlgrtyt2w9hw8f94zty9vacmvfgcnlqtq0txdxt"); + "bc1p4jueea9m897g4me0ef8eyqg9x5n02jzpwnl0yydvdtrl459r3fyqg8wvnj"); EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); EXPECT_STREQ(locking_script.ToString().c_str(), - "1 8c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816"); + "1 acb99cf4bb397c8aef2fca4f9201053526f5484174fef211ac6ac7fad0a38a48"); EXPECT_STREQ(pubkey.GetHex().c_str(), pubkey_hex.c_str()); } @@ -1108,16 +1134,16 @@ TEST(Descriptor, Parse_Taproot_tapleaf_pubkey) { EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), - "bcrt1pnmjdd6u2wjpwv74pc6mclsf036pm56svzpzc7n2xswg4zq06canqc7uvq2"); + "bcrt1p2druqmxfa49j9ph0ea8d9y4gzrhy2x7u2zj0p2622d9r7k28v02s6x9jx3"); EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); EXPECT_STREQ(locking_script.ToString().c_str(), - "1 9ee4d6eb8a7482e67aa1c6b78fc12f8e83ba6a0c10458f4d4683915101fac766"); + "1 5347c06cc9ed4b2286efcf4ed292a810ee451bdc50a4f0ab4a534a3f594763d5"); EXPECT_STREQ(pubkey.GetHex().c_str(), pubkey_hex.c_str()); EXPECT_STREQ(tree.ToString().c_str(), - "tl(21028c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac)"); + "tl(208c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac)"); EXPECT_STREQ(tree.GetScript().GetHex().c_str(), - "21028c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac"); + "208c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac"); try { desc = Descriptor::Parse(descriptor2); @@ -1174,6 +1200,138 @@ TEST(Descriptor, Parse_Taproot_tapbranch) { "208c6f5956c3cc7251d483fc683fa06b22d4e2ddc7496a2590acee36c4a313f816ac"); } +TEST(Descriptor, Parse_Taproot_tapbranch2) { + // pubkey: '04ef514f1aeb14baa6cc57ab3268fb329ca540c48454f7f46771ed731e34ba521a116bc35b3f8d748aea5dfad083a73961908797c97fc0ca4f8d874aba9778fc77', + // privkey: '5JB4Tt43VA4qbBVRtf88CVKTkJ82pC6mhm9aHywDG27htnFHgqC' + std::string internal_pubkey_hex = "ef514f1aeb14baa6cc57ab3268fb329ca540c48454f7f46771ed731e34ba521a"; + std::string pubkey_hex = "ef514f1aeb14baa6cc57ab3268fb329ca540c48454f7f46771ed731e34ba521a"; + std::string descriptor1 = "tr(ef514f1aeb14baa6cc57ab3268fb329ca540c48454f7f46771ed731e34ba521a,{1717a480c2e3a474eed8dba83f684731243cff8ef384521936cf3a730dd0a286,{1717a480c2e3a474eed8dba83f684731243cff8ef384521936cf3a730dd0a286,80039cda864c4f2f1c87f161b0038e57fb7a4a59ff37517048696b85cdaaf911}})"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + DescriptorScriptReference script_ref; + TapBranch tree; + SchnorrPubkey pubkey; + NetType nettype = NetType::kRegtest; + std::vector child_nums = {"1"}; + + try { + desc = Descriptor::Parse(descriptor1); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + return; + } + + EXPECT_NO_THROW(script_ref = desc.GetReference(&child_nums)); + EXPECT_TRUE(script_ref.HasKey()); + EXPECT_TRUE(script_ref.HasAddress()); + EXPECT_EQ(1, script_ref.GetKeyNum()); + EXPECT_FALSE(script_ref.HasChild()); + EXPECT_FALSE(script_ref.HasReqNum()); + EXPECT_FALSE(script_ref.HasRedeemScript()); + EXPECT_TRUE(script_ref.HasTapBranch()); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript(child_nums)); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(tree = script_ref.GetTapBranch()); + EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); + EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); + EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), + "bcrt1pfuqf4j7ceyzmu3rsmude93ctu948r565hf2ucrn9z7zn7a7hjegskj3rsv"); + EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); + EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), + "1 4f009acbd8c905be4470df1b92c70be16a71d354ba55cc0e6517853f77d79651"); + EXPECT_STREQ(pubkey.GetHex().c_str(), pubkey_hex.c_str()); + EXPECT_STREQ(tree.ToString().c_str(), + "{1717a480c2e3a474eed8dba83f684731243cff8ef384521936cf3a730dd0a286,{1717a480c2e3a474eed8dba83f684731243cff8ef384521936cf3a730dd0a286,80039cda864c4f2f1c87f161b0038e57fb7a4a59ff37517048696b85cdaaf911}}"); + EXPECT_STREQ(tree.GetCurrentBranchHash().GetHex().c_str(), + "2f36d93d14c4cbc292f7fd0f837da92fea69f4b9644acaac62c6bd305e9d63bf"); +} + +TEST(Descriptor, Parse_Taproot_tapbranch_simple) { + std::string descriptor1 = "tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{pk(fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),pk(e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)})"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + DescriptorScriptReference script_ref; + TaprootScriptTree tree; + SchnorrPubkey pubkey; + NetType nettype = NetType::kRegtest; + + try { + desc = Descriptor::Parse(descriptor1); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + } + + EXPECT_NO_THROW(script_ref = desc.GetReference()); + EXPECT_TRUE(script_ref.HasKey()); + EXPECT_TRUE(script_ref.HasAddress()); + EXPECT_EQ(1, script_ref.GetKeyNum()); + EXPECT_FALSE(script_ref.HasChild()); + EXPECT_FALSE(script_ref.HasReqNum()); + EXPECT_FALSE(script_ref.HasRedeemScript()); + EXPECT_TRUE(script_ref.HasScriptTree()); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(tree = script_ref.GetScriptTree()); + EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); + EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); + EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), + "bcrt1p6r63tl53d930my75e76ncsgw05gyk838e08079kxuuguwxr0yyjssdax9t"); + EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); + EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), + "1 d0f515fe916962fd93d4cfb53c410e7d104b1e27cbceff16c6e711c7186f2125"); + EXPECT_STREQ(tree.ToString().c_str(), + "{tl(20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ac),tl(20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac)}"); + EXPECT_STREQ(tree.GetScript().GetHex().c_str(), + "20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac"); +} + +TEST(Descriptor, Parse_Taproot_tapbranch_simple2) { + std::string descriptor1 = "tr(c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,{663e2b115c7bc9af54395266c7c0b0dc6f879c55ecaa0becb2e041ab1cfee294,0e167d3d619d2b16fb08736a6e696eb6814c6d99a578c8c268d4195aeb9f72d1})"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + DescriptorScriptReference script_ref; + TapBranch tree; + SchnorrPubkey pubkey; + NetType nettype = NetType::kRegtest; + + try { + desc = Descriptor::Parse(descriptor1); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + return; + } + + EXPECT_NO_THROW(script_ref = desc.GetReference()); + EXPECT_TRUE(script_ref.HasKey()); + EXPECT_TRUE(script_ref.HasAddress()); + EXPECT_EQ(1, script_ref.GetKeyNum()); + EXPECT_FALSE(script_ref.HasChild()); + EXPECT_FALSE(script_ref.HasReqNum()); + EXPECT_FALSE(script_ref.HasRedeemScript()); + EXPECT_TRUE(script_ref.HasTapBranch()); + EXPECT_FALSE(script_ref.HasScriptTree()); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(tree = script_ref.GetTapBranch()); + EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); + EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); + EXPECT_STREQ(script_ref.GenerateAddress(nettype).GetAddress().c_str(), + "bcrt1p6r63tl53d930my75e76ncsgw05gyk838e08079kxuuguwxr0yyjssdax9t"); + EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); + EXPECT_STREQ(desc_str.c_str(), descriptor1.c_str()); + EXPECT_STREQ(locking_script.ToString().c_str(), + "1 d0f515fe916962fd93d4cfb53c410e7d104b1e27cbceff16c6e711c7186f2125"); + EXPECT_STREQ(tree.ToString().c_str(), + "{0e167d3d619d2b16fb08736a6e696eb6814c6d99a578c8c268d4195aeb9f72d1,663e2b115c7bc9af54395266c7c0b0dc6f879c55ecaa0becb2e041ab1cfee294}"); + EXPECT_STREQ(tree.GetCurrentBranchHash().GetHex().c_str(), + "6c2c113096e1876557951f05211acde706640f832862fb40549d60d9421c7f14"); +} + TEST(DescriptorKeyInfo, Constructor_Pubkey) { Pubkey pubkey("03d3f817091de0bbe51e19b53303b12e463f664894d49cb5bf5bb19c88fbc54d8d"); std::string parent_info = "[ef57314e/0'/0'/4']"; @@ -1194,6 +1352,56 @@ TEST(DescriptorKeyInfo, Constructor_Pubkey) { EXPECT_STREQ(key_info.GetPubkey().GetHex().c_str(), pubkey.GetHex().c_str()); } +TEST(Descriptor, Parse_Taproot_bip86) { + // https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki + // Account 0, second receiving address = m/86'/0'/0'/0/1 + // xprv = xprvA449goEeU9okyiF1LmKiDaTgeXvmh87DVyRd35VPbsSop8n8uALpbtrUhUXByPFKK7C2yuqrB1FrhiDkEMC4RGmA5KTwsE1aB5jRu9zHsuQ + // xpub = xpub6H3W6JmYJXN4CCKUSnriaiQRCZmG6aq4sCMDqTu1ACyngw7HShf59hAxYjXgKDuuHThVEUzdHrc3aXCr9kfvQvZPit5dnD3K9xVRBzjK3rX + // internal_key = 83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145 + // output_key = a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb + // scriptPubKey = 5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb + // address = bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh + std::string pubkey_hex = "83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145"; + std::string descriptor1 = "tr(83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145)"; + Descriptor desc; + Script locking_script; + std::string desc_str = ""; + DescriptorScriptReference script_ref; + TapBranch tree; + SchnorrPubkey pubkey; + NetType nettype = NetType::kMainnet; + + try { + desc = Descriptor::Parse(descriptor1); + } catch (const CfdException& except) { + EXPECT_STREQ(except.what(), ""); + return; + } + + EXPECT_NO_THROW(script_ref = desc.GetReference()); + EXPECT_TRUE(script_ref.HasKey()); + EXPECT_TRUE(script_ref.HasAddress()); + EXPECT_EQ(1, script_ref.GetKeyNum()); + EXPECT_FALSE(script_ref.HasChild()); + EXPECT_FALSE(script_ref.HasReqNum()); + EXPECT_FALSE(script_ref.HasRedeemScript()); + EXPECT_FALSE(script_ref.HasScriptTree()); + EXPECT_TRUE(script_ref.HasTapBranch()); + EXPECT_NO_THROW(locking_script = desc.GetLockingScript()); + EXPECT_NO_THROW(desc_str = desc.ToString(false)); + EXPECT_NO_THROW(tree = script_ref.GetTapBranch()); + EXPECT_EQ(AddressType::kTaprootAddress, script_ref.GetAddressType()); + EXPECT_EQ(HashType::kTaproot, script_ref.GetHashType()); + EXPECT_EQ(script_ref.GenerateAddress(nettype).GetAddress(), + "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"); + EXPECT_NO_THROW(pubkey = script_ref.GetKeyList()[0].GetSchnorrPubkey()); + EXPECT_EQ(desc_str, descriptor1); + EXPECT_EQ(locking_script.ToString(), + "1 a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb"); + EXPECT_EQ(pubkey.GetHex(), pubkey_hex); + EXPECT_EQ(tree.ToString(), ""); +} + TEST(DescriptorKeyInfo, Constructor_Privkey_Testnet_Compress) { Privkey privkey("0b64eb8f5ddfffed8ffd09339cbb9de1b9ceee2a76760173fe4b130a91e56383"); std::string privkey_wif_str = "cPoefvB147bYpWCf9JqRBVMXENt4isSBAn91RYeiBh1jUp3ThhKN"; @@ -1344,3 +1552,25 @@ TEST(DescriptorKeyInfo, GetExtPubkeyInformation_root) { EXPECT_NO_THROW(key_str = DescriptorKeyInfo::GetExtPubkeyInformation(pubkey, path)); EXPECT_STREQ(key_str.c_str(), ext_path_str.c_str()); } + +TEST(DescriptorKeyInfo, Constructor_SchnorrPubkey) { + SchnorrPubkey pubkey("d3f817091de0bbe51e19b53303b12e463f664894d49cb5bf5bb19c88fbc54d8d"); + std::string parent_info = "[ef57314e/0'/0'/4']"; + DescriptorKeyInfo key_info; + std::string key_str; + + EXPECT_NO_THROW(key_info = DescriptorKeyInfo(pubkey)); + EXPECT_NO_THROW(key_str = key_info.ToString()); + EXPECT_STREQ(key_str.c_str(), pubkey.GetHex().c_str()); + EXPECT_EQ(key_info.GetKeyType(), DescriptorKeyType::kDescriptorKeySchnorr); + + EXPECT_NO_THROW(key_info = DescriptorKeyInfo(pubkey, parent_info)); + EXPECT_NO_THROW(key_str = key_info.ToString()); + EXPECT_STREQ(key_str.c_str(), (parent_info + pubkey.GetHex()).c_str()); + EXPECT_FALSE(key_info.HasPrivkey()); + EXPECT_TRUE(key_info.HasSchnorrPubkey()); + EXPECT_FALSE(key_info.HasExtPubkey()); + EXPECT_FALSE(key_info.HasExtPrivkey()); + EXPECT_EQ(key_info.GetSchnorrPubkey().GetHex(), pubkey.GetHex()); + EXPECT_EQ(key_info.GetSchnorrPubkey().GetHex(), pubkey.GetHex()); +} diff --git a/test/test_deserializer.cpp b/test/test_deserializer.cpp index 654c2909..ec354880 100644 --- a/test/test_deserializer.cpp +++ b/test/test_deserializer.cpp @@ -26,7 +26,31 @@ TEST(Deserializer, Normal) { } } + EXPECT_EQ(3, parser.GetReadSize()); EXPECT_EQ(0x07060504, parser.ReadUint32()); EXPECT_STREQ("08090a0b0c0d0e0f", parser.ReadVariableData().GetHex().c_str()); + EXPECT_EQ(16, parser.GetReadSize()); + EXPECT_TRUE(parser.HasEof()); + + Deserializer parser2; + parser2 = parser; + EXPECT_TRUE(parser2.HasEof()); +} + +TEST(Deserializer, BigEndian) { + Deserializer parser(ByteData("010203040506070808090a0b0c0d0e0f")); + Deserializer parser2(parser); + EXPECT_FALSE(parser2.HasEof()); + + EXPECT_EQ(1, parser.ReadUint8()); + auto buf = parser.ReadBuffer(2); + EXPECT_EQ("0203", ByteData(buf).GetHex()); + EXPECT_EQ(0x04050607, parser.ReadUint32FromBigEndian()); + EXPECT_EQ(7, parser.GetReadSize()); + EXPECT_FALSE(parser.HasEof()); + EXPECT_STREQ("08090a0b0c0d0e0f", + parser.ReadVariableData().GetHex().c_str()); + EXPECT_EQ(16, parser.GetReadSize()); + EXPECT_TRUE(parser.HasEof()); } diff --git a/test/test_elements_confidentialaddress.cpp b/test/test_elements_confidentialaddress.cpp index b636bdd3..ea5fc25f 100644 --- a/test/test_elements_confidentialaddress.cpp +++ b/test/test_elements_confidentialaddress.cpp @@ -95,6 +95,10 @@ TEST(ElementsConfidentialAddress, P2pkhAddress) { "04d570f84ffe5bdf7583400af2e6b9e219210ecf29a333757481cbca826ada8e16e50cd61e20eb14e59a0c763d9cda790becb868ceeb00e5f74da0d15ff8381534"); EXPECT_THROW((address = ElementsConfidentialAddress(unblind_addr, uc_key)), CfdException); + + // copy check + ElementsConfidentialAddress ca(address); + EXPECT_EQ(address.GetAddress(), ca.GetAddress()); } TEST(ElementsConfidentialAddress, P2shAddress) { diff --git a/test/test_elements_confidentialtransaction.cpp b/test/test_elements_confidentialtransaction.cpp index c039e993..3cb6419b 100644 --- a/test/test_elements_confidentialtransaction.cpp +++ b/test/test_elements_confidentialtransaction.cpp @@ -915,7 +915,8 @@ TEST(ConfidentialTransaction, BlindTxOutIgnorePegoutAndEnptyTest) { // blind txout[0] pubkeys[0] = pubkey1; EXPECT_NO_THROW((tx.BlindTxOut(blind_list, pubkeys))); - // 乱数が混ざるため、サイズだけチェック + // EXPECT_EQ(tx.GetHex(), ""); + // Check is size only EXPECT_EQ(tx.GetHex().length(), 9348); std::string blind_tx = tx.GetHex(); @@ -1935,6 +1936,13 @@ TEST(ConfidentialTransaction, GetPegoutPubkeyDataTest) { EXPECT_STREQ( addr.GetAddress().c_str(), testdata.address.c_str()); + + auto pegout_addr = ConfidentialTransaction::GetPegoutAddressFromDescriptor( + bitcoin_descriptor, bip32_counter, net_type, + NetType::kElementsRegtest); + EXPECT_STREQ( + pegout_addr.GetAddress().c_str(), + testdata.address.c_str()); ++bip32_counter; } } @@ -1974,4 +1982,13 @@ TEST(ConfidentialTransaction, GetPegoutPubkeyDataPkhNoCounterTest) { testdata.address.c_str()); } +TEST(ConfidentialTransaction, SetTxInSequence) { + std::string tx_hex = "020000000101319bff5f4311e6255ecf4dd472650a6ef85fde7d11cd10d3e6ba5974174aeb560100008000ffffffff6f1a4b6bd5571b5f08ab79c314dc6483f9b952af2f5ef206cd6f8e68eb1186f36f2a4b6bd5571b5f08ab79c314dc6483f9b952af2f5ef206cd6f8e68eb1186f301000000000011223301000000000011224400000000000800110011001100110800110011001100220000"; + ConfidentialTransaction tx(tx_hex); + tx.SetTxInSequence(0, 4294967294); + EXPECT_EQ( + "020000000101319bff5f4311e6255ecf4dd472650a6ef85fde7d11cd10d3e6ba5974174aeb560100008000feffffff6f1a4b6bd5571b5f08ab79c314dc6483f9b952af2f5ef206cd6f8e68eb1186f36f2a4b6bd5571b5f08ab79c314dc6483f9b952af2f5ef206cd6f8e68eb1186f301000000000011223301000000000011224400000000000800110011001100110800110011001100220000", + tx.GetHex()); +} + #endif // CFD_DISABLE_ELEMENTS diff --git a/test/test_elements_confidentialtxin.cpp b/test/test_elements_confidentialtxin.cpp index 2ef28552..215cfb5c 100644 --- a/test/test_elements_confidentialtxin.cpp +++ b/test/test_elements_confidentialtxin.cpp @@ -230,40 +230,41 @@ struct TestEstimateConfidentialTxInSizeVector { uint32_t witness_size; Script redeem_script; uint32_t pegin_btc_tx; - Script fedpeg_script; + Script claim_script; bool is_issuance; bool is_blind; bool is_reissuance; std::string script_template; int exponent; int minimum_bits; + uint32_t txoutproof_size; }; TEST(ConfidentialTxIn, EstimateTxInSize) { static const std::string multisig_script = "522102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b8253ae"; static const std::string scriptsig_template = "00473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb014752210205ffcdde75f262d66ada3dd877c7471f8f8ee9ee24d917c3e18d01cee458bafe2102be61f4350b4ae7544f99649a917f48ba16cf48c983ac1599774958d88ad17ec552ae"; static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wpkhAddress, 176, 112, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wshAddress, 222, 146, Script("51"), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wpkhAddress, 153, 112, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 208, 167, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 301, 260, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 191, 150, exp_script, 0, Script(), false, false, false, scriptsig_template, 0, 0}, + {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shP2wpkhAddress, 176, 112, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shP2wshAddress, 222, 146, Script("51"), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wpkhAddress, 153, 112, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wshAddress, 208, 167, exp_script, 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wshAddress, 301, 260, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wshAddress, 191, 150, exp_script, 0, Script(), false, false, false, scriptsig_template, 0, 0, 0}, // pegin - {AddressType::kP2wpkhAddress, 611, 570, Script(), 226, Script("51"), false, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 860, 819, Script(), 226, Script(), false, false, false, "", 0, 0, 400}, // issue - {AddressType::kP2wpkhAddress, 235, 112, Script(), 0, Script(), true, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 235, 112, Script(), 0, Script(), true, false, false, "", 0, 0, 0}, {AddressType::kP2wpkhAddress, 8499, 8328, Script(), 0, Script(), true, true, false, - "", 0, 36}, + "", 0, 36, 0}, {AddressType::kP2wpkhAddress, 8627, 8456, Script(), 0, Script(), true, true, false, - "", 0, 52}, + "", 0, 52, 0}, // reissue {AddressType::kP2wpkhAddress, 4391, 4220, Script(), 0, Script(), true, true, true, - "", 0, 36}, + "", 0, 36, 0}, {AddressType::kP2wpkhAddress, 4455, 4284, Script(), 0, Script(), true, true, true, - "", 0, 52}, + "", 0, 52, 0}, }; for (const auto& test_data : test_vector) { @@ -277,9 +278,10 @@ TEST(ConfidentialTxIn, EstimateTxInSize) { uint32_t cache_size = 0; EXPECT_NO_THROW((size = ConfidentialTxIn::EstimateTxInSize( test_data.addr_type, test_data.redeem_script, test_data.pegin_btc_tx, - test_data.fedpeg_script, test_data.is_issuance, test_data.is_blind, + test_data.claim_script, test_data.is_issuance, test_data.is_blind, &wit_size, nullptr, test_data.is_reissuance, template_ptr, - test_data.exponent, test_data.minimum_bits, &cache_size))); + test_data.exponent, test_data.minimum_bits, &cache_size, + test_data.txoutproof_size))); EXPECT_EQ(size, test_data.size); EXPECT_EQ(wit_size, test_data.witness_size); if (test_data.minimum_bits == 36) { @@ -294,22 +296,23 @@ TEST(ConfidentialTxIn, EstimateTxInSize) { TEST(ConfidentialTxIn, EstimateTxInVsize) { static const std::string multisig_script = "522102522952c3fc2a53a8651b08ce10988b7506a3b40a5c26f9648a911be33e73e1a0210340b52ae45bc1be5de083f1730fe537374e219c4836400623741d2a874e60590c21024a3477bc8b933a320eb5667ee72c35a81aa155c8e20cc51c65fb666de3a43b8253ae"; static const std::vector test_vector = { - {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wpkhAddress, 92, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2shP2wshAddress, 113, 0, Script("51"), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wpkhAddress, 69, 0, Script(), 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 83, 0, exp_script, 0, Script(), false, false, false, "", 0, 0}, - {AddressType::kP2wshAddress, 106, 0, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0}, + {AddressType::kP2pkhAddress, 150, 0, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shAddress, 205, 0, exp_script, 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shP2wpkhAddress, 92, 0, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2shP2wshAddress, 113, 0, Script("51"), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wpkhAddress, 69, 0, Script(), 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wshAddress, 83, 0, exp_script, 0, Script(), false, false, false, "", 0, 0, 0}, + {AddressType::kP2wshAddress, 106, 0, Script(multisig_script), 0, Script(), false, false, false, "", 0, 0, 0}, // pegin - {AddressType::kP2wpkhAddress, 184, 0, Script(), 226, Script("51"), false, false, false, "", 0, 0}, + {AddressType::kP2wpkhAddress, 252, 0, Script(), 226, + Script("0014e8d28b573816ddfcf98578d7b28543e273f5a72a"), false, false, false, "", 0, 0, 400}, // issue - {AddressType::kP2wpkhAddress, 151, 0, Script(), 0, Script(), true, false, false, "", 0, 0}, - {AddressType::kP2wpkhAddress, /*1645*/ 2253, 0, Script(), 0, Script(), true, true, false, "", 0, 36}, - {AddressType::kP2wpkhAddress, 2285, 0, Script(), 0, Script(), true, true, false, "", 0, 52}, + {AddressType::kP2wpkhAddress, 151, 0, Script(), 0, Script(), true, false, false, "", 0, 0, 0}, + {AddressType::kP2wpkhAddress, /*1645*/ 2253, 0, Script(), 0, Script(), true, true, false, "", 0, 36, 0}, + {AddressType::kP2wpkhAddress, 2285, 0, Script(), 0, Script(), true, true, false, "", 0, 52, 0}, // reissue - {AddressType::kP2wpkhAddress, /*922*/ 1226, 0, Script(), 0, Script(), true, true, true, "", 0, 36}, - {AddressType::kP2wpkhAddress, 1242, 0, Script(), 0, Script(), true, true, true, "", 0, 52}, + {AddressType::kP2wpkhAddress, /*922*/ 1226, 0, Script(), 0, Script(), true, true, true, "", 0, 36, 0}, + {AddressType::kP2wpkhAddress, 1242, 0, Script(), 0, Script(), true, true, true, "", 0, 52, 0}, }; for (const auto& test_data : test_vector) { @@ -321,9 +324,10 @@ TEST(ConfidentialTxIn, EstimateTxInVsize) { } EXPECT_NO_THROW((vsize = ConfidentialTxIn::EstimateTxInVsize( test_data.addr_type, test_data.redeem_script, test_data.pegin_btc_tx, - test_data.fedpeg_script, test_data.is_issuance, test_data.is_blind, + test_data.claim_script, test_data.is_issuance, test_data.is_blind, test_data.is_reissuance, template_ptr, - test_data.exponent, test_data.minimum_bits))); + test_data.exponent, test_data.minimum_bits, nullptr, + test_data.txoutproof_size))); EXPECT_EQ(vsize, test_data.size); } } @@ -383,7 +387,7 @@ struct TestEstimateConfidentialTxInRefVector { uint32_t size; uint32_t witness_size; Script redeem_script; - Script fedpeg_script; + Script claim_script; bool is_blind; std::string script_template; int exponent; @@ -441,8 +445,10 @@ TEST(ConfidentialTxInReference, EstimateTxInSize) { "", 0, 0}, {txin, AddressType::kP2wshAddress, 191, 150, exp_script, Script(), false, scriptsig_template, 0, 0}, - // pegin - {pegin_txin, AddressType::kP2wpkhAddress, 609, 568, Script(), Script("51"), false, "", 0, 0}, + // pegin (input claim script) + {pegin_txin, AddressType::kP2wpkhAddress, 644, 603, Script(), + Script("0020e8d28b573816ddfcf98578d7b28543e273f5a72a112233445566778899aabbccdd"), + false, "", 0, 0}, // issue {issue_txin, AddressType::kP2wpkhAddress, 4143, 3972, Script(), Script(), true, "", 0, 0}, {issue_txin, AddressType::kP2wpkhAddress, 6065, 5894, Script(), Script(), true, "", 0, 36}, @@ -463,7 +469,7 @@ TEST(ConfidentialTxInReference, EstimateTxInSize) { ConfidentialTxInReference txin_ref(test_data.txin); EXPECT_NO_THROW((size = txin_ref.EstimateTxInSize( test_data.addr_type, test_data.redeem_script, test_data.is_blind, - test_data.exponent, test_data.minimum_bits, test_data.fedpeg_script, + test_data.exponent, test_data.minimum_bits, test_data.claim_script, template_ptr, &wit_size, nullptr))); EXPECT_EQ(size, test_data.size); EXPECT_EQ(wit_size, test_data.witness_size); @@ -518,7 +524,9 @@ TEST(ConfidentialTxInReference, EstimateTxInVsize) { {txin, AddressType::kP2wshAddress, 83, 0, exp_script, Script(), false, "", 0, 0}, {txin, AddressType::kP2wshAddress, 106, 0, Script(multisig_script), Script(), false, "", 0, 0}, // pegin - {pegin_txin, AddressType::kP2wpkhAddress, 183, 0, Script(), Script("51"), false, "", 0, 0}, + {pegin_txin, AddressType::kP2wpkhAddress, 192, 0, Script(), + Script("0020e8d28b573816ddfcf98578d7b28543e273f5a72a112233445566778899aabbccdd"), + false, "", 0, 0}, // issue {issue_txin, AddressType::kP2wpkhAddress, 1164, 0, Script(), Script(), true, "", 0, 0}, {issue_txin, AddressType::kP2wpkhAddress, 1645, 0, Script(), Script(), true, "", 0, 36}, @@ -539,7 +547,7 @@ TEST(ConfidentialTxInReference, EstimateTxInVsize) { EXPECT_NO_THROW((vsize = txin_ref.EstimateTxInVsize( test_data.addr_type, test_data.redeem_script, test_data.is_blind, test_data.exponent, test_data.minimum_bits, - test_data.fedpeg_script, template_ptr))); + test_data.claim_script, template_ptr))); EXPECT_EQ(vsize, test_data.size); } } diff --git a/test/test_elements_confidentialtxout.cpp b/test/test_elements_confidentialtxout.cpp index 563baf61..915907d5 100644 --- a/test/test_elements_confidentialtxout.cpp +++ b/test/test_elements_confidentialtxout.cpp @@ -23,6 +23,7 @@ using cfd::core::ConfidentialNonce; using cfd::core::ConfidentialTxOut; using cfd::core::ConfidentialTxOutReference; using cfd::core::ConfidentialValue; +using cfd::core::Privkey; using cfd::core::RangeProofInfo; using cfd::core::Script; using cfd::core::ScriptWitness; @@ -355,4 +356,57 @@ TEST(ConfidentialTxOutReference, GetSerializeVsize) { EXPECT_EQ(1191, txout_ref.GetSerializeVsize(true, 0, 52, nullptr, 2)); } +TEST(ConfidentialTxOutReference, Unblind) { + /* + + { + "value-minimum": 1, + "value-maximum": 4503599627370496, + "ct-exponent": 0, + "ct-bits": 52, + "surjectionproof": "010001f6f270a50e47a77305832363b2ab9c7eea39297a4e2872b078552bca67a0fb0fb4e4c5389dd57b344bb70039538148c7c88b661538742f6632c2d4699616169c", + "valuecommitment": "091dd6cd19781f14385175b586607221d1f11b3f19f149e707810de78f7c7f6f79", + "assetcommitment": "0bb0852f9c11249a0c8ebb1c1bc3f99c5f643fa3426d4e5c730e702aaf3f2581fc", + "commitmentnonce": "0398e7cd1cb3c9c13506f91b946f5586cc8fa45f36dde615f2bdb48c1dfe904270", + "commitmentnonce_fully_valid": true, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 001d6db698e75a5a8af771730c4ab258af30546b OP_EQUAL", + "hex": "a914001d6db698e75a5a8af771730c4ab258af30546b87", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "GhC8ey85yGqMr7e8k2hPcZAE1YvEHffk6F" + ] + } + }, + */ + Script locking_script("a914001d6db698e75a5a8af771730c4ab258af30546b87"); + ConfidentialAssetId asset_commitment( + "0bb0852f9c11249a0c8ebb1c1bc3f99c5f643fa3426d4e5c730e702aaf3f2581fc"); + ConfidentialValue value_commitment( + "091dd6cd19781f14385175b586607221d1f11b3f19f149e707810de78f7c7f6f79"); + ConfidentialNonce nonce_commitment( + "0398e7cd1cb3c9c13506f91b946f5586cc8fa45f36dde615f2bdb48c1dfe904270"); + ByteData rangeproof( + "6033000000000000000121579e0109f21bb7cd0326804abb14059b54fa186892f060384435402393a6589fb54df6ede461ce206af5b13abbe1a564404abf7ab1cc4ed6bd1d93a91728224b21c10a540d76f0ccc047ffa3e06a8abad69687213373d5bcfd4fcf62ecd11c2aff9e1a9d64602cab441e3777d771f88f7e805223a5f192ed3d63b937e93e0e170e6b76e8a05fc5bb46826967d4850dbd2e9128ed615e71149f9aac9304194e968cd8b1ad28a96230b4064795040d16554776ee40b3793bdc9758285df2bc90a5c4be023d206293c071962939160f640ecedbba5a3731aa0aa60afd94f2d9a9d366b4e55ec8c561455ba6a849f8f0df0b19eaf98993655102500a47b76ad320522e4c1f9f829333331017cd0040226f60e426fc00b3844d08e364f6c9f346e3eb7dcf8de6e12a2ac98f11bed6b0c3e06c0bf767e96414ec276a2eb145f64eccb663b46135a19ac17cce785f15b1b2cb7206483329f4c38236ad3cd6cf1646a95ddb94beccb237dc6a854098b139df24f2c8ce7ce7e6d799a434f415ba52ce643bd51619edd786df734b0cee6a5a9ff0442f50a0ca011d8e5b695e6a44016884dbf2f6f681bc3faa5a1a9be514b87ab47183a85bd98b6eff7d5865246b7564a3008cba9b5bcac11e8b3b76dcc38c5915f14a1c7c08a76a31de11d9084cc0553871d0fb9e6ca25b2d191eb292c8f14c1b0cf9c4fedd0a85989a388c675e51003722bae92e356dd680a68f9861fc99be81bdc4f57fa0bf6f63613f80598960aacb8867a94cfbea8d4b776a4771ce1e8b20cf8506cd0524ba85e117085addf4de1d45c2ec6f8e441f74081b792208050ae890a91d5554e37d33ddba2b2c0bbdc2b4a31d0f7eaa1538e5a9fcbd86a87fdf5c6f161c0de5bdc9331b5936b06470c16778077887b3c80a53c5372914d2c3fffd5cb88ee00ffbbbdb10d7ce0fbbca1e725438b7f51a81b611b9f0c4330c3ec3b8d191218fc720c3165305412425f73efacdf44fb0cda9bd1f5290cac09a7f27ca68cb9b485d83c822282cadbfab69ef4f079d1d9e6042033a4a2c8ee9203509f9669f0553ece40d2958d3a733b81232b6ef6c21adc2e2716942588146d6dcadcdc94a13326c6828d04692da1a129e73a3f60cdd30d16b19b29f15a734ddcee491159fedec88a6c986e0243e99b9d2ceed1b9b8c7b3180857f067d8d778f8c0d19ef059f4bf2de3de7f3fc26c86a37cf046fae93cbaaf06e9d95ed73e95957aa69c2680087c6b36d51f93d8e3b9596ab4d793619e70a61edd12c14dc928cc56d1047ea3383b77d8d7527fe43317b21c86a323ed982ed1b766698dcda6c6a91bf50d3f01d7f1d8c20b44441cab3eeb5fac0995298eb7a7a6877bc420119a9acc713e221ddcd2038423c431247dd328372744aea39b9797a9757e46f9d83c5bcce8e2af2baef8416d27a2e5274c21ebdf432f844511081cede5f794ac5e4521d432a6497946d014af706cf431f3437d9d8ad04a3c84cbec65c5596569bdd268a2d7bf0025e6debf4209f7a7a6ae29d9357f84007e72358869caa02ce25813115c98ae6054e9bf48732b55bed6cfe87e7ae05eb435a6eece76046dc2da86d0c33283ff6fb4ffb65e8c943a8c298146c31b48eddc0e0255094c5761d9a8e7b155058efc755579301781355607f369724dd81b58d76977c19a9787ed92760ecce6b2ff0b03093f0936ba6b5872cdf0dcad63f4080f9501a813df248c3484f3445e75f8ca7ab028409bcf63c191b9bd352687397a0cab873dbf6c4fec4cf364cb2e7966f4b6d77e6b9a8477d44d3cf3ef5ecdb1faebd35dff45f39d660accf3bb3fa86cbd021e572eba6014e5c2e48702ab874bde9314c20e9073c20bad928b672249d01d8d8c89b368962a2e9cabca801fb27e59b1a58fd11fcf4d697746e1a7e72a8e32186155dd0f90468e6bb40e39e1d599434e7d504e577f2aee60479a21152c8c64c57f11930611385a605c150cb5c582489dd8e268ba8e1dd4b38fb337f347f3aa953a1d437f69209c2672c755d84957522938f6e69ec39a59725fe786e442a3f39083283dcb6177cde1a4abe38d17c4a45e7b1e78460474bee3eb8ccd4a1d0bd9ac7cc4dcac3dc9263117b0f9e05ff85853d9b28444ee840bd3f674dc2abc53fa5d2db8cd64a1739eec76063ffd93352128135b02377f6ca684b11add9c9434f6060f98c1c0f14ad941c12a4c6ca753afd36f22e353cf7147ab62c7245871a87166b7ca5d5e179953ee4d0328d0c7d69fd4128e06f6e546109732bd7d213e1559b280c0f2304ab874fe87410a77ed8c41378e5530e6ba6eb605f87fc75a4763244433366866ea9227cea868d67dde1287460351f8bc132c398bd4aaeadfe2959394ff96b3cbe1a3265777ea0fe676ecfd71daaf213d93edcc4a83fb9d159f858f61f8c28476430d97fd8879df97bb89914bc8e767a008b952976fb54d44492da79036004c36de129118f8fca1528dae9becd6c1471c9b1690fc00d64af653b1672f0db041ae7a0592a85dff25ca470892f8064b9ed01085a929d7b0ef03775fd5546bdab4ffd494a4af9c35ca0b6d3313c2be2c817522ff79fc54d28baf7707463dfe13423ae368bb1c80e7e2e509617f41c54f57ef67c6a786c28f1578be5a64e36cfb3b63c5e1c07619503619031772505987b515faabf7203b4d4413d6abbf8a6c8f00cd69a24e6c0f9c278b0f166763dbeed28ff447814e2d61c92d31840811f2b4e69323e7a35670b7629dbbe04afc7167ee217fe0f74930cf10ef5c07240c70ff3b36d200c1ce866bf108d5566fbff4860389f532871e2d8075fa28c39c85bd68a95f3a1e23265cbc5ed1657711239a32c4c67245d70eb93082323fcc7f113f912c1e0b818186e044b19dfc22d33c82e2cdfb88ead2f152790f47c1b432bce6d899e9c2dcf03969ab2cafd22e4cf7bbefe10bd2b8fb0078e8b5e006cf898259061efbeaa41aa46bd5cc19d3efe05a8c590bd8212b25f862b635876a9a63cde5fb194358c619fd39ef389b60eb7688513ea4dcf6dacc8cc97456a5131bf8de5b424edff078604456e8b6bce2b1504b8715e301a2957cd79f5f942524af676279645ba9f41f141544b0adac60703027cab8c5d9b51ec8103e2da72c3f7c61803cd94765493506e82921ca469b919cbce716ebe10864655fe6fc7177bba55baf3276007b196c81eb97b1fc7d925eb3c91da45a5ea5c5121f109e73f05937a33ecce115a325914bec1cb5590bc01b57fdba899f5e048ab18fb47c71e8cf351dbcf4140c6961a10ef1c3e322bf760ed222ba83b363977172ea2d7c40e23bcd307e28d69c868af7f1f0ace4965d68ee540ef2ba0b81a698db7239a40cf7845d7bf322e444178921dfcf9c23fbcbfc4d306146ccae6ecb707b7fcc7f43aa92071caba893507117621032d9a2c7ac602f013186b7faaa8596105bfdfe47b6e0e93538f322e7417db403a0288d7a6f635ba1c13f43927061717014c80c88b65840a6ca83d59abce9ef4873e34b936650ab0158d145e89e6aaf2d17fc6a9a8bbf8c01906404aac849189b3865687350764be2872532a2271e05d02f820644a33bf6b64c7bef615d6b8dc27250e58a55039b41c6d97f6d472bed31e76b042d4c79c770c242179acbaa6dc625b4b1e7d6525de8aa4cbc4dfbd5d9562ea60be9eafcda1ca7328c4c9db4064550c5268f9259dfa1a2aa3cffdd02ef19c76d32650da3b22ea83e6c04a063ece816fa9371d7669feb1a9a03eca17a9798eeb1626cb4cc996f2cbc8d8a0c5d78ef3e921a276eb03717a423e5cde35a878bf3961aeb3eacb26332bf7eecb5259074d865478bde6810233217cfd1f91c8c85ffd89285e8579ece30591fac67f863b000d665d4298be4142a0f9991c51ec03b828e7620e71b9eee7753b35328028ba0e73e65191a6516f842c65a423f015f3f8edba667a756f4e479d3f8a6d40a45a6698cf8b6a226649b17192fdc39977d9913eafb917fb9ad25ce26cda7f51e10535d3a29a3fb0fe5fe566f9ab8097d4b6c8f6236c42fde414cf1bdf88f83638d152c3504aa9758778c7107b2b43e3a1b5ade047afe4c527afe7c8bc1ae4559f9f393b6c4c35fbcd5089cb9fc96c43a15c7e052e28073046817b969c0e624bd7c147e3e2de3bc8eac41c160f400568bedeb1549f5b4081e1cbf106eff16c546fd03b552960abc0b64969bcdc85797e14bc73eaa45ca04ddbf228308190eed13503934f60b3b715bc3089241745b1df8439e7f08309fe873ea31ce35a6ccdb634dc9f27ee0cbfff9f4602bcd8f8b10a05f2fd7d46948caad11c09ca71effbe0ee63458874df827d9f652cbf1e55402c2a69b8a483029bf807ad63eb41cc92216d9f171284eb5d7dddb8e5d791d746ec35677a0b0b3eb435f65ecbf8dcd5ae2536eccbd554416e070ce4aaa566e2c5f925e6129617f93e531d17d06a18f1049a16e1e6aff5e46d48e0a6406c5a480c9884690c13ec302b9c03491be7bfe7981663a6716c6bc45ffd45b0c8ec581ec1ad7b1f7f409697bb39b6da1e27cf5355d9b6eca4a306c908083e3a81026ba1be096fdb6d9b7004cc21d439d5c6ce1a45afc951c84c7b26bcf9b4de4aef365ed514edc4a04e6999c1b9fa3f1ee332669228f3cdedd0edec4aa56824f4a64f295835b8830d153b0a0bf3f80c32d11d4b541be45d2c2522503d4bb3440829daf60ff383d71748becce66a78c2dab29fb82a6d7692c0da0326e65b8101a8099f7e2fdb60bdb08ed1e627747492c18b9f2f4653c6a8181cadc56cb684e878acc4583cc2f53ddbb18990291a26a750c422025b29de72d36581ef8fb047e7854cf04b4bba931a7a560d8cb4394a047c44926cee25d0b457e86ffaf4ad4f419df6e6c9d89b8f971310f109eaaa39403a9a5e4dc22ab5ce98afafd4f9b97c4202999ea0b62c20995c4c5b3cd466a99622980fb0478ee471168137cba6266ed7ce5caaaeec8df1921ce3749bcb6a6a488d40bc1f84df8f37847efd4833ec9bc39b5890bcda0ba97acab58ac1e389c752340ad20fbd2d6885f1ce7349376302cb3d5070f30288e55b033c4242f328b9ae4565af08519f052f758b4f6eff1e8fb1ef6d0a96eddc4f6d135cdd930d37ce9e9a0699989e5f54c9104a710070a5458c38af044b6335457d6ffa477fdaa8507b6043d8639e30190613ae5667c6a757522345ee1c1ca73b279255f2cf7b7ddfbdb60fa0e218e0675a9c0fc13d8f043230bae9db8073cd0ae2627c663fdedf7b8fe7341b3817623894874932797320ef582943b8fb5ea96827aef251193bc50dc4400cc7f9bc44d83651c05ff17904367f66d736eede1964f90d7fe7ff086b3849ce32a1554182b1ea8703447a61b2d2d6e2753558f1355a3fd84af25fd89fdf495b641880081d974ca641846c8d4495ce11b79933f1c47a1fef41adc1c7992fb11479ec8d117d06668580cf3558ae681626e50d559bf4147b2b1ec59c4979e19ef851612c3c888d57d512a073c0ff9fa7dfb7e7d9552925baf3e95a83f41a6a5bbd867820e791ae13b0923ebbfca3adeb46323c2c85c3fceb9525b5fc50a078f0b0a9c7820fef73429a617b476e89fa77627fe9816be6ba912da6353092c5953c4ff2911500c4e88f4a0124db5edbdce22cb34235d9c9c9d0e00559eabb222aab6dede02d6b0c46adbf6e9437339e267b26b9f2471e94e41a72a4445ff0a855f72bd04e028f845ec445f13f1d28f173a0494d769b552c85721ac10aed7e8a79b72adf8f5e4f409637a0541579cfa918dd50d3d93d00dabe7dac95675b07ac078fbb31e35e77baa280e6b5b510dfafbc0ee5445524e8a4ddc0c6df98080d74df263bf022566e2791d1fa4a72ebf880852e57b1eac8"); + ConfidentialTxOut txout(locking_script, asset_commitment, + value_commitment, nonce_commitment, ByteData(), + rangeproof); + Privkey blinding_key("66e4df5035a64acef16b4aa52ddc8bebd22b22c9eca150774e355abc72909d83"); + + auto unblind_data = txout.Unblind(blinding_key); + + EXPECT_EQ("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", + unblind_data.asset.GetHex()); + EXPECT_EQ(int64_t{209998999992700}, + unblind_data.value.GetAmount().GetSatoshiValue()); + EXPECT_EQ("6b49938ded88d5c2c335133665158134041769882dd560ca47c14631052a981c", + unblind_data.abf.GetHex()); + EXPECT_EQ("0e396bcbc4c0b74329712b1b5c7f8c9c4f054996da3748b8820563f68a07dedd", + unblind_data.vbf.GetHex()); + + // 020000000101a38845c1a19b389f27217b91e2120273b447db3e595bba628f0be833f301a24a0000000000fdffffff030bb0852f9c11249a0c8ebb1c1bc3f99c5f643fa3426d4e5c730e702aaf3f2581fc091dd6cd19781f14385175b586607221d1f11b3f19f149e707810de78f7c7f6f790398e7cd1cb3c9c13506f91b946f5586cc8fa45f36dde615f2bdb48c1dfe90427017a914001d6db698e75a5a8af771730c4ab258af30546b870125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000003b9aca00009e6a2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f17a914a722b257cabc3b8e7d46f8fb293f893f368219da872103700dcb030588ed828d85f645b48971de0d31e8c0244da46710d18681627f5a4a4101044e949dcf8ac2daac82a3e4999ee28e2711661793570c4daab34cd38d76a425d6bfe102f3fea8be12109925fad32c78b65afea4de1d17a826e7375d0e2d00660125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a010000000000001c840000000000000000000043010001f6f270a50e47a77305832363b2ab9c7eea39297a4e2872b078552bca67a0fb0fb4e4c5389dd57b344bb70039538148c7c88b661538742f6632c2d4699616169cfd4e106033000000000000000121579e0109f21bb7cd0326804abb14059b54fa186892f060384435402393a6589fb54df6ede461ce206af5b13abbe1a564404abf7ab1cc4ed6bd1d93a91728224b21c10a540d76f0ccc047ffa3e06a8abad69687213373d5bcfd4fcf62ecd11c2aff9e1a9d64602cab441e3777d771f88f7e805223a5f192ed3d63b937e93e0e170e6b76e8a05fc5bb46826967d4850dbd2e9128ed615e71149f9aac9304194e968cd8b1ad28a96230b4064795040d16554776ee40b3793bdc9758285df2bc90a5c4be023d206293c071962939160f640ecedbba5a3731aa0aa60afd94f2d9a9d366b4e55ec8c561455ba6a849f8f0df0b19eaf98993655102500a47b76ad320522e4c1f9f829333331017cd0040226f60e426fc00b3844d08e364f6c9f346e3eb7dcf8de6e12a2ac98f11bed6b0c3e06c0bf767e96414ec276a2eb145f64eccb663b46135a19ac17cce785f15b1b2cb7206483329f4c38236ad3cd6cf1646a95ddb94beccb237dc6a854098b139df24f2c8ce7ce7e6d799a434f415ba52ce643bd51619edd786df734b0cee6a5a9ff0442f50a0ca011d8e5b695e6a44016884dbf2f6f681bc3faa5a1a9be514b87ab47183a85bd98b6eff7d5865246b7564a3008cba9b5bcac11e8b3b76dcc38c5915f14a1c7c08a76a31de11d9084cc0553871d0fb9e6ca25b2d191eb292c8f14c1b0cf9c4fedd0a85989a388c675e51003722bae92e356dd680a68f9861fc99be81bdc4f57fa0bf6f63613f80598960aacb8867a94cfbea8d4b776a4771ce1e8b20cf8506cd0524ba85e117085addf4de1d45c2ec6f8e441f74081b792208050ae890a91d5554e37d33ddba2b2c0bbdc2b4a31d0f7eaa1538e5a9fcbd86a87fdf5c6f161c0de5bdc9331b5936b06470c16778077887b3c80a53c5372914d2c3fffd5cb88ee00ffbbbdb10d7ce0fbbca1e725438b7f51a81b611b9f0c4330c3ec3b8d191218fc720c3165305412425f73efacdf44fb0cda9bd1f5290cac09a7f27ca68cb9b485d83c822282cadbfab69ef4f079d1d9e6042033a4a2c8ee9203509f9669f0553ece40d2958d3a733b81232b6ef6c21adc2e2716942588146d6dcadcdc94a13326c6828d04692da1a129e73a3f60cdd30d16b19b29f15a734ddcee491159fedec88a6c986e0243e99b9d2ceed1b9b8c7b3180857f067d8d778f8c0d19ef059f4bf2de3de7f3fc26c86a37cf046fae93cbaaf06e9d95ed73e95957aa69c2680087c6b36d51f93d8e3b9596ab4d793619e70a61edd12c14dc928cc56d1047ea3383b77d8d7527fe43317b21c86a323ed982ed1b766698dcda6c6a91bf50d3f01d7f1d8c20b44441cab3eeb5fac0995298eb7a7a6877bc420119a9acc713e221ddcd2038423c431247dd328372744aea39b9797a9757e46f9d83c5bcce8e2af2baef8416d27a2e5274c21ebdf432f844511081cede5f794ac5e4521d432a6497946d014af706cf431f3437d9d8ad04a3c84cbec65c5596569bdd268a2d7bf0025e6debf4209f7a7a6ae29d9357f84007e72358869caa02ce25813115c98ae6054e9bf48732b55bed6cfe87e7ae05eb435a6eece76046dc2da86d0c33283ff6fb4ffb65e8c943a8c298146c31b48eddc0e0255094c5761d9a8e7b155058efc755579301781355607f369724dd81b58d76977c19a9787ed92760ecce6b2ff0b03093f0936ba6b5872cdf0dcad63f4080f9501a813df248c3484f3445e75f8ca7ab028409bcf63c191b9bd352687397a0cab873dbf6c4fec4cf364cb2e7966f4b6d77e6b9a8477d44d3cf3ef5ecdb1faebd35dff45f39d660accf3bb3fa86cbd021e572eba6014e5c2e48702ab874bde9314c20e9073c20bad928b672249d01d8d8c89b368962a2e9cabca801fb27e59b1a58fd11fcf4d697746e1a7e72a8e32186155dd0f90468e6bb40e39e1d599434e7d504e577f2aee60479a21152c8c64c57f11930611385a605c150cb5c582489dd8e268ba8e1dd4b38fb337f347f3aa953a1d437f69209c2672c755d84957522938f6e69ec39a59725fe786e442a3f39083283dcb6177cde1a4abe38d17c4a45e7b1e78460474bee3eb8ccd4a1d0bd9ac7cc4dcac3dc9263117b0f9e05ff85853d9b28444ee840bd3f674dc2abc53fa5d2db8cd64a1739eec76063ffd93352128135b02377f6ca684b11add9c9434f6060f98c1c0f14ad941c12a4c6ca753afd36f22e353cf7147ab62c7245871a87166b7ca5d5e179953ee4d0328d0c7d69fd4128e06f6e546109732bd7d213e1559b280c0f2304ab874fe87410a77ed8c41378e5530e6ba6eb605f87fc75a4763244433366866ea9227cea868d67dde1287460351f8bc132c398bd4aaeadfe2959394ff96b3cbe1a3265777ea0fe676ecfd71daaf213d93edcc4a83fb9d159f858f61f8c28476430d97fd8879df97bb89914bc8e767a008b952976fb54d44492da79036004c36de129118f8fca1528dae9becd6c1471c9b1690fc00d64af653b1672f0db041ae7a0592a85dff25ca470892f8064b9ed01085a929d7b0ef03775fd5546bdab4ffd494a4af9c35ca0b6d3313c2be2c817522ff79fc54d28baf7707463dfe13423ae368bb1c80e7e2e509617f41c54f57ef67c6a786c28f1578be5a64e36cfb3b63c5e1c07619503619031772505987b515faabf7203b4d4413d6abbf8a6c8f00cd69a24e6c0f9c278b0f166763dbeed28ff447814e2d61c92d31840811f2b4e69323e7a35670b7629dbbe04afc7167ee217fe0f74930cf10ef5c07240c70ff3b36d200c1ce866bf108d5566fbff4860389f532871e2d8075fa28c39c85bd68a95f3a1e23265cbc5ed1657711239a32c4c67245d70eb93082323fcc7f113f912c1e0b818186e044b19dfc22d33c82e2cdfb88ead2f152790f47c1b432bce6d899e9c2dcf03969ab2cafd22e4cf7bbefe10bd2b8fb0078e8b5e006cf898259061efbeaa41aa46bd5cc19d3efe05a8c590bd8212b25f862b635876a9a63cde5fb194358c619fd39ef389b60eb7688513ea4dcf6dacc8cc97456a5131bf8de5b424edff078604456e8b6bce2b1504b8715e301a2957cd79f5f942524af676279645ba9f41f141544b0adac60703027cab8c5d9b51ec8103e2da72c3f7c61803cd94765493506e82921ca469b919cbce716ebe10864655fe6fc7177bba55baf3276007b196c81eb97b1fc7d925eb3c91da45a5ea5c5121f109e73f05937a33ecce115a325914bec1cb5590bc01b57fdba899f5e048ab18fb47c71e8cf351dbcf4140c6961a10ef1c3e322bf760ed222ba83b363977172ea2d7c40e23bcd307e28d69c868af7f1f0ace4965d68ee540ef2ba0b81a698db7239a40cf7845d7bf322e444178921dfcf9c23fbcbfc4d306146ccae6ecb707b7fcc7f43aa92071caba893507117621032d9a2c7ac602f013186b7faaa8596105bfdfe47b6e0e93538f322e7417db403a0288d7a6f635ba1c13f43927061717014c80c88b65840a6ca83d59abce9ef4873e34b936650ab0158d145e89e6aaf2d17fc6a9a8bbf8c01906404aac849189b3865687350764be2872532a2271e05d02f820644a33bf6b64c7bef615d6b8dc27250e58a55039b41c6d97f6d472bed31e76b042d4c79c770c242179acbaa6dc625b4b1e7d6525de8aa4cbc4dfbd5d9562ea60be9eafcda1ca7328c4c9db4064550c5268f9259dfa1a2aa3cffdd02ef19c76d32650da3b22ea83e6c04a063ece816fa9371d7669feb1a9a03eca17a9798eeb1626cb4cc996f2cbc8d8a0c5d78ef3e921a276eb03717a423e5cde35a878bf3961aeb3eacb26332bf7eecb5259074d865478bde6810233217cfd1f91c8c85ffd89285e8579ece30591fac67f863b000d665d4298be4142a0f9991c51ec03b828e7620e71b9eee7753b35328028ba0e73e65191a6516f842c65a423f015f3f8edba667a756f4e479d3f8a6d40a45a6698cf8b6a226649b17192fdc39977d9913eafb917fb9ad25ce26cda7f51e10535d3a29a3fb0fe5fe566f9ab8097d4b6c8f6236c42fde414cf1bdf88f83638d152c3504aa9758778c7107b2b43e3a1b5ade047afe4c527afe7c8bc1ae4559f9f393b6c4c35fbcd5089cb9fc96c43a15c7e052e28073046817b969c0e624bd7c147e3e2de3bc8eac41c160f400568bedeb1549f5b4081e1cbf106eff16c546fd03b552960abc0b64969bcdc85797e14bc73eaa45ca04ddbf228308190eed13503934f60b3b715bc3089241745b1df8439e7f08309fe873ea31ce35a6ccdb634dc9f27ee0cbfff9f4602bcd8f8b10a05f2fd7d46948caad11c09ca71effbe0ee63458874df827d9f652cbf1e55402c2a69b8a483029bf807ad63eb41cc92216d9f171284eb5d7dddb8e5d791d746ec35677a0b0b3eb435f65ecbf8dcd5ae2536eccbd554416e070ce4aaa566e2c5f925e6129617f93e531d17d06a18f1049a16e1e6aff5e46d48e0a6406c5a480c9884690c13ec302b9c03491be7bfe7981663a6716c6bc45ffd45b0c8ec581ec1ad7b1f7f409697bb39b6da1e27cf5355d9b6eca4a306c908083e3a81026ba1be096fdb6d9b7004cc21d439d5c6ce1a45afc951c84c7b26bcf9b4de4aef365ed514edc4a04e6999c1b9fa3f1ee332669228f3cdedd0edec4aa56824f4a64f295835b8830d153b0a0bf3f80c32d11d4b541be45d2c2522503d4bb3440829daf60ff383d71748becce66a78c2dab29fb82a6d7692c0da0326e65b8101a8099f7e2fdb60bdb08ed1e627747492c18b9f2f4653c6a8181cadc56cb684e878acc4583cc2f53ddbb18990291a26a750c422025b29de72d36581ef8fb047e7854cf04b4bba931a7a560d8cb4394a047c44926cee25d0b457e86ffaf4ad4f419df6e6c9d89b8f971310f109eaaa39403a9a5e4dc22ab5ce98afafd4f9b97c4202999ea0b62c20995c4c5b3cd466a99622980fb0478ee471168137cba6266ed7ce5caaaeec8df1921ce3749bcb6a6a488d40bc1f84df8f37847efd4833ec9bc39b5890bcda0ba97acab58ac1e389c752340ad20fbd2d6885f1ce7349376302cb3d5070f30288e55b033c4242f328b9ae4565af08519f052f758b4f6eff1e8fb1ef6d0a96eddc4f6d135cdd930d37ce9e9a0699989e5f54c9104a710070a5458c38af044b6335457d6ffa477fdaa8507b6043d8639e30190613ae5667c6a757522345ee1c1ca73b279255f2cf7b7ddfbdb60fa0e218e0675a9c0fc13d8f043230bae9db8073cd0ae2627c663fdedf7b8fe7341b3817623894874932797320ef582943b8fb5ea96827aef251193bc50dc4400cc7f9bc44d83651c05ff17904367f66d736eede1964f90d7fe7ff086b3849ce32a1554182b1ea8703447a61b2d2d6e2753558f1355a3fd84af25fd89fdf495b641880081d974ca641846c8d4495ce11b79933f1c47a1fef41adc1c7992fb11479ec8d117d06668580cf3558ae681626e50d559bf4147b2b1ec59c4979e19ef851612c3c888d57d512a073c0ff9fa7dfb7e7d9552925baf3e95a83f41a6a5bbd867820e791ae13b0923ebbfca3adeb46323c2c85c3fceb9525b5fc50a078f0b0a9c7820fef73429a617b476e89fa77627fe9816be6ba912da6353092c5953c4ff2911500c4e88f4a0124db5edbdce22cb34235d9c9c9d0e00559eabb222aab6dede02d6b0c46adbf6e9437339e267b26b9f2471e94e41a72a4445ff0a855f72bd04e028f845ec445f13f1d28f173a0494d769b552c85721ac10aed7e8a79b72adf8f5e4f409637a0541579cfa918dd50d3d93d00dabe7dac95675b07ac078fbb31e35e77baa280e6b5b510dfafbc0ee5445524e8a4ddc0c6df98080d74df263bf022566e2791d1fa4a72ebf880852e57b1eac800000000 +} + #endif // CFD_DISABLE_ELEMENTS diff --git a/test/test_hashutil.cpp b/test/test_hashutil.cpp index 2b8ca428..527fdc5a 100644 --- a/test/test_hashutil.cpp +++ b/test/test_hashutil.cpp @@ -65,6 +65,10 @@ TEST(HashUtil, Ripemd160ByOperator) { << ByteData160("0123456789abcdef0123456789abcdef01234567")).Output(); EXPECT_STREQ(byte_data.GetHex().c_str(), "49ec9207a365f6f330d529ca2a79e23a7ea2b526"); + auto bytedata160 = (HashUtil(HashUtil::kRipemd160) + << ByteData160("0123456789abcdef0123456789abcdef01234567")).Output160(); + EXPECT_STREQ(bytedata160.GetHex().c_str(), "49ec9207a365f6f330d529ca2a79e23a7ea2b526"); + byte_data = (HashUtil(HashUtil::kRipemd160) << ByteData256("1234567890123456789012345678901234567890123456789012345678901234")).Output(); EXPECT_STREQ(byte_data.GetHex().c_str(), "a5b1c86f10c81c3c543304e9891815d8de036296"); @@ -331,3 +335,15 @@ TEST(HashUtil, Sha512ByteScript) { byte_data.GetHex().c_str(), "7ad6132c2611fd0496ad42c758edc1bc2a23c3a4c463e139e144e25c35a53765c4c4c99d68d821a1bdd71b10e88afebdba72bfa0ae3877f628f1e2eab5320229"); } + +TEST(HashUtil, operator) { + ByteData target( + "21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac"); + HashUtil hash_util(HashUtil::kSha512); + hash_util << target.GetBytes(); + HashUtil hash_util2(HashUtil::kSha512); + hash_util2 = hash_util; + EXPECT_EQ( + hash_util2.Output().GetHex(), + "7ad6132c2611fd0496ad42c758edc1bc2a23c3a4c463e139e144e25c35a53765c4c4c99d68d821a1bdd71b10e88afebdba72bfa0ae3877f628f1e2eab5320229"); +} diff --git a/test/test_outpoint.cpp b/test/test_outpoint.cpp index d80b6904..42d80f48 100644 --- a/test/test_outpoint.cpp +++ b/test/test_outpoint.cpp @@ -78,19 +78,19 @@ TEST(OutPoint, Operators) { EXPECT_TRUE((outpoint1 >= outpoint2)); EXPECT_FALSE((outpoint1 >= outpoint3)); - EXPECT_FALSE((outpoint1 >= outpoint4)); + EXPECT_TRUE((outpoint1 >= outpoint4)); EXPECT_FALSE((outpoint1 > outpoint2)); EXPECT_FALSE((outpoint1 > outpoint3)); - EXPECT_FALSE((outpoint1 > outpoint4)); + EXPECT_TRUE((outpoint1 > outpoint4)); EXPECT_TRUE((outpoint1 <= outpoint2)); EXPECT_TRUE((outpoint1 <= outpoint3)); - EXPECT_TRUE((outpoint1 <= outpoint4)); + EXPECT_FALSE((outpoint1 <= outpoint4)); EXPECT_FALSE((outpoint1 < outpoint2)); EXPECT_TRUE((outpoint1 < outpoint3)); - EXPECT_TRUE((outpoint1 < outpoint4)); + EXPECT_FALSE((outpoint1 < outpoint4)); EXPECT_TRUE((outpoint4 < outpoint3)); } diff --git a/test/test_schnorrsig.cpp b/test/test_schnorrsig.cpp index 33401680..55fd5c7f 100644 --- a/test/test_schnorrsig.cpp +++ b/test/test_schnorrsig.cpp @@ -13,6 +13,7 @@ using cfd::core::SchnorrPubkey; using cfd::core::SchnorrSignature; using cfd::core::SchnorrUtil; using cfd::core::SigHashType; +using cfd::core::SigHashAlgorithm; const ByteData256 msg( "e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614"); @@ -87,6 +88,15 @@ TEST(SchnorrSig, Constructor) { EXPECT_EQ(0, empty_obj.GetData().GetDataSize()); } +TEST(SchnorrSig, SetSigHashType) { + SchnorrSignature sig( + "6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee" + "5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8"); + sig.SetSigHashType(SigHashType(SigHashAlgorithm::kSigHashAll)); + EXPECT_EQ(SigHashAlgorithm::kSigHashAll, + sig.GetSigHashType().GetSigHashAlgorithm()); +} + TEST(SchnorrPubkey, FromPubkey) { bool is_parity = false; auto actual_pubkey = SchnorrPubkey::FromPubkey(sk.GetPubkey(), &is_parity); diff --git a/test/test_script.cpp b/test/test_script.cpp index bda6662a..3a3ecef6 100644 --- a/test/test_script.cpp +++ b/test/test_script.cpp @@ -532,3 +532,20 @@ TEST(Script, IsPegoutScriptTest) { EXPECT_FALSE(script.IsP2wshScript()); EXPECT_TRUE(script.IsPegoutScript()); } + +TEST(Script, ParseCoinbaseScriptsigTest) { + const std::string script = "03632b1e045352b260425443506f6f6cfabe6d6d4b081c2a3c7cb234c159b8e198294dfa79c04b54803e0e54c4a37d239445eb42020000007296cd100100000e8338000000000000"; + Script obj(script); + EXPECT_EQ(script, obj.GetHex()); + auto list = obj.GetElementList(); + EXPECT_EQ(3, list.size()); + if (list.size() == 3) { + EXPECT_EQ("03632b1e", list[0].GetData().GetHex()); + EXPECT_EQ("045352b260", list[1].GetData().GetHex()); + EXPECT_TRUE(list[2].IsBinary()); + // buffer size is low from length. + EXPECT_EQ( + "5443506f6f6cfabe6d6d4b081c2a3c7cb234c159b8e198294dfa79c04b54803e0e54c4a37d239445eb42020000007296cd100100000e8338000000000000", + list[2].GetBinaryData().GetHex()); + } +} diff --git a/test/test_scriptbuilder.cpp b/test/test_scriptbuilder.cpp index 8b8e1037..753a4062 100644 --- a/test/test_scriptbuilder.cpp +++ b/test/test_scriptbuilder.cpp @@ -304,7 +304,7 @@ TEST(ScriptBuilder, StringBuildByOperator) { << "144" << "OP_CHECKLOCKTIMEVERIFY" << "OP_DROP" << "1469272661" << "OP_SHA256" - << "f6116d61351c05df34e116f1cc63fcacbd4f1a3882d2f629e7a0986ac03005c4" + << ByteData256("f6116d61351c05df34e116f1cc63fcacbd4f1a3882d2f629e7a0986ac03005c4") << "OP_EQUALVERIFY" << "OP_CHECKSIG").Build(); EXPECT_STREQ(script.GetHex().c_str(), expect_hex.c_str()); EXPECT_STREQ(script.ToString().c_str(), expect_asm.c_str()); @@ -320,3 +320,17 @@ TEST(ScriptBuilder, StringBuildByOperator) { EXPECT_STREQ(script.ToString().c_str(), "0 17 8738 3355443"); } + +TEST(ScriptBuilder, ScriptStackByOperator) { + std::string exp_hex = "4c6a47304402205a2f94921f645669b2b4e073da43e6a5d32335b50207f9d27f0e8a8c0a24e75902205dea52d27ad747f2df786e0ad737595cf9c5a489143170668399764a5b4be44a01210229e026bab56c1c41d16e67f084362aef204b5b7ea08dafc2fb2e0db89d9c9551"; + + ScriptBuilder build1; + build1 << ByteData("304402205a2f94921f645669b2b4e073da43e6a5d32335b50207f9d27f0e8a8c0a24e75902205dea52d27ad747f2df786e0ad737595cf9c5a489143170668399764a5b4be44a01"); + build1 << Pubkey("0229e026bab56c1c41d16e67f084362aef204b5b7ea08dafc2fb2e0db89d9c9551"); + auto script1 = build1.Build(); + + ScriptBuilder builder; + builder << script1; + auto script = builder.Build(); + EXPECT_EQ(exp_hex, script.GetHex()); +} diff --git a/test/test_scriptoperator.cpp b/test/test_scriptoperator.cpp index fc185c41..cb7dbed0 100644 --- a/test/test_scriptoperator.cpp +++ b/test/test_scriptoperator.cpp @@ -143,3 +143,11 @@ TEST(ScriptOperator, GetOperator) { EXPECT_THROW((ope = ScriptOperator::Get("OP_xxxx")), CfdException); } +TEST(ScriptOperator, IsOpSuccess) { + EXPECT_TRUE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess137)); + EXPECT_TRUE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess137, true)); + EXPECT_TRUE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess192)); + EXPECT_FALSE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess192, true)); + EXPECT_TRUE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess195)); + EXPECT_TRUE(ScriptOperator::IsOpSuccess(ScriptType::kOpSuccess195, true)); +} diff --git a/test/test_serializer.cpp b/test/test_serializer.cpp index 46904162..192fe6cb 100644 --- a/test/test_serializer.cpp +++ b/test/test_serializer.cpp @@ -20,6 +20,39 @@ TEST(Serializer, Normal) { builder.AddPrefixBuffer(0xe1e2e3e4, ByteData("d1d2d3d4")); builder.AddDirectBytes(ByteData("c1c2c3c4")); + EXPECT_EQ("01000000020000000000000003feffff010002f1f209fee4e3e2e1d1d2d3d4c1c2c3c4", + builder.Output().GetHex()); + + Serializer obj(builder); + Serializer obj2; + obj2 = builder; + EXPECT_EQ(builder.Output().GetHex(), obj.Output().GetHex()); + EXPECT_EQ(builder.Output().GetHex(), obj2.Output().GetHex()); +} + +TEST(Serializer, Operator1) { + Serializer builder; + builder << uint32_t{1} << uint64_t{2} << uint8_t{3}; + builder.AddVariableInt(0x01ffff); + builder.AddVariableBuffer(ByteData("f1f2")); + builder.AddPrefixBuffer(0xe1e2e3e4, ByteData("d1d2d3d4")); + builder << ByteData("c1c2c3c4"); + EXPECT_STREQ("01000000020000000000000003feffff010002f1f209fee4e3e2e1d1d2d3d4c1c2c3c4", builder.Output().GetHex().c_str()); } + +TEST(Serializer, Operator2) { + Serializer builder; + builder << int64_t{2}; + builder << ByteData256("00020000000000000003feffff010002f1f209fee4e3e2e1d1d2d3d4c1c2c3c4"); + + EXPECT_EQ("020000000000000000020000000000000003feffff010002f1f209fee4e3e2e1d1d2d3d4c1c2c3c4", + builder.Output().GetHex()); +} + +TEST(Serializer, BigEndian) { + Serializer builder; + builder.AddDirectBigEndianNumber(0x01020304); + EXPECT_EQ("01020304", builder.Output().GetHex()); +} diff --git a/test/test_sighashtype.cpp b/test/test_sighashtype.cpp index ca8b477a..b1b7b262 100644 --- a/test/test_sighashtype.cpp +++ b/test/test_sighashtype.cpp @@ -42,3 +42,23 @@ TEST(SigHashType, SetFromSigHashFlag) { EXPECT_FALSE(type.IsAnyoneCanPay()); EXPECT_FALSE(type.IsForkId()); } + +TEST(SigHashType, CheckFlag) { + SigHashType sighash = SigHashType(SigHashAlgorithm::kSigHashUnknown); + EXPECT_FALSE(sighash.IsValid()); + + sighash = SigHashType(SigHashAlgorithm::kSigHashAll); + sighash.SetRangeproof(true); + EXPECT_FALSE(sighash.IsAnyoneCanPay()); + EXPECT_TRUE(sighash.IsRangeproof()); + sighash.SetAnyoneCanPay(true); + EXPECT_TRUE(sighash.IsAnyoneCanPay()); + EXPECT_TRUE(sighash.IsRangeproof()); +} + +TEST(SigHashType, Create) { + auto sighash = SigHashType::Create(0x41); + EXPECT_EQ(SigHashAlgorithm::kSigHashAll, sighash.GetSigHashAlgorithm()); + EXPECT_FALSE(sighash.IsAnyoneCanPay()); + EXPECT_TRUE(sighash.IsRangeproof()); +} diff --git a/test/test_stringutil.cpp b/test/test_stringutil.cpp index 8c5009eb..dcfa1f1d 100644 --- a/test/test_stringutil.cpp +++ b/test/test_stringutil.cpp @@ -71,6 +71,11 @@ TEST(StringUtil, ToLower) { EXPECT_STREQ(result.c_str(), "abcde_1fg"); } +TEST(StringUtil, IsValidHexString) { + EXPECT_TRUE(StringUtil::IsValidHexString("AbCdEf01")); + EXPECT_FALSE(StringUtil::IsValidHexString("AbCdE_1fg")); +} + TEST(StringUtil, SplitAndJoinTest) { std::vector expect_vec = { "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog" diff --git a/test/test_taproot_merkletree.cpp b/test/test_taproot_merkletree.cpp index 973ad3bd..e7f966c6 100644 --- a/test/test_taproot_merkletree.cpp +++ b/test/test_taproot_merkletree.cpp @@ -21,6 +21,34 @@ using cfd::core::ScriptBuilder; using cfd::core::ScriptOperator; using cfd::core::SchnorrUtil; +TEST(TapBranch, Empty) { + Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); + Pubkey pubkey = key.GeneratePubkey(); + bool is_parity = false; + SchnorrPubkey schnorr_pubkey = SchnorrPubkey::FromPubkey(pubkey, &is_parity); + EXPECT_EQ("1777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb", + schnorr_pubkey.GetHex()); + EXPECT_TRUE(is_parity); + + TapBranch tree; + EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000000", + tree.GetBaseHash().GetHex()); + EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000000", + tree.GetCurrentBranchHash().GetHex()); + EXPECT_EQ("cc3b1538e0c8144375f71e848b12d609d743992fddfc60dd6ca9b33b8392f27a", + tree.GetTweakedPubkey(schnorr_pubkey).GetHex()); + EXPECT_EQ("3a56ec9129732312a78db4b845138a3180c102621d7381ae6e6a5d530f14856a", + tree.GetTweakedPrivkey(key).GetHex()); + EXPECT_FALSE(tree.HasTapLeaf()); + EXPECT_EQ("", tree.ToString()); + + ByteData256 msg("e5b11ddceab1e4fc49a8132ae589a39b07acf49cabb2b0fbf6104bc31da12c02"); + auto pk = tree.GetTweakedPubkey(schnorr_pubkey); + auto sk = tree.GetTweakedPrivkey(key); + auto sig = SchnorrUtil::Sign(msg, sk); + EXPECT_TRUE(pk.Verify(sig, msg)); +} + TEST(TaprootScriptTree, Empty) { Privkey key("305e293b010d29bf3c888b617763a438fee9054c8cab66eb12ad078f819d9f27"); Pubkey pubkey = key.GeneratePubkey(); @@ -67,7 +95,7 @@ TEST(TaprootScriptTree, Branch) { ByteData256("dc82121e4ff8d23745f3859e8939ecb0a38af63e6ddea2fff97a7fd61a1d2d54") }; TaprootScriptTree tree(leaf_version, script); - tree.AddBranch(nodes[0]); + tree.AddBranch(TapBranch(nodes[0])); tree.AddBranch(SchnorrPubkey(nodes[1])); EXPECT_EQ(leaf_version, tree.GetLeafVersion()); diff --git a/test/test_taproot_util.cpp b/test/test_taproot_util.cpp index c3de3b24..4d9db477 100644 --- a/test/test_taproot_util.cpp +++ b/test/test_taproot_util.cpp @@ -38,6 +38,7 @@ using cfd::core::NetType; using cfd::core::SchnorrUtil; using cfd::core::SchnorrSignature; using cfd::core::TapScriptData; +using cfd::core::TapBranch; TEST(TaprootUtil, ValidLeafVersion) { EXPECT_FALSE(TaprootUtil::IsValidLeafVersion(0)); @@ -289,3 +290,27 @@ TEST(TaprootUtil, ParseAndVerifyTapScriptParityBit) { SchnorrPubkey("262d16c95b41f6a90a360837b5e9c3e213334deacffaec0413f8b6e98ad40165"), pk, nodes, tapscript)); } + +TEST(TaprootUtil, Bip86_1) { + // https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki + // Account 0, second receiving address = m/86'/0'/0'/0/1 + // xprv = xprvA449goEeU9okyiF1LmKiDaTgeXvmh87DVyRd35VPbsSop8n8uALpbtrUhUXByPFKK7C2yuqrB1FrhiDkEMC4RGmA5KTwsE1aB5jRu9zHsuQ + // xpub = xpub6H3W6JmYJXN4CCKUSnriaiQRCZmG6aq4sCMDqTu1ACyngw7HShf59hAxYjXgKDuuHThVEUzdHrc3aXCr9kfvQvZPit5dnD3K9xVRBzjK3rX + // internal_key = 83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145 + // output_key = a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb + // scriptPubKey = 5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb + // address = bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh + SchnorrPubkey pk("83dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145"); + TapBranch branch; + + SchnorrPubkey output_key; + Script locking_script; + auto ctrl = TaprootUtil::CreateTapScriptControl( + pk, branch, &output_key, &locking_script); + EXPECT_EQ("a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb", + output_key.GetHex()); + EXPECT_EQ("5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb", + locking_script.GetHex()); + EXPECT_EQ("c083dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145", + ctrl.GetHex()); +} diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 9d782b25..920d9478 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -571,3 +571,41 @@ TEST(Transaction, GetSchnorrSignatureHashNonce) { EXPECT_TRUE(schnorr_pubkey.Verify(schnorr_sig, sighash2)); } + +TEST(Transaction, ParseCoinbaseTx) { + const std::string tx = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4803632b1e045352b260425443506f6f6cfabe6d6d4b081c2a3c7cb234c159b8e198294dfa79c04b54803e0e54c4a37d239445eb42020000007296cd100100000e8338000000000000ffffffff0245039b000000000017a9147bef0b4a4dafa77b2ec52b81659cbcf0d9a91487870000000000000000266a24aa21a9edba23c37a95438644cda3c06c06ef03047168f3201aade74f3518530a4ba9db710120000000000000000000000000000000000000000000000000000000000000000000000000"; + Transaction tx_obj(tx); + EXPECT_EQ(1, tx_obj.GetTxInCount()); + EXPECT_EQ(2, tx_obj.GetTxOutCount()); + if (tx_obj.GetTxInCount() == 1) { + auto txin = tx_obj.GetTxIn(0); + EXPECT_EQ( + "03632b1e045352b260425443506f6f6cfabe6d6d4b081c2a3c7cb234c159b8e198294dfa79c04b54803e0e54c4a37d239445eb42020000007296cd100100000e8338000000000000", + txin.GetUnlockingScript().GetHex()); + EXPECT_EQ( + "0000000000000000000000000000000000000000000000000000000000000000", + txin.GetTxid().GetHex()); + EXPECT_EQ(0xffffffff, txin.GetVout()); + } + if (tx_obj.GetTxOutCount() == 2) { + auto txout1 = tx_obj.GetTxOut(0); + auto txout2 = tx_obj.GetTxOut(1); + EXPECT_EQ( + "a9147bef0b4a4dafa77b2ec52b81659cbcf0d9a9148787", + txout1.GetLockingScript().GetHex()); + EXPECT_EQ(10158917, txout1.GetValue().GetSatoshiValue()); + EXPECT_EQ( + "6a24aa21a9edba23c37a95438644cda3c06c06ef03047168f3201aade74f3518530a4ba9db71", + txout2.GetLockingScript().GetHex()); + EXPECT_EQ(0, txout2.GetValue().GetSatoshiValue()); + } +} + +TEST(Transaction, SetTxInSequence) { + std::string tx_hex = "02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000ffffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000"; + Transaction tx(tx_hex); + tx.SetTxInSequence(0, 4294967294); + EXPECT_EQ( + "02000000000101ffa8db90b81db256874ff7a98fb7202cdc0b91b5b02d7c3427c4190adc66981f0000000000feffffff0118f50295000000002251201777701648fa4dd93c74edd9d58cfcc7bdc2fa30a2f6fa908b6fd70c92833cfb02473044022018b10265080f8c491c43595000461a19212239fea9ee4c6fd26498f358b1760d0220223c1389ac26a2ed5f77ad73240af2fa6eb30ef5d19520026c2f7b7e817592530121023179b32721d07deb06cade59f56dedefdc932e89fde56e998f7a0e93a3e30c4400000000", + tx.GetHex()); +}