Skip to content

Commit bb291b5

Browse files
committed
Merge #16021: p2p: Avoid logging transaction decode errors to stderr
fa2b52a Disallow extended encoding for non-witness transactions (take 3) (MarcoFalke) Pull request description: (previous title "p2p: Disallow extended encoding for non-witness transactions (take 3)") Remote peers can send us illegally encoded txs and thus have us write to stderr. Fix that by not writing to stderr. This is a follow up to the previous (incomplete) attempts at this: * Disallow extended encoding for non-witness transactions #14039 * Add test for superfluous witness record in deserialization #15893 ACKs for commit fa2b52: laanwj: utACK fa2b52a ryanofsky: utACK fa2b52a. Would change title to something like "Avoid logging transaction decode errors to stderr" instead of "Disallow extended encoding for non-witness transactions." The current title is confusing because this PR isn't really allowing or disallowing anything, just logging the condition differently. "Disallow" also seems to contradict the "Allow exceptions from..." comments in the actual code. Tree-SHA512: c66990e69b432d00dc1c5510bf976a1188664d0890a32d1e5c6459094e7e27da82a5d227627afcbc203676f5540eec74b7d9b1d71d2c62d3b2069e1781824b4d
2 parents dfc02da + fa2b52a commit bb291b5

File tree

2 files changed

+33
-14
lines changed

2 files changed

+33
-14
lines changed

src/net_processing.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3288,23 +3288,22 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
32883288
if (m_enable_bip61) {
32893289
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
32903290
}
3291-
if (strstr(e.what(), "end of data"))
3292-
{
3291+
if (strstr(e.what(), "end of data")) {
32933292
// Allow exceptions from under-length message on vRecv
32943293
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
3295-
}
3296-
else if (strstr(e.what(), "size too large"))
3297-
{
3294+
} else if (strstr(e.what(), "size too large")) {
32983295
// Allow exceptions from over-long size
32993296
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
3300-
}
3301-
else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
3302-
{
3297+
} else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
33033298
// Allow exceptions from non-canonical encoding
33043299
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
3305-
}
3306-
else
3307-
{
3300+
} else if (strstr(e.what(), "Superfluous witness record")) {
3301+
// Allow exceptions from illegal witness encoding
3302+
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
3303+
} else if (strstr(e.what(), "Unknown transaction optional data")) {
3304+
// Allow exceptions from unknown witness encoding
3305+
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
3306+
} else {
33083307
PrintExceptionContinue(&e, "ProcessMessages()");
33093308
}
33103309
}

test/functional/p2p_segwit.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,9 +2038,9 @@ def test_witness_sigops(self):
20382038
# TODO: test p2sh sigop counting
20392039

20402040
def test_superfluous_witness(self):
2041-
# Serialization of tx that puts witness flag to 1 always
2041+
# Serialization of tx that puts witness flag to 3 always
20422042
def serialize_with_bogus_witness(tx):
2043-
flags = 1
2043+
flags = 3
20442044
r = b""
20452045
r += struct.pack("<i", tx.nVersion)
20462046
if flags:
@@ -2059,9 +2059,29 @@ def serialize_with_bogus_witness(tx):
20592059
r += struct.pack("<I", tx.nLockTime)
20602060
return r
20612061

2062-
raw = self.nodes[0].createrawtransaction([{"txid":"00"*32, "vout":0}], {self.nodes[0].getnewaddress():1})
2062+
class msg_bogus_tx(msg_tx):
2063+
def serialize(self):
2064+
return serialize_with_bogus_witness(self.tx)
2065+
2066+
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(address_type='bech32'), 5)
2067+
self.nodes[0].generate(1)
2068+
unspent = next(u for u in self.nodes[0].listunspent() if u['spendable'] and u['address'].startswith('bcrt'))
2069+
2070+
raw = self.nodes[0].createrawtransaction([{"txid": unspent['txid'], "vout": unspent['vout']}], {self.nodes[0].getnewaddress(): 1})
20632071
tx = FromHex(CTransaction(), raw)
20642072
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
2073+
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
2074+
self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
2075+
self.nodes[0].p2p.sync_with_ping()
2076+
raw = self.nodes[0].signrawtransactionwithwallet(raw)
2077+
assert raw['complete']
2078+
raw = raw['hex']
2079+
tx = FromHex(CTransaction(), raw)
2080+
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
2081+
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
2082+
self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
2083+
self.nodes[0].p2p.sync_with_ping()
2084+
20652085

20662086
if __name__ == '__main__':
20672087
SegWitTest().main()

0 commit comments

Comments
 (0)