consensus: Check JoinSplit signatures against the previous network upgrade

We only check failing signatures against the previous epoch to minimise
the extra computational load on nodes.
This commit is contained in:
Jack Grigg 2020-02-21 17:07:57 +00:00
parent dc99cd74a0
commit f21de9d0d6
2 changed files with 67 additions and 1 deletions

View File

@ -535,6 +535,54 @@ TEST(ChecktransactionTests, BadTxnsInvalidJoinsplitSignature) {
ContextualCheckTransaction(tx, state, chainparams, 0, true, [](const CChainParams&) { return false; });
}
TEST(ChecktransactionTests, JoinsplitSignatureDetectsOldBranchId) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_BLOSSOM, 10);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_HEARTWOOD, 20);
auto chainparams = Params();
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
auto blossomBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_BLOSSOM].nBranchId;
// Create a valid transaction for the Sapling epoch.
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
CTransaction tx(mtx);
MockCValidationState state;
// Ensure that the transaction validates against Sapling.
EXPECT_TRUE(ContextualCheckTransaction(
tx, state, chainparams, 5, false,
[](const CChainParams&) { return false; }));
// Attempt to validate the inputs against Blossom. We should be notified
// that an old consensus branch ID was used for an input.
EXPECT_CALL(state, DoS(
10, false, REJECT_INVALID,
strprintf("old-consensus-branch-id (Expected %s, found %s)",
HexInt(blossomBranchId),
HexInt(saplingBranchId)),
false)).Times(1);
EXPECT_FALSE(ContextualCheckTransaction(
tx, state, chainparams, 15, false,
[](const CChainParams&) { return false; }));
// Attempt to validate the inputs against Heartwood. All we should learn is
// that the signature is invalid, because we don't check more than one
// network upgrade back.
EXPECT_CALL(state, DoS(
10, false, REJECT_INVALID,
"bad-txns-invalid-joinsplit-signature", false)).Times(1);
EXPECT_FALSE(ContextualCheckTransaction(
tx, state, chainparams, 25, false,
[](const CChainParams&) { return false; }));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_HEARTWOOD, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
RegtestDeactivateBlossom();
}
TEST(ChecktransactionTests, NonCanonicalEd25519Signature) {
SelectParams(CBaseChainParams::REGTEST);
auto chainparams = Params();

View File

@ -936,17 +936,20 @@ bool ContextualCheckTransaction(
}
}
auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
auto prevConsensusBranchId = PrevEpochBranchId(consensusBranchId, chainparams.GetConsensus());
uint256 dataToBeSigned;
uint256 prevDataToBeSigned;
if (!tx.vJoinSplit.empty() ||
!tx.vShieldedSpend.empty() ||
!tx.vShieldedOutput.empty())
{
auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
// Empty output script.
CScript scriptCode;
try {
dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
prevDataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, prevConsensusBranchId);
} catch (std::logic_error ex) {
// A logic error should never occur because we pass NOT_AN_INPUT and
// SIGHASH_ALL to SignatureHash().
@ -965,6 +968,21 @@ bool ContextualCheckTransaction(
dataToBeSigned.begin(), 32,
tx.joinSplitPubKey.begin()
) != 0) {
// Check whether the failure was caused by an outdated consensus
// branch ID; if so, inform the node that they need to upgrade. We
// only check the previous epoch's branch ID, on the assumption that
// users creating transactions will notice their transactions
// failing before a second network upgrade occurs.
if (crypto_sign_verify_detached(&tx.joinSplitSig[0],
prevDataToBeSigned.begin(), 32,
tx.joinSplitPubKey.begin()
) == 0) {
return state.DoS(
dosLevelPotentiallyRelaxing, false, REJECT_INVALID, strprintf(
"old-consensus-branch-id (Expected %s, found %s)",
HexInt(consensusBranchId),
HexInt(prevConsensusBranchId)));
}
return state.DoS(
dosLevelPotentiallyRelaxing,
error("CheckTransaction(): invalid joinsplit signature"),