Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions src/test/evo_mnhf_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <uint256.h>
#include <util/string.h>
#include <validation.h>
#include <versionbits.h>

#include <boost/test/unit_test.hpp>
#include <test/util/setup_common.h>
Expand All @@ -19,10 +20,10 @@
#include <vector>


static CMutableTransaction CreateMNHFTx(const uint256& quorumHash, const CBLSSignature& cblSig, const uint16_t& versionBit)
static CMutableTransaction CreateMNHFTx(const uint256& quorumHash, const CBLSSignature& cblSig, const uint16_t& versionBit, const uint16_t payload_version = MNHFTxPayload::CURRENT_VERSION)
{
MNHFTxPayload extraPayload;
extraPayload.nVersion = 1;
extraPayload.nVersion = payload_version;
extraPayload.signal.versionBit = versionBit;
extraPayload.signal.quorumHash = quorumHash;
extraPayload.signal.sig = cblSig;
Expand Down Expand Up @@ -68,19 +69,64 @@ BOOST_AUTO_TEST_CASE(verify_mnhf_specialtx_tests)
auto& qman = *Assert(m_node.llmq_ctx)->qman;
const CBlockIndex* pindex = chainman->ActiveChain().Tip();
uint256 hash = GetRandHash();
TxValidationState state;
{ // bad type
CMutableTransaction tx = CreateMNHFTx(hash, sig, bit);
tx.nVersion = 2;
TxValidationState state;
CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state);
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-type");
}

{ // bad payload
CMutableTransaction tx;
tx.nVersion = 3;
tx.nType = TRANSACTION_MNHF_SIGNAL;
tx.vExtraPayload = {0x01};
TxValidationState state;
CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state);
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-payload");
}

{ // bad version
const CTransaction tx{CTransaction(CreateMNHFTx(hash, sig, bit, 0))};
TxValidationState state;
CheckMNHFTx(*chainman, qman, tx, pindex, state);
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-version");
}

{ // wrong quorum (we don't have any indeed)
const CTransaction tx{CTransaction(CreateMNHFTx(hash, sig, bit))};
CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state);
TxValidationState state;
CheckMNHFTx(*chainman, qman, tx, pindex, state);
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-quorum-hash");
}

{ // non EHF fork
const CTransaction tx{CTransaction(CreateMNHFTx(hash, sig, 28))};
CheckMNHFTx(*chainman, qman, CTransaction(tx), pindex, state);
TxValidationState state;
CheckMNHFTx(*chainman, qman, tx, pindex, state);
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-non-ehf");
}

{ // out-of-bounds bit is rejected before quorum lookup
MNHFTx mnhfTx;
mnhfTx.versionBit = VERSIONBITS_NUM_BITS;
mnhfTx.quorumHash = hash;
mnhfTx.sig = sig;
TxValidationState state;
BOOST_CHECK(!mnhfTx.Verify(qman, hash, GetRandHash(), GetRandHash(), state));
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-nbit-out-of-bounds");
}

{ // well-formed bit still requires a known quorum
MNHFTx mnhfTx;
mnhfTx.versionBit = bit;
mnhfTx.quorumHash = hash;
mnhfTx.sig = sig;
TxValidationState state;
BOOST_CHECK(!mnhfTx.Verify(qman, hash, GetRandHash(), GetRandHash(), state));
BOOST_CHECK_EQUAL(state.ToString(), "bad-mnhf-missing-quorum");
}
}

BOOST_AUTO_TEST_SUITE_END()
49 changes: 48 additions & 1 deletion test/functional/feature_mnehf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
from io import BytesIO

from test_framework.authproxy import JSONRPCException
from test_framework.blocktools import (
create_block,
create_coinbase,
)
from test_framework.key import ECKey
from test_framework.messages import (
CMnEhf,
CTransaction,
hash256,
ser_string,
tx_from_hex,
)

from test_framework.test_framework import (
Expand Down Expand Up @@ -79,6 +84,7 @@ def create_mnehf(self, versionBit, pubkey=None):

mnehf_payload.quorumSig = bytearray.fromhex(recsig["sig"])
mnehf_tx.vExtraPayload = mnehf_payload.serialize()
mnehf_tx.rehash()
return mnehf_tx


Expand Down Expand Up @@ -121,6 +127,34 @@ def send_tx(self, tx, expected_error = None, reason = None):
self.log.info(f"Send tx triggered an error: {e.error}")
assert expected_error in e.error['message']

def assert_mempool_reject(self, tx, reject_reason):
result = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
assert_equal(result["allowed"], False)
assert reject_reason in result["reject-reason"], result

def assert_submitblock(self, txs, expected_error):
node = self.nodes[0]
best_block_hash = node.getbestblockhash()
best_block = node.getblock(best_block_hash)
height = best_block["height"] + 1
block_time = best_block["time"] + 1

coinbase = create_coinbase(height, dip4_activated=True, v20_activated=True)
gbt = node.getblocktemplate()
coinbase.vExtraPayload = bytes.fromhex(gbt["coinbase_payload"])
coinbase.rehash()

block = create_block(int(best_block_hash, 16), coinbase, block_time, version=4)
for tx_obj in gbt["transactions"]:
tx = tx_from_hex(tx_obj["data"])
if tx.nType == 6:
block.vtx.append(tx)
block.vtx.extend(txs)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()

assert_equal(node.submitblock(block.serialize().hex()), expected_error)


def run_test(self):
node = self.nodes[0]
Expand Down Expand Up @@ -148,6 +182,18 @@ def run_test(self):
self.log.info(f"ehf tx: {ehf_tx_sent}")
ehf_unknown_tx_sent = self.send_tx(ehf_unknown_tx)
self.log.info(f"unknown ehf tx: {ehf_unknown_tx_sent}")

self.log.info("Testing MNHF rejection for invalid signature")
invalid_sig_tx = self.create_mnehf(27, pubkey)
invalid_sig_payload = CMnEhf()
invalid_sig_payload.deserialize(BytesIO(invalid_sig_tx.vExtraPayload))
wrong_sig_payload = CMnEhf()
wrong_sig_payload.deserialize(BytesIO(ehf_tx.vExtraPayload))
invalid_sig_payload.quorumSig = wrong_sig_payload.quorumSig
invalid_sig_tx.vExtraPayload = invalid_sig_payload.serialize()
invalid_sig_tx.rehash()
self.assert_mempool_reject(invalid_sig_tx, "bad-mnhf-invalid")

self.sync_all()
ehf_blockhash = self.generate(self.nodes[1], 1)[0]

Expand Down Expand Up @@ -212,7 +258,8 @@ def run_test(self):
self.check_fork('active')

self.log.info("Testing duplicate EHF signal with same bit")
ehf_tx_duplicate = self.send_tx(self.create_mnehf(28, pubkey))
self.assert_submitblock([ehf_tx], "bad-mnhf-duplicate")
ehf_tx_duplicate = self.send_tx(ehf_tx)
tip_blockhash = self.generate(node, 1, sync_fun=lambda: self.sync_blocks())[0]
block = node.getblock(tip_blockhash)
assert ehf_tx_duplicate in node.getrawmempool() and ehf_tx_duplicate not in block['tx']
Expand Down
Loading