0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-14 11:26:09 -05:00
bitcoin-bitcoin-core/src/test/util
Ryan Ofsky 1a7d20509f
Merge bitcoin/bitcoin#30526: doc: Correct uint256 hex string endianness
73e3fa10b4 doc + test: Correct uint256 hex string endianness (Hodlinator)

Pull request description:

  This PR is a follow-up to #30436.

  Only changes test-code and modifies/adds comments.

  Byte order of hex string representation was wrongfully documented as little-endian, but are in fact closer to "big-endian" (endianness is a memory-order concept rather than a numeric concept). `[arith_]uint256` both store their data in arrays with little-endian byte order (`arith_uint256` has host byte order within each `uint32_t` element).

  **uint256_tests.cpp** - Avoid using variable from the left side of the condition in the right side. Credits to @maflcko: https://github.com/bitcoin/bitcoin/pull/30436#discussion_r1688273553

  **setup_common.cpp** - Skip needless ArithToUint256-conversion. Credits to @stickies-v: https://github.com/bitcoin/bitcoin/pull/30436#discussion_r1688621638

  ---

  <details>
  <summary>

  ## Logical reasoning for endianness

  </summary>

  1. Comparing an `arith_uint256` (`base_uint<256>`) to a `uint64_t` compares the beginning of the array, and verifies the remaining elements are zero.
  ```C++
  template <unsigned int BITS>
  bool base_uint<BITS>::EqualTo(uint64_t b) const
  {
      for (int i = WIDTH - 1; i >= 2; i--) {
          if (pn[i])
              return false;
      }
      if (pn[1] != (b >> 32))
          return false;
      if (pn[0] != (b & 0xfffffffful))
          return false;
      return true;
  }
  ```
  ...that is consistent with little endian ordering of the array.

  2. They have the same endianness (but `arith_*` has host-ordering of each `uint32_t` element):
  ```C++
  arith_uint256 UintToArith256(const uint256 &a)
  {
      arith_uint256 b;
      for(int x=0; x<b.WIDTH; ++x)
          b.pn[x] = ReadLE32(a.begin() + x*4);
      return b;
  }
  ```

  ### String conversions

  The reversal of order which happens when converting hex-strings <=> uint256 means strings are actually closer to big-endian, see the end of `base_blob<BITS>::SetHexDeprecated`:
  ```C++
      unsigned char* p1 = m_data.data();
      unsigned char* pend = p1 + WIDTH;
      while (digits > 0 && p1 < pend) {
          *p1 = ::HexDigit(trimmed[--digits]);
          if (digits > 0) {
              *p1 |= ((unsigned char)::HexDigit(trimmed[--digits]) << 4);
              p1++;
          }
      }
  ```
  Same reversal here:
  ```C++
  template <unsigned int BITS>
  std::string base_blob<BITS>::GetHex() const
  {
      uint8_t m_data_rev[WIDTH];
      for (int i = 0; i < WIDTH; ++i) {
          m_data_rev[i] = m_data[WIDTH - 1 - i];
      }
      return HexStr(m_data_rev);
  }
  ```
  It now makes sense to me that `SetHexDeprecated`, upon receiving a shorter hex string that requires zero-padding, would pad as if the missing hex chars where towards the end of the little-endian byte array, as they are the most significant bytes. "Big-endian" string representation is also consistent with the case where `SetHexDeprecated` receives too many hex digits and discards the leftmost ones, as a form of integer narrowing takes place.

  ### How I got it wrong in #30436

  Previously I used the less than (`<`) comparison to prove endianness, but for `uint256` it uses `memcmp` and thereby gives priority to the *lower* bytes at the beginning of the array.
  ```C++
      constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); }
  ```

  `arith_uint256` is different in that it begins by comparing the bytes from the end, as it is using little endian representation, where the bytes toward the end are more significant.
  ```C++
  template <unsigned int BITS>
  int base_uint<BITS>::CompareTo(const base_uint<BITS>& b) const
  {
      for (int i = WIDTH - 1; i >= 0; i--) {
          if (pn[i] < b.pn[i])
              return -1;
          if (pn[i] > b.pn[i])
              return 1;
      }
      return 0;
  }
  ```
  (The commit documents that `base_blob::Compare()` is doing lexicographic ordering unlike the `arith_*`-variant which is doing numeric ordering).

  </details>

ACKs for top commit:
  paplorinc:
    ACK 73e3fa10b4
  ryanofsky:
    Code review ACK 73e3fa10b4

Tree-SHA512: 121630c37ab01aa7f7097f10322ab37da3cbc0696a6bbdbf2bbd6db180dc5938c7ed91003aaa2df7cf4a4106f973f5118ba541b5e077cf3588aa641bbd528f4e
2024-08-04 22:27:10 -04:00
..
blockfilter.cpp Remove unused includes from blockfilter.h 2023-08-17 18:28:15 +02:00
blockfilter.h refactor: Move functions to BlockManager methods 2023-05-10 19:06:53 +02:00
chainstate.h refactor: Move early loadtxoutset checks into ActiveSnapshot 2024-06-19 22:32:33 +02:00
cluster_linearize.h clusterlin: add Linearize function 2024-07-25 10:16:37 -04:00
coins.cpp Use Txid in COutpoint 2023-11-21 13:15:44 +00:00
coins.h De-duplicate add_coin methods to a test util helper 2023-02-09 15:03:36 -08:00
index.cpp refactor: Remove call to ShutdownRequested from IndexWaitSynced 2023-12-04 15:39:15 -04:00
index.h refactor: Remove call to ShutdownRequested from IndexWaitSynced 2023-12-04 15:39:15 -04:00
json.cpp test, build: Separate read_json function into its own module 2023-01-27 09:26:29 +00:00
json.h test, build: Separate read_json function into its own module 2023-01-27 09:26:29 +00:00
logging.cpp scripted-diff: Bump copyright headers 2021-12-30 19:36:57 +02:00
logging.h scripted-diff: Bump copyright headers 2022-12-24 23:49:50 +00:00
mining.cpp refactor: De-globalize g_signals 2024-02-15 14:37:01 +01:00
mining.h test: Add util to mine invalid blocks 2023-05-02 17:17:06 +02:00
net.cpp Merge bitcoin/bitcoin#30394: net: fix race condition in self-connect detection 2024-07-16 09:40:53 +01:00
net.h [fuzz] Harness for version handshake 2024-07-31 13:25:52 +01:00
poolresourcetester.h Add pool based memory resource & allocator 2023-03-23 19:38:38 +01:00
random.cpp random: replace construct/assign with explicit Reseed() 2024-07-01 12:39:57 -04:00
random.h tests: overhaul deterministic test randomness 2024-07-01 10:26:46 -04:00
README.md
script.cpp fuzz: [refactor] Use IsValidFlagCombination in signature_checker fuzz target 2021-03-30 10:42:45 +02:00
script.h fuzz: allow reaching MempoolAcceptResult::ResultType::DIFFERENT_WITNESS in tx_package_eval target 2023-11-09 09:07:03 -05:00
setup_common.cpp doc + test: Correct uint256 hex string endianness 2024-08-03 21:59:54 +02:00
setup_common.h Merge bitcoin/bitcoin#30399: test: Add arguments for creating a slimmer TestingSetup 2024-07-25 13:53:50 +01:00
str.cpp
str.h [test] move string helper functions into test library 2019-11-25 01:33:17 +01:00
transaction_utils.cpp refactor: Rename CTransaction::nVersion to version 2024-06-07 13:55:23 -04:00
transaction_utils.h scripted-diff: TxoutType C++11 scoped enum class 2020-06-21 06:41:55 -04:00
txmempool.cpp scripted-diff: change names from V3 to TRUC 2024-07-02 12:06:07 +01:00
txmempool.h scripted-diff: change names from V3 to TRUC 2024-07-02 12:06:07 +01:00
validation.cpp validation: pass ChainstateRole for validationinterface calls 2023-09-30 06:38:47 -04:00
validation.h validation: pass ChainstateRole for validationinterface calls 2023-09-30 06:38:47 -04:00

Test library

This contains files for the test library, which is used by the test binaries (unit tests, benchmarks, fuzzers, gui tests).

Generally, the files in this folder should be well-separated modules. New code should be added to existing modules or (when in doubt) a new module should be created.

The utilities in here are compiled into a library, which does not hold any state. However, the main file setup_common defines the common test setup for all test binaries. The test binaries will handle the global state when they instantiate the BasicTestingSetup (or one of its derived classes).