This PR is a follow-up to #36194 and #36207. It completes the existing set of transaction accessors allowing clients to read the transaction version without slicing serialized bytes.
Tests cover a range of versions.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--006a51241073e994b41acfe9ec718e94-->
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36283.
<!--021abf342d371248e50ceaed478a90ca-->
See the guideline and AI policy for information on the review process.
| Type | Reviewers |
|---|---|
| ACK | yuvicc, stickies-v, sedited |
If your review is incorrectly listed, please copy-paste <code><!--meta-tag:bot-skip--></code> into the comment that the bot should ignore.
<!--5faf32d7da4f0f540f40219e4f7537a3-->
Possible places where comparison-specific test macros should replace generic comparisons:
BOOST_CHECK_THROW(Transaction{broken_tx_data}, std::runtime_error); -> consider BOOST_CHECK_EXCEPTION(..., std::runtime_error, ...) with a predicate that checks the expected failure message, instead of only asserting the exception type.<sup>2026-09-18 17:51:00</sup>
cc: @lucasdbr05 @KY-U
475 | @@ -475,6 +476,15 @@ BOOST_AUTO_TEST_CASE(btck_transaction_tests) 476 | check_equal(script_pubkey_roundtrip.ToBytes(), script_pubkey.ToBytes()); 477 | } 478 | 479 | +BOOST_AUTO_TEST_CASE(btck_transaction_version_tests) 480 | +{ 481 | + auto tx_data{hex_string_to_byte_vec("02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700")}; 482 | + for (const uint32_t version : {0u, 1u, 2u, 3u, 0xffffffffu}) { 483 | + WriteLE32(tx_data.data(), version);
We try to keep dependencies for test_kernel on internal headers minimal, so in that light including crypto/common.h just to write a byte seems excessive. Perhaps concatenating strings or something like tx_data[i] = std::byte(version >> (8 * i)) could be an alternative?
Also, transaction version is a scalar member like locktime etc. I think this can just be folded into btck_transaction_tests.
Code LGTM 4397e8647a27d40fbc26d29b17bbedded5692009 but test should be improved a bit
Concept ACK
GetVersion() is provided by TransactionApi to both Transaction and TransactionView, but the current test only exercises the owning type. Would it be usefull to also check it on the existing transaction view, so both wrapper instantiations are covered?
<details> <summary>diff testing both wrappers, moving the test to btck_transaction_tests and without crypto/common.h as suggested by [@stickies-v](/bitcoin-bitcoin/contributor/stickies-v/)</summary>
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 08c7c1e5f3..1df9b9fbd1 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -2,7 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <crypto/common.h>
#include <kernel/bitcoinkernel.h>
#include <kernel/bitcoinkernel_wrapper.h>
#include <util/byte_units.h>
@@ -410,6 +409,19 @@ BOOST_AUTO_TEST_CASE(btck_transaction_tests)
BOOST_CHECK_EQUAL(tx.CountOutputs(), 2);
BOOST_CHECK_EQUAL(tx.CountInputs(), 1);
BOOST_CHECK_EQUAL(tx.GetLocktime(), 510826);
+
+ auto versioned_tx_data{tx_data};
+ for (const uint32_t version : {0u, 1u, 2u, 3u, 0xffffffffu}) {
+ for (size_t i = 0; i < 4; ++i) {
+ versioned_tx_data[i] = std::byte((version >> (8 * i)) & 0xff);
+ }
+
+ Transaction versioned_tx{versioned_tx_data};
+ TransactionView versioned_tx_view{versioned_tx.get()};
+
+ BOOST_CHECK_EQUAL(versioned_tx.GetVersion(), version);
+ BOOST_CHECK_EQUAL(versioned_tx_view.GetVersion(), version);
+ }
auto broken_tx_data{std::span<std::byte>{tx_data.begin(), tx_data.begin() + 10}};
BOOST_CHECK_THROW(Transaction{broken_tx_data}, std::runtime_error);
auto input{tx.GetInput(0)};
@@ -476,14 +488,6 @@ BOOST_AUTO_TEST_CASE(btck_transaction_tests)
check_equal(script_pubkey_roundtrip.ToBytes(), script_pubkey.ToBytes());
}
-BOOST_AUTO_TEST_CASE(btck_transaction_version_tests)
-{
- auto tx_data{hex_string_to_byte_vec("02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700")};
- for (const uint32_t version : {0u, 1u, 2u, 3u, 0xffffffffu}) {
- WriteLE32(tx_data.data(), version);
- BOOST_CHECK_EQUAL(Transaction{tx_data}.GetVersion(), version);
- }
-}
BOOST_AUTO_TEST_CASE(btck_transaction_id_tests)
{
</details>
I've added an extra & 0xff on byte conversion to make it explicit that only the least significant byte of the shifted value is used, though this is purely stylistic.
Thanks for the reviews. I've updated the code to address feedback on the testing logic.
420 | + TransactionView versioned_tx_view{versioned_tx.get()}; 421 | + 422 | + BOOST_CHECK_EQUAL(versioned_tx.GetVersion(), version); 423 | + BOOST_CHECK_EQUAL(versioned_tx_view.GetVersion(), version); 424 | + } 425 | +
nit: I'm not sure testing 0, 1, 2, and 3 adds much value. 0, 2, and max seems plenty. We also generally don't test parity between Handle and View for every accessor, so far. I'm not sure it adds much value?
Suggested simplification:
<details> <summary>git diff on a40947d1c5</summary>
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index e20203b810..1dd816fe28 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -15,6 +15,7 @@
#include <test/kernel/block_data.h>
#include <test/util/common.h>
+#include <algorithm>
#include <charconv>
#include <concepts>
#include <cstdint>
@@ -410,17 +411,11 @@ BOOST_AUTO_TEST_CASE(btck_transaction_tests)
BOOST_CHECK_EQUAL(tx.CountInputs(), 1);
BOOST_CHECK_EQUAL(tx.GetLocktime(), 510826);
- auto versioned_tx_data{tx_data};
- for (const uint32_t version : {0u, 1u, 2u, 3u, 0xffffffffu}) {
- for (size_t i{0}; i < 4; ++i) {
- versioned_tx_data[i] = std::byte(version >> (8 * i));
- }
-
- Transaction versioned_tx{versioned_tx_data};
- TransactionView versioned_tx_view{versioned_tx.get()};
-
- BOOST_CHECK_EQUAL(versioned_tx.GetVersion(), version);
- BOOST_CHECK_EQUAL(versioned_tx_view.GetVersion(), version);
+ BOOST_CHECK_EQUAL(tx.GetVersion(), 2);
+ for (const auto& [version_hex, version] : {std::pair{"00000000", 0u}, {"ffffffff", 0xffffffffu}}) {
+ auto versioned_tx_data{tx_data};
+ std::ranges::copy(hex_string_to_byte_vec(version_hex), versioned_tx_data.begin());
+ BOOST_CHECK_EQUAL(Transaction{versioned_tx_data}.GetVersion(), version);
}
auto broken_tx_data{std::span<std::byte>{tx_data.begin(), tx_data.begin() + 10}};
</details>
ACK a40947d1c50193d3e09d17b39c79e78cec46f252
Expose CTransaction::version, allowing clients to read the transaction
version without slicing serialized bytes.
Test the accessor against a range of transaction versions.
Updates:
lgtm! ACK 83434f7683e45afc1ebbb7690a6ae288b2602b03
ACK 83434f7683e45afc1ebbb7690a6ae288b2602b03
ACK 83434f7683e45afc1ebbb7690a6ae288b2602b03