Skip to content

Commit 0a82c10

Browse files
random-zebraFuzzbawls
authored andcommitted
Script: Properly sign/verify shielded tx with sigversion sapling
Github-Pull: #2064 Rebased-From: 2d7f8e2
1 parent 6b5d96d commit 0a82c10

18 files changed

+101
-62
lines changed

src/kernel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ bool CheckProofOfStake(const CBlock& block, std::string& strError, const CBlockI
186186
const CTxIn& txin = tx->vin[0];
187187
ScriptError serror;
188188
if (!VerifyScript(txin.scriptSig, stakePrevout.scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
189-
TransactionSignatureChecker(tx.get(), 0, stakePrevout.nValue), &serror)) {
189+
TransactionSignatureChecker(tx.get(), 0, stakePrevout.nValue), tx->GetRequiredSigVersion(), &serror)) {
190190
strError = strprintf("signature fails: %s", serror ? ScriptErrorString(serror) : "");
191191
return false;
192192
}

src/pivx-tx.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,14 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
448448
const CAmount& amount = coin.out.nValue;
449449

450450
SignatureData sigdata;
451+
SigVersion sigversion = mergedTx.GetRequiredSigVersion();
451452
// Only sign SIGHASH_SINGLE if there's a corresponding output:
452453
if (!fHashSingle || (i < mergedTx.vout.size()))
453454
ProduceSignature(
454455
MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType),
455456
prevPubKey,
456457
sigdata,
458+
sigversion,
457459
false // no cold stake
458460
);
459461

@@ -462,7 +464,8 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
462464
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
463465
}
464466
UpdateTransaction(mergedTx, i, sigdata);
465-
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
467+
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
468+
MutableTransactionSignatureChecker(&mergedTx, i, amount), sigversion))
466469
fComplete = false;
467470
}
468471

src/policy/policy.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
215215
// IsStandard() will have already returned false
216216
// and this method isn't called.
217217
std::vector<std::vector<unsigned char> > stack;
218-
if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker(), SIGVERSION_BASE))
218+
if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker(), tx.GetRequiredSigVersion()))
219219
return false;
220220

221221
if (whichType == TX_SCRIPTHASH) {

src/primitives/transaction.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121

2222
class CTransaction;
2323

24+
enum SigVersion
25+
{
26+
SIGVERSION_BASE = 0,
27+
SIGVERSION_SAPLING = 1,
28+
};
29+
2430
// contextual flag to guard the serialization for v5 upgrade.
2531
// can be removed once v5 enforcement is activated.
2632
extern std::atomic<bool> g_IsSaplingActive;
@@ -322,6 +328,12 @@ class CTransaction
322328
return isSaplingVersion() && nType != TxType::NORMAL && hasExtraPayload();
323329
}
324330

331+
// Ensure that special and sapling fields are signed
332+
SigVersion GetRequiredSigVersion() const
333+
{
334+
return isSaplingVersion() ? SIGVERSION_SAPLING : SIGVERSION_BASE;
335+
}
336+
325337
/*
326338
* Context for the two methods below:
327339
* We can think of the Sapling shielded part of the transaction as an input
@@ -421,6 +433,17 @@ struct CMutableTransaction
421433
* fly, as opposed to GetHash() in CTransaction, which uses a cached result.
422434
*/
423435
uint256 GetHash() const;
436+
437+
bool isSaplingVersion() const
438+
{
439+
return nVersion >= CTransaction::TxVersion::SAPLING;
440+
}
441+
442+
// Ensure that special and sapling fields are signed
443+
SigVersion GetRequiredSigVersion() const
444+
{
445+
return isSaplingVersion() ? SIGVERSION_SAPLING : SIGVERSION_BASE;
446+
}
424447
};
425448

426449
typedef std::shared_ptr<const CTransaction> CTransactionRef;

src/rpc/rawtransaction.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -799,9 +799,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
799799
}
800800

801801
SignatureData sigdata;
802+
SigVersion sigversion = mergedTx.GetRequiredSigVersion();
802803
// Only sign SIGHASH_SINGLE if there's a corresponding output:
803804
if (!fHashSingle || (i < mergedTx.vout.size()))
804-
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, fColdStake);
805+
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType),
806+
prevPubKey, sigdata, sigversion, fColdStake);
805807

806808
// ... and merge in other signatures:
807809
for (const CMutableTransaction& txv : txVariants) {
@@ -811,7 +813,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
811813
UpdateTransaction(mergedTx, i, sigdata);
812814

813815
ScriptError serror = SCRIPT_ERR_OK;
814-
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
816+
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
817+
TransactionSignatureChecker(&txConst, i, amount), sigversion, &serror)) {
815818
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
816819
}
817820
}

src/sapling/transaction_builder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ TransactionBuilderResult TransactionBuilder::Build()
364364
bool signSuccess = ProduceSignature(
365365
TransactionSignatureCreator(
366366
keystore, &txNewConst, nIn, tIn.value, SIGHASH_ALL),
367-
tIn.scriptPubKey, sigdata, false);
367+
tIn.scriptPubKey, sigdata, SIGVERSION_SAPLING, false);
368368

369369
if (!signSuccess) {
370370
return TransactionBuilderResult("Failed to sign transaction");

src/script/bitcoinconsensus.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i
8888
// Regardless of the verification result, the tx did not error.
8989
set_error(err, bitcoinconsensus_ERR_OK);
9090

91-
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn), NULL);
91+
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
92+
flags, TransactionSignatureChecker(&tx, nIn), tx.GetRequiredSigVersion(), NULL);
9293
} catch (const std::exception&) {
9394
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
9495
}

src/script/interpreter.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,10 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
11701170
return UINT256_ONE;
11711171
}
11721172

1173-
// currently: sigversion_sapling is disabled everywhere.
1173+
if (txTo.isSaplingVersion() && sigversion != SIGVERSION_SAPLING) {
1174+
throw std::runtime_error("SignatureHash in Sapling tx with wrong sigversion " + std::to_string(sigversion));
1175+
}
1176+
11741177
if (sigversion == SIGVERSION_SAPLING) {
11751178

11761179
uint256 hashPrevouts;
@@ -1292,7 +1295,7 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch
12921295
int nHashType = vchSig.back();
12931296
vchSig.pop_back();
12941297

1295-
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->precomTxData);
1298+
const uint256& sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->precomTxData);
12961299

12971300
if (!VerifySignature(vchSig, pubkey, sighash))
12981301
return false;
@@ -1337,7 +1340,7 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
13371340
}
13381341

13391342

1340-
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
1343+
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
13411344
{
13421345
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
13431346

@@ -1346,12 +1349,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne
13461349
}
13471350

13481351
std::vector<std::vector<unsigned char> > stack, stackCopy;
1349-
if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror))
1352+
if (!EvalScript(stack, scriptSig, flags, checker, sigversion, serror))
13501353
// serror is set
13511354
return false;
13521355
if (flags & SCRIPT_VERIFY_P2SH)
13531356
stackCopy = stack;
1354-
if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror))
1357+
if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror))
13551358
// serror is set
13561359
return false;
13571360
if (stack.empty())
@@ -1376,7 +1379,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne
13761379
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
13771380
popstack(stackCopy);
13781381

1379-
if (!EvalScript(stackCopy, pubKey2, flags, checker, SIGVERSION_BASE, serror))
1382+
if (!EvalScript(stackCopy, pubKey2, flags, checker, sigversion, serror))
13801383
// serror is set
13811384
return false;
13821385
if (stackCopy.empty())

src/script/interpreter.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,6 @@ struct PrecomputedTransactionData
9696
PrecomputedTransactionData(const CTransaction& tx);
9797
};
9898

99-
enum SigVersion
100-
{
101-
SIGVERSION_BASE = 0,
102-
SIGVERSION_SAPLING = 1,
103-
};
104-
10599
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
106100

107101
class BaseSignatureChecker
@@ -157,6 +151,6 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker
157151
};
158152

159153
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL);
160-
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL);
154+
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror = NULL);
161155

162156
#endif // BITCOIN_SCRIPT_INTERPRETER_H

src/script/sign.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,13 @@ static CScript PushAll(const std::vector<valtype>& values)
151151
return result;
152152
}
153153

154-
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata, bool fColdStake, ScriptError* serror)
154+
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata, SigVersion sigversion, bool fColdStake, ScriptError* serror)
155155
{
156156
CScript script = fromPubKey;
157157
bool solved = true;
158158
std::vector<valtype> result;
159159
txnouttype whichType;
160-
solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE, fColdStake);
160+
solved = SignStep(creator, script, result, whichType, sigversion, fColdStake);
161161
CScript subscript;
162162

163163
if (solved && whichType == TX_SCRIPTHASH)
@@ -166,14 +166,14 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu
166166
// the final scriptSig is the signatures from that
167167
// and then the serialized subscript:
168168
script = subscript = CScript(result[0].begin(), result[0].end());
169-
solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE, fColdStake) && whichType != TX_SCRIPTHASH;
169+
solved = solved && SignStep(creator, script, result, whichType, sigversion, fColdStake) && whichType != TX_SCRIPTHASH;
170170
result.emplace_back(subscript.begin(), subscript.end());
171171
}
172172

173173
sigdata.scriptSig = PushAll(result);
174174

175175
// Test solution
176-
return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), serror);
176+
return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), sigversion, serror);
177177
}
178178

179179
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
@@ -198,7 +198,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl
198198
TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType);
199199

200200
SignatureData sigdata;
201-
bool ret = ProduceSignature(creator, fromPubKey, sigdata, fColdStake);
201+
bool ret = ProduceSignature(creator, fromPubKey, sigdata, txToConst.GetRequiredSigVersion(), fColdStake);
202202
UpdateTransaction(txTo, nIn, sigdata);
203203
return ret;
204204
}
@@ -276,8 +276,8 @@ struct Stacks
276276

277277
Stacks() {}
278278
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_) {}
279-
explicit Stacks(const SignatureData& data) {
280-
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
279+
explicit Stacks(const SignatureData& data, SigVersion sigversion) {
280+
EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), sigversion);
281281
}
282282

283283
SignatureData Output() const {
@@ -342,7 +342,7 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature
342342
std::vector<std::vector<unsigned char> > vSolutions;
343343
Solver(scriptPubKey, txType, vSolutions);
344344

345-
return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output();
345+
return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1, SIGVERSION_BASE), Stacks(scriptSig2, SIGVERSION_BASE), SIGVERSION_BASE).Output();
346346
}
347347

348348
namespace {
@@ -388,9 +388,9 @@ bool IsSolvable(const CKeyStore& store, const CScript& script, bool fColdStaking
388388
// if found in a transaction, we would still accept and relay that transaction. In particular,
389389
DummySignatureCreator creator(&store);
390390
SignatureData sigs;
391-
if (ProduceSignature(creator, script, sigs, fColdStaking)) {
391+
if (ProduceSignature(creator, script, sigs, SIGVERSION_BASE, fColdStaking)) {
392392
// VerifyScript check is just defensive, and should never fail.
393-
assert(VerifyScript(sigs.scriptSig, script, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()));
393+
assert(VerifyScript(sigs.scriptSig, script, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), SIGVERSION_BASE));
394394
return true;
395395
}
396396
return false;

0 commit comments

Comments
 (0)