BIP144: Serialization, hashes, relay (sender side)
Contains refactorings by Eric Lombrozo. Contains fixup by Nicolas Dorier. Contains cleanup of CInv::GetCommand by Alex Morcos
This commit is contained in:
parent
ecacfd98e6
commit
7030d9eb47
21 changed files with 339 additions and 90 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
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
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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 + ")";
|
||||||
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"],
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue