Skip to content

Commit 9d2d8cc

Browse files
codablockUdjinM6
authored andcommitted
Add a few malleability tests for DIP2/3 transactions (#3060)
* Remove unused scriptPubKey * Add payload malleability check to DIP3 tests * Also check malleability protection by signatures inside CProUpRegTx
1 parent 4983f7a commit 9d2d8cc

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

src/test/evo_deterministicmns_tests.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "base58.h"
1212
#include "netbase.h"
1313
#include "messagesigner.h"
14+
#include "policy/policy.h"
1415
#include "keystore.h"
1516
#include "spork.h"
1617

@@ -65,8 +66,6 @@ static std::vector<COutPoint> SelectUTXOs(SimpleUTXOMap& utoxs, CAmount amount,
6566

6667
static void FundTransaction(CMutableTransaction& tx, SimpleUTXOMap& utoxs, const CScript& scriptPayout, CAmount amount, const CKey& coinbaseKey)
6768
{
68-
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
69-
7069
CAmount change;
7170
auto inputs = SelectUTXOs(utoxs, amount, change);
7271
for (size_t i = 0; i < inputs.size(); i++) {
@@ -183,6 +182,22 @@ static CMutableTransaction CreateProUpRevTx(SimpleUTXOMap& utxos, const uint256&
183182
return tx;
184183
}
185184

185+
template<typename ProTx>
186+
static CMutableTransaction MalleateProTxPayout(const CMutableTransaction& tx)
187+
{
188+
ProTx proTx;
189+
GetTxPayload(tx, proTx);
190+
191+
CKey key;
192+
key.MakeNewKey(false);
193+
proTx.scriptPayout = GetScriptForDestination(key.GetPubKey().GetID());
194+
195+
CMutableTransaction tx2 = tx;
196+
SetTxPayload(tx2, proTx);
197+
198+
return tx2;
199+
}
200+
186201
static CScript GenerateRandomAddress()
187202
{
188203
CKey key;
@@ -208,6 +223,21 @@ static CDeterministicMNCPtr FindPayoutDmn(const CBlock& block)
208223
return nullptr;
209224
}
210225

226+
static bool CheckTransactionSignature(const CMutableTransaction& tx)
227+
{
228+
for (unsigned int i = 0; i < tx.vin.size(); i++) {
229+
const auto& txin = tx.vin[i];
230+
CTransactionRef txFrom;
231+
uint256 hashBlock;
232+
BOOST_ASSERT(GetTransaction(txin.prevout.hash, txFrom, Params().GetConsensus(), hashBlock));
233+
234+
if (!VerifyScript(txin.scriptSig, txFrom->vout[txin.prevout.n].scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&tx, i))) {
235+
return false;
236+
}
237+
}
238+
return true;
239+
}
240+
211241
BOOST_AUTO_TEST_SUITE(evo_dip3_activation_tests)
212242

213243
BOOST_FIXTURE_TEST_CASE(dip3_activation, TestChainDIP3BeforeActivationSetup)
@@ -268,6 +298,20 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChainDIP3Setup)
268298
dmnHashes.emplace_back(tx.GetHash());
269299
ownerKeys.emplace(tx.GetHash(), ownerKey);
270300
operatorKeys.emplace(tx.GetHash(), operatorKey);
301+
302+
// also verify that payloads are not malleable after they have been signed
303+
// the form of ProRegTx we use here is one with a collateral included, so there is no signature inside the
304+
// payload itself. This means, we need to rely on script verification, which takes the hash of the extra payload
305+
// into account
306+
auto tx2 = MalleateProTxPayout<CProRegTx>(tx);
307+
CValidationState dummyState;
308+
// Technically, the payload is still valid...
309+
BOOST_ASSERT(CheckProRegTx(tx, chainActive.Tip(), dummyState));
310+
BOOST_ASSERT(CheckProRegTx(tx2, chainActive.Tip(), dummyState));
311+
// But the signature should not verify anymore
312+
BOOST_ASSERT(CheckTransactionSignature(tx));
313+
BOOST_ASSERT(!CheckTransactionSignature(tx2));
314+
271315
CreateAndProcessBlock({tx}, coinbaseKey);
272316
deterministicMNManager->UpdatedBlockTip(chainActive.Tip());
273317

@@ -362,6 +406,14 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChainDIP3Setup)
362406
newOperatorKey.MakeNewKey();
363407
dmn = deterministicMNManager->GetListAtChainTip().GetMN(dmnHashes[0]);
364408
tx = CreateProUpRegTx(utxos, dmnHashes[0], ownerKeys[dmnHashes[0]], newOperatorKey.GetPublicKey(), ownerKeys[dmnHashes[0]].GetPubKey().GetID(), dmn->pdmnState->scriptPayout, coinbaseKey);
409+
// check malleability protection again, but this time by also relying on the signature inside the ProUpRegTx
410+
auto tx2 = MalleateProTxPayout<CProUpRegTx>(tx);
411+
CValidationState dummyState;
412+
BOOST_ASSERT(CheckProUpRegTx(tx, chainActive.Tip(), dummyState));
413+
BOOST_ASSERT(!CheckProUpRegTx(tx2, chainActive.Tip(), dummyState));
414+
BOOST_ASSERT(CheckTransactionSignature(tx));
415+
BOOST_ASSERT(!CheckTransactionSignature(tx2));
416+
// now process the block
365417
CreateAndProcessBlock({tx}, coinbaseKey);
366418
deterministicMNManager->UpdatedBlockTip(chainActive.Tip());
367419
BOOST_ASSERT(chainActive.Height() == nHeight + 1);

0 commit comments

Comments
 (0)