Skip to content

Commit

Permalink
BIP144: Serialization, hashes, relay (sender side)
Browse files Browse the repository at this point in the history
Contains refactorings by Eric Lombrozo.
Contains fixup by Nicolas Dorier.
Contains cleanup of CInv::GetCommand by Alex Morcos
  • Loading branch information
sipa committed Jun 22, 2016
1 parent ecacfd9 commit 7030d9e
Show file tree
Hide file tree
Showing 21 changed files with 339 additions and 90 deletions.
2 changes: 1 addition & 1 deletion src/core_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 22 additions & 2 deletions src/core_memusage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
16 changes: 15 additions & 1 deletion src/core_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 16 additions & 12 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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)
{
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
}
}
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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);
}


Expand Down
17 changes: 17 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,23 @@ class CNode
}
}

/** 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)
{
Expand Down
2 changes: 0 additions & 2 deletions src/primitives/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class CBlockHeader
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);
Expand Down Expand Up @@ -120,7 +119,6 @@ class CBlock : public CBlockHeader
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.
Expand Down
16 changes: 12 additions & 4 deletions src/primitives/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,34 @@ 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();
}

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;
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 7030d9e

Please sign in to comment.