Browse Source

BIP144: Serialization, hashes, relay (sender side)

Contains refactorings by Eric Lombrozo.
Contains fixup by Nicolas Dorier.
Contains cleanup of CInv::GetCommand by Alex Morcos
0.13
Pieter Wuille 9 years ago
parent
commit
7030d9eb47
  1. 2
      src/core_io.h
  2. 24
      src/core_memusage.h
  3. 16
      src/core_read.cpp
  4. 28
      src/main.cpp
  5. 17
      src/net.h
  6. 2
      src/primitives/block.h
  7. 16
      src/primitives/transaction.cpp
  8. 151
      src/primitives/transaction.h
  9. 46
      src/protocol.cpp
  10. 30
      src/protocol.h
  11. 18
      src/qt/coincontroldialog.cpp
  12. 3
      src/qt/walletmodeltransaction.cpp
  13. 6
      src/rpc/rawtransaction.cpp
  14. 12
      src/script/script.cpp
  15. 14
      src/script/script.h
  16. 33
      src/streams.h
  17. 4
      src/test/data/tx_invalid.json
  18. 2
      src/test/sighash_tests.cpp
  19. 2
      src/test/transaction_tests.cpp
  20. 2
      src/wallet/rpcwallet.cpp
  21. 1
      src/wallet/walletdb.h

2
src/core_io.h

@ -17,7 +17,7 @@ class UniValue;
// core_read.cpp // core_read.cpp
extern CScript ParseScript(const std::string& s); extern CScript ParseScript(const std::string& s);
extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName); extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
extern uint256 ParseHashStr(const std::string&, const std::string& strName); extern uint256 ParseHashStr(const std::string&, const std::string& strName);

24
src/core_memusage.h

@ -25,8 +25,28 @@ static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
return RecursiveDynamicUsage(out.scriptPubKey); return RecursiveDynamicUsage(out.scriptPubKey);
} }
static inline size_t RecursiveDynamicUsage(const CScriptWitness& scriptWit) {
size_t mem = memusage::DynamicUsage(scriptWit.stack);
for (std::vector<std::vector<unsigned char> >::const_iterator it = scriptWit.stack.begin(); it != scriptWit.stack.end(); it++) {
mem += memusage::DynamicUsage(*it);
}
return mem;
}
static inline size_t RecursiveDynamicUsage(const CTxinWitness& txinwit) {
return RecursiveDynamicUsage(txinwit.scriptWitness);
}
static inline size_t RecursiveDynamicUsage(const CTxWitness& txwit) {
size_t mem = memusage::DynamicUsage(txwit.vtxinwit);
for (std::vector<CTxinWitness>::const_iterator it = txwit.vtxinwit.begin(); it != txwit.vtxinwit.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
return mem;
}
static inline size_t RecursiveDynamicUsage(const CTransaction& tx) { static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout); size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout) + RecursiveDynamicUsage(tx.wit);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) { for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it); mem += RecursiveDynamicUsage(*it);
} }
@ -37,7 +57,7 @@ static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
} }
static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) { static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout); size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout) + RecursiveDynamicUsage(tx.wit);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) { for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it); mem += RecursiveDynamicUsage(*it);
} }

16
src/core_read.cpp

@ -90,12 +90,26 @@ CScript ParseScript(const std::string& s)
return result; return result;
} }
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx) bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
{ {
if (!IsHex(strHexTx)) if (!IsHex(strHexTx))
return false; return false;
vector<unsigned char> txData(ParseHex(strHexTx)); vector<unsigned char> txData(ParseHex(strHexTx));
if (fTryNoWitness) {
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
try {
ssData >> tx;
if (ssData.eof()) {
return true;
}
}
catch (const std::exception&) {
// Fall through.
}
}
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try { try {
ssData >> tx; ssData >> tx;

28
src/main.cpp

@ -1029,8 +1029,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
if (tx.vout.empty()) if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
// Size limits // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values // Check for negative or overflow output values
@ -3396,7 +3396,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// because we receive the wrong transactions for it. // because we receive the wrong transactions for it.
// Size limits // Size limits
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be // First transaction must be coinbase, the rest must not be
@ -4508,6 +4508,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
switch (inv.type) switch (inv.type)
{ {
case MSG_TX: case MSG_TX:
case MSG_WITNESS_TX:
{ {
assert(recentRejects); assert(recentRejects);
if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
@ -4528,6 +4529,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
pcoinsTip->HaveCoinsInCache(inv.hash); pcoinsTip->HaveCoinsInCache(inv.hash);
} }
case MSG_BLOCK: case MSG_BLOCK:
case MSG_WITNESS_BLOCK:
return mapBlockIndex.count(inv.hash); return mapBlockIndex.count(inv.hash);
} }
// Don't know what it is, just say we already got one // Don't know what it is, just say we already got one
@ -4552,7 +4554,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
it++; it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
{ {
bool send = false; bool send = false;
BlockMap::iterator mi = mapBlockIndex.find(inv.hash); BlockMap::iterator mi = mapBlockIndex.find(inv.hash);
@ -4593,6 +4595,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
if (!ReadBlockFromDisk(block, (*mi).second, consensusParams)) if (!ReadBlockFromDisk(block, (*mi).second, consensusParams))
assert(!"cannot load block from disk"); assert(!"cannot load block from disk");
if (inv.type == MSG_BLOCK) if (inv.type == MSG_BLOCK)
pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block);
else if (inv.type == MSG_WITNESS_BLOCK)
pfrom->PushMessage(NetMsgType::BLOCK, block); pfrom->PushMessage(NetMsgType::BLOCK, block);
else if (inv.type == MSG_FILTERED_BLOCK) else if (inv.type == MSG_FILTERED_BLOCK)
{ {
@ -4609,7 +4613,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// however we MUST always provide at least what the remote peer needs // however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType; typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]); pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, block.vtx[pair.first]);
} }
// else // else
// no response // no response
@ -4622,9 +4626,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// instead we respond with the full, non-compact block. // instead we respond with the full, non-compact block.
if (mi->second->nHeight >= chainActive.Height() - 10) { if (mi->second->nHeight >= chainActive.Height() - 10) {
CBlockHeaderAndShortTxIDs cmpctblock(block); CBlockHeaderAndShortTxIDs cmpctblock(block);
pfrom->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock); pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
} else } else
pfrom->PushMessage(NetMsgType::BLOCK, block); pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block);
} }
// Trigger the peer node to send a getblocks request for the next batch of inventory // Trigger the peer node to send a getblocks request for the next batch of inventory
@ -4640,20 +4644,20 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
} }
} }
} }
else if (inv.type == MSG_TX) else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
{ {
// Send stream from relay memory // Send stream from relay memory
bool push = false; bool push = false;
auto mi = mapRelay.find(inv.hash); auto mi = mapRelay.find(inv.hash);
if (mi != mapRelay.end()) { if (mi != mapRelay.end()) {
pfrom->PushMessage(NetMsgType::TX, *mi->second); pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *mi->second);
push = true; push = true;
} else if (pfrom->timeLastMempoolReq) { } else if (pfrom->timeLastMempoolReq) {
auto txinfo = mempool.info(inv.hash); auto txinfo = mempool.info(inv.hash);
// To protect privacy, do not answer getdata using the mempool when // To protect privacy, do not answer getdata using the mempool when
// that TX couldn't have been INVed in reply to a MEMPOOL request. // that TX couldn't have been INVed in reply to a MEMPOOL request.
if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
pfrom->PushMessage(NetMsgType::TX, *txinfo.tx); pfrom->PushMessageWithFlag(inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *txinfo.tx);
push = true; push = true;
} }
} }
@ -4665,7 +4669,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// Track requests for our stuff. // Track requests for our stuff.
GetMainSignals().Inventory(inv.hash); GetMainSignals().Inventory(inv.hash);
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK)
break; break;
} }
} }
@ -5146,7 +5150,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
} }
resp.txn[i] = block.vtx[req.indexes[i]]; resp.txn[i] = block.vtx[req.indexes[i]];
} }
pfrom->PushMessage(NetMsgType::BLOCKTXN, resp); pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp);
} }

17
src/net.h

@ -598,6 +598,23 @@ public:
} }
} }
/** Send a message containing a1, serialized with flag flag. */
template<typename T1>
void PushMessageWithFlag(int flag, const char* pszCommand, const T1& a1)
{
try
{
BeginMessage(pszCommand);
WithOrVersion(&ssSend, flag) << a1;
EndMessage(pszCommand);
}
catch (...)
{
AbortMessage();
throw;
}
}
template<typename T1, typename T2> template<typename T1, typename T2>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
{ {

2
src/primitives/block.h

@ -38,7 +38,6 @@ public:
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion); READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(hashPrevBlock); READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot); READWRITE(hashMerkleRoot);
READWRITE(nTime); READWRITE(nTime);
@ -120,7 +119,6 @@ public:
std::string ToString() const; std::string ToString() const;
}; };
/** Describes a place in the block chain to another node such that if the /** 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. * 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. * The further back it is, the further before the fork it may be.

16
src/primitives/transaction.cpp

@ -60,21 +60,26 @@ std::string CTxOut::ToString() const
} }
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {}
uint256 CMutableTransaction::GetHash() const uint256 CMutableTransaction::GetHash() const
{ {
return SerializeHash(*this); return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
} }
void CTransaction::UpdateHash() const void CTransaction::UpdateHash() const
{ {
*const_cast<uint256*>(&hash) = SerializeHash(*this); *const_cast<uint256*>(&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() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) { CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {
UpdateHash(); UpdateHash();
} }
@ -82,6 +87,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<int*>(&nVersion) = tx.nVersion; *const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin; *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout; *const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
*const_cast<CTxWitness*>(&wit) = tx.wit;
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime; *const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
*const_cast<uint256*>(&hash) = tx.hash; *const_cast<uint256*>(&hash) = tx.hash;
return *this; return *this;
@ -136,6 +142,8 @@ std::string CTransaction::ToString() const
nLockTime); nLockTime);
for (unsigned int i = 0; i < vin.size(); i++) for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n"; 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++) for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n"; str += " " + vout[i].ToString() + "\n";
return str; return str;

151
src/primitives/transaction.h

@ -11,6 +11,8 @@
#include "serialize.h" #include "serialize.h"
#include "uint256.h" #include "uint256.h"
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
/** An outpoint - a combination of a transaction hash and an index n into its vout */ /** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint class COutPoint
{ {
@ -194,8 +196,137 @@ public:
std::string ToString() const; std::string ToString() const;
}; };
class CTxinWitness
{
public:
CScriptWitness scriptWitness;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
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<CTxinWitness> 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 <typename Stream, typename Operation>
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; struct CMutableTransaction;
/**
* Basic transaction serialization format:
* - int32_t nVersion
* - std::vector<CTxIn> vin
* - std::vector<CTxOut> vout
* - uint32_t nLockTime
*
* Extended transaction serialization format:
* - int32_t nVersion
* - unsigned char dummy = 0x00
* - unsigned char flags (!= 0)
* - std::vector<CTxIn> vin
* - std::vector<CTxOut> vout
* - if (flags & 1):
* - CTxWitness wit;
* - uint32_t nLockTime
*/
template<typename Stream, typename Operation, typename TxType>
inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*const_cast<int32_t*>(&tx.nVersion));
unsigned char flags = 0;
if (ser_action.ForRead()) {
const_cast<std::vector<CTxIn>*>(&tx.vin)->clear();
const_cast<std::vector<CTxOut>*>(&tx.vout)->clear();
const_cast<CTxWitness*>(&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<std::vector<CTxIn>*>(&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<std::vector<CTxIn>*>(&tx.vin));
READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
}
} else {
/* We read a non-empty vin. Assume a normal vout follows. */
READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
}
if ((flags & 1) && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
/* The witness flag is present, and we support witnesses. */
flags ^= 1;
const_cast<CTxWitness*>(&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<CTxIn> vinDummy;
READWRITE(vinDummy);
READWRITE(flags);
}
READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
if (flags & 1) {
const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
READWRITE(tx.wit);
}
}
READWRITE(*const_cast<uint32_t*>(&tx.nLockTime));
}
/** The basic transaction that is broadcasted on the network and contained in /** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs. * blocks. A transaction can contain multiple inputs and outputs.
*/ */
@ -224,6 +355,7 @@ public:
const int32_t nVersion; const int32_t nVersion;
const std::vector<CTxIn> vin; const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout; const std::vector<CTxOut> vout;
CTxWitness wit; // Not const: can change without invalidating the txid cache
const uint32_t nLockTime; const uint32_t nLockTime;
/** Construct a CTransaction that qualifies as IsNull() */ /** Construct a CTransaction that qualifies as IsNull() */
@ -238,13 +370,10 @@ public:
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*const_cast<int32_t*>(&this->nVersion)); SerializeTransaction(*this, s, ser_action, nType, nVersion);
nVersion = this->nVersion; if (ser_action.ForRead()) {
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
READWRITE(*const_cast<uint32_t*>(&nLockTime));
if (ser_action.ForRead())
UpdateHash(); UpdateHash();
}
} }
bool IsNull() const { bool IsNull() const {
@ -255,6 +384,9 @@ public:
return hash; return hash;
} }
// Compute a hash that includes both transaction and witness data
uint256 GetWitnessHash() const;
// Return sum of txouts. // Return sum of txouts.
CAmount GetValueOut() const; CAmount GetValueOut() const;
// GetValueIn() is a method on CCoinsViewCache, because // GetValueIn() is a method on CCoinsViewCache, because
@ -290,6 +422,7 @@ struct CMutableTransaction
int32_t nVersion; int32_t nVersion;
std::vector<CTxIn> vin; std::vector<CTxIn> vin;
std::vector<CTxOut> vout; std::vector<CTxOut> vout;
CTxWitness wit;
uint32_t nLockTime; uint32_t nLockTime;
CMutableTransaction(); CMutableTransaction();
@ -299,11 +432,7 @@ struct CMutableTransaction
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion); SerializeTransaction(*this, s, ser_action, nType, nVersion);
nVersion = this->nVersion;
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
} }
/** Compute the hash of this CMutableTransaction. This is computed on the /** Compute the hash of this CMutableTransaction. This is computed on the

46
src/protocol.cpp

@ -41,15 +41,6 @@ const char *GETBLOCKTXN="getblocktxn";
const char *BLOCKTXN="blocktxn"; const char *BLOCKTXN="blocktxn";
}; };
static const char* ppszTypeName[] =
{
"ERROR", // Should never occur
NetMsgType::TX,
NetMsgType::BLOCK,
"filtered block", // Should never occur
"compact block" // Should never occur
};
/** All known message types. Keep this in the same order as the list of /** All known message types. Keep this in the same order as the list of
* messages above and in protocol.h. * messages above and in protocol.h.
*/ */
@ -166,37 +157,26 @@ CInv::CInv(int typeIn, const uint256& hashIn)
hash = hashIn; hash = hashIn;
} }
CInv::CInv(const std::string& strType, const uint256& hashIn)
{
unsigned int i;
for (i = 1; i < ARRAYLEN(ppszTypeName); i++)
{
if (strType == ppszTypeName[i])
{
type = i;
break;
}
}
if (i == ARRAYLEN(ppszTypeName))
throw std::out_of_range(strprintf("CInv::CInv(string, uint256): unknown type '%s'", strType));
hash = hashIn;
}
bool operator<(const CInv& a, const CInv& b) bool operator<(const CInv& a, const CInv& b)
{ {
return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); return (a.type < b.type || (a.type == b.type && a.hash < b.hash));
} }
bool CInv::IsKnownType() const std::string CInv::GetCommand() const
{ {
return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); std::string cmd;
} if (type & MSG_WITNESS_FLAG)
cmd.append("witness-");
const char* CInv::GetCommand() const int masked = type & MSG_TYPE_MASK;
{ switch (masked)
if (!IsKnownType()) {
case MSG_TX: return cmd.append(NetMsgType::TX);
case MSG_BLOCK: return cmd.append(NetMsgType::BLOCK);
case MSG_FILTERED_BLOCK: return cmd.append(NetMsgType::MERKLEBLOCK);
case MSG_CMPCT_BLOCK: return cmd.append(NetMsgType::CMPCTBLOCK);
default:
throw std::out_of_range(strprintf("CInv::GetCommand(): type=%d unknown type", type)); throw std::out_of_range(strprintf("CInv::GetCommand(): type=%d unknown type", type));
return ppszTypeName[type]; }
} }
std::string CInv::ToString() const std::string CInv::ToString() const

30
src/protocol.h

@ -309,13 +309,29 @@ public:
unsigned int nTime; unsigned int nTime;
}; };
/** getdata message types */
const uint32_t MSG_WITNESS_FLAG = 1 << 30;
const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2;
enum GetDataMsg
{
UNDEFINED = 0,
MSG_TX,
MSG_BLOCK,
MSG_TYPE_MAX = MSG_BLOCK,
// The following can only occur in getdata. Invs always use TX or BLOCK.
MSG_FILTERED_BLOCK,
MSG_CMPCT_BLOCK,
MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG,
MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG,
MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
};
/** inv message data */ /** inv message data */
class CInv class CInv
{ {
public: public:
CInv(); CInv();
CInv(int typeIn, const uint256& hashIn); CInv(int typeIn, const uint256& hashIn);
CInv(const std::string& strType, const uint256& hashIn);
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -328,8 +344,7 @@ public:
friend bool operator<(const CInv& a, const CInv& b); friend bool operator<(const CInv& a, const CInv& b);
bool IsKnownType() const; std::string GetCommand() const;
const char* GetCommand() const;
std::string ToString() const; std::string ToString() const;
// TODO: make private (improves encapsulation) // TODO: make private (improves encapsulation)
@ -338,13 +353,4 @@ public:
uint256 hash; uint256 hash;
}; };
enum {
MSG_TX = 1,
MSG_BLOCK,
// Nodes may always request a MSG_FILTERED_BLOCK/MSG_CMPCT_BLOCK in a getdata, however,
// MSG_FILTERED_BLOCK/MSG_CMPCT_BLOCK should not appear in any invs except as a part of getdata.
MSG_FILTERED_BLOCK,
MSG_CMPCT_BLOCK,
};
#endif // BITCOIN_PROTOCOL_H #endif // BITCOIN_PROTOCOL_H

18
src/qt/coincontroldialog.cpp

@ -485,6 +485,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
unsigned int nQuantity = 0; unsigned int nQuantity = 0;
int nQuantityUncompressed = 0; int nQuantityUncompressed = 0;
bool fAllowFree = false; bool fAllowFree = false;
bool fWitness = false;
std::vector<COutPoint> vCoinControl; std::vector<COutPoint> vCoinControl;
std::vector<COutput> vOutputs; std::vector<COutput> vOutputs;
@ -513,7 +514,14 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Bytes // Bytes
CTxDestination address; CTxDestination address;
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
if (out.tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
fWitness = true;
}
else if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
{ {
CPubKey pubkey; CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address); CKeyID *keyid = boost::get<CKeyID>(&address);
@ -534,6 +542,14 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{ {
// Bytes // Bytes
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
if (fWitness)
{
// there is some fudging in these numbers related to the actual virtual transaction size calculation that will keep this estimate from being exact.
// usually, the result will be an overestimate within a couple of satoshis so that the confirmation dialog ends up displaying a slightly smaller fee.
// also, the witness stack size value value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit.
nBytes += 2; // account for the serialized marker and flag bytes
nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input.
}
// Priority // Priority
double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);

3
src/qt/walletmodeltransaction.cpp

@ -4,6 +4,7 @@
#include "walletmodeltransaction.h" #include "walletmodeltransaction.h"
#include "policy/policy.h"
#include "wallet/wallet.h" #include "wallet/wallet.h"
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) : WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) :
@ -33,7 +34,7 @@ CWalletTx *WalletModelTransaction::getTransaction()
unsigned int WalletModelTransaction::getTransactionSize() unsigned int WalletModelTransaction::getTransactionSize()
{ {
return (!walletTransaction ? 0 : (::GetSerializeSize(*(CTransaction*)walletTransaction, SER_NETWORK, PROTOCOL_VERSION))); return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction));
} }
CAmount WalletModelTransaction::getTransactionFee() CAmount WalletModelTransaction::getTransactionFee()

6
src/rpc/rawtransaction.cpp

@ -276,7 +276,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
if (ntxFound != setTxids.size()) if (ntxFound != setTxids.size())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block");
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock mb(block, setTxids); CMerkleBlock mb(block, setTxids);
ssMB << mb; ssMB << mb;
std::string strHex = HexStr(ssMB.begin(), ssMB.end()); std::string strHex = HexStr(ssMB.begin(), ssMB.end());
@ -296,7 +296,7 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n"
); );
CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock merkleBlock; CMerkleBlock merkleBlock;
ssMB >> merkleBlock; ssMB >> merkleBlock;
@ -487,7 +487,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
CTransaction tx; CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str())) if (!DecodeHexTx(tx, params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);

12
src/script/script.cpp

@ -231,3 +231,15 @@ bool CScript::IsPushOnly() const
{ {
return this->IsPushOnly(begin()); return this->IsPushOnly(begin());
} }
std::string CScriptWitness::ToString() const
{
std::string ret = "CScriptWitness(";
for (unsigned int i = 0; i < stack.size(); i++) {
if (i) {
ret += ", ";
}
ret += HexStr(stack[i]);
}
return ret + ")";
}

14
src/script/script.h

@ -643,6 +643,20 @@ public:
} }
}; };
struct CScriptWitness
{
// Note that this encodes the data elements being pushed, rather than
// encoding them as a CScript that pushes them.
std::vector<std::vector<unsigned char> > stack;
// Some compilers complain without a default constructor
CScriptWitness() { }
bool IsNull() const { return stack.empty(); }
std::string ToString() const;
};
class CReserveScript class CReserveScript
{ {
public: public:

33
src/streams.h

@ -22,6 +22,39 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
template<typename Stream>
class OverrideStream
{
Stream* stream;
public:
const int nType;
const int nVersion;
OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {}
template<typename T>
OverrideStream<Stream>& operator<<(const T& obj)
{
// Serialize to this stream
::Serialize(*this->stream, obj, nType, nVersion);
return (*this);
}
template<typename T>
OverrideStream<Stream>& operator>>(T& obj)
{
// Unserialize from this stream
::Unserialize(*this->stream, obj, nType, nVersion);
return (*this);
}
};
template<typename S>
OverrideStream<S> WithOrVersion(S* s, int nVersionFlag)
{
return OverrideStream<S>(s, s->GetType(), s->GetVersion() | nVersionFlag);
}
/** Double ended buffer combining vector and stream-like interfaces. /** Double ended buffer combining vector and stream-like interfaces.
* *
* >> and << read and write unformatted data using the above serialization templates. * >> and << read and write unformatted data using the above serialization templates.

4
src/test/data/tx_invalid.json

@ -30,10 +30,6 @@
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"], "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"],
["Tests for CheckTransaction()"], ["Tests for CheckTransaction()"],
["No inputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
"0100000000010000000000000000015100000000", "P2SH"],
["No outputs"], ["No outputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"],

2
src/test/sighash_tests.cpp

@ -82,7 +82,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
} }
// Serialize and hash // Serialize and hash
CHashWriter ss(SER_GETHASH, 0); CHashWriter ss(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
ss << txTmp << nHashType; ss << txTmp << nHashType;
return ss.GetHash(); return ss.GetHash();
} }

2
src/test/transaction_tests.cpp

@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
} }
string transaction = test[1].get_str(); string transaction = test[1].get_str();
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION );
CTransaction tx; CTransaction tx;
stream >> tx; stream >> tx;

2
src/wallet/rpcwallet.cpp

@ -2451,7 +2451,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
// parse hex string from parameter // parse hex string from parameter
CTransaction origTx; CTransaction origTx;
if (!DecodeHexTx(origTx, params[0].get_str())) if (!DecodeHexTx(origTx, params[0].get_str(), true))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
if (origTx.vout.size() == 0) if (origTx.vout.size() == 0)

1
src/wallet/walletdb.h

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLETDB_H #define BITCOIN_WALLET_WALLETDB_H
#include "amount.h" #include "amount.h"
#include "primitives/transaction.h"
#include "wallet/db.h" #include "wallet/db.h"
#include "key.h" #include "key.h"

Loading…
Cancel
Save