diff --git a/src/Makefile.am b/src/Makefile.am index 704c59dd36..c5eddc6cf0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -288,6 +288,8 @@ libbitcoin_wallet_a_SOURCES = \ wallet/walletdb.cpp \ wallet/walletutil.cpp \ wallet/coinselection.cpp \ + primitives/bitcoin/merkleblock.cpp \ + primitives/bitcoin/block.cpp \ $(BITCOIN_CORE_H) # crypto primitives library @@ -352,6 +354,12 @@ libbitcoin_consensus_a_SOURCES = \ primitives/block.h \ primitives/transaction.cpp \ primitives/transaction.h \ + primitives/bitcoin/block.cpp \ + primitives/bitcoin/block.h \ + primitives/bitcoin/merkleblock.cpp \ + primitives/bitcoin/merkleblock.h \ + primitives/bitcoin/transaction.cpp \ + primitives/bitcoin/transaction.h \ pubkey.cpp \ pubkey.h \ script/bitcoinconsensus.cpp \ diff --git a/src/primitives/bitcoin/block.cpp b/src/primitives/bitcoin/block.cpp new file mode 100644 index 0000000000..de4c6653a6 --- /dev/null +++ b/src/primitives/bitcoin/block.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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 + +#include +#include +#include +#include + +namespace Sidechain { +namespace Bitcoin { + +uint256 CBlockHeader::GetHash() const +{ + return SerializeHash(*this); +} + +std::string CBlock::ToString() const +{ + std::stringstream s; + s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", + GetHash().ToString(), + nVersion, + hashPrevBlock.ToString(), + hashMerkleRoot.ToString(), + nTime, nBits, nNonce, + vtx.size()); + for (const auto& tx : vtx) { + s << " " << tx->ToString() << "\n"; + } + return s.str(); +} + +} // Bitcoin +} // Sidechain diff --git a/src/primitives/bitcoin/block.h b/src/primitives/bitcoin/block.h new file mode 100644 index 0000000000..4ee0f1a889 --- /dev/null +++ b/src/primitives/bitcoin/block.h @@ -0,0 +1,161 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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_PRIMITIVES_BITCOIN_BLOCK_H +#define BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H + +#include +#include +#include + +namespace Sidechain { +namespace Bitcoin { + +/** Nodes collect new transactions into a block, hash them into a hash tree, + * and scan through nonce values to make the block's hash satisfy proof-of-work + * requirements. When they solve the proof-of-work, they broadcast the block + * to everyone and the block is added to the block chain. The first transaction + * in the block is a special one that creates a new coin owned by the creator + * of the block. + */ +class CBlockHeader +{ +public: + // header + int32_t nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; + + CBlockHeader() + { + SetNull(); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(this->nVersion); + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + } + + void SetNull() + { + nVersion = 0; + hashPrevBlock.SetNull(); + hashMerkleRoot.SetNull(); + nTime = 0; + nBits = 0; + nNonce = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const; + + int64_t GetBlockTime() const + { + return (int64_t)nTime; + } +}; + + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector vtx; + + // memory only + mutable bool fChecked; + + CBlock() + { + SetNull(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *(static_cast(this)) = header; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITEAS(CBlockHeader, *this); + READWRITE(vtx); + } + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + fChecked = false; + } + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrevBlock; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + std::string ToString() const; +}; + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +struct CBlockLocator +{ + std::vector vHave; + + CBlockLocator() {} + + explicit CBlockLocator(const std::vector& vHaveIn) : vHave(vHaveIn) {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + } + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() const + { + return vHave.empty(); + } +}; + +} // namespace Bitcoin +} // namespace Sidechain + +#endif // BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H diff --git a/src/primitives/bitcoin/merkleblock.cpp b/src/primitives/bitcoin/merkleblock.cpp new file mode 100644 index 0000000000..c0a1a352d7 --- /dev/null +++ b/src/primitives/bitcoin/merkleblock.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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 + +#include +#include +#include + +namespace Sidechain { +namespace Bitcoin { +/* +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set* txids) +{ + header = block.GetBlockHeader(); + + std::vector vMatch; + std::vector vHashes; + + vMatch.reserve(block.vtx.size()); + vHashes.reserve(block.vtx.size()); + + for (unsigned int i = 0; i < block.vtx.size(); i++) + { + const uint256& hash = block.vtx[i]->GetHash(); + if (txids && txids->count(hash)) { + vMatch.push_back(true); + } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) { + vMatch.push_back(true); + vMatchedTxn.emplace_back(i, hash); + } else { + vMatch.push_back(false); + } + vHashes.push_back(hash); + } + + txn = CPartialMerkleTree(vHashes, vMatch); +} +*/ +uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector &vTxid) { + //we can never have zero txs in a merkle block, we always need the coinbase tx + //if we do not have this assert, we can hit a memory access violation when indexing into vTxid + assert(vTxid.size() != 0); + if (height == 0) { + // hash at height 0 is the txids themself + return vTxid[pos]; + } else { + // calculate left hash + uint256 left = CalcHash(height-1, pos*2, vTxid), right; + // calculate right hash if not beyond the end of the array - copy left hash otherwise + if (pos*2+1 < CalcTreeWidth(height-1)) + right = CalcHash(height-1, pos*2+1, vTxid); + else + right = left; + // combine subhashes + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch) { + // determine whether this node is the parent of at least one matched txid + bool fParentOfMatch = false; + for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++) + fParentOfMatch |= vMatch[p]; + // store as flag bit + vBits.push_back(fParentOfMatch); + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, store hash and stop + vHash.push_back(CalcHash(height, pos, vTxid)); + } else { + // otherwise, don't store any hash, but descend into the subtrees + TraverseAndBuild(height-1, pos*2, vTxid, vMatch); + if (pos*2+1 < CalcTreeWidth(height-1)) + TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch); + } +} + +uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch, std::vector &vnIndex) { + if (nBitsUsed >= vBits.size()) { + // overflowed the bits array - failure + fBad = true; + return uint256(); + } + bool fParentOfMatch = vBits[nBitsUsed++]; + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, use stored hash and do not descend + if (nHashUsed >= vHash.size()) { + // overflowed the hash array - failure + fBad = true; + return uint256(); + } + const uint256 &hash = vHash[nHashUsed++]; + if (height==0 && fParentOfMatch) { // in case of height 0, we have a matched txid + vMatch.push_back(hash); + vnIndex.push_back(pos); + } + return hash; + } else { + // otherwise, descend into the subtrees to extract matched txids and hashes + uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch, vnIndex), right; + if (pos*2+1 < CalcTreeWidth(height-1)) { + right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch, vnIndex); + if (right == left) { + // The left and right branches should never be identical, as the transaction + // hashes covered by them must each be unique. + fBad = true; + } + } else { + right = left; + } + // and combine them before returning + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +CPartialMerkleTree::CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch) : nTransactions(vTxid.size()), fBad(false) { + // reset state + vBits.clear(); + vHash.clear(); + + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + + // traverse the partial tree + TraverseAndBuild(nHeight, 0, vTxid, vMatch); +} + +CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} + +uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch, std::vector &vnIndex) { + vMatch.clear(); + // An empty set will not work + if (nTransactions == 0) + return uint256(); + // check for excessively high numbers of transactions + if (nTransactions > MAX_BLOCK_WEIGHT / MIN_TRANSACTION_WEIGHT) + return uint256(); + // there can never be more hashes provided than one for every txid + if (vHash.size() > nTransactions) + return uint256(); + // there must be at least one bit per node in the partial tree, and at least one node per hash + if (vBits.size() < vHash.size()) + return uint256(); + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + // traverse the partial tree + unsigned int nBitsUsed = 0, nHashUsed = 0; + uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch, vnIndex); + // verify that no problems occurred during the tree traversal + if (fBad) + return uint256(); + // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) + if ((nBitsUsed+7)/8 != (vBits.size()+7)/8) + return uint256(); + // verify that all hashes were consumed + if (nHashUsed != vHash.size()) + return uint256(); + return hashMerkleRoot; +} + +} // namespace Bitcoin +} // namespace Sidechain diff --git a/src/primitives/bitcoin/merkleblock.h b/src/primitives/bitcoin/merkleblock.h new file mode 100644 index 0000000000..87de695df5 --- /dev/null +++ b/src/primitives/bitcoin/merkleblock.h @@ -0,0 +1,179 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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_PRIMITIVES_BITCOIN_MERKLEBLOCK_H +#define BITCOIN_PRIMITIVES_BITCOIN_MERKLEBLOCK_H + +#include +#include +#include +#include + +#include + +namespace Sidechain { +namespace Bitcoin { + +/** Data structure that represents a partial merkle tree. + * + * It represents a subset of the txid's of a known block, in a way that + * allows recovery of the list of txid's and the merkle root, in an + * authenticated way. + * + * The encoding works as follows: we traverse the tree in depth-first order, + * storing a bit for each traversed node, signifying whether the node is the + * parent of at least one matched leaf txid (or a matched txid itself). In + * case we are at the leaf level, or this bit is 0, its merkle node hash is + * stored, and its children are not explored further. Otherwise, no hash is + * stored, but we recurse into both (or the only) child branch. During + * decoding, the same depth-first traversal is performed, consuming bits and + * hashes as they written during encoding. + * + * The serialization is fixed and provides a hard guarantee about the + * encoded size: + * + * SIZE <= 10 + ceil(32.25*N) + * + * Where N represents the number of leaf nodes of the partial tree. N itself + * is bounded by: + * + * N <= total_transactions + * N <= 1 + matched_transactions*tree_height + * + * The serialization format: + * - uint32 total_transactions (4 bytes) + * - varint number of hashes (1-3 bytes) + * - uint256[] hashes in depth-first order (<= 32*N bytes) + * - varint number of bytes of flag bits (1-3 bytes) + * - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits) + * The size constraints follow from this. + */ +class CPartialMerkleTree +{ +protected: + /** the total number of transactions in the block */ + unsigned int nTransactions; + + /** node-is-parent-of-matched-txid bits */ + std::vector vBits; + + /** txids and internal hashes */ + std::vector vHash; + + /** flag set when encountering invalid data */ + bool fBad; + + /** helper function to efficiently calculate the number of nodes at given height in the merkle tree */ + unsigned int CalcTreeWidth(int height) const { + return (nTransactions+(1 << height)-1) >> height; + } + + /** calculate the hash of a node in the merkle tree (at leaf level: the txid's themselves) */ + uint256 CalcHash(int height, unsigned int pos, const std::vector &vTxid); + + /** recursive function that traverses tree nodes, storing the data as bits and hashes */ + void TraverseAndBuild(int height, unsigned int pos, const std::vector &vTxid, const std::vector &vMatch); + + /** + * recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild. + * it returns the hash of the respective node and its respective index. + */ + uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector &vMatch, std::vector &vnIndex); + +public: + + /** serialization implementation */ + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(nTransactions); + READWRITE(vHash); + std::vector vBytes; + if (ser_action.ForRead()) { + READWRITE(vBytes); + CPartialMerkleTree &us = *(const_cast(this)); + us.vBits.resize(vBytes.size() * 8); + for (unsigned int p = 0; p < us.vBits.size(); p++) + us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0; + us.fBad = false; + } else { + vBytes.resize((vBits.size()+7)/8); + for (unsigned int p = 0; p < vBits.size(); p++) + vBytes[p / 8] |= vBits[p] << (p % 8); + READWRITE(vBytes); + } + } + + /** Construct a partial merkle tree from a list of transaction ids, and a mask that selects a subset of them */ + CPartialMerkleTree(const std::vector &vTxid, const std::vector &vMatch); + + CPartialMerkleTree(); + + /** + * extract the matching txid's represented by this partial merkle tree + * and their respective indices within the partial tree. + * returns the merkle root, or 0 in case of failure + */ + uint256 ExtractMatches(std::vector &vMatch, std::vector &vnIndex); + + /** Get number of transactions the merkle proof is indicating for cross-reference with + * local blockchain knowledge. + */ + unsigned int GetNumTransactions() const { return nTransactions; }; + +}; + + +/** + * Used to relay blocks as header + vector + * to filtered nodes. + * + * NOTE: The class assumes that the given CBlock has *at least* 1 transaction. If the CBlock has 0 txs, it will hit an assertion. + */ +class CMerkleBlock +{ +public: + /** Public only for unit testing */ + CBlockHeader header; + CPartialMerkleTree txn; + + /** + * Public only for unit testing and relay testing (not relayed). + * + * Used only when a bloom filter is specified to allow + * testing the transactions which matched the bloom filter. + */ + std::vector > vMatchedTxn; + + /** + * Create from a CBlock, filtering transactions according to filter + * Note that this will call IsRelevantAndUpdate on the filter for each transaction, + * thus the filter will likely be modified. + */ + CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { } + + // Create from a CBlock, matching the txids in the set + CMerkleBlock(const CBlock& block, const std::set& txids) : CMerkleBlock(block, nullptr, &txids) { } + + CMerkleBlock() {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(header); + READWRITE(txn); + } + +private: + // Combined constructor to consolidate code + CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set* txids); +}; + +} // namespace Bitcoin +} // namespace Sidechain + +#endif // BITCOIN_PRIMITIVES_BITCOIN_MERKLEBLOCK_H diff --git a/src/primitives/bitcoin/transaction.cpp b/src/primitives/bitcoin/transaction.cpp new file mode 100644 index 0000000000..e60b8c305a --- /dev/null +++ b/src/primitives/bitcoin/transaction.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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 + +#include +#include +#include + +namespace Sidechain { +namespace Bitcoin { + +std::string COutPoint::ToString() const +{ + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); +} + +CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn) +{ + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; +} + +CTxIn::CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn) +{ + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; +} + +std::string CTxIn::ToString() const +{ + std::string str; + str += "CTxIn("; + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig)); + else + str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24)); + if (nSequence != SEQUENCE_FINAL) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; +} + +CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) +{ + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; +} + +std::string CTxOut::ToString() const +{ + return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); +} + +CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {} + +uint256 CMutableTransaction::GetHash() const +{ + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); +} + +uint256 CTransaction::ComputeHash() const +{ + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); +} + +uint256 CTransaction::ComputeWitnessHash() const +{ + if (!HasWitness()) { + return hash; + } + return SerializeHash(*this, SER_GETHASH, 0); +} + +/* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ +CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{} {} +CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} + +CAmount CTransaction::GetValueOut() const +{ + CAmount nValueOut = 0; + for (const auto& tx_out : vout) { + nValueOut += tx_out.nValue; + if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nValueOut; +} + +unsigned int CTransaction::GetTotalSize() const +{ + return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); +} + +std::string CTransaction::ToString() const +{ + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + GetHash().ToString().substr(0,10), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (const auto& tx_in : vin) + str += " " + tx_in.ToString() + "\n"; + for (const auto& tx_in : vin) + str += " " + tx_in.scriptWitness.ToString() + "\n"; + for (const auto& tx_out : vout) + str += " " + tx_out.ToString() + "\n"; + return str; +} + +} // namespace Bitcoin +} // namespace Sidechain diff --git a/src/primitives/bitcoin/transaction.h b/src/primitives/bitcoin/transaction.h new file mode 100644 index 0000000000..e71d442b6a --- /dev/null +++ b/src/primitives/bitcoin/transaction.h @@ -0,0 +1,412 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 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_PRIMITIVES_BITCOIN_TRANSACTION_H +#define BITCOIN_PRIMITIVES_BITCOIN_TRANSACTION_H + +#include +#include +#include