Skip to content

Commit 725e62a

Browse files
committed
Merge #446: Duplicate serializations of consensus objects to use as Bitcoin seria…
5b4aff7 Duplicate serializations of consensus objects to use as Bitcoin serialization (Gregory Sanders) Pull request description: …lization This will allow us to break elements serialization in other PRs such as height in block header. Tree-SHA512: 1c58bb6caeef8d874579abcf536c7ca4541538e85441f51709971b7c7c5ce9e48e0c6dc09626a27aed0f045fbb1fe21c756b1d3986ce75489cf06fecc16eb15b
2 parents 3d27418 + 5b4aff7 commit 725e62a

File tree

7 files changed

+1087
-0
lines changed

7 files changed

+1087
-0
lines changed

src/Makefile.am

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ libbitcoin_wallet_a_SOURCES = \
288288
wallet/walletdb.cpp \
289289
wallet/walletutil.cpp \
290290
wallet/coinselection.cpp \
291+
primitives/bitcoin/merkleblock.cpp \
292+
primitives/bitcoin/block.cpp \
291293
$(BITCOIN_CORE_H)
292294

293295
# crypto primitives library
@@ -352,6 +354,12 @@ libbitcoin_consensus_a_SOURCES = \
352354
primitives/block.h \
353355
primitives/transaction.cpp \
354356
primitives/transaction.h \
357+
primitives/bitcoin/block.cpp \
358+
primitives/bitcoin/block.h \
359+
primitives/bitcoin/merkleblock.cpp \
360+
primitives/bitcoin/merkleblock.h \
361+
primitives/bitcoin/transaction.cpp \
362+
primitives/bitcoin/transaction.h \
355363
pubkey.cpp \
356364
pubkey.h \
357365
script/bitcoinconsensus.cpp \

src/primitives/bitcoin/block.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2018 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include <primitives/bitcoin/block.h>
7+
8+
#include <hash.h>
9+
#include <tinyformat.h>
10+
#include <utilstrencodings.h>
11+
#include <crypto/common.h>
12+
13+
namespace Sidechain {
14+
namespace Bitcoin {
15+
16+
uint256 CBlockHeader::GetHash() const
17+
{
18+
return SerializeHash(*this);
19+
}
20+
21+
std::string CBlock::ToString() const
22+
{
23+
std::stringstream s;
24+
s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n",
25+
GetHash().ToString(),
26+
nVersion,
27+
hashPrevBlock.ToString(),
28+
hashMerkleRoot.ToString(),
29+
nTime, nBits, nNonce,
30+
vtx.size());
31+
for (const auto& tx : vtx) {
32+
s << " " << tx->ToString() << "\n";
33+
}
34+
return s.str();
35+
}
36+
37+
} // Bitcoin
38+
} // Sidechain

src/primitives/bitcoin/block.h

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2018 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H
7+
#define BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H
8+
9+
#include <primitives/bitcoin/transaction.h>
10+
#include <serialize.h>
11+
#include <uint256.h>
12+
13+
namespace Sidechain {
14+
namespace Bitcoin {
15+
16+
/** Nodes collect new transactions into a block, hash them into a hash tree,
17+
* and scan through nonce values to make the block's hash satisfy proof-of-work
18+
* requirements. When they solve the proof-of-work, they broadcast the block
19+
* to everyone and the block is added to the block chain. The first transaction
20+
* in the block is a special one that creates a new coin owned by the creator
21+
* of the block.
22+
*/
23+
class CBlockHeader
24+
{
25+
public:
26+
// header
27+
int32_t nVersion;
28+
uint256 hashPrevBlock;
29+
uint256 hashMerkleRoot;
30+
uint32_t nTime;
31+
uint32_t nBits;
32+
uint32_t nNonce;
33+
34+
CBlockHeader()
35+
{
36+
SetNull();
37+
}
38+
39+
ADD_SERIALIZE_METHODS;
40+
41+
template <typename Stream, typename Operation>
42+
inline void SerializationOp(Stream& s, Operation ser_action) {
43+
READWRITE(this->nVersion);
44+
READWRITE(hashPrevBlock);
45+
READWRITE(hashMerkleRoot);
46+
READWRITE(nTime);
47+
READWRITE(nBits);
48+
READWRITE(nNonce);
49+
}
50+
51+
void SetNull()
52+
{
53+
nVersion = 0;
54+
hashPrevBlock.SetNull();
55+
hashMerkleRoot.SetNull();
56+
nTime = 0;
57+
nBits = 0;
58+
nNonce = 0;
59+
}
60+
61+
bool IsNull() const
62+
{
63+
return (nBits == 0);
64+
}
65+
66+
uint256 GetHash() const;
67+
68+
int64_t GetBlockTime() const
69+
{
70+
return (int64_t)nTime;
71+
}
72+
};
73+
74+
75+
class CBlock : public CBlockHeader
76+
{
77+
public:
78+
// network and disk
79+
std::vector<CTransactionRef> vtx;
80+
81+
// memory only
82+
mutable bool fChecked;
83+
84+
CBlock()
85+
{
86+
SetNull();
87+
}
88+
89+
CBlock(const CBlockHeader &header)
90+
{
91+
SetNull();
92+
*(static_cast<CBlockHeader*>(this)) = header;
93+
}
94+
95+
ADD_SERIALIZE_METHODS;
96+
97+
template <typename Stream, typename Operation>
98+
inline void SerializationOp(Stream& s, Operation ser_action) {
99+
READWRITEAS(CBlockHeader, *this);
100+
READWRITE(vtx);
101+
}
102+
103+
void SetNull()
104+
{
105+
CBlockHeader::SetNull();
106+
vtx.clear();
107+
fChecked = false;
108+
}
109+
110+
CBlockHeader GetBlockHeader() const
111+
{
112+
CBlockHeader block;
113+
block.nVersion = nVersion;
114+
block.hashPrevBlock = hashPrevBlock;
115+
block.hashMerkleRoot = hashMerkleRoot;
116+
block.nTime = nTime;
117+
block.nBits = nBits;
118+
block.nNonce = nNonce;
119+
return block;
120+
}
121+
122+
std::string ToString() const;
123+
};
124+
125+
/** Describes a place in the block chain to another node such that if the
126+
* other node doesn't have the same branch, it can find a recent common trunk.
127+
* The further back it is, the further before the fork it may be.
128+
*/
129+
struct CBlockLocator
130+
{
131+
std::vector<uint256> vHave;
132+
133+
CBlockLocator() {}
134+
135+
explicit CBlockLocator(const std::vector<uint256>& vHaveIn) : vHave(vHaveIn) {}
136+
137+
ADD_SERIALIZE_METHODS;
138+
139+
template <typename Stream, typename Operation>
140+
inline void SerializationOp(Stream& s, Operation ser_action) {
141+
int nVersion = s.GetVersion();
142+
if (!(s.GetType() & SER_GETHASH))
143+
READWRITE(nVersion);
144+
READWRITE(vHave);
145+
}
146+
147+
void SetNull()
148+
{
149+
vHave.clear();
150+
}
151+
152+
bool IsNull() const
153+
{
154+
return vHave.empty();
155+
}
156+
};
157+
158+
} // namespace Bitcoin
159+
} // namespace Sidechain
160+
161+
#endif // BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2018 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include <primitives/bitcoin/merkleblock.h>
7+
8+
#include <hash.h>
9+
#include <consensus/consensus.h>
10+
#include <utilstrencodings.h>
11+
12+
namespace Sidechain {
13+
namespace Bitcoin {
14+
/*
15+
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
16+
{
17+
header = block.GetBlockHeader();
18+
19+
std::vector<bool> vMatch;
20+
std::vector<uint256> vHashes;
21+
22+
vMatch.reserve(block.vtx.size());
23+
vHashes.reserve(block.vtx.size());
24+
25+
for (unsigned int i = 0; i < block.vtx.size(); i++)
26+
{
27+
const uint256& hash = block.vtx[i]->GetHash();
28+
if (txids && txids->count(hash)) {
29+
vMatch.push_back(true);
30+
} else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) {
31+
vMatch.push_back(true);
32+
vMatchedTxn.emplace_back(i, hash);
33+
} else {
34+
vMatch.push_back(false);
35+
}
36+
vHashes.push_back(hash);
37+
}
38+
39+
txn = CPartialMerkleTree(vHashes, vMatch);
40+
}
41+
*/
42+
uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
43+
//we can never have zero txs in a merkle block, we always need the coinbase tx
44+
//if we do not have this assert, we can hit a memory access violation when indexing into vTxid
45+
assert(vTxid.size() != 0);
46+
if (height == 0) {
47+
// hash at height 0 is the txids themself
48+
return vTxid[pos];
49+
} else {
50+
// calculate left hash
51+
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
52+
// calculate right hash if not beyond the end of the array - copy left hash otherwise
53+
if (pos*2+1 < CalcTreeWidth(height-1))
54+
right = CalcHash(height-1, pos*2+1, vTxid);
55+
else
56+
right = left;
57+
// combine subhashes
58+
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
59+
}
60+
}
61+
62+
void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) {
63+
// determine whether this node is the parent of at least one matched txid
64+
bool fParentOfMatch = false;
65+
for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++)
66+
fParentOfMatch |= vMatch[p];
67+
// store as flag bit
68+
vBits.push_back(fParentOfMatch);
69+
if (height==0 || !fParentOfMatch) {
70+
// if at height 0, or nothing interesting below, store hash and stop
71+
vHash.push_back(CalcHash(height, pos, vTxid));
72+
} else {
73+
// otherwise, don't store any hash, but descend into the subtrees
74+
TraverseAndBuild(height-1, pos*2, vTxid, vMatch);
75+
if (pos*2+1 < CalcTreeWidth(height-1))
76+
TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch);
77+
}
78+
}
79+
80+
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
81+
if (nBitsUsed >= vBits.size()) {
82+
// overflowed the bits array - failure
83+
fBad = true;
84+
return uint256();
85+
}
86+
bool fParentOfMatch = vBits[nBitsUsed++];
87+
if (height==0 || !fParentOfMatch) {
88+
// if at height 0, or nothing interesting below, use stored hash and do not descend
89+
if (nHashUsed >= vHash.size()) {
90+
// overflowed the hash array - failure
91+
fBad = true;
92+
return uint256();
93+
}
94+
const uint256 &hash = vHash[nHashUsed++];
95+
if (height==0 && fParentOfMatch) { // in case of height 0, we have a matched txid
96+
vMatch.push_back(hash);
97+
vnIndex.push_back(pos);
98+
}
99+
return hash;
100+
} else {
101+
// otherwise, descend into the subtrees to extract matched txids and hashes
102+
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch, vnIndex), right;
103+
if (pos*2+1 < CalcTreeWidth(height-1)) {
104+
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch, vnIndex);
105+
if (right == left) {
106+
// The left and right branches should never be identical, as the transaction
107+
// hashes covered by them must each be unique.
108+
fBad = true;
109+
}
110+
} else {
111+
right = left;
112+
}
113+
// and combine them before returning
114+
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
115+
}
116+
}
117+
118+
CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) : nTransactions(vTxid.size()), fBad(false) {
119+
// reset state
120+
vBits.clear();
121+
vHash.clear();
122+
123+
// calculate height of tree
124+
int nHeight = 0;
125+
while (CalcTreeWidth(nHeight) > 1)
126+
nHeight++;
127+
128+
// traverse the partial tree
129+
TraverseAndBuild(nHeight, 0, vTxid, vMatch);
130+
}
131+
132+
CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
133+
134+
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
135+
vMatch.clear();
136+
// An empty set will not work
137+
if (nTransactions == 0)
138+
return uint256();
139+
// check for excessively high numbers of transactions
140+
if (nTransactions > MAX_BLOCK_WEIGHT / MIN_TRANSACTION_WEIGHT)
141+
return uint256();
142+
// there can never be more hashes provided than one for every txid
143+
if (vHash.size() > nTransactions)
144+
return uint256();
145+
// there must be at least one bit per node in the partial tree, and at least one node per hash
146+
if (vBits.size() < vHash.size())
147+
return uint256();
148+
// calculate height of tree
149+
int nHeight = 0;
150+
while (CalcTreeWidth(nHeight) > 1)
151+
nHeight++;
152+
// traverse the partial tree
153+
unsigned int nBitsUsed = 0, nHashUsed = 0;
154+
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch, vnIndex);
155+
// verify that no problems occurred during the tree traversal
156+
if (fBad)
157+
return uint256();
158+
// verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
159+
if ((nBitsUsed+7)/8 != (vBits.size()+7)/8)
160+
return uint256();
161+
// verify that all hashes were consumed
162+
if (nHashUsed != vHash.size())
163+
return uint256();
164+
return hashMerkleRoot;
165+
}
166+
167+
} // namespace Bitcoin
168+
} // namespace Sidechain

0 commit comments

Comments
 (0)