From f39435a877ca8468c518196fc331bc72c5cf436e Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Tue, 28 Feb 2017 16:36:56 -0800 Subject: [PATCH] 2WP: Separate mainchain and sidechain data structures into different classes, with the bitcoin data structures living in the src/primitives/bitcoin subdirectory. --- src/Makefile.am | 7 + src/bitcoin-tx.cpp | 9 +- src/pow.cpp | 7 +- src/pow.h | 3 +- src/primitives/bitcoin/block.cpp | 52 +++ src/primitives/bitcoin/block.h | 170 +++++++++ src/primitives/bitcoin/merkleblock.cpp | 196 ++++++++++ src/primitives/bitcoin/merkleblock.h | 171 +++++++++ src/primitives/bitcoin/transaction.cpp | 165 +++++++++ src/primitives/bitcoin/transaction.h | 475 +++++++++++++++++++++++++ src/primitives/block.cpp | 6 - src/primitives/block.h | 55 +-- src/primitives/transaction.cpp | 5 - src/primitives/transaction.h | 91 ++--- src/script/interpreter.cpp | 12 +- src/script/script.cpp | 5 +- src/wallet/rpcwallet.cpp | 11 +- 17 files changed, 1297 insertions(+), 143 deletions(-) create mode 100644 src/primitives/bitcoin/block.cpp create mode 100644 src/primitives/bitcoin/block.h create mode 100644 src/primitives/bitcoin/merkleblock.cpp create mode 100644 src/primitives/bitcoin/merkleblock.h create mode 100644 src/primitives/bitcoin/transaction.cpp create mode 100644 src/primitives/bitcoin/transaction.h diff --git a/src/Makefile.am b/src/Makefile.am index 12f1c5264c..f471db2d4c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -266,6 +266,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 \ rpc/protocol.cpp \ @@ -444,6 +450,7 @@ CLEANFILES += consensus/*.gcda consensus/*.gcno CLEANFILES += crypto/*.gcda crypto/*.gcno CLEANFILES += policy/*.gcda policy/*.gcno CLEANFILES += primitives/*.gcda primitives/*.gcno +CLEANFILES += primitives/bitcoin/*.gcda primitives/bitcoin/*.gcno CLEANFILES += script/*.gcda script/*.gcno CLEANFILES += support/*.gcda support/*.gcno CLEANFILES += univalue/*.gcda univalue/*.gcno diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 7c8a7d5a30..1cc047e5ca 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -17,6 +17,7 @@ #include "policy/policy.h" #include "pow.h" #include "primitives/transaction.h" +#include "primitives/bitcoin/merkleblock.h" #include "script/script.h" #include "script/sign.h" #include "streams.h" @@ -610,12 +611,12 @@ static void MutateTxPeginSign(CMutableTransaction& tx, const string& flagStr) if (contractData.size() != 40) throw runtime_error("contract must be 40 bytes"); - CDataStream ssProof(txoutproofData,SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); - CMerkleBlock merkleBlock; + CDataStream ssProof(txoutproofData,SER_NETWORK, PROTOCOL_VERSION); + Sidechain::Bitcoin::CMerkleBlock merkleBlock; ssProof >> merkleBlock; - CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); - CTransaction txBTC; + CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION); + Sidechain::Bitcoin::CTransaction txBTC; ssTx >> txBTC; vector transactionHashes; diff --git a/src/pow.cpp b/src/pow.cpp index 140718d58d..48367b8bc9 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -12,6 +12,7 @@ #include "hash.h" #include "keystore.h" #include "primitives/block.h" +#include "primitives/bitcoin/block.h" #include "script/generic.hpp" #include "script/standard.h" #include "uint256.h" @@ -37,15 +38,13 @@ void ResetChallenge(CBlockHeader& block, const CBlockIndex& indexLast, const Con block.proof.challenge = indexLast.proof.challenge; } -bool CheckBitcoinProof(const CBlockHeader& block) +bool CheckBitcoinProof(const Sidechain::Bitcoin::CBlockHeader& block) { - assert(block.IsBitcoinBlock()); - bool fNegative; bool fOverflow; arith_uint256 bnTarget; - bnTarget.SetCompact(block.bitcoinproof.challenge, &fNegative, &fOverflow); + bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow); // Check range if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(Params().GetConsensus().parentChainPowLimit)) diff --git a/src/pow.h b/src/pow.h index a786a68791..892b9f0fad 100644 --- a/src/pow.h +++ b/src/pow.h @@ -7,6 +7,7 @@ #define BITCOIN_POW_H #include "consensus/params.h" +#include "primitives/bitcoin/block.h" #include #include @@ -20,7 +21,7 @@ class uint256; /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ -bool CheckBitcoinProof(const CBlockHeader& block); +bool CheckBitcoinProof(const Sidechain::Bitcoin::CBlockHeader& block); bool CheckProof(const CBlockHeader& block, const Consensus::Params&); /** Scans nonces looking for a hash with at least some zero bits */ bool MaybeGenerateProof(CBlockHeader* pblock, CWallet* pwallet); diff --git a/src/primitives/bitcoin/block.cpp b/src/primitives/bitcoin/block.cpp new file mode 100644 index 0000000000..4b5d25477b --- /dev/null +++ b/src/primitives/bitcoin/block.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/primitives/block.cpp +// with minimal modification. + +#include "primitives/bitcoin/block.h" + +#include "hash.h" +#include "tinyformat.h" +#include "utilstrencodings.h" +#include "crypto/common.h" + +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 (unsigned int i = 0; i < vtx.size(); i++) + { + s << " " << vtx[i].ToString() << "\n"; + } + return s.str(); +} + +int64_t GetBlockWeight(const CBlock& block) +{ + // This implements the weight = (stripped_size * 4) + witness_size formula, + // using only serialization with and without witness data. As witness_size + // is equal to total_size - stripped_size, this formula is identical to: + // weight = (stripped_size * 3) + total_size. + return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); +} + +} // Bitcoin +} // Sidechain diff --git a/src/primitives/bitcoin/block.h b/src/primitives/bitcoin/block.h new file mode 100644 index 0000000000..3a2d97e314 --- /dev/null +++ b/src/primitives/bitcoin/block.h @@ -0,0 +1,170 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/primitives/block.h +// with minimal modification. + +#ifndef BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H +#define BITCOIN_PRIMITIVES_BITCOIN_BLOCK_H + +#include "primitives/bitcoin/transaction.h" +#include "serialize.h" +#include "uint256.h" + +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, int nType, int nVersion) { + 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(); + *((CBlockHeader*)this) = header; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*(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() {} + + CBlockLocator(const std::vector& vHaveIn) + { + vHave = vHaveIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + } + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() const + { + return vHave.empty(); + } +}; + +/** Compute the consensus-critical block weight (see BIP 141). */ +int64_t GetBlockWeight(const CBlock& tx); + +} // 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..0633dc9048 --- /dev/null +++ b/src/primitives/bitcoin/merkleblock.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/merkleblock.cpp +// with minimal modification. + +#include "primitives/bitcoin/merkleblock.h" + +#include "hash.h" +#include "consensus/consensus.h" +#include "utilstrencodings.h" + +using namespace std; + +namespace Sidechain { +namespace Bitcoin { + +// Bloom filter support is not required for the sidechain peg. +#if 0 +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) +{ + header = block.GetBlockHeader(); + + vector vMatch; + 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 (filter.IsRelevantAndUpdate(block.vtx[i])) + { + vMatch.push_back(true); + vMatchedTxn.push_back(make_pair(i, hash)); + } + else + vMatch.push_back(false); + vHashes.push_back(hash); + } + + txn = CPartialMerkleTree(vHashes, vMatch); +} +#endif // 0 + +CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) +{ + header = block.GetBlockHeader(); + + vector vMatch; + 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.count(hash)) + vMatch.push_back(true); + 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) { + 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 otherwise1 + 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_BASE_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction + 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; +} + +} // Bitcoin +} // Sidechain diff --git a/src/primitives/bitcoin/merkleblock.h b/src/primitives/bitcoin/merkleblock.h new file mode 100644 index 0000000000..9d5b067eb9 --- /dev/null +++ b/src/primitives/bitcoin/merkleblock.h @@ -0,0 +1,171 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/merkleblock.h +// with minimal modification. + +#ifndef BITCOIN_PRIMITIVES_BITCOIN_MERKLEBLOCK_H +#define BITCOIN_PRIMITIVES_BITCOIN_MERKLEBLOCK_H + +#include "serialize.h" +#include "uint256.h" +#include "primitives/bitcoin/block.h" +#include "bloom.h" + +#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 explorer 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) { + 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, int nType, int nVersion) { + 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); +}; + + +/** + * Used to relay blocks as header + vector + * to filtered nodes. + */ +class CMerkleBlock +{ +public: + /** Public only for unit testing */ + CBlockHeader header; + CPartialMerkleTree txn; + +public: + /** Public only for unit testing and relay testing (not relayed) */ + std::vector > vMatchedTxn; + +// Bloom filter support is not required for the sidechain peg. Enabling the +// following API would require modifying CBloomFilter as well. +#if 0 + /** + * 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); +#endif // 0 + + // Create from a CBlock, matching the txids in the set + CMerkleBlock(const CBlock& block, const std::set& txids); + + CMerkleBlock() {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(header); + READWRITE(txn); + } +}; + +} // Bitcoin +} // 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..3a90213581 --- /dev/null +++ b/src/primitives/bitcoin/transaction.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/primitives/transaction.cpp +// with minimal modification. + +#include "primitives/bitcoin/transaction.h" + +#include "hash.h" +#include "tinyformat.h" +#include "utilstrencodings.h" + +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; +} + +uint256 CTxOut::GetHash() const +{ + return SerializeHash(*this); +} + +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) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {} + +uint256 CMutableTransaction::GetHash() const +{ + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); +} + +void CTransaction::UpdateHash() const +{ + *const_cast(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); +} + +uint256 CTransaction::GetWitnessHash() const +{ + return SerializeHash(*this, SER_GETHASH, 0); +} + +CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } + +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) { + UpdateHash(); +} + +CTransaction& CTransaction::operator=(const CTransaction &tx) { + *const_cast(&nVersion) = tx.nVersion; + *const_cast*>(&vin) = tx.vin; + *const_cast*>(&vout) = tx.vout; + *const_cast(&wit) = tx.wit; + *const_cast(&nLockTime) = tx.nLockTime; + *const_cast(&hash) = tx.hash; + return *this; +} + +CAmount CTransaction::GetValueOut() const +{ + CAmount nValueOut = 0; + for (std::vector::const_iterator it(vout.begin()); it != vout.end(); ++it) + { + nValueOut += it->nValue; + if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error(std::string(__func__) + ": value out of range"); + } + return nValueOut; +} + +double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const +{ + nTxSize = CalculateModifiedSize(nTxSize); + if (nTxSize == 0) return 0.0; + + return dPriorityInputs / nTxSize; +} + +unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const +{ + // In order to avoid disincentivizing cleaning up the UTXO set we don't count + // the constant overhead for each txin and up to 110 bytes of scriptSig (which + // is enough to cover a compressed pubkey p2sh redemption) for priority. + // Providing any more cleanup incentive than making additional inputs free would + // risk encouraging people to create junk outputs to redeem later. + if (nTxSize == 0) + nTxSize = (GetTransactionWeight(*this) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR; + for (std::vector::const_iterator it(vin.begin()); it != vin.end(); ++it) + { + unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size()); + if (nTxSize > offset) + nTxSize -= offset; + } + return nTxSize; +} + +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 (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < wit.vtxinwit.size(); i++) + str += " " + wit.vtxinwit[i].scriptWitness.ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; +} + +int64_t GetTransactionWeight(const CTransaction& tx) +{ + return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); +} + +} // namespace Bitcoin +} // namespace Sidechain diff --git a/src/primitives/bitcoin/transaction.h b/src/primitives/bitcoin/transaction.h new file mode 100644 index 0000000000..d9c4040b33 --- /dev/null +++ b/src/primitives/bitcoin/transaction.h @@ -0,0 +1,475 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Taken from +// //github.com/bitcoin/bitcoin/blob/0d719145b018e28d48d35c2646a5962b87c60436/src/primitives/transaction.h +// with minimal modification. + +#ifndef BITCOIN_PRIMITIVES_BITCOIN_TRANSACTION_H +#define BITCOIN_PRIMITIVES_BITCOIN_TRANSACTION_H + +#include "amount.h" +#include "script/script.h" +#include "serialize.h" +#include "uint256.h" + +namespace Sidechain { +namespace Bitcoin { + +static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; + +static const int WITNESS_SCALE_FACTOR = 4; + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + uint32_t n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(hash); + READWRITE(n); + } + + void SetNull() { hash.SetNull(); n = (uint32_t) -1; } + bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + int cmp = a.hash.Compare(b.hash); + return cmp < 0 || (cmp == 0 && a.n < b.n); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + uint32_t nSequence; + + /* Setting nSequence to this value for every input in a transaction + * disables nLockTime. */ + static const uint32_t SEQUENCE_FINAL = 0xffffffff; + + /* Below flags apply in the context of BIP 68*/ + /* If this flag set, CTxIn::nSequence is NOT interpreted as a + * relative lock-time. */ + static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31); + + /* If CTxIn::nSequence encodes a relative lock-time and this flag + * is set, the relative lock-time has units of 512 seconds, + * otherwise it specifies blocks with a granularity of 1. */ + static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); + + /* If CTxIn::nSequence encodes a relative lock-time, this mask is + * applied to extract that lock-time from the sequence field. */ + static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; + + /* In order to use the same number of bits to encode roughly the + * same wall-clock duration, and because blocks are naturally + * limited to occur every 600s on average, the minimum granularity + * for time-based relative lock-time is fixed at 512 seconds. + * Converting from CTxIn::nSequence to seconds is performed by + * multiplying by 512 = 2^9, or equivalently shifting up by + * 9 bits. */ + static const int SEQUENCE_LOCKTIME_GRANULARITY = 9; + + CTxIn() + { + nSequence = SEQUENCE_FINAL; + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); + CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(prevout); + READWRITE(*(CScriptBase*)(&scriptSig)); + READWRITE(nSequence); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + CAmount nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(nValue); + READWRITE(*(CScriptBase*)(&scriptPubKey)); + } + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + uint256 GetHash() const; + + CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const + { + // "Dust" is defined in terms of CTransaction::minRelayTxFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical spendable non-segwit txout is 34 bytes big, and will + // need a CTxIn of at least 148 bytes to spend: + // so dust is a spendable txout less than + // 546*minRelayTxFee/1000 (in satoshis). + // A typical spendable segwit txout is 31 bytes big, and will + // need a CTxIn of at least 67 bytes to spend: + // so dust is a spendable txout less than + // 294*minRelayTxFee/1000 (in satoshis). + if (scriptPubKey.IsUnspendable()) + return 0; + + size_t nSize = GetSerializeSize(SER_DISK, 0); + int witnessversion = 0; + std::vector witnessprogram; + + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + // sum the sizes of the parts of a transaction input + // with 75% segwit discount applied to the script size. + nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); + } else { + nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above + } + + return 3 * minRelayTxFee.GetFee(nSize); + } + + bool IsDust(const CFeeRate &minRelayTxFee) const + { + return (nValue < GetDustThreshold(minRelayTxFee)); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToString() const; +}; + +class CTxInWitness +{ +public: + CScriptWitness scriptWitness; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(scriptWitness.stack); + } + + bool IsNull() const { return scriptWitness.IsNull(); } + + CTxInWitness() { } +}; + +class CTxWitness +{ +public: + /** In case vtxinwit is missing, all entries are treated as if they were empty CTxInWitnesses */ + std::vector vtxinwit; + + ADD_SERIALIZE_METHODS; + + bool IsEmpty() const { return vtxinwit.empty(); } + + bool IsNull() const + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + if (!vtxinwit[n].IsNull()) { + return false; + } + } + return true; + } + + void SetNull() + { + vtxinwit.clear(); + } + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + READWRITE(vtxinwit[n]); + } + if (IsNull()) { + /* It's illegal to encode a witness when all vtxinwit entries are empty. */ + throw std::ios_base::failure("Superfluous witness record"); + } + } +}; + +struct CMutableTransaction; + +/** + * Basic transaction serialization format: + * - int32_t nVersion + * - std::vector vin + * - std::vector vout + * - uint32_t nLockTime + * + * Extended transaction serialization format: + * - int32_t nVersion + * - unsigned char dummy = 0x00 + * - unsigned char flags (!= 0) + * - std::vector vin + * - std::vector vout + * - if (flags & 1): + * - CTxWitness wit; + * - uint32_t nLockTime + */ +template +inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*const_cast(&tx.nVersion)); + unsigned char flags = 0; + if (ser_action.ForRead()) { + const_cast*>(&tx.vin)->clear(); + const_cast*>(&tx.vout)->clear(); + const_cast(&tx.wit)->SetNull(); + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + READWRITE(*const_cast*>(&tx.vin)); + if (tx.vin.size() == 0 && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* We read a dummy or an empty vin. */ + READWRITE(flags); + if (flags != 0) { + READWRITE(*const_cast*>(&tx.vin)); + READWRITE(*const_cast*>(&tx.vout)); + } + } else { + /* We read a non-empty vin. Assume a normal vout follows. */ + READWRITE(*const_cast*>(&tx.vout)); + } + if ((flags & 1) && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* The witness flag is present, and we support witnesses. */ + flags ^= 1; + const_cast(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + if (flags) { + /* Unknown flag in the serialization */ + throw std::ios_base::failure("Unknown transaction optional data"); + } + } else { + // Consistency check + assert(tx.wit.vtxinwit.size() <= tx.vin.size()); + if (!(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) { + /* Check whether witnesses need to be serialized. */ + if (!tx.wit.IsNull()) { + flags |= 1; + } + } + if (flags) { + /* Use extended format in case witnesses are to be serialized. */ + std::vector vinDummy; + READWRITE(vinDummy); + READWRITE(flags); + } + READWRITE(*const_cast*>(&tx.vin)); + READWRITE(*const_cast*>(&tx.vout)); + if (flags & 1) { + const_cast(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + } + READWRITE(*const_cast(&tx.nLockTime)); +} + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +private: + /** Memory only. */ + const uint256 hash; + +public: + // Default transaction version. + static const int32_t CURRENT_VERSION=1; + + // Changing the default transaction version requires a two step process: first + // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date + // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and + // MAX_STANDARD_VERSION will be equal. + static const int32_t MAX_STANDARD_VERSION=2; + + // The local variables are made const to prevent unintended modification + // without updating the cached hash value. However, CTransaction is not + // actually immutable; deserialization and assignment are implemented, + // and bypass the constness. This is safe, as they update the entire + // structure, including the hash. + const int32_t nVersion; + const std::vector vin; + const std::vector vout; + CTxWitness wit; // Not const: can change without invalidating the txid cache + const uint32_t nLockTime; + + /** Construct a CTransaction that qualifies as IsNull() */ + CTransaction(); + + /** Convert a CMutableTransaction into a CTransaction. */ + CTransaction(const CMutableTransaction &tx); + + CTransaction& operator=(const CTransaction& tx); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + Sidechain::Bitcoin::SerializeTransaction(*this, s, ser_action, nType, nVersion); + if (ser_action.ForRead()) { + UpdateHash(); + } + } + + bool IsNull() const { + return vin.empty() && vout.empty(); + } + + const uint256& GetHash() const { + return hash; + } + + // Compute a hash that includes both transaction and witness data + uint256 GetWitnessHash() const; + + // Return sum of txouts. + CAmount GetValueOut() const; + // GetValueIn() is a method on CCoinsViewCache, because + // inputs must be known to compute value in. + + // Compute priority, given priority of inputs and (optionally) tx size + double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; + + // Compute modified tx size for priority calculation (optionally given tx size) + unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return a.hash == b.hash; + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return a.hash != b.hash; + } + + std::string ToString() const; + + void UpdateHash() const; +}; + +/** A mutable version of CTransaction. */ +struct CMutableTransaction +{ + int32_t nVersion; + std::vector vin; + std::vector vout; + CTxWitness wit; + uint32_t nLockTime; + + CMutableTransaction(); + CMutableTransaction(const CTransaction& tx); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + Sidechain::Bitcoin::SerializeTransaction(*this, s, ser_action, nType, nVersion); + } + + /** Compute the hash of this CMutableTransaction. This is computed on the + * fly, as opposed to GetHash() in CTransaction, which uses a cached result. + */ + uint256 GetHash() const; +}; + +/** Compute the weight of a transaction, as defined by BIP 141 */ +int64_t GetTransactionWeight(const CTransaction &tx); + +} // Bitcoin +} // Sidechain + +#endif // BITCOIN_PRIMITIVES_BITCOIN_TRANSACTION_H diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index c6944cb986..cf01930f33 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -11,12 +11,6 @@ #include "crypto/common.h" #include "core_io.h" -std::string CBitcoinProof::ToString() const -{ - return strprintf("CBitcoinProof(challenge=%08x, solution=%u)", - challenge, solution); -} - std::string CProof::ToString() const { return strprintf("CProof(challenge=%s, solution=%s)", diff --git a/src/primitives/block.h b/src/primitives/block.h index 9e95fe781c..5c5fcf52ac 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -11,43 +11,6 @@ #include "serialize.h" #include "uint256.h" -class CBitcoinProof -{ -public: - uint32_t challenge; - uint32_t solution; - - CBitcoinProof() - { - SetNull(); - } - CBitcoinProof(uint32_t challengeIn, uint32_t solutionIn) : - challenge(challengeIn), solution(solutionIn) {} - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) - { - READWRITE(challenge); - READWRITE(solution); - } - - void SetNull() - { - challenge = 0; - solution = 0; - } - - bool IsNull() const - { - return (challenge == 0); - } - - std::string ToString() const; -}; - - class CProof { public: @@ -100,7 +63,6 @@ public: uint256 hashMerkleRoot; uint32_t nTime; uint32_t nHeight; - CBitcoinProof bitcoinproof; CProof proof; CBlockHeader() @@ -116,12 +78,8 @@ public: READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); READWRITE(nTime); - if (IsBitcoinBlock() || (nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX)) - READWRITE(bitcoinproof); - else { - READWRITE(nHeight); - READWRITE(proof); - } + READWRITE(nHeight); + READWRITE(proof); } void SetNull() @@ -131,13 +89,12 @@ public: hashMerkleRoot.SetNull(); nTime = 0; nHeight = 0; - bitcoinproof.SetNull(); proof.SetNull(); } bool IsNull() const { - return proof.IsNull() && bitcoinproof.IsNull(); + return proof.IsNull(); } uint256 GetHash() const; @@ -146,11 +103,6 @@ public: { return (int64_t)nTime; } - - bool IsBitcoinBlock() const - { - return !bitcoinproof.IsNull(); - } }; @@ -197,7 +149,6 @@ public: block.hashMerkleRoot = hashMerkleRoot; block.nTime = nTime; block.nHeight = nHeight; - block.bitcoinproof = bitcoinproof; block.proof = proof; return block; } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 8b37b6c90a..dc29360490 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -72,11 +72,6 @@ CAmount CTxOutValue::GetAmount() const return ReadBE64(&vchCommitment[1]); } -void CTxOutValue::SetToBitcoinAmount(const CAmount nAmount) { - SetToAmount(nAmount); - vchCommitment[0] = 0; -} - void CTxOutValue::SetToAmount(const CAmount nAmount) { vchCommitment.resize(nExplicitSize); vchCommitment[0] = 1; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 18398a6e97..20fbf9752d 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -12,7 +12,6 @@ #include "uint256.h" static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; -static const int SERIALIZE_BITCOIN_BLOCK_OR_TX = 0x20000000; static const int WITNESS_SCALE_FACTOR = 4; @@ -41,17 +40,9 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if ((nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX) || IsInBitcoinTransaction()) { - if (ser_action.ForRead()) { - vchAssetTag.resize(1); - vchAssetTag[0] = 0; - vchSurjectionproof.clear(); - } - } else { - vchAssetTag.resize(nAssetTagSize); - READWRITE(REF(CFlatData(&vchAssetTag[0], &vchAssetTag[nAssetTagSize]))); - // The surjection proof is serialized as part of the witness data - } + vchAssetTag.resize(nAssetTagSize); + READWRITE(REF(CFlatData(&vchAssetTag[0], &vchAssetTag[nAssetTagSize]))); + // The surjection proof is serialized as part of the witness data } bool IsNull() const @@ -115,38 +106,29 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if ((nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX) || IsInBitcoinTransaction()) { - CAmount nAmount = 0; - if (!ser_action.ForRead()) - nAmount = GetAmount(); - READWRITE(nAmount); - if (ser_action.ForRead()) - SetToBitcoinAmount(nAmount); - } else { - // We only serialize the value commitment here. - // The ECDH key and range proof are serialized through CTxOutWitnessSerializer. - READWRITE(vchCommitment.front()); - if (ser_action.ForRead()) { - switch (vchCommitment.front()) { - case 0: - case 1: - vchCommitment.resize(nExplicitSize); - break; - // Alpha used 2 and 3 for value commitments - case 2: - case 3: - break; - case 8: - case 9: - vchCommitment.resize(nCommittedSize); - break; - default: - vchCommitment.resize(1); - return; - } + // We only serialize the value commitment here. + // The ECDH key and range proof are serialized through CTxOutWitnessSerializer. + READWRITE(vchCommitment.front()); + if (ser_action.ForRead()) { + switch (vchCommitment.front()) { + case 0: + case 1: + vchCommitment.resize(nExplicitSize); + break; + // Alpha used 2 and 3 for value commitments + case 2: + case 3: + break; + case 8: + case 9: + vchCommitment.resize(nCommittedSize); + break; + default: + vchCommitment.resize(1); + return; } - READWRITE(REF(CFlatData(&vchCommitment[1], &vchCommitment[vchCommitment.size()]))); } + READWRITE(REF(CFlatData(&vchCommitment[1], &vchCommitment[vchCommitment.size()]))); } void SetNull(); @@ -170,9 +152,7 @@ public: return !(a == b); } -private: // "Bitcoin amounts" can only be set by deserializing with SERIALIZE_BITCOIN_BLOCK_OR_TX - void SetToBitcoinAmount(const CAmount nAmount); - bool IsInBitcoinTransaction() const { return vchCommitment[0] == 0; } +private: void SetToAmount(const CAmount nAmount); }; @@ -544,11 +524,9 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if (!(nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX)) { - READWRITE(ref.nAsset.vchSurjectionproof); - READWRITE(ref.nValue.vchRangeproof); - READWRITE(ref.nValue.vchNonceCommitment); - } + READWRITE(ref.nAsset.vchSurjectionproof); + READWRITE(ref.nValue.vchRangeproof); + READWRITE(ref.nValue.vchNonceCommitment); } void SetNull() { @@ -640,7 +618,6 @@ static const CAmount TX_FEE_BITCOIN_TX_FLAG = -42; template inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) { const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS); - const bool fIsBitcoinTx = (nVersion & SERIALIZE_BITCOIN_BLOCK_OR_TX); READWRITE(*const_cast(&tx.nVersion)); unsigned char flags = 0; if (ser_action.ForRead()) { @@ -666,7 +643,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in const_cast(&tx.wit)->vtxinwit.resize(tx.vin.size()); READWRITE(tx.wit); } - if ((flags & 2) && fAllowWitness && !fIsBitcoinTx) { + if ((flags & 2) && fAllowWitness) { /* The witness output flag is present, and we support witnesses. */ flags ^= 2; bool fHadOutputWitness = false; @@ -693,12 +670,10 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in if (!tx.wit.IsNull()) { flags |= 1; } - if (!fIsBitcoinTx) { - for (size_t i = 0; i < tx.vout.size(); i++) { - if (!CTxOutWitnessSerializer(*const_cast(&tx.vout[i])).IsNull()) { - flags |= 2; - break; - } + for (size_t i = 0; i < tx.vout.size(); i++) { + if (!CTxOutWitnessSerializer(*const_cast(&tx.vout[i])).IsNull()) { + flags |= 2; + break; } } } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index fe22ed29a7..3794301f9d 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -8,6 +8,7 @@ #include #include "primitives/transaction.h" +#include "primitives/bitcoin/merkleblock.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" @@ -1476,8 +1477,8 @@ bool EvalScript(vector >& stack, const CScript& script, un uint256 genesishash(vgenesisHash); try { - CMerkleBlock merkleBlock; - CDataStream merkleBlockStream(vmerkleBlock, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); + Sidechain::Bitcoin::CMerkleBlock merkleBlock; + CDataStream merkleBlockStream(vmerkleBlock, SER_NETWORK, PROTOCOL_VERSION); merkleBlockStream >> merkleBlock; if (!merkleBlockStream.empty() || !CheckBitcoinProof(merkleBlock.header)) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCK); @@ -1493,8 +1494,8 @@ bool EvalScript(vector >& stack, const CScript& script, un if (merkleBlock.header.GetHash() == genesishash) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCK); - CTransaction locktx; - CDataStream locktxStream(vlockTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); + Sidechain::Bitcoin::CTransaction locktx; + CDataStream locktxStream(vlockTx, SER_NETWORK, PROTOCOL_VERSION); locktxStream >> locktx; if (!locktxStream.empty()) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX); @@ -1548,8 +1549,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // We check values by doing the following: // * Tx must relock at least - // * Tx must send at least the withdraw value to its P2SH withdraw, but may send more - assert(locktx.vout[nlocktxOut].nValue.IsAmount()); // Its a SERIALIZE_BITCOIN_BLOCK_OR_TX - CAmount withdrawVal = locktx.vout[nlocktxOut].nValue.GetAmount(); + CAmount withdrawVal = locktx.vout[nlocktxOut].nValue; if (!checker.GetValueIn().IsAmount()) // Heh, you just destroyed coins return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); diff --git a/src/script/script.cpp b/src/script/script.cpp index 380acae5d2..9dcdca483f 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -9,6 +9,7 @@ #include "hash.h" #include "primitives/transaction.h" +#include "primitives/bitcoin/transaction.h" #include "streams.h" #include "tinyformat.h" #include "utilstrencodings.h" @@ -333,8 +334,8 @@ COutPoint CScript::GetWithdrawSpent() const vector vTx; if (!PopWithdrawPush(pushes, &vTx)) return COutPoint(); - CTransaction tx; - CDataStream(vTx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX) >> tx; + Sidechain::Bitcoin::CTransaction tx; + CDataStream(vTx, SER_NETWORK, PROTOCOL_VERSION) >> tx; if (ntxOut < 0 || (unsigned int)ntxOut >= tx.vout.size()) return COutPoint(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 543981508c..77528f4b33 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -14,6 +14,7 @@ #include "net.h" #include "netbase.h" #include "policy/rbf.h" +#include "primitives/bitcoin/merkleblock.h" #include "rpc/server.h" #include "random.h" #include "timedata.h" @@ -3064,8 +3065,8 @@ UniValue claimpegin(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_TYPE_ERROR, "the last two arguments must be hex strings"); std::vector txData = ParseHex(params[1].get_str()); - CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); - CTransaction txBTC; + CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION); + Sidechain::Bitcoin::CTransaction txBTC; try { ssTx >> txBTC; } @@ -3074,8 +3075,8 @@ UniValue claimpegin(const UniValue& params, bool fHelp) } std::vector txOutProofData = ParseHex(params[2].get_str()); - CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_BITCOIN_BLOCK_OR_TX); - CMerkleBlock merkleBlock; + CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION); + Sidechain::Bitcoin::CMerkleBlock merkleBlock; try { ssTxOutProof >> merkleBlock; } @@ -3105,7 +3106,7 @@ UniValue claimpegin(const UniValue& params, bool fHelp) break; if (nOut == txBTC.vout.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find output in bitcoinTx to the mainchain_address from getpeginaddress"); - CAmount value = txBTC.vout[nOut].nValue.GetAmount(); + CAmount value = txBTC.vout[nOut].nValue; uint256 genesisBlockHash = Params().ParentGenesisBlockHash();