Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes-xxxxx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
RPC
---

The new `gettxproof` and `verifytxproof` RPCs can produce and verify transaction
inclusion proofs for a single witness transaction id in a specified block.
(#xxxxx)
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
common/init.cpp
common/interfaces.cpp
common/license_info.cpp
common/merkle.cpp
common/messages.cpp
common/netif.cpp
common/pcp.cpp
Expand Down Expand Up @@ -241,6 +242,7 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
node/transaction.cpp
node/txdownloadman_impl.cpp
node/txorphanage.cpp
node/txproof.cpp
node/txreconciliation.cpp
node/utxo_snapshot.cpp
node/warnings.cpp
Expand Down
137 changes: 137 additions & 0 deletions src/common/merkle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <common/merkle.h>

#include <hash.h>
#include <util/check.h>

std::optional<uint256> ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& branch, uint32_t position, uint32_t leaf_count)
{
if (leaf_count == 0 || position >= leaf_count) return std::nullopt;

uint256 hash{leaf};
uint32_t count{leaf_count};
uint32_t index{position};
for (const uint256& sibling : branch) {
if (count <= 1) return std::nullopt;

const bool duplicate{(index ^ 1U) >= count};
if (duplicate && sibling != hash) return std::nullopt;

hash = (index & 1) ? Hash(sibling, hash) : Hash(hash, sibling);
index >>= 1;
count = (count + 1) / 2;
}
if (count != 1) return std::nullopt;
return hash;
}

/* This implements a constant-space merkle path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint32_t leaf_pos, std::vector<uint256>& path)
{
path.clear();
Assume(leaves.size() <= UINT32_MAX);
if (leaves.size() == 0) {
return;
}
// count is the number of leaves processed so far.
uint32_t count = 0;
// inner is an array of eagerly computed subtree hashes, indexed by tree
// level (0 being the leaves).
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
// the last leaf. The other inner entries are undefined.
uint256 inner[32];
// Which position in inner is a hash that depends on the matching leaf.
int matchlevel = -1;
// First process all leaves into 'inner' values.
while (count < leaves.size()) {
uint256 h = leaves[count];
bool matchh = count == leaf_pos;
count++;
int level;
// For each of the lower bits in count that are 0, do 1 step. Each
// corresponds to an inner value that existed before processing the
// current leaf, and each needs a hash to combine it.
for (level = 0; !(count & ((uint32_t{1}) << level)); level++) {
if (matchh) {
path.push_back(inner[level]);
} else if (matchlevel == level) {
path.push_back(h);
matchh = true;
}
h = Hash(inner[level], h);
}
// Store the resulting hash at inner position level.
inner[level] = h;
if (matchh) {
matchlevel = level;
}
}
// Do a final 'sweep' over the rightmost branch of the tree to process
// odd levels, and reduce everything to a single top value.
// Level is the level (counted from the bottom) up to which we've sweeped.
int level = 0;
// As long as bit number level in count is zero, skip it. It means there
// is nothing left at this level.
while (!(count & ((uint32_t{1}) << level))) {
level++;
}
uint256 h = inner[level];
bool matchh = matchlevel == level;
while (count != ((uint32_t{1}) << level)) {
// If we reach this point, h is an inner value that is not the top.
// We combine it with itself (Bitcoin's special rule for odd levels in
// the tree) to produce a higher level one.
if (matchh) {
path.push_back(h);
}
h = Hash(h, h);
// Increment count to the value it would have if two entries at this
// level had existed.
count += ((uint32_t{1}) << level);
level++;
// And propagate the result upwards accordingly.
while (!(count & ((uint32_t{1}) << level))) {
if (matchh) {
path.push_back(inner[level]);
} else if (matchlevel == level) {
path.push_back(h);
matchh = true;
}
h = Hash(inner[level], h);
level++;
}
}
}

static std::vector<uint256> ComputeMerklePath(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, position, ret);
return ret;
}

std::vector<uint256> TransactionMerklePath(const CBlock& block, uint32_t position)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetHash().ToUint256();
}
return ComputeMerklePath(leaves, position);
}

std::vector<uint256> WitnessMerklePath(const CBlock& block, uint32_t position)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
if (!leaves.empty()) {
leaves[0] = uint256{};
}
for (size_t s = 1; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetWitnessHash().ToUint256();
}
return ComputeMerklePath(leaves, position);
}
42 changes: 42 additions & 0 deletions src/common/merkle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 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_COMMON_MERKLE_H
#define BITCOIN_COMMON_MERKLE_H

#include <primitives/block.h>
#include <uint256.h>

#include <cstdint>
#include <optional>
#include <vector>

/**
* Compute the Merkle root for a leaf from a merkle branch and a known tree
* shape, or nullopt if the branch is inconsistent with the supplied position
* and leaf count.
*/
std::optional<uint256> ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& branch, uint32_t position, uint32_t leaf_count);

/**
* Compute merkle path to the specified transaction
*
* @param[in] block the block
* @param[in] position transaction for which to calculate the merkle path (0 is the coinbase)
*
* @return merkle path ordered from the deepest
*/
std::vector<uint256> TransactionMerklePath(const CBlock& block, uint32_t position);

/**
* Compute merkle path to the specified transaction's witness transaction id.
*
* @param[in] block the block
* @param[in] position transaction for which to calculate the merkle path (0 is the coinbase's null wtxid)
*
* @return merkle path ordered from the deepest
*/
std::vector<uint256> WitnessMerklePath(const CBlock& block, uint32_t position);

#endif // BITCOIN_COMMON_MERKLE_H
97 changes: 0 additions & 97 deletions src/consensus/merkle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include <consensus/merkle.h>
#include <hash.h>
#include <util/check.h>

/* WARNING! If you're reading this because you're learning about crypto
and/or designing a new system that will use merkle trees, keep in mind
Expand Down Expand Up @@ -62,7 +61,6 @@ uint256 ComputeMerkleRoot(std::vector<uint256> hashes, bool* mutated) {
return hashes[0];
}


uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
{
std::vector<uint256> leaves;
Expand All @@ -83,98 +81,3 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block)
}
return ComputeMerkleRoot(std::move(leaves));
}

/* This implements a constant-space merkle path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint32_t leaf_pos, std::vector<uint256>& path)
{
path.clear();
Assume(leaves.size() <= UINT32_MAX);
if (leaves.size() == 0) {
return;
}
// count is the number of leaves processed so far.
uint32_t count = 0;
// inner is an array of eagerly computed subtree hashes, indexed by tree
// level (0 being the leaves).
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
// the last leaf. The other inner entries are undefined.
uint256 inner[32];
// Which position in inner is a hash that depends on the matching leaf.
int matchlevel = -1;
// First process all leaves into 'inner' values.
while (count < leaves.size()) {
uint256 h = leaves[count];
bool matchh = count == leaf_pos;
count++;
int level;
// For each of the lower bits in count that are 0, do 1 step. Each
// corresponds to an inner value that existed before processing the
// current leaf, and each needs a hash to combine it.
for (level = 0; !(count & ((uint32_t{1}) << level)); level++) {
if (matchh) {
path.push_back(inner[level]);
} else if (matchlevel == level) {
path.push_back(h);
matchh = true;
}
h = Hash(inner[level], h);
}
// Store the resulting hash at inner position level.
inner[level] = h;
if (matchh) {
matchlevel = level;
}
}
// Do a final 'sweep' over the rightmost branch of the tree to process
// odd levels, and reduce everything to a single top value.
// Level is the level (counted from the bottom) up to which we've sweeped.
int level = 0;
// As long as bit number level in count is zero, skip it. It means there
// is nothing left at this level.
while (!(count & ((uint32_t{1}) << level))) {
level++;
}
uint256 h = inner[level];
bool matchh = matchlevel == level;
while (count != ((uint32_t{1}) << level)) {
// If we reach this point, h is an inner value that is not the top.
// We combine it with itself (Bitcoin's special rule for odd levels in
// the tree) to produce a higher level one.
if (matchh) {
path.push_back(h);
}
h = Hash(h, h);
// Increment count to the value it would have if two entries at this
// level had existed.
count += ((uint32_t{1}) << level);
level++;
// And propagate the result upwards accordingly.
while (!(count & ((uint32_t{1}) << level))) {
if (matchh) {
path.push_back(inner[level]);
} else if (matchlevel == level) {
path.push_back(h);
matchh = true;
}
h = Hash(inner[level], h);
level++;
}
}
}

static std::vector<uint256> ComputeMerklePath(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, position, ret);
return ret;
}

std::vector<uint256> TransactionMerklePath(const CBlock& block, uint32_t position)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetHash().ToUint256();
}
return ComputeMerklePath(leaves, position);
}
14 changes: 2 additions & 12 deletions src/consensus/merkle.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#ifndef BITCOIN_CONSENSUS_MERKLE_H
#define BITCOIN_CONSENSUS_MERKLE_H

#include <vector>

#include <primitives/block.h>
#include <uint256.h>

#include <vector>

uint256 ComputeMerkleRoot(std::vector<uint256> hashes, bool* mutated = nullptr);

/*
Expand All @@ -23,14 +23,4 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = nullptr);
*/
uint256 BlockWitnessMerkleRoot(const CBlock& block);

/**
* Compute merkle path to the specified transaction
*
* @param[in] block the block
* @param[in] position transaction for which to calculate the merkle path (0 is the coinbase)
*
* @return merkle path ordered from the deepest
*/
std::vector<uint256> TransactionMerklePath(const CBlock& block, uint32_t position);

#endif // BITCOIN_CONSENSUS_MERKLE_H
16 changes: 12 additions & 4 deletions src/consensus/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
}

/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
inline int GetWitnessCommitmentIndex(const CBlock& block)
inline int GetWitnessCommitmentIndex(const CTransaction& coinbase_tx)
{
int commitpos = NO_WITNESS_COMMITMENT;
if (!block.vtx.empty()) {
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
const CTxOut& vout = block.vtx[0]->vout[o];
{
for (size_t o = 0; o < coinbase_tx.vout.size(); ++o) {
const CTxOut& vout = coinbase_tx.vout[o];
if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
vout.scriptPubKey[0] == OP_RETURN &&
vout.scriptPubKey[1] == 0x24 &&
Expand All @@ -164,4 +164,12 @@ inline int GetWitnessCommitmentIndex(const CBlock& block)
return commitpos;
}

inline int GetWitnessCommitmentIndex(const CBlock& block)
{
if (block.vtx.empty()) {
return NO_WITNESS_COMMITMENT;
}
return GetWitnessCommitmentIndex(*block.vtx[0]);
}

#endif // BITCOIN_CONSENSUS_VALIDATION_H
2 changes: 1 addition & 1 deletion src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
#include <chainparams.h>
#include <coins.h>
#include <common/args.h>
#include <common/merkle.h>
#include <common/settings.h>
#include <consensus/amount.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <external_signer.h>
#include <httprpc.h>
Expand Down
Loading
Loading