aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2015-11-06 01:32:04 +0100
committerPieter Wuille <pieter.wuille@gmail.com>2016-06-22 15:42:59 +0200
commit7030d9eb47254499bba14f1c00abc6bf493efd91 (patch)
tree95055978907c7418c1b96728ff09413781b9c0b7
parentecacfd98e657c5819a1bcb1da93b25d3ad4e5045 (diff)
downloadbitcoin-7030d9eb47254499bba14f1c00abc6bf493efd91.tar.xz
BIP144: Serialization, hashes, relay (sender side)
Contains refactorings by Eric Lombrozo. Contains fixup by Nicolas Dorier. Contains cleanup of CInv::GetCommand by Alex Morcos
-rw-r--r--src/core_io.h2
-rw-r--r--src/core_memusage.h24
-rw-r--r--src/core_read.cpp16
-rw-r--r--src/main.cpp28
-rw-r--r--src/net.h17
-rw-r--r--src/primitives/block.h2
-rw-r--r--src/primitives/transaction.cpp16
-rw-r--r--src/primitives/transaction.h151
-rw-r--r--src/protocol.cpp46
-rw-r--r--src/protocol.h30
-rw-r--r--src/qt/coincontroldialog.cpp18
-rw-r--r--src/qt/walletmodeltransaction.cpp3
-rw-r--r--src/rpc/rawtransaction.cpp6
-rw-r--r--src/script/script.cpp12
-rw-r--r--src/script/script.h14
-rw-r--r--src/streams.h33
-rw-r--r--src/test/data/tx_invalid.json4
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp2
-rw-r--r--src/wallet/walletdb.h1
21 files changed, 339 insertions, 90 deletions
diff --git a/src/core_io.h b/src/core_io.h
index e8c0c49e84..b559d44bf5 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -17,7 +17,7 @@ class UniValue;
// core_read.cpp
extern CScript ParseScript(const std::string& s);
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 uint256 ParseHashUV(const UniValue& v, const std::string& strName);
extern uint256 ParseHashStr(const std::string&, const std::string& strName);
diff --git a/src/core_memusage.h b/src/core_memusage.h
index 450537d059..dd86f805fe 100644
--- a/src/core_memusage.h
+++ b/src/core_memusage.h
@@ -25,8 +25,28 @@ static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
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) {
- 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++) {
mem += RecursiveDynamicUsage(*it);
}
@@ -37,7 +57,7 @@ static inline size_t RecursiveDynamicUsage(const CTransaction& 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++) {
mem += RecursiveDynamicUsage(*it);
}
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 444a4c7eba..7cfda6dd6d 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -90,12 +90,26 @@ CScript ParseScript(const std::string& s)
return result;
}
-bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
+bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
{
if (!IsHex(strHexTx))
return false;
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);
try {
ssData >> tx;
diff --git a/src/main.cpp b/src/main.cpp
index ae195ecc40..2184940232 100644
--- a/src/main.cpp
+++ b/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");
if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
- // Size limits
- if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
+ // 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 | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// 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.
// 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");
// 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)
{
case MSG_TX:
+ case MSG_WITNESS_TX:
{
assert(recentRejects);
if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip)
@@ -4528,6 +4529,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
pcoinsTip->HaveCoinsInCache(inv.hash);
}
case MSG_BLOCK:
+ case MSG_WITNESS_BLOCK:
return mapBlockIndex.count(inv.hash);
}
// 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();
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;
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))
assert(!"cannot load block from disk");
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);
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
typedef std::pair<unsigned int, uint256> PairType;
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
// no response
@@ -4622,9 +4626,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// instead we respond with the full, non-compact block.
if (mi->second->nHeight >= chainActive.Height() - 10) {
CBlockHeaderAndShortTxIDs cmpctblock(block);
- pfrom->PushMessage(NetMsgType::CMPCTBLOCK, cmpctblock);
+ pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock);
} 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
@@ -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
bool push = false;
auto mi = mapRelay.find(inv.hash);
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;
} else if (pfrom->timeLastMempoolReq) {
auto txinfo = mempool.info(inv.hash);
// To protect privacy, do not answer getdata using the mempool when
// that TX couldn't have been INVed in reply to a MEMPOOL request.
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;
}
}
@@ -4665,7 +4669,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// Track requests for our stuff.
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;
}
}
@@ -5146,7 +5150,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
resp.txn[i] = block.vtx[req.indexes[i]];
}
- pfrom->PushMessage(NetMsgType::BLOCKTXN, resp);
+ pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp);
}
diff --git a/src/net.h b/src/net.h
index aa9b2c11a3..cb35d8c4f2 100644
--- a/src/net.h
+++ b/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>
void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
{
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 42276b2bc2..29307aed5b 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -38,7 +38,6 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion);
- nVersion = this->nVersion;
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
@@ -120,7 +119,6 @@ public:
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.
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 947f2e6a73..b0230530e8 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -60,21 +60,26 @@ std::string CTxOut::ToString() const
}
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
{
- return SerializeHash(*this);
+ return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
}
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(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();
}
@@ -82,6 +87,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
+ *const_cast<CTxWitness*>(&wit) = tx.wit;
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
*const_cast<uint256*>(&hash) = tx.hash;
return *this;
@@ -136,6 +142,8 @@ std::string CTransaction::ToString() const
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;
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 149816406a..d8ae41ad78 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -11,6 +11,8 @@
#include "serialize.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 */
class COutPoint
{
@@ -194,8 +196,137 @@ public:
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;
+/**
+ * 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
* blocks. A transaction can contain multiple inputs and outputs.
*/
@@ -224,6 +355,7 @@ public:
const int32_t nVersion;
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
+ CTxWitness wit; // Not const: can change without invalidating the txid cache
const uint32_t nLockTime;
/** Construct a CTransaction that qualifies as IsNull() */
@@ -238,13 +370,10 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- READWRITE(*const_cast<int32_t*>(&this->nVersion));
- nVersion = this->nVersion;
- READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
- READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
- READWRITE(*const_cast<uint32_t*>(&nLockTime));
- if (ser_action.ForRead())
+ SerializeTransaction(*this, s, ser_action, nType, nVersion);
+ if (ser_action.ForRead()) {
UpdateHash();
+ }
}
bool IsNull() const {
@@ -255,6 +384,9 @@ public:
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
@@ -290,6 +422,7 @@ struct CMutableTransaction
int32_t nVersion;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
+ CTxWitness wit;
uint32_t nLockTime;
CMutableTransaction();
@@ -299,11 +432,7 @@ struct CMutableTransaction
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
- READWRITE(this->nVersion);
- nVersion = this->nVersion;
- READWRITE(vin);
- READWRITE(vout);
- READWRITE(nLockTime);
+ SerializeTransaction(*this, s, ser_action, nType, nVersion);
}
/** Compute the hash of this CMutableTransaction. This is computed on the
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 2f90fb764c..247c6c2120 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -41,15 +41,6 @@ const char *GETBLOCKTXN="getblocktxn";
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
* messages above and in protocol.h.
*/
@@ -166,37 +157,26 @@ CInv::CInv(int typeIn, const uint256& 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)
{
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));
-}
-
-const char* CInv::GetCommand() const
-{
- if (!IsKnownType())
+ std::string cmd;
+ if (type & MSG_WITNESS_FLAG)
+ cmd.append("witness-");
+ int masked = type & MSG_TYPE_MASK;
+ switch (masked)
+ {
+ 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));
- return ppszTypeName[type];
+ }
}
std::string CInv::ToString() const
diff --git a/src/protocol.h b/src/protocol.h
index a72813e959..dd07092f5f 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -309,13 +309,29 @@ public:
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 */
class CInv
{
public:
CInv();
CInv(int typeIn, const uint256& hashIn);
- CInv(const std::string& strType, const uint256& hashIn);
ADD_SERIALIZE_METHODS;
@@ -328,8 +344,7 @@ public:
friend bool operator<(const CInv& a, const CInv& b);
- bool IsKnownType() const;
- const char* GetCommand() const;
+ std::string GetCommand() const;
std::string ToString() const;
// TODO: make private (improves encapsulation)
@@ -338,13 +353,4 @@ public:
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
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f909499952..837f8ba6c1 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -485,6 +485,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
unsigned int nQuantity = 0;
int nQuantityUncompressed = 0;
bool fAllowFree = false;
+ bool fWitness = false;
std::vector<COutPoint> vCoinControl;
std::vector<COutput> vOutputs;
@@ -513,7 +514,14 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Bytes
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;
CKeyID *keyid = boost::get<CKeyID>(&address);
@@ -534,6 +542,14 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
// Bytes
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
double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 8c970ee8aa..ffadf89cc8 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -4,6 +4,7 @@
#include "walletmodeltransaction.h"
+#include "policy/policy.h"
#include "wallet/wallet.h"
WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) :
@@ -33,7 +34,7 @@ CWalletTx *WalletModelTransaction::getTransaction()
unsigned int WalletModelTransaction::getTransactionSize()
{
- return (!walletTransaction ? 0 : (::GetSerializeSize(*(CTransaction*)walletTransaction, SER_NETWORK, PROTOCOL_VERSION)));
+ return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction));
}
CAmount WalletModelTransaction::getTransactionFee()
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9723e394d6..e4a6e0f39b 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -276,7 +276,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp)
if (ntxFound != setTxids.size())
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);
ssMB << mb;
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"
);
- 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;
ssMB >> merkleBlock;
@@ -487,7 +487,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
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");
UniValue result(UniValue::VOBJ);
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 9f2809e593..6b1eb52bbf 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -231,3 +231,15 @@ bool CScript::IsPushOnly() const
{
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 + ")";
+}
diff --git a/src/script/script.h b/src/script/script.h
index a2941ce901..0b6d822d81 100644
--- a/src/script/script.h
+++ b/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
{
public:
diff --git a/src/streams.h b/src/streams.h
index ed14f3f412..7132364eb1 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -22,6 +22,39 @@
#include <utility>
#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.
*
* >> and << read and write unformatted data using the above serialization templates.
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index 2d7d9b9585..4719f2b388 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -30,10 +30,6 @@
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"],
["Tests for CheckTransaction()"],
-["No inputs"],
-[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
-"0100000000010000000000000000015100000000", "P2SH"],
-
["No outputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"],
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index e43b2ff6c4..375accbac7 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -82,7 +82,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
}
// Serialize and hash
- CHashWriter ss(SER_GETHASH, 0);
+ CHashWriter ss(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
ss << txTmp << nHashType;
return ss.GetHash();
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index d9195bf345..318fddc1ae 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
}
string transaction = test[1].get_str();
- CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION );
CTransaction tx;
stream >> tx;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2d4e95911d..3666c37ac3 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2451,7 +2451,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
// parse hex string from parameter
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");
if (origTx.vout.size() == 0)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 71b0ff26db..d083722dd2 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLETDB_H
#include "amount.h"
+#include "primitives/transaction.h"
#include "wallet/db.h"
#include "key.h"