From de75222ef70218e94cbdf80d280f30765bbe5702 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Mon, 20 Apr 2020 13:56:13 +0100 Subject: [PATCH 01/18] Fix Bitcoin name usage in Windows installer --- share/setup.nsi.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/setup.nsi.in b/share/setup.nsi.in index 51a560dad1..6b435c7051 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -51,7 +51,7 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English # Installer attributes -InstallDir $PROGRAMFILES64\Bitcoin +InstallDir $PROGRAMFILES64\Elements CRCCheck on XPStyle on BrandingText " " @@ -104,7 +104,7 @@ Section -post SEC0001 WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 WriteRegStr HKCR "@PACKAGE_TARNAME@" "URL Protocol" "" - WriteRegStr HKCR "@PACKAGE_TARNAME@" "" "URL:Bitcoin" + WriteRegStr HKCR "@PACKAGE_TARNAME@" "" "URL:Elements" WriteRegStr HKCR "@PACKAGE_TARNAME@\DefaultIcon" "" $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@ WriteRegStr HKCR "@PACKAGE_TARNAME@\shell\open\command" "" '"$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" "%1"' SectionEnd @@ -137,7 +137,7 @@ Section -un.post UNSEC0001 Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, 64-bit).lnk" - Delete /REBOOTOK "$SMSTARTUP\Bitcoin.lnk" + Delete /REBOOTOK "$SMSTARTUP\Elements.lnk" Delete /REBOOTOK $INSTDIR\uninstall.exe Delete /REBOOTOK $INSTDIR\debug.log Delete /REBOOTOK $INSTDIR\db.log From 6c55d1d53f2b4ca9fb23b71ccf5540cd5cd39eb2 Mon Sep 17 00:00:00 2001 From: James Dorfman Date: Fri, 2 Sep 2022 11:18:50 -0400 Subject: [PATCH 02/18] docs: fix typo in elements-tx-format.md and remove trailing whitespace --- doc/elements-tx-format.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/elements-tx-format.md b/doc/elements-tx-format.md index 4bd263a529..945ac48bbf 100644 --- a/doc/elements-tx-format.md +++ b/doc/elements-tx-format.md @@ -21,7 +21,7 @@ This document assumes some familiarity with Bitcoin and Elements (UTXOs, [Script | Flags | Yes | 1 byte | `unsigned char` | | 1 if the transaction contains a witness, otherwise 0. All other values are invalid. | | Num Inputs | Yes | Varies | `VarInt` | | Number of inputs to the transaction. | | Inputs | Yes | Varies | `Vector` | | | -| Num Inputs | Yes | Varies | `VarInt` | | Number of outputs from the transaction. | +| Num Outputs | Yes | Varies | `VarInt` | | Number of outputs from the transaction. | | Outputs | Yes | Varies | `Vector` | | | | Locktime | Yes | 4 bytes | `uint32_t` | Little-endian | See [BIP 113](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki). | | Witness | Only if flags is 1 | Varies | `Witness` | | See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki). Note that Elements witnesses contain more data than Bitcoin witnesses. This extra data is described further below. | @@ -203,13 +203,13 @@ Deserialization: | Input #2 | 8d83eb1b0826f46d473003d041116927 | 470e2ce0f7cc0c634a983d438d770ac8 ... Outpoint TXID: c80a778d433d984a630cccf7e02c0e4727691141d00330476df426081beb838d -| 00000000 ........................... Outpoint index +| 00000000 ........................... Outpoint index | | 00 ................................. ScriptSig length | | .................................. ScriptSig (empty) | | ffffffff ........................... Sequence number: UINT32_MAX - + 02 ................................... Num Outputs | | Output #1 @@ -239,7 +239,7 @@ Deserialization: | 03 ................................. Nonce header (0x03 → compressed point) | 72fdd5c6e805a50d73ab15ec41cfaadc | be408ecc7a5867621918f1070f84ec95 ... Nonce x-coordinate (big-endian) -| +| | 16 ................................. ScriptPubKey length (0x16 = 22 bytes) | | 001424ae71d4804ca7dd1fa66486a8 | | 7af9dff1663c84 ................... ScriptPubKey @@ -317,18 +317,18 @@ Deserialization: | | .................................. ScriptSig (empty: segwit transaction) | | fdffffff ........................... Sequence number -| +| | .................................... Asset issuance | | 000000000000000000000000000000 | | 000000000000000000000000000000 | | 0000 ............................. Asset blinding nonce (0 for new asset issuance) -| | +| | | | 000000000000000000000000000000 | | 000000000000000000000000000000 | | 0000 ............................. Asset entropy | | | | 01 ............................... Amount header (0x01 → explicit, unblinded value) -| | 00000000c4b20100 ................. Amount: 0xc4b20100 = 3,300,000,000 → 33 units (each divisible by 100,000,000) +| | 00000000c4b20100 ................. Amount: 0xc4b20100 = 3,300,000,000 → 33 units (each divisible by 100,000,000) | | | | 01 ............................... Num inflation keys header (0x01 → explicit, unblinded value) | | 0000000029b92700 ................. Value. 0x29b92700 = 700,000,000 inflation keys @@ -343,7 +343,7 @@ Deserialization: | 08 ................................. Amount header (0x08 → blinded value) | 66abe471dfadfb650825abe6f757860b | 6760d30ff62bc7c9ebd438608f45368b ... Amount x-coordinate (big-endian) -| +| | 02 ................................. Nonce header (0x02 → blinded value) | 115750003261bc64bb73d83401a91279 | 6d0c0fb9d54c72751a7ca7a5149a9bdf ... Nonce x-coordinate (big-endian) @@ -518,7 +518,7 @@ Deserialization: | 6d521c38ec1ea15734ae22b7c4606441 | 2829c0d0579f0a713d1c04ede979026f ... Asset ID: 6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d | -| 01 ................................. Amount header (0x01 → explicit, unblinded value) +| 01 ................................. Amount header (0x01 → explicit, unblinded value) | 00000000002b09c1 ................... Amount: 0.02820545 L-BTC | | 00 ................................. Nonce header (0x00 → null) @@ -532,7 +532,7 @@ Deserialization: | 6d521c38ec1ea15734ae22b7c4606441 | 2829c0d0579f0a713d1c04ede979026f ... Asset ID: 6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d | -| 01 ................................. Amount header (0x01 → explicit, unblinded value) +| 01 ................................. Amount header (0x01 → explicit, unblinded value) | 0000000000000027 ................... Amount: 0.00000039 L-BTC | | 00 ................................. Nonce header (0x00 → null) @@ -560,10 +560,10 @@ Deserialization: | | | f34227cbba1cf25eb0778aa45f8b | | | 7cb3495046 ..................... Stack item #2 | 06 ................................. Peg-in witness stack length -| | 08 ............................... Stack item #1 length +| | 08 ............................... Stack item #1 length | | | e8092b0000000000 ............... Peg-in value (little-endian): 0x2b09e8 = 0.02820545 BTC) | | 20 ............................... Stack item #2 length (0x20 = 32) -| | | 6d521c38ec1ea15734ae22b7c46064 +| | | 6d521c38ec1ea15734ae22b7c46064 | | | 412829c0d0579f0a713d1c04ede979 | | | 026f ........................... Asset ID: 6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d | | 20 ............................... Stack item #3 length (0x20 = 32) @@ -592,4 +592,4 @@ Deserialization: | Output #2 witness | 00 ................................. Surjection proof length | 00 ................................. Range proof length -``` \ No newline at end of file +``` From fb5132b7bb3646e940db58c22e88ceca9329a464 Mon Sep 17 00:00:00 2001 From: Andrea Bonel Date: Mon, 5 Sep 2022 11:35:19 -0300 Subject: [PATCH 03/18] Fix createblindedaddress help message --- src/rpc/misc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 4c48f10439..790f6a6b32 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -991,13 +991,13 @@ static RPCHelpMan createblindedaddress() "\nCreates a blinded address using the provided blinding key.\n", { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The unblinded address to be blinded."}, - {"blinding_key", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The blinding public key. This can be obtained for a given address using `validateaddress`."}, + {"blinding_key", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The blinding public key. This can be obtained for a given address using `getaddressinfo` (`confidential_key` field)."}, }, RPCResult{ RPCResult::Type::STR, "blinded_address", "The blinded address" }, RPCExamples{ - "\nCreate a multisig address from 2 addresses\n" + "\nCreate a blinded address\n" + HelpExampleCli("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16 ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") + "\nAs a json rpc call\n" + HelpExampleRpc("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16, ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") From 172a959c7afba127dd6a5f028bd2240819583f1f Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 13:17:48 -0400 Subject: [PATCH 04/18] doc: Specify input explicit values and assets and their proofs --- doc/pset.mediawiki | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/pset.mediawiki b/doc/pset.mediawiki index da769b7169..90c8ff73fd 100644 --- a/doc/pset.mediawiki +++ b/doc/pset.mediawiki @@ -255,6 +255,46 @@ The currently defined elements per-input proprietary types are as follows: | | 0 | 2 +|- +| Explicit Value +| PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11 +| None +| No key data +| <64-bit little endian int value> +| The explicit value for the input being spent. If provided, PSBT_ELEMENTS_IN_VALUE_PROOF must be provided too. Must not be provided if the input's value in the UTXO is already explicit. +| +| 0 +| 2 +|- +| Explicit Value Proof +| PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12 +| None +| No key data +| +| An explicit value rangeproof that proves that the value commitment in this input's UTXO matches the explicit value in PSBT_ELEMENTS_IN_EXPLICIT_VALUE. If provided, PSBT_ELEMENTS_IN_EXPLICIT_VALUE must be provided too. +| +| 0 +| 2 +|- +| Explicit Asset +| PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13 +| None +| No key data +| <32 byte asset tag> +| The explicit asset for the input being spent. If provided, PSBT_ELEMENTS_IN_ASSET_PROOF must be provided too. Must not be provided if the input's asset in the UTXO is already explicit. +| +| 0 +| 2 +|- +| Explicit Asset Proof +| PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14 +| None +| No key data +| +| An asset surjection proof with this input's asset as the only asset in the input set in order to prove that the asset commitment in the UTXO matches the explicit asset in PSBT_ELEMENTS_IN_EXPLICIT_ASSET. If provided, PSBT_ELEMENTS_IN_EXPLICIT_ASSET must be provided too. +| +| 0 +| 2 |} The currently defined elements per-output proprietary types are as follows: From 59658ece6d41cb49e6cc77e3b065b764e3a89c41 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 13:18:08 -0400 Subject: [PATCH 05/18] pset: Implement de/ser of input explicit value, assets, and proofs --- src/psbt.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/psbt.h b/src/psbt.h index 0bd2b3bf73..145410f3b0 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -73,6 +73,10 @@ static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_ASSET_ENTROPY = 0x0d; static constexpr uint8_t PSBT_ELEMENTS_IN_UTXO_RANGEPROOF = 0x0e; static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_VALUE_PROOF = 0x0f; static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF = 0x10; +static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11; +static constexpr uint8_t PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12; +static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13; +static constexpr uint8_t PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14; // Output types static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00; @@ -259,6 +263,10 @@ struct PSBTInput // Auxiliary elements stuff std::vector m_utxo_rangeproof; + std::optional m_explicit_value; + std::vector m_value_proof; + uint256 m_explicit_asset; + std::vector m_asset_proof; bool IsNull() const; void FillSignatureData(SignatureData& sigdata) const; @@ -473,6 +481,26 @@ struct PSBTInput SerializeToVector(s, CompactSizeWriter(PSBT_OUT_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ISSUANCE_BLIND_INFLATION_KEYS_PROOF)); s << m_blind_issuance_inflation_keys_proof; } + + // Explicit value and its proof + if (m_explicit_value.has_value()) { + SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_VALUE)); + SerializeToVector(s, m_explicit_value.value()); + } + if (!m_value_proof.empty()) { + SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_VALUE_PROOF)); + s << m_value_proof; + } + + // Explicit asset and its proof + if (!m_explicit_asset.IsNull()) { + SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_EXPLICIT_ASSET)); + SerializeToVector(s, m_explicit_asset); + } + if (!m_asset_proof.empty()) { + SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ASSET_PROOF)); + s << m_asset_proof; + } } // Write proprietary things @@ -886,6 +914,48 @@ struct PSBTInput s >> m_blind_issuance_inflation_keys_proof; break; } + case PSBT_ELEMENTS_IN_EXPLICIT_VALUE: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, explicit value is already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input explicit value is more than one byte type"); + } + CAmount v; + UnserializeFromVector(s, v); + m_explicit_value = v; + break; + } + case PSBT_ELEMENTS_IN_VALUE_PROOF: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, explicit value proof is already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input explicit value proof is more than one byte type"); + } + s >> m_value_proof; + break; + } + case PSBT_ELEMENTS_IN_EXPLICIT_ASSET: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, explicit asset is already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input explicit asset is more than one byte type"); + } + UnserializeFromVector(s, m_explicit_asset); + break; + } + case PSBT_ELEMENTS_IN_ASSET_PROOF: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, explicit asset proof is already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input explicit asset proof is more than one byte type"); + } + s >> m_value_proof; + break; + } default: { known = false; @@ -936,6 +1006,12 @@ struct PSBTInput if (!m_issuance_inflation_keys_commitment.IsNull() && m_issuance_inflation_keys_rangeproof.empty()) { throw std::ios_base::failure("Issuance inflation keys commitment provided without inflation keys rangeproof"); } + if ((m_explicit_value.has_value() || !m_value_proof.empty()) && (!m_explicit_value.has_value() || m_value_proof.empty())) { + throw std::ios_base::failure("Input explicit value and value proof must be provided together"); + } + if ((!m_explicit_asset.IsNull() || !m_asset_proof.empty()) && (!m_explicit_asset.IsNull() || m_asset_proof.empty())) { + throw std::ios_base::failure("Input explicit asset and asset proof must be provided together"); + } } } From be602495f95cf7675aa1228195ed187d00f26422 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 13:48:47 -0400 Subject: [PATCH 06/18] pset: Implement VerifyBlindProofs for PSBTInput --- src/blindpsbt.cpp | 29 +++++++++++++++++++++++++++++ src/blindpsbt.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/src/blindpsbt.cpp b/src/blindpsbt.cpp index c2b927e265..8b964a9f17 100644 --- a/src/blindpsbt.cpp +++ b/src/blindpsbt.cpp @@ -220,6 +220,35 @@ BlindProofResult VerifyBlindProofs(const PSBTOutput& o) { return BlindProofResult::OK; } +BlindProofResult VerifyBlindProofs(const PSBTInput& i) { + CTxOut utxo; + if (!i.GetUTXO(utxo)) { + return BlindProofResult::OK; + } + + if (i.m_explicit_value != std::nullopt) { + if (i.m_value_proof.empty()) { + return BlindProofResult::MISSING_VALUE_PROOF; + } else if (!utxo.nValue.IsCommitment()) { + return BlindProofResult::NOT_FULLY_BLINDED; + } else if (!VerifyBlindValueProof(*i.m_explicit_value, utxo.nValue, i.m_value_proof, utxo.nAsset)) { + return BlindProofResult::INVALID_VALUE_PROOF; + } + } + + if (!i.m_explicit_asset.IsNull()) { + if (i.m_asset_proof.empty()) { + return BlindProofResult::MISSING_ASSET_PROOF; + } else if (!utxo.nAsset.IsCommitment()) { + return BlindProofResult::NOT_FULLY_BLINDED; + } else if (!VerifyBlindAssetProof(i.m_explicit_asset, i.m_asset_proof, utxo.nAsset)) { + return BlindProofResult::INVALID_ASSET_PROOF; + } + } + + return BlindProofResult::OK; +} + void CreateAssetCommitment(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const uint256& asset_blinder) { conf_asset.vchCommitment.resize(CConfidentialAsset::nCommittedSize); diff --git a/src/blindpsbt.h b/src/blindpsbt.h index 17fda2b7d9..49c753db78 100644 --- a/src/blindpsbt.h +++ b/src/blindpsbt.h @@ -17,6 +17,7 @@ struct PartiallySignedTransaction; struct PSBTOutput; +struct PSBTInput; enum class BlindingStatus { @@ -52,5 +53,6 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map& proof, const CConfidentialAsset& conf_asset); bool VerifyBlindAssetProof(const uint256& asset, const std::vector& proof, const CConfidentialAsset& conf_asset); BlindProofResult VerifyBlindProofs(const PSBTOutput& o); +BlindProofResult VerifyBlindProofs(const PSBTInput& i); #endif //BITCOIN_BLINDPSBT_H From 4bacc852e9c7c12cfd1927df0aaa401d609123da Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 13:56:52 -0400 Subject: [PATCH 07/18] rpc: Add input explicit value, asset, and proofs to decodepsbt --- src/rpc/rawtransaction.cpp | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4124b64f9e..3b7820f667 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1214,6 +1214,10 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"}, }}, {RPCResult::Type::STR_HEX, "utxo_rangeproof", "The rangeproof for the UTXO"}, + {RPCResult::Type::NUM, "explicit_value", /*optional=*/true, "The explicit value for this input"}, + {RPCResult::Type::STR_HEX, "value_proof", /*optional=*/true, "The explicit value proof for this input"}, + {RPCResult::Type::STR_HEX, "explicit_asset", /*optional=*/true, "The explicit asset for this input"}, + {RPCResult::Type::STR_HEX, "asset_proof", /*optional=*/true, "The explicit asset proof for this input"}, {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, @@ -1596,6 +1600,41 @@ static RPCHelpMan decodepsbt() in.pushKV("utxo_rangeproof", HexStr(input.m_utxo_rangeproof)); } + if (input.m_explicit_value.has_value()) { + in.pushKV("explicit_value", ValueFromAmount(*input.m_explicit_value)); + } + if (!input.m_value_proof.empty()) { + in.pushKV("value_proof", HexStr(input.m_value_proof)); + } + if (!input.m_explicit_asset.IsNull()) { + in.pushKV("explicit_asset", input.m_explicit_asset.GetHex()); + } + if (!input.m_asset_proof.empty()) { + in.pushKV("asset_proof", HexStr(input.m_asset_proof)); + } + + switch (VerifyBlindProofs(input)) { + case BlindProofResult::OK: + // all good + break; + case BlindProofResult::NOT_FULLY_BLINDED: + in.pushKV("status", "ERROR: Proofs provided for unblinded input"); + break; + case BlindProofResult::MISSING_VALUE_PROOF: + in.pushKV("status", "WARNING: has confidential and explicit values but no proof connecting them"); + break; + case BlindProofResult::MISSING_ASSET_PROOF: + in.pushKV("status", "WARNING: has confidential and explicit assets but no proof connecting them"); + break; + case BlindProofResult::INVALID_VALUE_PROOF: + in.pushKV("status", "ERROR: has invalid value proof, the value may be a lie!"); + break; + case BlindProofResult::INVALID_ASSET_PROOF: + in.pushKV("status", "ERROR: has invalid asset proof, the asset may be a lie!"); + break; + } + + // Proprietary if (!input.m_proprietary.empty()) { UniValue proprietary(UniValue::VARR); From 6291af43f90b6fdb27c5425e14bf2936aaef0b7c Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 14:01:48 -0400 Subject: [PATCH 08/18] doc: Specify flag for blinding issuances --- doc/pset.mediawiki | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/pset.mediawiki b/doc/pset.mediawiki index 90c8ff73fd..d5f4680b77 100644 --- a/doc/pset.mediawiki +++ b/doc/pset.mediawiki @@ -295,6 +295,16 @@ The currently defined elements per-input proprietary types are as follows: | | 0 | 2 +|- +| Blinded Issuance Flag +| PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15 +| None +| No key data +| <1 byte boolean> +| A boolean flag. 0x00 indicates the issuance should not be blinded, 0x01 indicates it should be. If not specified, assumed to be 0x01. Note that this does not indicate actual blinding status, but rather the expected blinding status prior to signing. +| +| 0 +| 2 |} The currently defined elements per-output proprietary types are as follows: From 9599661475827866b2da68452de4b9a7e7d4aa50 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 14:09:32 -0400 Subject: [PATCH 09/18] pset: Implement de/ser of issuance needs blinded flag --- src/psbt.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/psbt.h b/src/psbt.h index 145410f3b0..585ca67da9 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -77,6 +77,7 @@ static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_VALUE = 0x11; static constexpr uint8_t PSBT_ELEMENTS_IN_VALUE_PROOF = 0x12; static constexpr uint8_t PSBT_ELEMENTS_IN_EXPLICIT_ASSET = 0x13; static constexpr uint8_t PSBT_ELEMENTS_IN_ASSET_PROOF = 0x14; +static constexpr uint8_t PSBT_ELEMENTS_IN_BLINDED_ISSUANCE = 0x15; // Output types static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00; @@ -252,6 +253,7 @@ struct PSBTInput uint256 m_issuance_asset_entropy; std::vector m_blind_issuance_value_proof; std::vector m_blind_issuance_inflation_keys_proof; + std::optional m_blinded_issuance; // Peg-in std::variant m_peg_in_tx; @@ -501,6 +503,11 @@ struct PSBTInput SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_ASSET_PROOF)); s << m_asset_proof; } + + if (m_blinded_issuance.has_value()) { + SerializeToVector(s, CompactSizeWriter(PSBT_IN_PROPRIETARY), PSBT_ELEMENTS_ID, CompactSizeWriter(PSBT_ELEMENTS_IN_BLINDED_ISSUANCE)); + SerializeToVector(s, *m_blinded_issuance); + } } // Write proprietary things @@ -956,6 +963,18 @@ struct PSBTInput s >> m_value_proof; break; } + case PSBT_ELEMENTS_IN_BLINDED_ISSUANCE: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, issuance needs blinded flag is already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input issuance needs blinded flag is more than one byte type"); + } + bool b; + UnserializeFromVector(s, b); + m_blinded_issuance = b; + break; + } default: { known = false; From cf870ed8d00ff513e6adfb815d276f9df37a7fd5 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 14:11:37 -0400 Subject: [PATCH 10/18] rpc: Implement decodepsbt for issuance needs blinded flag --- src/rpc/rawtransaction.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 3b7820f667..01aed15ecf 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1218,6 +1218,7 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR_HEX, "value_proof", /*optional=*/true, "The explicit value proof for this input"}, {RPCResult::Type::STR_HEX, "explicit_asset", /*optional=*/true, "The explicit asset for this input"}, {RPCResult::Type::STR_HEX, "asset_proof", /*optional=*/true, "The explicit asset proof for this input"}, + {RPCResult::Type::BOOL, "blinded_issuance", /*optional=*/true, "Whether the issuance should be blinded prior to signing"}, {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, @@ -1613,6 +1614,10 @@ static RPCHelpMan decodepsbt() in.pushKV("asset_proof", HexStr(input.m_asset_proof)); } + if (input.m_blinded_issuance.has_value()) { + in.pushKV("blinded_issuance", *input.m_blinded_issuance); + } + switch (VerifyBlindProofs(input)) { case BlindProofResult::OK: // all good From 9ecbf38050ecb67939a111bf3e3cef43aee49720 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 1 Sep 2022 14:24:46 -0400 Subject: [PATCH 11/18] pset: Respect issuance needs blinded flag --- src/blindpsbt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blindpsbt.cpp b/src/blindpsbt.cpp index 8b964a9f17..6910d5ae85 100644 --- a/src/blindpsbt.cpp +++ b/src/blindpsbt.cpp @@ -415,7 +415,8 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map Date: Mon, 5 Sep 2022 19:38:11 +0000 Subject: [PATCH 12/18] Add more CPUs to the fuzzer test --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 48c1471121..dd94f725fb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -159,7 +159,7 @@ task: << : *GLOBAL_TASK_TEMPLATE container: image: ubuntu:focal - cpu: 4 # Increase CPU and memory to avoid timeout + cpu: 8 # Increase CPU and memory to avoid timeout memory: 16G env: << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV From 547b3e30ca6dca36eb29c724cda3a8fa2b1d3836 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 6 Sep 2022 15:56:39 -0400 Subject: [PATCH 13/18] psbt: Check subkey_len not key.size() --- src/psbt.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/psbt.h b/src/psbt.h index 585ca67da9..b648418cea 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -925,7 +925,7 @@ struct PSBTInput { if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, explicit value is already provided"); - } else if (key.size() != 1) { + } else if (subkey_len != 1) { throw std::ios_base::failure("Input explicit value is more than one byte type"); } CAmount v; @@ -937,7 +937,7 @@ struct PSBTInput { if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, explicit value proof is already provided"); - } else if (key.size() != 1) { + } else if (subkey_len != 1) { throw std::ios_base::failure("Input explicit value proof is more than one byte type"); } s >> m_value_proof; @@ -947,7 +947,7 @@ struct PSBTInput { if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, explicit asset is already provided"); - } else if (key.size() != 1) { + } else if (subkey_len != 1) { throw std::ios_base::failure("Input explicit asset is more than one byte type"); } UnserializeFromVector(s, m_explicit_asset); @@ -957,7 +957,7 @@ struct PSBTInput { if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, explicit asset proof is already provided"); - } else if (key.size() != 1) { + } else if (subkey_len != 1) { throw std::ios_base::failure("Input explicit asset proof is more than one byte type"); } s >> m_value_proof; @@ -967,7 +967,7 @@ struct PSBTInput { if (!key_lookup.emplace(key).second) { throw std::ios_base::failure("Duplicate Key, issuance needs blinded flag is already provided"); - } else if (key.size() != 1) { + } else if (subkey_len != 1) { throw std::ios_base::failure("Input issuance needs blinded flag is more than one byte type"); } bool b; From 6e6a92209c8cfa50f52341b4e1f35119de778534 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 6 Sep 2022 15:56:57 -0400 Subject: [PATCH 14/18] psbt: Store m_asset_proof for PSBT_IN_ELEMENTS_ASSET_PROOF --- src/psbt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psbt.h b/src/psbt.h index b648418cea..0936787853 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -960,7 +960,7 @@ struct PSBTInput } else if (subkey_len != 1) { throw std::ios_base::failure("Input explicit asset proof is more than one byte type"); } - s >> m_value_proof; + s >> m_asset_proof; break; } case PSBT_ELEMENTS_IN_BLINDED_ISSUANCE: From 2bf627e282edb526ef98bb9a8916e78226e68787 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 6 Sep 2022 15:57:21 -0400 Subject: [PATCH 15/18] psbt: Fix check for missing asset and its proof --- src/psbt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psbt.h b/src/psbt.h index 0936787853..ff00faf297 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -1028,7 +1028,7 @@ struct PSBTInput if ((m_explicit_value.has_value() || !m_value_proof.empty()) && (!m_explicit_value.has_value() || m_value_proof.empty())) { throw std::ios_base::failure("Input explicit value and value proof must be provided together"); } - if ((!m_explicit_asset.IsNull() || !m_asset_proof.empty()) && (!m_explicit_asset.IsNull() || m_asset_proof.empty())) { + if ((!m_explicit_asset.IsNull() || !m_asset_proof.empty()) && (m_explicit_asset.IsNull() || m_asset_proof.empty())) { throw std::ios_base::failure("Input explicit asset and asset proof must be provided together"); } } From 66290e60cfe797c81e5c90eca36414cdcd27c094 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 6 Sep 2022 16:19:05 -0400 Subject: [PATCH 16/18] tests: Tests for pset input explicit value, asset, and proofs --- test/functional/rpc_psbt.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index b164dd9398..8d36b135cb 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -824,6 +824,15 @@ class PSBTTest(BitcoinTestFramework): NO_ASSET_PROOF = "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" BAD_ASSET_PROOF = "cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQECAfsEAgAAAAABAP1UAQIAAAAAASopobdl5W15RSedscp/8bxEXKuKIMOZw+JTqgD8qJEKBAAAAAD9////Awrye7Xu4kI5VnpTDeGaq8sYdXP3qdzYaHrLDRzaC8y51ggl1U8hJxSo+8GcTzHv926wsqTTkOrdBnJo8qcLwLQauQKktt71EJU7HTH5HsgG4kJV/tC32F992/WgieIPRkUkmxYAFPrs/iioimRS5hoJKl/hua83d7rwC1uuuLvfuQh38wHS+0Vg2ecXzypsUabYofOFaGSrICByCKvjgTF6TdHNp2el7Cwi+94dy4qMDrEh/25Aqnc+5qABAqWPEY9ZNCz7m64pANrr04bVgPxaWCr7LvvWGH5FLzvRFgAU96wAzcLFRah7B8gq17sVY9Uso18BIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIBAAAAAAAAKfQAAAAAAAABAXoK8nu17uJCOVZ6Uw3hmqvLGHVz96nc2Gh6yw0c2gvMudYIJdVPIScUqPvBnE8x7/dusLKk05Dq3QZyaPKnC8C0GrkCpLbe9RCVOx0x+R7IBuJCVf7Qt9hffdv1oIniD0ZFJJsWABT67P4oqIpkUuYaCSpf4bmvN3e68CIGA3pgD7iheh1WkyCWvviXQBa9KOJk6JBeYxEpPuxiRBOvEElHIxkAAACAAQAAgAMAAIABDiB25bQww62kp1L1uQVb7MxEVoem8kCzSmM5DW09I9V6DQEPBAAAAAABEAT/////B/wEcHNldA79CwFgAgAHY7+9IRzxAXWemL7C9M7CBAqQoSrXRoxI5/YnMLV6nV/GBMEhmvoDFJcNzRXI/LrIRMLZFvNrP5IupN8OZ+4q+++aJTnuYCZIDR1pssb0JHA0z2UXkEYdHv26qoW26RbLf2LNh29yVIOHG3jqqc7+L7F4UELZmjlEs6R1sulqQ0ePCUUgAsqURkdnNKtl0nORiyLN/9JfqGGTC30WhsdXifWRmqOfkWil0Va1bDYumMU7zJdW/go83ODuZ5VZVWFsBLFSn9HxF1SaFCGt197qo8dr+vhPZwb72k13A72D+5Lx7UKoYqamRJsoAZdUZ/oVd9GRlPbAmRPV7iOxmPYf+t9AQiEd0Z4AIgICuujF5+Lk/uCeX9+RWtJ8ioG51rogGduwt+iY1tZFtjUQSUcjGQAAAIAAAACACwAAgAEDCAC/fEgYCQAAAQQWABSD7wBNJDxW82D5YkC7B5ebqReUWQf8BHBzZXQBIQgGgfEKdAooxyl0pgId27fqPNDTNJzj2ga4NCXs2+6zeAf8BHBzZXQCICMPT11LfG+oRYBu5PZ3E0WeG2no5g/O4uSUDHoNXeGyB/wEcHNldAMhCy5VZJhP9SRRur+Fix1a6ijcBFLLkrt7//fqHxCGN0pEB/wEcHNldAT9CwFgAgAACRhIfL75AJaUOCJ2q+YnbnYTFqluECvtDoJFGcrYvu5VsxPdASJNduFIJRBglnPdW73QRjqt+r3KlxBQ3XUWTce6is6cGED9eySEVJwBXz4Mt8SjqM2GsyUfqC+Ey3+APGgh54MYLt+HHKmt6ibcvE1DDU/UGpVo+I3cY/kgKJzrWMG6y/jDm/CHcF49L8EBtYC7iSrBhwzmDk7DmiViiQFCTUDfIqilX/piqS9ZlO4JNydA5kmLqXkj/xtR2hKt57wknqqvM7/car1S4Do8VljtG9lCzvSOBtBvijSwpFY1KaVFjpj0UZI9XJQ2eEbMrqC0qygNBi1f+ULyZFccNSGpXaZnrZAH/ARwc2V0BUMBAAECnwdoJ4rVnGgLT0He5GaLEhDnGqCKcH0nlTi1T53tBYMI8InonQGT61IAjoLcRxOqzMLgEC3KXg7yW8x6d6VmB/wEcHNldAYhAwxmNPa94Vg9u/nZBWC/8IYTgnp85V5TMOEFWTTAcF2pB/wEcHNldAchAitGVbG/bZNcV2ifjimuh04FOwRlxNrNPva66U6/RiHFB/wEcHNldAgEAAAAAAf8BHBzZXQJSSAAAAkYSHy/AIN6lvAUJ1o6ZQK5i/ewcpqRz4eW8zMzXFO/ZlNvAomxweIBD8YyywTguhBMI0BdLs2VeS5mc5e1oR0R27YAUccH/ARwc2V0CkMBAAGJm91DfvVBUOaEFZ0uH1RcT2cgI9MN9k1lE1hlWc2AtALpMJ17khkivt8F7dgCAVdBvcHFaw138ZsVfiD7g480AAEDCADh9QUAAAAAAQQAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAA==" ONLY_BLIND = "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" + INPUT_ALL_PROOFS = "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" + INPUT_BAD_VALUE = "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" + INPUT_BAD_VALUE_PROOF = "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" + INPUT_BAD_ASSET = "cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEDAfsEAgAAAAABAP19AQIAAAAAAqTuHc11KinNVvXd/9sQr+ipDYZjtigEcm2oUT69BIpzAAAAAAD9////pO4dzXUqKc1W9d3/2xCv6KkNhmO2KARybahRPr0EinMBAAAAAP3///8DC7mQAAC2x+7vHmDpKr609OKkdrg0eF1kBTx/wAEmdQdsCUtYYVDtovHBZr8q8sMVSW5ecl9gGAFx7fv6aiTdslG8A2MtncskgsY320AFJIcDbsam59WJAEAvWZPrYGNEbNjgFgAUl3Rcv6DUKzl5yB+5LJTsqra8YLgKzHQ1jw0pNVL+Aoicxf4EfueweOUt60iMBEeZV2FTLu0IgGd6tSBPsOyg7uvgs1084gyqdkEKToYDJ5cfvfCUNZkDxZhNJF/j/pBHW4I9RVFojsI0Iob7snJfOcPPYk8mmG8WABQsltC8n2vOc1kjzIs6Lhr4Q4ivxAEjD09dS3xvqEWAbuT2dxNFnhtp6OYPzuLklAx6DV3hsgEAAAAAAAABAQAAAAAAAAEBegrMdDWPDSk1Uv4CiJzF/gR+57B45S3rSIwER5lXYVMu7QiAZ3q1IE+w7KDu6+CzXTziDKp2QQpOhgMnlx+98JQ1mQPFmE0kX+P+kEdbgj1FUWiOwjQihvuycl85w89iTyaYbxYAFCyW0Lyfa85zWSPMizouGvhDiK/EIgYCe805REagU2O9f02V+8toEIHGdlsFtB6jnKwK6rkvntYQMS2SEQAAAIAAAACABAAAgAEOIBIOv8GomhHbERYzGvmdBWc7jzdsscdRxJZJn7RnkTqvAQ8EAQAAAAEQBP3///8H/ARwc2V0Dv1OEGAzAAAAAAAAAAEunv4A2qAui2yT75MMmSqcDy7H8X/myuWlEXfa7vrtrPsMLS5bWl3G2Pf3eNgiAQORf5P3/EG7CUSPTNzH/23M3yQ0VEA/MZpi2xIOaPkhvEi5asD91OCMR9hanjC4OrjRzSxJz1YYO7/f9Y8hXgxTQQnPb3N69LB4VeGCbtDMXbohoQonOfwY/aTpAIkbjX3Wnb0xjTtg6ulZ7xZYlRyU00ySPtSSx0TNKsSxXtLq9ddtda0BRfWAKNCLTPer2Ldv44ttJwee7I8acdN+2PqS/7jPHMUHF6PDiOBgu2RppI/UvQQby+Tfn4lwh9RRq1QB+q7J22rRoAWjxnRjSawleA9aczfi5ASI2bhZl3vHFEdjemwgeag6JQ6Bv1Z1UMopzgVL1vr+d+UgBUwHmQxW1zS4rwibcJEr78SfbNOXj+tN4J6xWB3xuGTp3G7k+L2w279uyGUBRbT5dHfwjZ18RH1yv0jZ5YNHN6TUDLBXipesDWsnH4Kh4EyC8Zo+ZnJmCIBu9YOMF7wAMiZMEnhBA6g9I06mhG7Whg4PBtsLKoiB+yr+m0JAazUbp2BMbjDLpEtNutoHTX4WWI9bnqX/f98LBTVXzcyBInNwlnqOyqtlHZX6oBULLVuZLaKj9ZCMY80o63uO2FH5twgWmcISvhWvaxNb1lY/Gt1t8UemdhwnsN7Bx4K0bHyataeYD886boPB5BqHtsb/h0AB0zrwymzpTsPe5ZBdj8wWAgQro3j5h6NAlQfnzCno06NXvkRgYUsd2XQNPTa8/fIDZxo4TFMflSRbBpxxs/h38xNgbBcdxBT7kLxLFxMZ60mhOT64dtrbIhjCDxZMs4TjgRBEM+AnGBsdq3gtzBEStt5bPOJyIpGu1/7k830MoDFC+l2Tev/xXznNhvMv9E1WK8oPzVQd3qwQyWPXGiUHlm8O/CgzRz/uLO6+KKmXFABukQzT500Hi71KU6jr7yCgTur6uO9lD1YZ5FqJY9ankzRhhh3A9rI/RGkYuEaBax4KlrPdsGx9FChMs4usWJ1PmNuA0MXlySFXNFopY+Knm3iSF/MymKSYFwEUjFzpcQ72M5OqU7JXgim+QTgb0v/WuwPw50rgivRXDAF1DynAGF8gy+LOtzftGbMESIB+2AGEgbvKCmKuw1V12Xd7XLUkKwJzLfHVopW4uv0EVslFwtPd5tWgBDLNhrlix3YOmVs0l5qPPxnYc9Ainhm7wptMJQg+bDvgSV2SxvYCiNtF6mINN8f3TEx8xfdKrV+4vhdJcS3YcgfWW0Bh+wwqnxbu0vmOf7LraVtI30K0xd11vpl8DXZGa7mO26gX1TKurDqu3jvR7K5tLENRt7O9QHhBSKaMTGg99rA7X0/tKT4eeTW+wztmGX3FvgfJJZ69VOC4Bl0mXmW6Xd80X6imfnSeqdGK97kZGkNZLU3vqbfP8DvntXtchWJXXHQBOASUUnGljstxFZvYgbm9398QN8KQGdCbmw77PCCbNqZHZ2wcEdr5YoacBZ6qCvSQ3VCT1XWI6yGJCZj4u2/Wz4mqiKu7j5/PmwFAiHrm7j09CjAdlpexLCVt13B58LSf9ZqsGfbT4cftYfL0aa4vgw8yqOG0JccTwDFh+crnbP87YpjGfBYwvdcSnMfzCP3ARZvj1N/q851bRtyS3kWlOZujH5qEYJ1wx/N1eJZm5+rU3CMDzbkehYJqwJp47XTac1lVWHd5pbZVKBgMVxxoN/e65MEznxU+GzN3o7FBrLVwXXt7PnL0cGkyuwVmWsFaBiXl2SsbnHcbtzlratjIDs+fpFuwCpldJUyHFnouqwMXH4CkoF6R/iOJM9HQI5ibPS2PgrxIYLJgPPkPCbglnVJlqvM97XbdQ7IwOEaGmhNQGdBhmsqw2tnHriG61jkruSsDBCap8TfdHGy849FVa8/tIRjEc1dibujtn/dryzT4WDUCNc/vnis+xM68RleRdaA8ESxuJS/7cdQvQIB7t1FeuOiqYobCimURIwaPRZHN5RkgNZpg0YmatqqP4AUretlf4zlkDwBcUN5eOrYqP15H12E5gTu+fd4QcNIfyMbojXsccCssM0hliXP8Fk0fzPEnMlgRTKxYWkFuKvaUm/F1ivkhsk4yfJfC4GROsmMNLOFCclTTqBgA+j+2Nv3WLu36yW+zBbqIMsedhnqdI1TYJF9zz+HvteoLSASZc6zCgnmzE4tbIgiC29Fr5W4SyUGfrqafKNOYwg2CnU7r6qNyiCdvXRjLpoBV2eKHgcVWKxI0QELOCXMYvUVXUxN8RlTdzdP9Y+3fmE/LpXJZOGn0RsrwRWDJBmjUduIH4HhX+x0dXEYYaoMU8Mf67LUtttZ1voDwdK2Jnb7KXeeEO8JkSwdw9z1Deg4vSnkkiZESVWulJLzEebv1hcj88+ZsxN20+wvcxzbK/CFGryeMOhsE795DpOyESk42raHViUPSZIvVxTGq1GEPtr4Z9ChcRj5SXkbltOEobXcTMWPFNTKhFHBve8QHjz5H55gFLqQ5+Eb2gpyLNaAR27dM51IWyX9+4gJpzyK+jyPuO6lD0m8uz3o5MKyh7ypvP5UrW4i2DukLv3Hu+HamoBR0Mw40lSLzZMc2W15oW6MAgc60HYg5bvPfVyaQjmPJelsz3vRPghRkeC1zAUb6/lTRt+NURtTFnB2GeELDsMK+1yvNYN8nHLGGcQRvkQ1e5Y3LaiJnXYlkPtDM1GnEWbVUTz8j8tT8SD8vWjrdGQIfn3qArVuEf7irEelvIYUDSCQfsKwLVgplU9zA4PaVRnddn9uKsFBpjM3p/AM6fN8cervTiNStO5N9G/mfHEoCu+Ofwcpr/RoPzUSDZj+C8E96wb6qZC+PYAvx5lYsVz38idK+hZN1/aRn/dgIZzYkpSM6Po92W39gt5VWbteSLZrYclZ/Zt0HwlIpm/LCFI/dshYc43vAmFYtPVUBgFNqp59YG1HeFx78wOk+F5EO5w+s5vN1q/QikT+wh2B2Eio9By8qxg3b2lg5acCUXWZ8bYq2IH6PaiI1nDmWqfgIctyHcToYijQCdZXJRnbHF+jXfJDEAEn7TBgknqur/OUlwE6U26vvANxPLkDnaoBdmwhOjwYAdE8w1D66ppVnwi9ji6/0RVy9LAnZx54n6WkrnT7YtDvKECtxjSbvQ5FNx+wbho+otZozuaa4LZv3vy9AxnM0PER0I6Bqd4GxRIo1e/HS3LAtN0ju16TPtEMDxW4HxScgdgvqjVQnlUeObHIOsYUY8aG/lf+MfHXgnWdNxF64CcTOlGFEa6/Sr5sOMHwTRJbU2txBXF67KS0wmZeZUlDAQ4VzdFWdNbgy+cFOHY7kJFDIE3kGrZ6tlTHu60JFfpe4XycM9xbaf1vMno3qQhRbSy4NlDwGVT14v8Qk9zq7fpVZjedfeU2RZ54Khr3Rf8/v5uMWZsp6Il87CwbtPfIF7UQiLXZWZQMsYr19Gz0zCt6RoKyGfu1ixEaENbYNm5zVbxRpVs1hrLCXh2vjZvEBneSCF5//gsDIpkUW1pXkLgetKTHtVUhYTw4E7IKfaWoxWdxWQ1s5xtEH8w998I3Siy0X37zImvdtaxLdus6sH2cGJ3xg6WnMTcCh5DeohxawGmjjgsnBnNb7FOh0wc/54Mk7/YwWTEeJCIzTj4ktlIfjWxq3Gmq8dI7zgP6B2DiVuDCwmpuNSyzKiEaQgljZK9BJgE7tpW/U1HJbF4BzrwkiSMnzzd9PZPF2oVZ/Fpl31PurFQL5UyQnwDSb2Lv+mzMTa4jMJUc9rDcCzclBVl4EdatFsr+RLYD/IIPLYMQV9MWajKVI77G1iysPANrmgEZqmk19y1Xv+QbYsgbSGrq9lwhVUhMIciOoNUCc3z6fIcg9QlV1gmJFLBOLINI55rpOzA2bu5qw9Z16JLk/uU4qw/g3bKByehr2OM66YM7pUykm9OAtOamc9JAnOiT3sMVE9KqUnBC/jldSt0Sx1Nb4HYg16EDsJiUPphsQHbhTGQNNBSD0IDf+A1R6vIs5HWUVmpqp+RPPQCOO8OXEeqvFwnYJZ35BWQibSBaR/Fvr5dhQd+w+Q8vQr4rqj+SEI+YPXfgo5Gf1r5IIualuC4e3SevLYOyoCBIiCAltzIqcFkQn6PiNCnzp9vWm2TeTIS3fL3LAwEVOXdmu88dNk15xpl0//jhh46p6HjvWsmi3ERlxXF/YBl+CXsNJuGALFWFDVDrPhFLUVCM+634fEDV0J4ZR3YTkFdUjAiEJcMYpLLoHEXjOyR1WjeQxFCdIT98MtPusDW+u4QTdvLqbg3bG3QkNvjEJCsB6J7hoJlC8BtXaRAQVLothhZhC/PDCUfIWIalOIKYO5EYopKniAbhTBT4ta5bU2eS/m9fSEnyMCQewvN0WOPHmAnyLZt0BNHQkIrYAl2DLPrnI9E899zXABCP2YYIg3z90Tdel3fsYYhaJtnjEsQECo1itI3BaK6pGkRCeylYr3bcmLWT/OBnABoeQkpl99Wg5xuNfAqEj97gPSeuQAwE69jbqj2IwXCOSThot6BViv8NA0ihyiUmQnP9ENYurh70tF+KhesWjd3Fn0LmycD7rf0dfa+rSgPoEnAasLNtlqIeYjM0k7Fhics4I8WKYwgXTU2ISmSgi7VxuiSyuxawmA12UmWimG7QHv3BfpcNZXIPDXW0g/pgWbeE9Wk4z1zFxbsuzWIWC9tKXRsg9eY9I0Twe56XUy5KF+Ooqwz+ZpUneW346ofXoZJEz3fbBTgY1pigqisaeZT6n2yMV/KJb6HqgernmP2WssPqm0OYaZCBu3O0jqA0rv40zu48P8vtge2DMEyBO7oKVGP1/Rdykl/dTZNuIOTqPKsIXCUqJrRpiF7bJnUPl98xU65sfI0/fbZX4KYC1o4k7PpiG7Q95M8eI+lKTLBcsV1dd+KFNoe2EretAJisd8HpexR1JcUmmWNoRmfmXAVEz0Uhxu+Cw6hBB6xIOW3nE7vrRcQ3/V/KyfUFIp5PlSADqxXsA0+JSkuEN2JY0O/0JJekOsPgYsMNaGl38X67wIPuOeNNATbIVklfd4S+31FFwVTt6y0QpHgx/Sdm9hqOTWmKxm3+3sdnnJHQ3lsrJLuy7rYOkOmjMcrHWP/qyjYzhMKuDVC5QEOFV4XDFWDOFdBof0ccH5DbdWgor4oYNv+6Sn5xzaLmcjSDmlxzZIsAQacOuG5SkabpPe0cnrzmLcqdQhKxGqERDLvFWSkVjQLZx6SHQk87EgcYH7srP47pWhWMsq4vntCfMv+hHpbh9/O0bmypCSJodkHiWlsUYnrCXbhtjWPypax+QpKRjcxwRy9eICbitSrpU82twbU/pJYms84Ho37jlh/oLOQCPzB4hWouEq32k2FC0vnACs1F6QFntpINrxlueGEnFpSAva8gsiqZvbWqXO+9S16+UKB4lZ1qlBXZeue+4sm/PM+HwYy27CKmu/Ju85BvIUmTrl8qrT7TCzs7TxQ5UdVwZRhq7E1Z/6FMYh1Y2sGBrzxnrOq/Vayw/LigH/ARwc2V0EQgACK8vAAAAAAf8BHBzZXQSSSAAAAAAL68IAOfA50MdepGFbjUOkF5BpuN9RllcqNE6E+vIA+DZrtXr3UkFaMr99CYyxxUGqzBXO2gnZDGPKRw4Q97c0L8fibUH/ARwc2V0EyAjD09dS3xvqEWAbuT2dxNFnhtp6OYPzuLklAx6DV3hsQf8BHBzZXQUQwEAAdsxNzWMrM4t4sNB0P2+1i1MJuLDswnBqLV5do/fwC+shXVYYNelfQoUqCbwCQZr1wNze4zc9m4wFw5ICDcAos8AIgIDb7DkaJ5uRSbHwQjjdOGADSnot8s8QMpRMk/DkQWJMMQQMS2SEQAAAIABAACABQAAgAEDCAei4REAAAAAAQQWABTxz85fpm8iPOpY0fuqQVyJEdiZwQf8BHBzZXQCICMPT11LfG+oRYBu5PZ3E0WeG2no5g/O4uSUDHoNXeGyB/wEcHNldAYhAgFJhKmTr/gus/JPF6lNs1zX548bWxaCiruPlaJcQkEBB/wEcHNldAgEAAAAAAAiAgJ7zTlERqBTY71/TZX7y2gQgcZ2WwW0HqOcrArquS+e1hAxLZIRAAAAgAAAAIAEAACAAQMIAGXNHQAAAAABBBYAFCyW0Lyfa85zWSPMizouGvhDiK/EB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0BiEDwfXIBuUb9hTv7Y+HYiPhOjNYIPvpn86jit1qob7Yo4YH/ARwc2V0CAQAAAAAAAEDCPkAAAAAAAAAAQQAB/wEcHNldAIgIw9PXUt8b6hFgG7k9ncTRZ4baejmD87i5JQMeg1d4bIH/ARwc2V0CAQAAAAAAA==" + INPUT_BAD_ASSET_PROOF = "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" + INPUT_MISSING_VALUE = "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" + INPUT_MISSING_VALUE_PROOF = "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" + INPUT_MISSING_ASSET = "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" + INPUT_MISSING_ASSET_PROOF = "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" # Check warnings for PSETs stats = [output.get("status") for output in self.nodes[0].decodepsbt(UNBLINDED)["outputs"]] @@ -891,6 +900,16 @@ class PSBTTest(BitcoinTestFramework): {"blind": False, "status": "done" }, ]) + assert "status" not in self.nodes[0].decodepsbt(INPUT_ALL_PROOFS)["inputs"][0] + assert_equal("ERROR: has invalid value proof, the value may be a lie!", self.nodes[0].decodepsbt(INPUT_BAD_VALUE)["inputs"][0]["status"]) + assert_equal("ERROR: has invalid value proof, the value may be a lie!", self.nodes[0].decodepsbt(INPUT_BAD_VALUE_PROOF)["inputs"][0]["status"]) + assert_equal("ERROR: has invalid asset proof, the asset may be a lie!", self.nodes[0].decodepsbt(INPUT_BAD_ASSET)["inputs"][0]["status"]) + assert_equal("ERROR: has invalid asset proof, the asset may be a lie!", self.nodes[0].decodepsbt(INPUT_BAD_ASSET_PROOF)["inputs"][0]["status"]) + assert_raises_rpc_error(-22, "TX decode failed Input explicit value and value proof must be provided together", self.nodes[0].decodepsbt, INPUT_MISSING_VALUE) + assert_raises_rpc_error(-22, "TX decode failed Input explicit value and value proof must be provided together", self.nodes[0].decodepsbt, INPUT_MISSING_VALUE_PROOF) + assert_raises_rpc_error(-22, "TX decode failed Input explicit asset and asset proof must be provided together", self.nodes[0].decodepsbt, INPUT_MISSING_ASSET) + assert_raises_rpc_error(-22, "TX decode failed Input explicit asset and asset proof must be provided together", self.nodes[0].decodepsbt, INPUT_MISSING_ASSET_PROOF) + # The fully-blinded (with proofs) PSET will combine with the blinded one, # and will copy the blinded data assert_equal (self.nodes[0].combinepsbt([UNBLINDED, BLINDED]), BLINDED) From 661c6c70944815204570dd01c8064a14f5877707 Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Wed, 7 Sep 2022 14:43:10 +0200 Subject: [PATCH 17/18] feat: change getnewblockhex to take multiple commitments Modifies the getnewblockhex json rpc call to accept an array of commitments instead of a single commitment. Backwards compatibility is maintained by first attempting to parse as a string for a singular commitment. --- src/miner.cpp | 8 +- src/miner.h | 2 +- src/rpc/client.cpp | 1 + src/rpc/mining.cpp | 33 +++++-- test/functional/rpc_getnewblockhex.py | 135 ++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 6 files changed, 168 insertions(+), 12 deletions(-) create mode 100755 test/functional/rpc_getnewblockhex.py diff --git a/src/miner.cpp b/src/miner.cpp index fba1b22ba9..75a6bbd9be 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -112,7 +112,7 @@ void BlockAssembler::resetBlock() nFees = 0; } -std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, std::chrono::seconds min_tx_age, DynaFedParamEntry* proposed_entry, CScript const* commit_script) +std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, std::chrono::seconds min_tx_age, DynaFedParamEntry* proposed_entry, const std::vector* commit_scripts) { assert(min_tx_age >= std::chrono::seconds(0)); int64_t nTimeStart = GetTimeMicros(); @@ -206,8 +206,10 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; // Non-consensus commitment output before finishing coinbase transaction - if (commit_script) { - coinbaseTx.vout.insert(coinbaseTx.vout.begin(), CTxOut(policyAsset, 0, *commit_script)); + if (commit_scripts && !commit_scripts->empty()) { + for (auto commit_script: *commit_scripts) { + coinbaseTx.vout.insert(std::prev(coinbaseTx.vout.end()), CTxOut(policyAsset, 0, commit_script)); + } } pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); diff --git a/src/miner.h b/src/miner.h index b7187a15eb..03182683d4 100644 --- a/src/miner.h +++ b/src/miner.h @@ -159,7 +159,7 @@ public: explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, std::chrono::seconds min_tx_age = std::chrono::seconds(0), DynaFedParamEntry* = nullptr, CScript const* commit_script = nullptr); + std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, std::chrono::seconds min_tx_age = std::chrono::seconds(0), DynaFedParamEntry* = nullptr, const std::vector* commit_scripts = nullptr); inline static std::optional m_last_block_num_txs{}; inline static std::optional m_last_block_weight{}; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index deb06b0ff6..9a6b0cd8cc 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -207,6 +207,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "rawreissueasset", 1, "reissuances" }, { "getnewblockhex", 0, "min_tx_age" }, { "getnewblockhex", 1, "proposed_parameters" }, + { "getnewblockhex", 2, "commit_data" }, { "testproposedblock", 1, "acceptnonstd" }, { "issueasset", 0, "assetamount" }, { "issueasset", 1, "tokenamount" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 5d2e901289..981a85ad25 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1277,7 +1277,7 @@ static RPCHelpMan getnewblockhex() "\nGets hex representation of a proposed, unmined new block\n", { {"min_tx_age", RPCArg::Type::NUM, RPCArg::Default{0}, "How many seconds a transaction must have been in the mempool to be included in the block proposal. This may help with faster block convergence among functionaries using compact blocks."}, - {"proposed_parameters", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED , "Parameters to be used in dynamic federations blocks as proposals. During a period of `-dynamic_epoch_length` blocks, 4/5 of total blocks must signal these parameters for the proposal to become activated in the next epoch.", + {"proposed_parameters", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "Parameters to be used in dynamic federations blocks as proposals. During a period of `-dynamic_epoch_length` blocks, 4/5 of total blocks must signal these parameters for the proposal to become activated in the next epoch.", { {"signblockscript", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Hex-encoded block signing script to propose"}, {"max_block_witness", RPCArg::Type::NUM, RPCArg::Optional::NO, "Total size in witness bytes that are allowed in the dynamic federations block witness for blocksigning"}, @@ -1289,7 +1289,11 @@ static RPCHelpMan getnewblockhex() }, }, "proposed_parameters"}, - {"commit_data", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "Data in hex to be committed to in an additional coinbase output."}, + {"commit_data", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of data in hex to be committed to in additional coinbase outputs.", + { + {"", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Hex encoded string for commit data"}, + }, + }, }, RPCResult{ RPCResult::Type::STR_HEX, "blockhex", "the block hex", @@ -1346,18 +1350,31 @@ static RPCHelpMan getnewblockhex() proposed.m_serialize_type = 2; } - // Any commitment required for non-consensus reasons. - // This will be placed in the first coinbase output. - CScript data_commitment; + // Any commitments required for non-consensus reasons. + // These will be placed in the first coinbase outputs. + std::vector data_commitments; if (!request.params[2].isNull()) { - std::vector data_bytes = ParseHex(request.params[2].get_str()); - data_commitment = CScript() << OP_RETURN << data_bytes; + UniValue commitments(UniValue::VARR); + + // backwards compatibility: attempt to parse as a string first + if (request.params[2].isStr()) { + UniValue hex = request.params[2].get_str(); + commitments.push_back(hex); + } else { + commitments = request.params[2].get_array(); + } + + for (unsigned int i = 0; i < commitments.size(); i++) { + std::vector data_bytes = ParseHex(commitments[i].get_str()); + CScript data_commitment = CScript() << OP_RETURN << data_bytes; + data_commitments.push_back(data_commitment); + } } CScript feeDestinationScript = Params().GetConsensus().mandatory_coinbase_destination; if (feeDestinationScript == CScript()) feeDestinationScript = CScript() << OP_TRUE; const NodeContext& node = EnsureAnyNodeContext(request.context); - std::unique_ptr pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), *node.mempool, Params()).CreateNewBlock(feeDestinationScript, std::chrono::seconds(required_wait), &proposed, data_commitment.empty() ? nullptr : &data_commitment)); + std::unique_ptr pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), *node.mempool, Params()).CreateNewBlock(feeDestinationScript, std::chrono::seconds(required_wait), &proposed, data_commitments.empty() ? nullptr : &data_commitments)); if (!pblocktemplate.get()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty"); } diff --git a/test/functional/rpc_getnewblockhex.py b/test/functional/rpc_getnewblockhex.py new file mode 100755 index 0000000000..4a0670e093 --- /dev/null +++ b/test/functional/rpc_getnewblockhex.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Test getnewblockhex +""" +from io import BytesIO + +from test_framework.messages import CBlock +from test_framework.p2p import ( + P2PDataStore, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + hex_str_to_bytes, +) + +class GetNewBlockHexTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def run_test(self): + self.log.info("Starting test...") + self.bootstrap_p2p() + + node = self.nodes[0] + + height = node.getblockcount() + assert_equal(height, 200) + + self.log.info("Call getnewblockhex with no args") + hex = node.getnewblockhex() + (height, hash) = self.mine_block(hex) + assert_equal(height, 201) + + self.log.info("Call getnewblockhex with single string commitment") + hex = node.getnewblockhex(0, None, "1234") + assert "6a021234" in hex + (height, hash) = self.mine_block(hex) + assert_equal(height, 202) + block = node.getblock(hash, 2) + vout = block["tx"][0]["vout"] + assert_equal(vout[0]["scriptPubKey"]["hex"], "6a021234") + + self.log.info("Call getnewblockhex with single string commitment with spaces") + # ParseHex only validates hex chars, so spaces skipped + hex = node.getnewblockhex(0, None, "1234 5678") + assert "6a0412345678" in hex + (height, hash) = self.mine_block(hex) + assert_equal(height, 203) + block = node.getblock(hash, 2) + vout = block["tx"][0]["vout"] + assert_equal(vout[0]["scriptPubKey"]["hex"], "6a0412345678") + + self.log.info("Call getnewblockhex with single commitment") + hex = node.getnewblockhex(0, None, ["1234"]) + assert "6a021234" in hex + (height, hash) = self.mine_block(hex) + assert_equal(height, 204) + block = node.getblock(hash, 2) + vout = block["tx"][0]["vout"] + assert_equal(vout[0]["scriptPubKey"]["hex"], "6a021234") + + self.log.info("Call getnewblockhex with multiple commitments") + hex = node.getnewblockhex(0, None, ["1234", "deadbeef"]) + assert "6a021234" in hex + assert "6a04deadbeef" in hex + (height, hash) = self.mine_block(hex) + assert_equal(height, 205) + block = node.getblock(hash, 2) + vout = block["tx"][0]["vout"] + assert_equal(vout[0]["scriptPubKey"]["hex"], "6a021234") + assert_equal(vout[1]["scriptPubKey"]["hex"], "6a04deadbeef") + + hex = node.getnewblockhex(0, None, ["1234", "dead", "cafe", "babe"]) + assert "6a021234" in hex + assert "6a02dead" in hex + assert "6a02cafe" in hex + assert "6a02babe" in hex + (height, hash) = self.mine_block(hex) + assert_equal(height, 206) + block = node.getblock(hash, 2) + vout = block["tx"][0]["vout"] + assert_equal(vout[0]["scriptPubKey"]["hex"], "6a021234") + assert_equal(vout[1]["scriptPubKey"]["hex"], "6a02dead") + assert_equal(vout[2]["scriptPubKey"]["hex"], "6a02cafe") + assert_equal(vout[3]["scriptPubKey"]["hex"], "6a02babe") + + self.log.info("Done.") + + def mine_block(self, hex): + """Mine and submit a block from a given hex template. + + Returns a tuple of the new chain height and the block hash.""" + + bytes = hex_str_to_bytes(hex) + block = CBlock() + block.deserialize(BytesIO(bytes)) + block.solve() + self.send_blocks([block], success=True) + height = self.nodes[0].getblockcount() + return (height, block.hash) + + def bootstrap_p2p(self, timeout=10): + """Add a P2P connection to the node. + + Helper to connect and wait for version handshake.""" + self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore()) + # We need to wait for the initial getheaders from the peer before we + # start populating our blockstore. If we don't, then we may run ahead + # to the next subtest before we receive the getheaders. We'd then send + # an INV for the next block and receive two getheaders - one for the + # IBD and one for the INV. We'd respond to both and could get + # unexpectedly disconnected if the DoS score for that error is 50. + self.helper_peer.wait_for_getheaders(timeout=timeout) + + def reconnect_p2p(self, timeout=60): + """Tear down and bootstrap the P2P connection to the node. + + The node gets disconnected several times in this test. This helper + method reconnects the p2p and restarts the network thread.""" + self.nodes[0].disconnect_p2ps() + self.bootstrap_p2p(timeout=timeout) + + def send_blocks(self, blocks, success=True, reject_reason=None, force_send=False, reconnect=False, timeout=960): + """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. + + Call with success = False if the tip shouldn't advance to the most recent block.""" + self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect) + + if reconnect: + self.reconnect_p2p(timeout=timeout) + + +if __name__ == '__main__': + GetNewBlockHexTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 9e4eaa58c3..ab0268f922 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -108,6 +108,7 @@ BASE_SCRIPTS = [ 'feature_assetsdir.py', 'feature_initial_reissuance_token.py', 'feature_progress.py', + 'rpc_getnewblockhex.py', # Longest test should go first, to favor running tests in parallel 'wallet_hd.py --legacy-wallet', 'wallet_hd.py --descriptors', From 5d10ff9f52d5bf372325390c1cda880b3dc7b9dc Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Wed, 7 Sep 2022 14:44:07 +0200 Subject: [PATCH 18/18] lint: fix typo in tx-format doc --- doc/elements-tx-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/elements-tx-format.md b/doc/elements-tx-format.md index 945ac48bbf..c97c7a9cb2 100644 --- a/doc/elements-tx-format.md +++ b/doc/elements-tx-format.md @@ -104,7 +104,7 @@ SegWit transactions have one such witness for each input. | Script Witness | Yes | Varies | `Vector` | | The vector represents the witness stack.
Can be empty (length of 0). | | Peg-in Witness | Yes | Varies | `Vector` | | The vector represents the witness stack.
Can be empty (length of 0). | -The range proofs must be empty if their asociated amounts (issuance / inflation keys) are explicit. +The range proofs must be empty if their associated amounts (issuance / inflation keys) are explicit. Refer [here](https://elementsproject.org/features/confidential-transactions/investigation) for more details on range proofs. A non-empty peg-in witness stack should always have a length of 6, and the items should be interpreted as follows: