diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b4b8bae1e..1da02a86b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,9 @@ name: CI on: - # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request. + # See: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request. pull_request: - # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push. + # See: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push. push: branches: - '**' @@ -26,7 +26,7 @@ jobs: name: 'test each commit' runs-on: ubuntu-24.04 if: github.event_name == 'pull_request' && github.event.pull_request.commits != 1 - timeout-minutes: 360 # Use maximum time, see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes. Assuming a worst case time of 1 hour per commit, this leads to a --max-count=6 below. + timeout-minutes: 360 # Use maximum time, see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes. Assuming a worst case time of 1 hour per commit, this leads to a --max-count=6 below. env: MAX_COUNT: 6 steps: @@ -270,16 +270,16 @@ jobs: timeout-minutes: 120 env: FILE_ENV: "./ci/test/00_setup_env_native_asan.sh" - DANGER_CI_ON_HOST_CACHE_FOLDERS: 1 + DANGER_CI_ON_HOST_FOLDERS: 1 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set Ccache directory - run: echo "CCACHE_DIR=${RUNNER_TEMP}/ccache_dir" >> "$GITHUB_ENV" - - - name: Set base root directory - run: echo "BASE_ROOT_DIR=${RUNNER_TEMP}" >> "$GITHUB_ENV" + - name: Set CI directories + run: | + echo "CCACHE_DIR=${{ runner.temp }}/ccache_dir" >> "$GITHUB_ENV" + echo "BASE_ROOT_DIR=${{ runner.temp }}" >> "$GITHUB_ENV" + echo "BASE_BUILD_DIR=${{ runner.temp }}/build-asan" >> "$GITHUB_ENV" - name: Restore Ccache cache id: ccache-cache diff --git a/CMakeLists.txt b/CMakeLists.txt index c86ecf12fe..a226b47f85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,6 +397,7 @@ target_link_libraries(core_interface INTERFACE warn_interface) if(MSVC) try_append_cxx_flags("/W3" TARGET warn_interface SKIP_LINK) try_append_cxx_flags("/wd4018" TARGET warn_interface SKIP_LINK) + try_append_cxx_flags("/wd4146" TARGET warn_interface SKIP_LINK) try_append_cxx_flags("/wd4244" TARGET warn_interface SKIP_LINK) try_append_cxx_flags("/wd4267" TARGET warn_interface SKIP_LINK) try_append_cxx_flags("/wd4715" TARGET warn_interface SKIP_LINK) diff --git a/ci/test/00_setup_env_native_centos.sh b/ci/test/00_setup_env_native_centos.sh index ff4b9bc570..c423d788eb 100755 --- a/ci/test/00_setup_env_native_centos.sh +++ b/ci/test/00_setup_env_native_centos.sh @@ -10,5 +10,6 @@ export CONTAINER_NAME=ci_native_centos export CI_IMAGE_NAME_TAG="quay.io/centos/centos:stream10" export CI_BASE_PACKAGES="gcc-c++ glibc-devel libstdc++-devel ccache make git python3 python3-pip which patch xz procps-ng ksh rsync coreutils bison e2fsprogs cmake" export PIP_PACKAGES="pyzmq" +export DEP_OPTS="DEBUG=1" # Temporarily enable a DEBUG=1 build to check for GCC-bug-117966 regressions. This can be removed once the minimum GCC version is bumped to 12 in the previous releases task, see https://github.com/bitcoin/bitcoin/issues/31436#issuecomment-2530717875 export GOAL="install" -export BITCOIN_CONFIG="-DWITH_ZMQ=ON -DBUILD_GUI=ON -DREDUCE_EXPORTS=ON" +export BITCOIN_CONFIG="-DWITH_ZMQ=ON -DBUILD_GUI=ON -DREDUCE_EXPORTS=ON -DCMAKE_BUILD_TYPE=Debug" diff --git a/ci/test/02_run_container.sh b/ci/test/02_run_container.sh index 28d0898a4a..20910e57b7 100755 --- a/ci/test/02_run_container.sh +++ b/ci/test/02_run_container.sh @@ -25,6 +25,29 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then fi echo "Creating $CI_IMAGE_NAME_TAG container to run in" + DOCKER_BUILD_CACHE_ARG="" + DOCKER_BUILD_CACHE_TEMPDIR="" + DOCKER_BUILD_CACHE_OLD_DIR="" + DOCKER_BUILD_CACHE_NEW_DIR="" + # If set, use an `docker build` cache directory on the CI host + # to cache docker image layers for the CI container image. + # This cache can be multiple GB in size. Prefixed with DANGER + # as setting it removes (old cache) files from the host. + if [ "$DANGER_DOCKER_BUILD_CACHE_HOST_DIR" ]; then + # Directory where the current cache for this run could be. If not existing + # or empty, "docker build" will warn, but treat it as cache-miss and continue. + DOCKER_BUILD_CACHE_OLD_DIR="${DANGER_DOCKER_BUILD_CACHE_HOST_DIR}/${CONTAINER_NAME}" + # Temporary directory for a newly created cache. We can't write the new + # cache into OLD_DIR directly, as old cache layers would not be removed. + # The NEW_DIR contents are moved to OLD_DIR after OLD_DIR has been cleared. + # This happens after `docker build`. If a task fails or is aborted, the + # DOCKER_BUILD_CACHE_TEMPDIR might be retained on the host. If the host isn't + # ephemeral, it has to take care of cleaning old TEMPDIR's up. + DOCKER_BUILD_CACHE_TEMPDIR="$(mktemp --directory ci-docker-build-cache-XXXXXXXXXX)" + DOCKER_BUILD_CACHE_NEW_DIR="${DOCKER_BUILD_CACHE_TEMPDIR}/${CONTAINER_NAME}" + DOCKER_BUILD_CACHE_ARG="--cache-from type=local,src=${DOCKER_BUILD_CACHE_OLD_DIR} --cache-to type=local,dest=${DOCKER_BUILD_CACHE_NEW_DIR},mode=max" + fi + # shellcheck disable=SC2086 DOCKER_BUILDKIT=1 docker build \ --file "${BASE_READ_ONLY_DIR}/ci/test_imagefile" \ @@ -34,8 +57,18 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then --platform="${CI_IMAGE_PLATFORM}" \ --label="${CI_IMAGE_LABEL}" \ --tag="${CONTAINER_NAME}" \ + $DOCKER_BUILD_CACHE_ARG \ "${BASE_READ_ONLY_DIR}" + if [ "$DANGER_DOCKER_BUILD_CACHE_HOST_DIR" ]; then + if [ -e "${DOCKER_BUILD_CACHE_NEW_DIR}/index.json" ]; then + echo "Removing the existing docker build cache in ${DOCKER_BUILD_CACHE_OLD_DIR}" + rm -rf "${DOCKER_BUILD_CACHE_OLD_DIR}" + echo "Moving the contents of ${DOCKER_BUILD_CACHE_NEW_DIR} to ${DOCKER_BUILD_CACHE_OLD_DIR}" + mv "${DOCKER_BUILD_CACHE_NEW_DIR}" "${DOCKER_BUILD_CACHE_OLD_DIR}" + fi + fi + docker volume create "${CONTAINER_NAME}_ccache" || true docker volume create "${CONTAINER_NAME}_depends" || true docker volume create "${CONTAINER_NAME}_depends_sources" || true @@ -45,18 +78,21 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then CI_DEPENDS_MOUNT="type=volume,src=${CONTAINER_NAME}_depends,dst=$DEPENDS_DIR/built" CI_DEPENDS_SOURCES_MOUNT="type=volume,src=${CONTAINER_NAME}_depends_sources,dst=$DEPENDS_DIR/sources" CI_PREVIOUS_RELEASES_MOUNT="type=volume,src=${CONTAINER_NAME}_previous_releases,dst=$PREVIOUS_RELEASES_DIR" + CI_BUILD_MOUNT="" - if [ "$DANGER_CI_ON_HOST_CACHE_FOLDERS" ]; then + if [ "$DANGER_CI_ON_HOST_FOLDERS" ]; then # ensure the directories exist mkdir -p "${CCACHE_DIR}" mkdir -p "${DEPENDS_DIR}/built" mkdir -p "${DEPENDS_DIR}/sources" mkdir -p "${PREVIOUS_RELEASES_DIR}" + mkdir -p "${BASE_BUILD_DIR}" # Unset by default, must be defined externally CI_CCACHE_MOUNT="type=bind,src=${CCACHE_DIR},dst=$CCACHE_DIR" CI_DEPENDS_MOUNT="type=bind,src=${DEPENDS_DIR}/built,dst=$DEPENDS_DIR/built" CI_DEPENDS_SOURCES_MOUNT="type=bind,src=${DEPENDS_DIR}/sources,dst=$DEPENDS_DIR/sources" CI_PREVIOUS_RELEASES_MOUNT="type=bind,src=${PREVIOUS_RELEASES_DIR},dst=$PREVIOUS_RELEASES_DIR" + CI_BUILD_MOUNT="--mount type=bind,src=${BASE_BUILD_DIR},dst=${BASE_BUILD_DIR}" fi if [ "$DANGER_CI_ON_HOST_CCACHE_FOLDER" ]; then @@ -98,6 +134,7 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then --mount "${CI_DEPENDS_MOUNT}" \ --mount "${CI_DEPENDS_SOURCES_MOUNT}" \ --mount "${CI_PREVIOUS_RELEASES_MOUNT}" \ + ${CI_BUILD_MOUNT} \ --env-file /tmp/env-$USER-$CONTAINER_NAME \ --name "$CONTAINER_NAME" \ --network ci-ip6net \ diff --git a/depends/packages/native_libmultiprocess.mk b/depends/packages/native_libmultiprocess.mk index 7c69e0f0c6..74d22207a0 100644 --- a/depends/packages/native_libmultiprocess.mk +++ b/depends/packages/native_libmultiprocess.mk @@ -1,8 +1,8 @@ package=native_libmultiprocess -$(package)_version=abe254b9734f2e2b220d1456de195532d6e6ac1e +$(package)_version=07c917f7ca910d66abc6d3873162fc9061704074 $(package)_download_path=https://github.com/chaincodelabs/libmultiprocess/archive $(package)_file_name=$($(package)_version).tar.gz -$(package)_sha256_hash=85777073259fdc75d24ac5777a19991ec1156c5f12db50b252b861c95dcb4f46 +$(package)_sha256_hash=ac9db311e3b22aac3c7b7b7b3f6b7fee5cf3043ebb3c3bf412049e8b17166de8 $(package)_dependencies=native_capnp define $(package)_config_cmds diff --git a/doc/release-notes-31600.md b/doc/release-notes-31600.md new file mode 100644 index 0000000000..54575e4cfd --- /dev/null +++ b/doc/release-notes-31600.md @@ -0,0 +1,11 @@ +Updated RPCs +--- +- the `getblocktemplate` RPC `curtime` (BIP22) and `mintime` (BIP23) fields now + account for the timewarp fix proposed in BIP94 on all networks. This ensures + that, in the event a timewarp fix softfork activates on mainnet, un-upgraded + miners will not accidentally violate the timewarp rule. (#31376, #31600) + +As a reminder, it's important that any software which uses the `getblocktemplate` +RPC takes these values into account (either `curtime` or `mintime` is fine). +Relying only on a clock can lead to invalid blocks under some circumstances, +especially once a timewarp fix is deployed. diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index 0d5b3d5b0e..850fc624b8 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -229,3 +229,7 @@ arith_uint256 UintToArith256(const uint256 &a) b.pn[x] = ReadLE32(a.begin() + x*4); return b; } + +// Explicit instantiations for base_uint<6144> (used in test/fuzz/muhash.cpp). +template base_uint<6144>& base_uint<6144>::operator*=(const base_uint<6144>& b); +template base_uint<6144>& base_uint<6144>::operator/=(const base_uint<6144>& b); diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index a0799aea7a..2f1ff56438 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -249,6 +249,19 @@ static void MuHashPrecompute(benchmark::Bench& bench) }); } +static void MuHashFinalize(benchmark::Bench& bench) +{ + FastRandomContext rng(true); + MuHash3072 acc{rng.randbytes(32)}; + acc /= MuHash3072{rng.rand256()}; + + bench.run([&] { + uint256 out; + acc.Finalize(out); + acc /= MuHash3072{out}; + }); +} + BENCHMARK(BenchRIPEMD160, benchmark::PriorityLevel::HIGH); BENCHMARK(SHA1, benchmark::PriorityLevel::HIGH); BENCHMARK(SHA256_STANDARD, benchmark::PriorityLevel::HIGH); @@ -272,3 +285,4 @@ BENCHMARK(MuHash, benchmark::PriorityLevel::HIGH); BENCHMARK(MuHashMul, benchmark::PriorityLevel::HIGH); BENCHMARK(MuHashDiv, benchmark::PriorityLevel::HIGH); BENCHMARK(MuHashPrecompute, benchmark::PriorityLevel::HIGH); +BENCHMARK(MuHashFinalize, benchmark::PriorityLevel::HIGH); diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 51c482607a..e657f01871 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -106,6 +106,7 @@ int main(int argc, char* argv[]) }; auto notifications = std::make_unique(); + kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE}; // SETUP: Chainstate auto chainparams = CChainParams::Main(); @@ -119,11 +120,14 @@ int main(int argc, char* argv[]) .chainparams = chainman_opts.chainparams, .blocks_dir = abs_datadir / "blocks", .notifications = chainman_opts.notifications, + .block_tree_db_params = DBParams{ + .path = abs_datadir / "blocks" / "index", + .cache_bytes = cache_sizes.block_tree_db, + }, }; util::SignalInterrupt interrupt; ChainstateManager chainman{interrupt, chainman_opts, blockman_opts}; - kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE}; node::ChainstateLoadOptions options; auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options); if (status != node::ChainstateLoadStatus::SUCCESS) { diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp index 9c35b0689d..4f502269f5 100644 --- a/src/crypto/muhash.cpp +++ b/src/crypto/muhash.cpp @@ -7,7 +7,9 @@ #include #include #include +#include +#include #include #include #include @@ -15,10 +17,22 @@ namespace { using limb_t = Num3072::limb_t; +using signed_limb_t = Num3072::signed_limb_t; using double_limb_t = Num3072::double_limb_t; +using signed_double_limb_t = Num3072::signed_double_limb_t; constexpr int LIMB_SIZE = Num3072::LIMB_SIZE; +constexpr int SIGNED_LIMB_SIZE = Num3072::SIGNED_LIMB_SIZE; +constexpr int LIMBS = Num3072::LIMBS; +constexpr int SIGNED_LIMBS = Num3072::SIGNED_LIMBS; +constexpr int FINAL_LIMB_POSITION = 3072 / SIGNED_LIMB_SIZE; +constexpr int FINAL_LIMB_MODULUS_BITS = 3072 % SIGNED_LIMB_SIZE; +constexpr limb_t MAX_LIMB = (limb_t)(-1); +constexpr limb_t MAX_SIGNED_LIMB = (((limb_t)1) << SIGNED_LIMB_SIZE) - 1; /** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */ constexpr limb_t MAX_PRIME_DIFF = 1103717; +/** The modular inverse of (2**3072 - MAX_PRIME_DIFF) mod (MAX_SIGNED_LIMB + 1). */ +constexpr limb_t MODULUS_INVERSE = limb_t(0x70a1421da087d93); + /** Extract the lowest limb of [c0,c1,c2] into n, and left shift the number by 1 limb. */ inline void extract3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& n) @@ -72,23 +86,6 @@ inline void muladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const l c2 += (c1 < th) ? 1 : 0; } -/** [c0,c1,c2] += 2 * a * b */ -inline void muldbladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b) -{ - double_limb_t t = (double_limb_t)a * b; - limb_t th = t >> LIMB_SIZE; - limb_t tl = t; - - c0 += tl; - limb_t tt = th + ((c0 < tl) ? 1 : 0); - c1 += tt; - c2 += (c1 < tt) ? 1 : 0; - c0 += tl; - th += (c0 < tl) ? 1 : 0; - c1 += th; - c2 += (c1 < th) ? 1 : 0; -} - /** * Add limb a to [c0,c1]: [c0,c1] += a. Then extract the lowest * limb of [c0,c1] into n, and left shift the number by 1 limb. @@ -103,8 +100,7 @@ inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n) c1 += 1; // Handle case when c1 has overflown - if (c1 == 0) - c2 = 1; + if (c1 == 0) c2 = 1; } // extract @@ -113,13 +109,6 @@ inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n) c1 = c2; } -/** in_out = in_out^(2^sq) * mul */ -inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul) -{ - for (int j = 0; j < sq; ++j) in_out.Square(); - in_out.Multiply(mul); -} - } // namespace /** Indicates whether d is larger than the modulus. */ @@ -141,98 +130,346 @@ void Num3072::FullReduce() } } -Num3072 Num3072::GetInverse() const +namespace { +/** A type representing a number in signed limb representation. */ +struct Num3072Signed { - // For fast exponentiation a sliding window exponentiation with repunit - // precomputation is utilized. See "Fast Point Decompression for Standard - // Elliptic Curves" (Brumley, Järvinen, 2008). - - Num3072 p[12]; // p[i] = a^(2^(2^i)-1) - Num3072 out; + /** The represented value is sum(limbs[i]*2^(SIGNED_LIMB_SIZE*i), i=0..SIGNED_LIMBS-1). + * Note that limbs may be negative, or exceed 2^SIGNED_LIMB_SIZE-1. */ + signed_limb_t limbs[SIGNED_LIMBS]; + + /** Construct a Num3072Signed with value 0. */ + Num3072Signed() + { + memset(limbs, 0, sizeof(limbs)); + } - p[0] = *this; + /** Convert a Num3072 to a Num3072Signed. Output will be normalized and in + * range 0..2^3072-1. */ + void FromNum3072(const Num3072& in) + { + double_limb_t c = 0; + int b = 0, outpos = 0; + for (int i = 0; i < LIMBS; ++i) { + c += double_limb_t{in.limbs[i]} << b; + b += LIMB_SIZE; + while (b >= SIGNED_LIMB_SIZE) { + limbs[outpos++] = limb_t(c) & MAX_SIGNED_LIMB; + c >>= SIGNED_LIMB_SIZE; + b -= SIGNED_LIMB_SIZE; + } + } + Assume(outpos == SIGNED_LIMBS - 1); + limbs[SIGNED_LIMBS - 1] = c; + c >>= SIGNED_LIMB_SIZE; + Assume(c == 0); + } - for (int i = 0; i < 11; ++i) { - p[i + 1] = p[i]; - for (int j = 0; j < (1 << i); ++j) p[i + 1].Square(); - p[i + 1].Multiply(p[i]); + /** Convert a Num3072Signed to a Num3072. Input must be in range 0..modulus-1. */ + void ToNum3072(Num3072& out) const + { + double_limb_t c = 0; + int b = 0, outpos = 0; + for (int i = 0; i < SIGNED_LIMBS; ++i) { + c += double_limb_t(limbs[i]) << b; + b += SIGNED_LIMB_SIZE; + if (b >= LIMB_SIZE) { + out.limbs[outpos++] = c; + c >>= LIMB_SIZE; + b -= LIMB_SIZE; + } + } + Assume(outpos == LIMBS); + Assume(c == 0); } - out = p[11]; - - square_n_mul(out, 512, p[9]); - square_n_mul(out, 256, p[8]); - square_n_mul(out, 128, p[7]); - square_n_mul(out, 64, p[6]); - square_n_mul(out, 32, p[5]); - square_n_mul(out, 8, p[3]); - square_n_mul(out, 2, p[1]); - square_n_mul(out, 1, p[0]); - square_n_mul(out, 5, p[2]); - square_n_mul(out, 3, p[0]); - square_n_mul(out, 2, p[0]); - square_n_mul(out, 4, p[0]); - square_n_mul(out, 4, p[1]); - square_n_mul(out, 3, p[0]); + /** Take a Num3072Signed in range 1-2*2^3072..2^3072-1, and: + * - optionally negate it (if negate is true) + * - reduce it modulo the modulus (2^3072 - MAX_PRIME_DIFF) + * - produce output with all limbs in range 0..2^SIGNED_LIMB_SIZE-1 + */ + void Normalize(bool negate) + { + // Add modulus if this was negative. This brings the range of *this to 1-2^3072..2^3072-1. + signed_limb_t cond_add = limbs[SIGNED_LIMBS-1] >> (LIMB_SIZE-1); // -1 if this is negative; 0 otherwise + limbs[0] += signed_limb_t(-MAX_PRIME_DIFF) & cond_add; + limbs[FINAL_LIMB_POSITION] += (signed_limb_t(1) << FINAL_LIMB_MODULUS_BITS) & cond_add; + // Next negate all limbs if negate was set. This does not change the range of *this. + signed_limb_t cond_negate = -signed_limb_t(negate); // -1 if this negate is true; 0 otherwise + for (int i = 0; i < SIGNED_LIMBS; ++i) { + limbs[i] = (limbs[i] ^ cond_negate) - cond_negate; + } + // Perform carry (make all limbs except the top one be in range 0..2^SIGNED_LIMB_SIZE-1). + for (int i = 0; i < SIGNED_LIMBS - 1; ++i) { + limbs[i + 1] += limbs[i] >> SIGNED_LIMB_SIZE; + limbs[i] &= MAX_SIGNED_LIMB; + } + // Again add modulus if *this was negative. This brings the range of *this to 0..2^3072-1. + cond_add = limbs[SIGNED_LIMBS-1] >> (LIMB_SIZE-1); // -1 if this is negative; 0 otherwise + limbs[0] += signed_limb_t(-MAX_PRIME_DIFF) & cond_add; + limbs[FINAL_LIMB_POSITION] += (signed_limb_t(1) << FINAL_LIMB_MODULUS_BITS) & cond_add; + // Perform another carry. Now all limbs are in range 0..2^SIGNED_LIMB_SIZE-1. + for (int i = 0; i < SIGNED_LIMBS - 1; ++i) { + limbs[i + 1] += limbs[i] >> SIGNED_LIMB_SIZE; + limbs[i] &= MAX_SIGNED_LIMB; + } + } +}; - return out; +/** 2x2 transformation matrix with signed_limb_t elements. */ +struct SignedMatrix +{ + signed_limb_t u, v, q, r; +}; + +/** Compute the transformation matrix for SIGNED_LIMB_SIZE divsteps. + * + * eta: initial eta value + * f: bottom SIGNED_LIMB_SIZE bits of initial f value + * g: bottom SIGNED_LIMB_SIZE bits of initial g value + * out: resulting transformation matrix, scaled by 2^SIGNED_LIMB_SIZE + * return: eta value after SIGNED_LIMB_SIZE divsteps + */ +inline limb_t ComputeDivstepMatrix(signed_limb_t eta, limb_t f, limb_t g, SignedMatrix& out) +{ + /** inv256[i] = -1/(2*i+1) (mod 256) */ + static const uint8_t NEGINV256[128] = { + 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, + 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, + 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, + 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, + 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, + 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, + 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, + 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, + 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, + 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, + 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 + }; + // Coefficients of returned SignedMatrix; starts off as identity matrix. */ + limb_t u = 1, v = 0, q = 0, r = 1; + // The number of divsteps still left. + int i = SIGNED_LIMB_SIZE; + while (true) { + /* Use a sentinel bit to count zeros only up to i. */ + int zeros = std::countr_zero(g | (MAX_LIMB << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* We're done once we've performed SIGNED_LIMB_SIZE divsteps. */ + if (i == 0) break; + /* If eta is negative, negate it and replace f,g with g,-f. */ + if (eta < 0) { + limb_t tmp; + eta = -eta; + tmp = f; f = g; g = -tmp; + tmp = u; u = q; q = -tmp; + tmp = v; v = r; r = -tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + int limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + limb_t m = (MAX_LIMB >> (LIMB_SIZE - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + limb_t w = (g * NEGINV256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + } + out.u = (signed_limb_t)u; + out.v = (signed_limb_t)v; + out.q = (signed_limb_t)q; + out.r = (signed_limb_t)r; + return eta; } -void Num3072::Multiply(const Num3072& a) +/** Apply matrix t/2^SIGNED_LIMB_SIZE to vector [d,e], modulo modulus. + * + * On input and output, d and e are in range 1-2*modulus..modulus-1. + */ +inline void UpdateDE(Num3072Signed& d, Num3072Signed& e, const SignedMatrix& t) { - limb_t c0 = 0, c1 = 0, c2 = 0; - Num3072 tmp; - - /* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */ - for (int j = 0; j < LIMBS - 1; ++j) { - limb_t d0 = 0, d1 = 0, d2 = 0; - mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]); - for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]); - mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF); - for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]); - extract3(c0, c1, c2, tmp.limbs[j]); + const signed_limb_t u = t.u, v=t.v, q=t.q, r=t.r; + + /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */ + signed_limb_t sd = d.limbs[SIGNED_LIMBS - 1] >> (LIMB_SIZE - 1); + signed_limb_t se = e.limbs[SIGNED_LIMBS - 1] >> (LIMB_SIZE - 1); + signed_limb_t md = (u & sd) + (v & se); + signed_limb_t me = (q & sd) + (r & se); + /* Begin computing t*[d,e]. */ + signed_limb_t di = d.limbs[0], ei = e.limbs[0]; + signed_double_limb_t cd = (signed_double_limb_t)u * di + (signed_double_limb_t)v * ei; + signed_double_limb_t ce = (signed_double_limb_t)q * di + (signed_double_limb_t)r * ei; + /* Correct md,me so that t*[d,e]+modulus*[md,me] has SIGNED_LIMB_SIZE zero bottom bits. */ + md -= (MODULUS_INVERSE * limb_t(cd) + md) & MAX_SIGNED_LIMB; + me -= (MODULUS_INVERSE * limb_t(ce) + me) & MAX_SIGNED_LIMB; + /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ + cd -= (signed_double_limb_t)1103717 * md; + ce -= (signed_double_limb_t)1103717 * me; + /* Verify that the low SIGNED_LIMB_SIZE bits of the computation are indeed zero, and then throw them away. */ + Assume((cd & MAX_SIGNED_LIMB) == 0); + Assume((ce & MAX_SIGNED_LIMB) == 0); + cd >>= SIGNED_LIMB_SIZE; + ce >>= SIGNED_LIMB_SIZE; + /* Now iteratively compute limb i=1..SIGNED_LIMBS-2 of t*[d,e]+modulus*[md,me], and store them in output + * limb i-1 (shifting down by SIGNED_LIMB_SIZE bits). The corresponding limbs in modulus are all zero, + * so modulus/md/me are not actually involved here. */ + for (int i = 1; i < SIGNED_LIMBS - 1; ++i) { + di = d.limbs[i]; + ei = e.limbs[i]; + cd += (signed_double_limb_t)u * di + (signed_double_limb_t)v * ei; + ce += (signed_double_limb_t)q * di + (signed_double_limb_t)r * ei; + d.limbs[i - 1] = (signed_limb_t)cd & MAX_SIGNED_LIMB; cd >>= SIGNED_LIMB_SIZE; + e.limbs[i - 1] = (signed_limb_t)ce & MAX_SIGNED_LIMB; ce >>= SIGNED_LIMB_SIZE; } + /* Compute limb SIGNED_LIMBS-1 of t*[d,e]+modulus*[md,me], and store it in output limb SIGNED_LIMBS-2. */ + di = d.limbs[SIGNED_LIMBS - 1]; + ei = e.limbs[SIGNED_LIMBS - 1]; + cd += (signed_double_limb_t)u * di + (signed_double_limb_t)v * ei; + ce += (signed_double_limb_t)q * di + (signed_double_limb_t)r * ei; + cd += (signed_double_limb_t)md << FINAL_LIMB_MODULUS_BITS; + ce += (signed_double_limb_t)me << FINAL_LIMB_MODULUS_BITS; + d.limbs[SIGNED_LIMBS - 2] = (signed_limb_t)cd & MAX_SIGNED_LIMB; cd >>= SIGNED_LIMB_SIZE; + e.limbs[SIGNED_LIMBS - 2] = (signed_limb_t)ce & MAX_SIGNED_LIMB; ce >>= SIGNED_LIMB_SIZE; + /* What remains goes into output limb SINGED_LIMBS-1 */ + d.limbs[SIGNED_LIMBS - 1] = (signed_limb_t)cd; + e.limbs[SIGNED_LIMBS - 1] = (signed_limb_t)ce; +} - /* Compute limb N-1 of a*b into tmp. */ - assert(c2 == 0); - for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]); - extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]); - - /* Perform a second reduction. */ - muln2(c0, c1, MAX_PRIME_DIFF); - for (int j = 0; j < LIMBS; ++j) { - addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]); +/** Apply matrix t/2^SIGNED_LIMB_SIZE to vector (f,g). + * + * The matrix t must be chosen such that t*(f,g) results in multiples of 2^SIGNED_LIMB_SIZE. + * This is the case for matrices computed by ComputeDivstepMatrix(). + */ +inline void UpdateFG(Num3072Signed& f, Num3072Signed& g, const SignedMatrix& t, int len) +{ + const signed_limb_t u = t.u, v=t.v, q=t.q, r=t.r; + + signed_limb_t fi, gi; + signed_double_limb_t cf, cg; + /* Start computing t*[f,g]. */ + fi = f.limbs[0]; + gi = g.limbs[0]; + cf = (signed_double_limb_t)u * fi + (signed_double_limb_t)v * gi; + cg = (signed_double_limb_t)q * fi + (signed_double_limb_t)r * gi; + /* Verify that the bottom SIGNED_LIMB_BITS bits of the result are zero, and then throw them away. */ + Assume((cf & MAX_SIGNED_LIMB) == 0); + Assume((cg & MAX_SIGNED_LIMB) == 0); + cf >>= SIGNED_LIMB_SIZE; + cg >>= SIGNED_LIMB_SIZE; + /* Now iteratively compute limb i=1..SIGNED_LIMBS-1 of t*[f,g], and store them in output limb i-1 (shifting + * down by SIGNED_LIMB_BITS bits). */ + for (int i = 1; i < len; ++i) { + fi = f.limbs[i]; + gi = g.limbs[i]; + cf += (signed_double_limb_t)u * fi + (signed_double_limb_t)v * gi; + cg += (signed_double_limb_t)q * fi + (signed_double_limb_t)r * gi; + f.limbs[i - 1] = (signed_limb_t)cf & MAX_SIGNED_LIMB; cf >>= SIGNED_LIMB_SIZE; + g.limbs[i - 1] = (signed_limb_t)cg & MAX_SIGNED_LIMB; cg >>= SIGNED_LIMB_SIZE; } + /* What remains is limb SIGNED_LIMBS of t*[f,g]; store it as output limb SIGNED_LIMBS-1. */ + f.limbs[len - 1] = (signed_limb_t)cf; + g.limbs[len - 1] = (signed_limb_t)cg; - assert(c1 == 0); - assert(c0 == 0 || c0 == 1); +} +} // namespace - /* Perform up to two more reductions if the internal state has already - * overflown the MAX of Num3072 or if it is larger than the modulus or - * if both are the case. - * */ - if (this->IsOverflow()) this->FullReduce(); - if (c0) this->FullReduce(); +Num3072 Num3072::GetInverse() const +{ + // Compute a modular inverse based on a variant of the safegcd algorithm: + // - Paper: https://gcd.cr.yp.to/papers.html + // - Inspired by this code in libsecp256k1: + // https://github.com/bitcoin-core/secp256k1/blob/master/src/modinv32_impl.h + // - Explanation of the algorithm: + // https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md + + // Local variables d, e, f, g: + // - f and g are the variables whose gcd we compute (despite knowing the answer is 1): + // - f is always odd, and initialized as modulus + // - g is initialized as *this (called x in what follows) + // - d and e are the numbers for which at every step it is the case that: + // - f = d * x mod modulus; d is initialized as 0 + // - g = e * x mod modulus; e is initialized as 1 + Num3072Signed d, e, f, g; + e.limbs[0] = 1; + // F is initialized as modulus, which in signed limb representation can be expressed + // simply as 2^3072 + -MAX_PRIME_DIFF. + f.limbs[0] = -MAX_PRIME_DIFF; + f.limbs[FINAL_LIMB_POSITION] = ((limb_t)1) << FINAL_LIMB_MODULUS_BITS; + g.FromNum3072(*this); + int len = SIGNED_LIMBS; //!< The number of significant limbs in f and g + signed_limb_t eta = -1; //!< State to track knowledge about ratio of f and g + // Perform divsteps on [f,g] until g=0 is reached, keeping (d,e) synchronized with them. + while (true) { + // Compute transformation matrix t that represents the next SIGNED_LIMB_SIZE divsteps + // to apply. This can be computed from just the bottom limb of f and g, and eta. + SignedMatrix t; + eta = ComputeDivstepMatrix(eta, f.limbs[0], g.limbs[0], t); + // Apply that transformation matrix to the full [f,g] vector. + UpdateFG(f, g, t, len); + // Apply that transformation matrix to the full [d,e] vector (mod modulus). + UpdateDE(d, e, t); + + // Check if g is zero. + if (g.limbs[0] == 0) { + signed_limb_t cond = 0; + for (int j = 1; j < len; ++j) { + cond |= g.limbs[j]; + } + // If so, we're done. + if (cond == 0) break; + } + + // Check if the top limbs of both f and g are both 0 or -1. + signed_limb_t fn = f.limbs[len - 1], gn = g.limbs[len - 1]; + signed_limb_t cond = ((signed_limb_t)len - 2) >> (LIMB_SIZE - 1); + cond |= fn ^ (fn >> (LIMB_SIZE - 1)); + cond |= gn ^ (gn >> (LIMB_SIZE - 1)); + if (cond == 0) { + // If so, drop the top limb, shrinking the size of f and g, by + // propagating the sign to the previous limb. + f.limbs[len - 2] |= (limb_t)f.limbs[len - 1] << SIGNED_LIMB_SIZE; + g.limbs[len - 2] |= (limb_t)g.limbs[len - 1] << SIGNED_LIMB_SIZE; + --len; + } + } + // At some point, [f,g] will have been rewritten into [f',0], such that gcd(f,g) = gcd(f',0). + // This is proven in the paper. As f started out being modulus, a prime number, we know that + // gcd is 1, and thus f' is 1 or -1. + Assume((f.limbs[0] & MAX_SIGNED_LIMB) == 1 || (f.limbs[0] & MAX_SIGNED_LIMB) == MAX_SIGNED_LIMB); + // As we've maintained the invariant that f = d * x mod modulus, we get d/f mod modulus is the + // modular inverse of x we're looking for. As f is 1 or -1, it is also true that d/f = d*f. + // Normalize d to prepare it for output, while negating it if f is negative. + d.Normalize(f.limbs[len - 1] >> (LIMB_SIZE - 1)); + Num3072 ret; + d.ToNum3072(ret); + return ret; } -void Num3072::Square() +void Num3072::Multiply(const Num3072& a) { limb_t c0 = 0, c1 = 0, c2 = 0; Num3072 tmp; - /* Compute limbs 0..N-2 of this*this into tmp, including one reduction. */ + /* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */ for (int j = 0; j < LIMBS - 1; ++j) { limb_t d0 = 0, d1 = 0, d2 = 0; - for (int i = 0; i < (LIMBS - 1 - j) / 2; ++i) muldbladd3(d0, d1, d2, this->limbs[i + j + 1], this->limbs[LIMBS - 1 - i]); - if ((j + 1) & 1) muladd3(d0, d1, d2, this->limbs[(LIMBS - 1 - j) / 2 + j + 1], this->limbs[LIMBS - 1 - (LIMBS - 1 - j) / 2]); + mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]); + for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]); mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF); - for (int i = 0; i < (j + 1) / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[j - i]); - if ((j + 1) & 1) muladd3(c0, c1, c2, this->limbs[(j + 1) / 2], this->limbs[j - (j + 1) / 2]); + for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]); extract3(c0, c1, c2, tmp.limbs[j]); } + /* Compute limb N-1 of a*b into tmp. */ assert(c2 == 0); - for (int i = 0; i < LIMBS / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[LIMBS - 1 - i]); + for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]); extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]); /* Perform a second reduction. */ diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h index 222b866b6d..798df57372 100644 --- a/src/crypto/muhash.h +++ b/src/crypto/muhash.h @@ -22,14 +22,22 @@ class Num3072 #ifdef __SIZEOF_INT128__ typedef unsigned __int128 double_limb_t; + typedef signed __int128 signed_double_limb_t; typedef uint64_t limb_t; + typedef int64_t signed_limb_t; static constexpr int LIMBS = 48; + static constexpr int SIGNED_LIMBS = 50; static constexpr int LIMB_SIZE = 64; + static constexpr int SIGNED_LIMB_SIZE = 62; #else typedef uint64_t double_limb_t; + typedef int64_t signed_double_limb_t; typedef uint32_t limb_t; + typedef int32_t signed_limb_t; static constexpr int LIMBS = 96; + static constexpr int SIGNED_LIMBS = 103; static constexpr int LIMB_SIZE = 32; + static constexpr int SIGNED_LIMB_SIZE = 30; #endif limb_t limbs[LIMBS]; @@ -37,6 +45,8 @@ class Num3072 static_assert(LIMB_SIZE * LIMBS == 3072, "Num3072 isn't 3072 bits"); static_assert(sizeof(double_limb_t) == sizeof(limb_t) * 2, "bad size for double_limb_t"); static_assert(sizeof(limb_t) * 8 == LIMB_SIZE, "LIMB_SIZE is incorrect"); + static_assert(SIGNED_LIMB_SIZE * SIGNED_LIMBS > 3072, "SIGNED_LIMBS * SIGNED_LIMB_SIZE is too small"); + static_assert(3072 / SIGNED_LIMB_SIZE == SIGNED_LIMBS - 1, "Bit 3072 must land in top signed limb"); // Hard coded values in MuHash3072 constructor and Finalize static_assert(sizeof(limb_t) == 4 || sizeof(limb_t) == 8, "bad size for limb_t"); @@ -44,7 +54,6 @@ class Num3072 void Multiply(const Num3072& a); void Divide(const Num3072& a); void SetToOne(); - void Square(); void ToBytes(unsigned char (&out)[BYTE_SIZE]); Num3072() { this->SetToOne(); }; diff --git a/src/init.cpp b/src/init.cpp index 740e0cd7a9..5b7e3de0a7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1072,6 +1072,10 @@ bool AppInitParameterInteraction(const ArgsManager& args) .chainparams = chainman_opts_dummy.chainparams, .blocks_dir = args.GetBlocksDirPath(), .notifications = chainman_opts_dummy.notifications, + .block_tree_db_params = DBParams{ + .path = args.GetDataDirNet() / "blocks" / "index", + .cache_bytes = 0, + }, }; auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)}; if (!blockman_result) { @@ -1218,18 +1222,33 @@ static ChainstateLoadResult InitAndLoadChainstate( .signals = node.validation_signals.get(), }; Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction + BlockManager::Options blockman_opts{ .chainparams = chainman_opts.chainparams, .blocks_dir = args.GetBlocksDirPath(), .notifications = chainman_opts.notifications, + .block_tree_db_params = DBParams{ + .path = args.GetDataDirNet() / "blocks" / "index", + .cache_bytes = cache_sizes.block_tree_db, + .wipe_data = do_reindex, + }, }; Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction + + // Creating the chainstate manager internally creates a BlockManager, opens + // the blocks tree db, and wipes existing block files in case of a reindex. + // The coinsdb is opened at a later point on LoadChainstate. try { node.chainman = std::make_unique(*Assert(node.shutdown_signal), chainman_opts, blockman_opts); + } catch (dbwrapper_error& e) { + LogError("%s", e.what()); + return {ChainstateLoadStatus::FAILURE, _("Error opening block database")}; } catch (std::exception& e) { return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated(strprintf("Failed to initialize ChainstateManager: %s", e.what()))}; } ChainstateManager& chainman = *node.chainman; + if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}}; + // This is defined and set here instead of inline in validation.h to avoid a hard // dependency between validation and index/base, since the latter is not in // libbitcoinkernel. @@ -1253,7 +1272,6 @@ static ChainstateLoadResult InitAndLoadChainstate( fNameHistory = args.GetBoolArg("-namehistory", false); node::ChainstateLoadOptions options; options.mempool = Assert(node.mempool.get()); - options.wipe_block_tree_db = do_reindex; options.wipe_chainstate_db = do_reindex || do_reindex_chainstate; options.prune = chainman.m_blockman.IsPruneMode(); options.nameHistory = fNameHistory; diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 4e858d1f89..c9ef46243c 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -289,6 +289,9 @@ class Chain //! Check if any block has been pruned. virtual bool havePruned() = 0; + //! Get the current prune height. + virtual std::optional getPruneHeight() = 0; + //! Check if the node is ready to broadcast transactions. virtual bool isReadyToBroadcast() = 0; diff --git a/src/ipc/capnp/common-types.h b/src/ipc/capnp/common-types.h index 51af6a5f0a..934dae5faf 100644 --- a/src/ipc/capnp/common-types.h +++ b/src/ipc/capnp/common-types.h @@ -14,6 +14,19 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -94,26 +107,6 @@ requires ipc::capnp::Deserializable return read_dest.construct(::deserialize, wrapper); } -//! Overload CustomBuildField and CustomReadField to serialize std::chrono -//! parameters and return values as numbers. -template -void CustomBuildField(TypeList>, Priority<1>, InvokeContext& invoke_context, Value&& value, - Output&& output) -{ - static_assert(std::numeric_limits::lowest() <= std::numeric_limits::lowest(), - "capnp type does not have enough range to hold lowest std::chrono::duration value"); - static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), - "capnp type does not have enough range to hold highest std::chrono::duration value"); - output.set(value.count()); -} - -template -decltype(auto) CustomReadField(TypeList>, Priority<1>, InvokeContext& invoke_context, - Input&& input, ReadDest&& read_dest) -{ - return read_dest.construct(input.get()); -} - //! Overload CustomBuildField and CustomReadField to serialize UniValue //! parameters and return values as JSON strings. template @@ -134,32 +127,6 @@ decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& i }); } -//! Generic ::capnp::Data field builder for any C++ type that can be converted -//! to a span of bytes, like std::vector or std::array, or custom -//! blob types like uint256 or PKHash with data() and size() methods pointing to -//! bytes. -//! -//! Note: it might make sense to move this function into libmultiprocess, since -//! it is fairly generic. However this would require decreasing its priority so -//! it can be overridden, which would require more changes inside -//! libmultiprocess to avoid conflicting with the Priority<1> CustomBuildField -//! function it already provides for std::vector. Also, it might make sense to -//! provide a CustomReadField counterpart to this function, which could be -//! called to read C++ types that can be constructed from spans of bytes from -//! ::capnp::Data fields. But so far there hasn't been a need for this. -template -void CustomBuildField(TypeList, Priority<2>, InvokeContext& invoke_context, Value&& value, Output&& output) -requires - (std::is_same_v) && - (std::convertible_to> || - std::convertible_to> || - std::convertible_to> || - std::convertible_to>) -{ - auto data = std::span{value}; - auto result = output.init(data.size()); - memcpy(result.begin(), data.data(), data.size()); -} } // namespace mp #endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H diff --git a/src/ipc/capnp/echo-types.h b/src/ipc/capnp/echo-types.h new file mode 100644 index 0000000000..14970c1adb --- /dev/null +++ b/src/ipc/capnp/echo-types.h @@ -0,0 +1,12 @@ +// Copyright (c) 2025 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_IPC_CAPNP_ECHO_TYPES_H +#define BITCOIN_IPC_CAPNP_ECHO_TYPES_H + +#include +#include +#include + +#endif // BITCOIN_IPC_CAPNP_ECHO_TYPES_H diff --git a/src/ipc/capnp/echo.capnp b/src/ipc/capnp/echo.capnp index df36ee0de3..03c40f906e 100644 --- a/src/ipc/capnp/echo.capnp +++ b/src/ipc/capnp/echo.capnp @@ -9,7 +9,7 @@ $Cxx.namespace("ipc::capnp::messages"); using Proxy = import "/mp/proxy.capnp"; $Proxy.include("interfaces/echo.h"); -$Proxy.include("ipc/capnp/echo.capnp.h"); +$Proxy.includeTypes("ipc/capnp/echo-types.h"); interface Echo $Proxy.wrap("interfaces::Echo") { destroy @0 (context :Proxy.Context) -> (); diff --git a/src/ipc/capnp/protocol.cpp b/src/ipc/capnp/protocol.cpp index 4b67a5bd1e..691bdf5f92 100644 --- a/src/ipc/capnp/protocol.cpp +++ b/src/ipc/capnp/protocol.cpp @@ -73,7 +73,7 @@ class CapnpProtocol : public Protocol } void addCleanup(std::type_index type, void* iface, std::function cleanup) override { - mp::ProxyTypeRegister::types().at(type)(iface).cleanup.emplace_back(std::move(cleanup)); + mp::ProxyTypeRegister::types().at(type)(iface).cleanup_fns.emplace_back(std::move(cleanup)); } Context& context() override { return m_context; } void startLoop(const char* exe_name) diff --git a/src/kernel/blockmanager_opts.h b/src/kernel/blockmanager_opts.h index 261ec3be58..5c7b24d717 100644 --- a/src/kernel/blockmanager_opts.h +++ b/src/kernel/blockmanager_opts.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H #define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H +#include #include #include @@ -27,6 +28,7 @@ struct BlockManagerOpts { bool fast_prune{false}; const fs::path blocks_dir; Notifications& notifications; + DBParams block_tree_db_params; }; } // namespace kernel diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h index 1b605f3d55..15a8fbec61 100644 --- a/src/kernel/chainstatemanager_opts.h +++ b/src/kernel/chainstatemanager_opts.h @@ -42,7 +42,6 @@ struct ChainstateManagerOpts { std::optional assumed_valid_block{}; //! If the tip is older than this, the node is considered to be in initial block download. std::chrono::seconds max_tip_age{DEFAULT_MAX_TIP_AGE}; - DBOptions block_tree_db{}; DBOptions coins_db{}; CoinsViewOptions coins_view{}; Notifications& notifications; diff --git a/src/net.cpp b/src/net.cpp index 47ef2d22d7..2849978871 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -558,6 +558,7 @@ void CNode::CloseSocketDisconnect() fDisconnect = true; LOCK(m_sock_mutex); if (m_sock) { + LogDebug(BCLog::NET, "Resetting socket for peer=%d%s", GetId(), LogIP(fLogIPs)); m_sock.reset(); } m_i2p_sam_session.reset(); @@ -1706,7 +1707,7 @@ bool CConnman::AttemptToEvictConnection() LOCK(m_nodes_mutex); for (CNode* pnode : m_nodes) { if (pnode->GetId() == *node_id_to_evict) { - LogDebug(BCLog::NET, "selected %s connection for eviction peer=%d; disconnecting\n", pnode->ConnectionTypeAsString(), pnode->GetId()); + LogDebug(BCLog::NET, "selected %s connection for eviction, %s", pnode->ConnectionTypeAsString(), pnode->DisconnectMsg(fLogIPs)); pnode->fDisconnect = true; return true; } @@ -3443,7 +3444,7 @@ void CConnman::StopNodes() std::vector nodes; WITH_LOCK(m_nodes_mutex, nodes.swap(m_nodes)); for (CNode* pnode : nodes) { - LogDebug(BCLog::NET, "%s\n", pnode->DisconnectMsg(fLogIPs)); + LogDebug(BCLog::NET, "Stopping node, %s", pnode->DisconnectMsg(fLogIPs)); pnode->CloseSocketDisconnect(); DeleteNode(pnode); } @@ -3607,7 +3608,7 @@ bool CConnman::DisconnectNode(const std::string& strNode) { LOCK(m_nodes_mutex); if (CNode* pnode = FindNode(strNode)) { - LogDebug(BCLog::NET, "disconnect by address%s matched peer=%d; disconnecting\n", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->GetId()); + LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->DisconnectMsg(fLogIPs)); pnode->fDisconnect = true; return true; } @@ -3620,7 +3621,7 @@ bool CConnman::DisconnectNode(const CSubNet& subnet) LOCK(m_nodes_mutex); for (CNode* pnode : m_nodes) { if (subnet.Match(pnode->addr)) { - LogDebug(BCLog::NET, "disconnect by subnet%s matched peer=%d; disconnecting\n", (fLogIPs ? strprintf("=%s", subnet.ToString()) : ""), pnode->GetId()); + LogDebug(BCLog::NET, "disconnect by subnet%s match, %s", (fLogIPs ? strprintf("=%s", subnet.ToString()) : ""), pnode->DisconnectMsg(fLogIPs)); pnode->fDisconnect = true; disconnected = true; } @@ -3638,7 +3639,7 @@ bool CConnman::DisconnectNode(NodeId id) LOCK(m_nodes_mutex); for(CNode* pnode : m_nodes) { if (id == pnode->GetId()) { - LogDebug(BCLog::NET, "disconnect by id peer=%d; disconnecting\n", pnode->GetId()); + LogDebug(BCLog::NET, "disconnect by id, %s", pnode->DisconnectMsg(fLogIPs)); pnode->fDisconnect = true; return true; } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 68e11c807b..9463de16b6 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4267,7 +4267,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, if (msg_type == NetMsgType::TX) { if (RejectIncomingTxs(pfrom)) { - LogDebug(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom.GetId()); + LogDebug(BCLog::NET, "transaction sent in violation of protocol, %s", pfrom.DisconnectMsg(fLogIPs)); pfrom.fDisconnect = true; return; } @@ -5269,7 +5269,7 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic { // The ping timeout is using mocktime. To disable the check during // testing, increase -peertimeout. - LogDebug(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), peer.m_id); + LogDebug(BCLog::NET, "ping timeout: %fs, %s", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), node_to.DisconnectMsg(fLogIPs)); node_to.fDisconnect = true; return; } diff --git a/src/node/blockmanager_args.cpp b/src/node/blockmanager_args.cpp index 0fc4e1646a..5186b65546 100644 --- a/src/node/blockmanager_args.cpp +++ b/src/node/blockmanager_args.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,8 @@ util::Result ApplyArgsManOptions(const ArgsManager& args, BlockManager::Op if (auto value{args.GetBoolArg("-fastprune")}) opts.fast_prune = *value; + ReadDatabaseArgs(args, opts.block_tree_db_params.options); + return {}; } } // namespace node diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index bf7fd5f542..a2d389250e 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -1194,7 +1195,19 @@ BlockManager::BlockManager(const util::SignalInterrupt& interrupt, Options opts) m_opts{std::move(opts)}, m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}}, m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}}, - m_interrupt{interrupt} {} + m_interrupt{interrupt} +{ + m_block_tree_db = std::make_unique(m_opts.block_tree_db_params); + + if (m_opts.block_tree_db_params.wipe_data) { + m_block_tree_db->WriteReindexing(true); + m_blockfiles_indexed = false; + // If we're reindexing in prune mode, wipe away unusable block files and all undo data files + if (m_prune_mode) { + CleanupBlockRevFiles(); + } + } +} class ImportingNow { diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 6f9aaeb29c..79e5abc5bf 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -24,10 +24,7 @@ #include #include -#include #include -#include -#include #include using kernel::CacheSizes; @@ -37,34 +34,8 @@ namespace node { // to ChainstateManager::InitializeChainstate(). static ChainstateLoadResult CompleteChainstateInitialization( ChainstateManager& chainman, - const CacheSizes& cache_sizes, const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - auto& pblocktree{chainman.m_blockman.m_block_tree_db}; - // new BlockTreeDB tries to delete the existing file, which - // fails if it's still open from the previous loop. Close it first: - pblocktree.reset(); - try { - pblocktree = std::make_unique(DBParams{ - .path = chainman.m_options.datadir / "blocks" / "index", - .cache_bytes = cache_sizes.block_tree_db, - .memory_only = options.block_tree_db_in_memory, - .wipe_data = options.wipe_block_tree_db, - .options = chainman.m_options.block_tree_db}); - } catch (dbwrapper_error& err) { - LogError("%s\n", err.what()); - return {ChainstateLoadStatus::FAILURE, _("Error opening block database")}; - } - - if (options.wipe_block_tree_db) { - pblocktree->WriteReindexing(true); - chainman.m_blockman.m_blockfiles_indexed = false; - //If we're reindexing in prune mode, wipe away unusable block files and all undo data files - if (options.prune) { - chainman.m_blockman.CleanupBlockRevFiles(); - } - } - if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}}; // LoadBlockIndex will load m_have_pruned if we've ever removed a @@ -161,14 +132,12 @@ static ChainstateLoadResult CompleteChainstateInitialization( } } - if (!options.wipe_block_tree_db) { - auto chainstates{chainman.GetAll()}; - if (std::any_of(chainstates.begin(), chainstates.end(), - [](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) { - return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."), - chainman.GetConsensus().SegwitHeight)}; - }; - } + auto chainstates{chainman.GetAll()}; + if (std::any_of(chainstates.begin(), chainstates.end(), + [](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) { + return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."), + chainman.GetConsensus().SegwitHeight)}; + }; // Now that chainstates are loaded and we're able to flush to // disk, rebalance the coins caches to desired levels based @@ -214,7 +183,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize } } - auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, options); if (init_status != ChainstateLoadStatus::SUCCESS) { return {init_status, init_error}; } @@ -250,7 +219,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize // for the fully validated chainstate. chainman.ActiveChainstate().ClearBlockIndexCandidates(); - auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, options); if (init_status != ChainstateLoadStatus::SUCCESS) { return {init_status, init_error}; } diff --git a/src/node/chainstate.h b/src/node/chainstate.h index f111cc451e..3b61fa797f 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -22,12 +22,7 @@ namespace node { struct ChainstateLoadOptions { CTxMemPool* mempool{nullptr}; - bool block_tree_db_in_memory{false}; bool coins_db_in_memory{false}; - // Whether to wipe the block tree database when loading it. If set, this - // will also set a reindexing flag so any existing block data files will be - // scanned and added to the database. - bool wipe_block_tree_db{false}; // Whether to wipe the chainstate database when loading it. If set, this // will cause the chainstate database to be rebuilt starting from genesis. bool wipe_chainstate_db{false}; diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp index 0ac96c5514..db36d03fd5 100644 --- a/src/node/chainstatemanager_args.cpp +++ b/src/node/chainstatemanager_args.cpp @@ -49,7 +49,6 @@ util::Result ApplyArgsManOptions(const ArgsManager& args, ChainstateManage if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value}; - ReadDatabaseArgs(args, opts.block_tree_db); ReadDatabaseArgs(args, opts.coins_db); ReadCoinsViewArgs(args, opts.coins_view); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 7ae2ff6453..73ce927f71 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -770,6 +771,11 @@ class ChainImpl : public Chain LOCK(::cs_main); return chainman().m_blockman.m_have_pruned; } + std::optional getPruneHeight() override + { + LOCK(chainman().GetMutex()); + return GetPruneHeight(chainman().m_blockman, chainman().ActiveChain()); + } bool isReadyToBroadcast() override { return !chainman().m_blockman.LoadingBlocks() && !isInitialBlockDownload(); } bool isInitialBlockDownload() override { diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 87042a9d6b..cf8ec07908 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -29,16 +29,25 @@ #include namespace node { -int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) -{ - int64_t nOldTime = pblock->nTime; - int64_t nNewTime{std::max(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch(NodeClock::now()))}; +int64_t GetMinimumTime(const CBlockIndex* pindexPrev, const int64_t difficulty_adjustment_interval) +{ + int64_t min_time{pindexPrev->GetMedianTimePast() + 1}; // Height of block to be mined. const int height{pindexPrev->nHeight + 1}; - if (height % consensusParams.DifficultyAdjustmentInterval() == 0) { - nNewTime = std::max(nNewTime, pindexPrev->GetBlockTime() - MAX_TIMEWARP); + // Account for BIP94 timewarp rule on all networks. This makes future + // activation safer. + if (height % difficulty_adjustment_interval == 0) { + min_time = std::max(min_time, pindexPrev->GetBlockTime() - MAX_TIMEWARP); } + return min_time; +} + +int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +{ + int64_t nOldTime = pblock->nTime; + int64_t nNewTime{std::max(GetMinimumTime(pindexPrev, consensusParams.DifficultyAdjustmentInterval()), + TicksSinceEpoch(NodeClock::now()))}; if (nOldTime < nNewTime) { pblock->nTime = nNewTime; diff --git a/src/node/miner.h b/src/node/miner.h index 732c89ca4b..b4c2eee248 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -220,6 +220,13 @@ class BlockAssembler bool DbLockLimitOk(const CTxMemPool::setEntries& candidates) const; }; +/** + * Get the minimum time a miner should use in the next block. This always + * accounts for the BIP94 timewarp rule, so does not necessarily reflect the + * consensus limit. + */ +int64_t GetMinimumTime(const CBlockIndex* pindexPrev, const int64_t difficulty_adjustment_interval); + int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); /** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */ diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 5a4b4442f3..454d9287ae 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -83,7 +83,7 @@ void PSBTOperationsDialog::signTransaction() WalletModel::UnlockContext ctx(m_wallet_model->requestUnlock()); - const auto err{m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, &n_signed, m_transaction_data, complete)}; + const auto err{m_wallet_model->wallet().fillPSBT(SIGHASH_DEFAULT, /*sign=*/true, /*bip32derivs=*/true, &n_signed, m_transaction_data, complete)}; if (err) { showStatus(tr("Failed to sign transaction: %1") diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index ac079acb81..21c2e16788 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -36,10 +36,10 @@ static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; double GetDifficultyForBits(uint32_t nBits); /** Block description to JSON */ -UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity) LOCKS_EXCLUDED(cs_main); +UniValue blockToJSON(node::BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit) LOCKS_EXCLUDED(cs_main); /** Block header to JSON */ -UniValue blockheaderToJSON(const node::BlockManager& blockman, const CBlockIndex& tip, const CBlockIndex& blockindex) LOCKS_EXCLUDED(cs_main); +UniValue blockheaderToJSON(const node::BlockManager& blockman, const CBlockIndex& tip, const CBlockIndex& blockindex, const uint256 pow_limit) LOCKS_EXCLUDED(cs_main); /** Used by getblockstats to get feerates at different percentiles by weight */ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector>& scores, int64_t total_weight); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2ef59d933f..80760a5d19 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -51,6 +51,7 @@ using interfaces::BlockTemplate; using interfaces::Mining; using node::BlockAssembler; +using node::GetMinimumTime; using node::NodeContext; using node::RegenerateCommitments; using node::UpdateTime; @@ -677,7 +678,7 @@ static RPCHelpMan getblocktemplate() {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"}, {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"}, {RPCResult::Type::STR, "target", "The hash target"}, - {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME + ". Adjusted for the proposed BIP94 timewarp rule."}, {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", { {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, @@ -686,7 +687,7 @@ static RPCHelpMan getblocktemplate() {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, {RPCResult::Type::NUM, "weightlimit", /*optional=*/true, "limit of block weight"}, - {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME + ". Adjusted for the proposed BIP94 timewarp rule."}, {RPCResult::Type::STR, "bits", "compressed target of next block"}, {RPCResult::Type::NUM, "height", "The height of the next block"}, {RPCResult::Type::STR_HEX, "signet_challenge", /*optional=*/true, "Only on signet"}, @@ -980,7 +981,7 @@ static RPCHelpMan getblocktemplate() result.pushKV("coinbasevalue", (int64_t)block.vtx[0]->vout[0].nValue); result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast)); result.pushKV("target", hashTarget.GetHex()); - result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); + result.pushKV("mintime", GetMinimumTime(pindexPrev, consensusParams.DifficultyAdjustmentInterval())); result.pushKV("mutable", std::move(aMutable)); result.pushKV("noncerange", "00000000ffffffff"); int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index 75a88f4a9f..68d8b5f754 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -33,6 +33,10 @@ BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos) .chainparams = *params, .blocks_dir = m_args.GetBlocksDirPath(), .notifications = notifications, + .block_tree_db_params = DBParams{ + .path = m_args.GetDataDirNet() / "blocks" / "index", + .cache_bytes = 0, + }, }; BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts}; // simulate adding a genesis block normally @@ -140,6 +144,10 @@ BOOST_AUTO_TEST_CASE(blockmanager_flush_block_file) .chainparams = Params(), .blocks_dir = m_args.GetBlocksDirPath(), .notifications = notifications, + .block_tree_db_params = DBParams{ + .path = m_args.GetDataDirNet() / "blocks" / "index", + .cache_bytes = 0, + }, }; BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts}; diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp index 6897e81494..c80c4877e6 100644 --- a/src/test/fuzz/float.cpp +++ b/src/test/fuzz/float.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 The Bitcoin Core developers +// Copyright (c) 2020-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,6 +11,7 @@ #include #include #include +#include FUZZ_TARGET(float) { @@ -18,7 +19,7 @@ FUZZ_TARGET(float) { const double d{[&] { - double tmp; + std::optional tmp; CallOneOf( fuzzed_data_provider, // an actual number @@ -42,7 +43,7 @@ FUZZ_TARGET(float) }); }, // Anything from raw memory (also checks that DecodeDouble doesn't crash on any input) [&] { tmp = DecodeDouble(fuzzed_data_provider.ConsumeIntegral()); }); - return tmp; + return *tmp; }()}; (void)memusage::DynamicUsage(d); diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp index e74bcb962f..f1dd5ebaf9 100644 --- a/src/test/fuzz/muhash.cpp +++ b/src/test/fuzz/muhash.cpp @@ -2,13 +2,169 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include +#include +#include #include #include #include +#include +#include #include +namespace { + +/** Class to represent 6144-bit numbers using arith_uint256 code. + * + * 6144 is sufficient to represent the product of two 3072-bit numbers. */ +class arith_uint6144 : public base_uint<6144> { +public: + arith_uint6144(uint64_t x) : base_uint{x} {} + + /** Construct an arith_uint6144 from any multiple of 4 bytes in LE notation, + * up to 768 bytes. */ + arith_uint6144(Span bytes) : base_uint{} + { + assert(bytes.size() % 4 == 0); + assert(bytes.size() <= 768); + for (unsigned i = 0; i * 4 < bytes.size(); ++i) { + pn[i] = ReadLE32(bytes.data() + 4 * i); + } + } + + /** Serialize an arithm_uint6144 to any multiply of 4 bytes in LE notation, + * on the condition that the represented number fits. */ + void Serialize(Span bytes) { + assert(bytes.size() % 4 == 0); + assert(bytes.size() <= 768); + for (unsigned i = 0; i * 4 < bytes.size(); ++i) { + WriteLE32(bytes.data() + 4 * i, pn[i]); + } + for (unsigned i = bytes.size() / 4; i * 4 < 768; ++i) { + assert(pn[i] == 0); + } + }; +}; + +/** The MuHash3072 modulus (2**3072 - 1103717) as 768 LE8 bytes. */ +constexpr std::array MODULUS_BYTES = { + 155, 40, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const arith_uint6144 ZERO{0}; +const arith_uint6144 ONE{1}; +const arith_uint6144 MODULUS{MODULUS_BYTES}; + +/** Update value to be the modulus of the input modulo MODULUS. */ +void Reduce(arith_uint6144& value) +{ + arith_uint6144 tmp = value; + tmp /= MODULUS; + tmp *= MODULUS; + value -= tmp; +} + +} // namespace + +FUZZ_TARGET(num3072_mul) +{ + // Test multiplication + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read two 3072-bit numbers from fuzz input, and construct arith_uint6144 + // and Num3072 objects with the read values. + uint16_t data_a_len = provider.ConsumeIntegralInRange(0, 384); + uint8_t data_a[384] = {0}; + provider.ConsumeData(data_a, data_a_len); + arith_uint6144 a_uint{data_a}; + Num3072 a_num{data_a}; + + uint8_t data_b[384] = {0}; + provider.ConsumeData(data_b, 384); + arith_uint6144 b_uint{data_b}; + Num3072 b_num{data_b}; + + // Multiply the first number with the second, in both representations. + a_num.Multiply(b_num); + a_uint *= b_uint; + Reduce(a_uint); + + // Serialize both to bytes and compare. + uint8_t buf_num[384], buf_uint[384]; + a_num.ToBytes(buf_num); + a_uint.Serialize(buf_uint); + assert(std::ranges::equal(buf_num, buf_uint)); +} + +FUZZ_TARGET(num3072_inv) +{ + // Test inversion + + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read a 3072-bit number from fuzz input, and construct arith_uint6144 + // and Num3072 objects with the read values. + uint8_t data[384] = {0}; + provider.ConsumeData(data, 384); + Num3072 num{data}; + arith_uint6144 uint{data}; + + // Bail out if the number has no inverse. + if ((uint == ZERO) || (uint == MODULUS)) return; + + // Compute the inverse of the Num3072 object. + Num3072 inv; + inv.SetToOne(); + inv.Divide(num); + + // Convert the computed inverse to arith_uint6144. + uint8_t buf[384]; + inv.ToBytes(buf); + arith_uint6144 uint_inv{buf}; + + // Multiply the original and the inverse, and expect 1. + uint *= uint_inv; + Reduce(uint); + assert(uint == ONE); +} + FUZZ_TARGET(muhash) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/ipc_test.capnp b/src/test/ipc_test.capnp index 46cd08b94a..7fd59cf588 100644 --- a/src/test/ipc_test.capnp +++ b/src/test/ipc_test.capnp @@ -20,4 +20,5 @@ interface FooInterface $Proxy.wrap("FooImplementation") { passTransaction @3 (arg :Data) -> (result :Data); passVectorChar @4 (arg :Data) -> (result :Data); passBlockState @5 (arg :Mining.BlockValidationState) -> (result :Mining.BlockValidationState); + passScript @6 (arg :Data) -> (result :Data); } diff --git a/src/test/ipc_test.cpp b/src/test/ipc_test.cpp index af37434980..d78d6695e7 100644 --- a/src/test/ipc_test.cpp +++ b/src/test/ipc_test.cpp @@ -121,6 +121,10 @@ void IpcPipeTest() BOOST_CHECK_EQUAL(bs3.GetRejectReason(), bs4.GetRejectReason()); BOOST_CHECK_EQUAL(bs3.GetDebugMessage(), bs4.GetDebugMessage()); + auto script1{CScript() << OP_11}; + auto script2{foo->passScript(script1)}; + BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2)); + // Test cleanup: disconnect pipe and join thread disconnect_client(); thread.join(); diff --git a/src/test/ipc_test.h b/src/test/ipc_test.h index 2d215a20f1..2304d8a4c0 100644 --- a/src/test/ipc_test.h +++ b/src/test/ipc_test.h @@ -6,6 +6,7 @@ #define BITCOIN_TEST_IPC_TEST_H #include +#include