Skip to content

Commit 6c53939

Browse files
committed
Make script validation observe input amounts
1 parent 0373dfd commit 6c53939

15 files changed

+108
-77
lines changed

src/bitcoin-tx.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,18 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
354354
return ParseHexUV(o[strKey], strKey);
355355
}
356356

357+
static CAmount AmountFromValue(const UniValue& value)
358+
{
359+
if (!value.isNum() && !value.isStr())
360+
throw runtime_error("Amount is not a number or string");
361+
CAmount amount;
362+
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
363+
throw runtime_error("Invalid amount");
364+
if (!MoneyRange(amount))
365+
throw runtime_error("Amount out of range");
366+
return amount;
367+
}
368+
357369
static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
358370
{
359371
int nHashType = SIGHASH_ALL;
@@ -425,7 +437,10 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
425437
if ((unsigned int)nOut >= coins->vout.size())
426438
coins->vout.resize(nOut+1);
427439
coins->vout[nOut].scriptPubKey = scriptPubKey;
428-
coins->vout[nOut].nValue = 0; // we don't know the actual output value
440+
coins->vout[nOut].nValue = 0;
441+
if (prevOut.exists("amount")) {
442+
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
443+
}
429444
}
430445

431446
// if redeemScript given and private keys given,
@@ -453,18 +468,19 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
453468
continue;
454469
}
455470
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
471+
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
456472

457473
SignatureData sigdata;
458474
// Only sign SIGHASH_SINGLE if there's a corresponding output:
459475
if (!fHashSingle || (i < mergedTx.vout.size()))
460-
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata);
476+
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
461477

462478
// ... and merge in other signatures:
463479
BOOST_FOREACH(const CTransaction& txv, txVariants)
464-
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i));
480+
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
465481
UpdateTransaction(mergedTx, i, sigdata);
466482

467-
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i)))
483+
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount)))
468484
fComplete = false;
469485
}
470486

src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1575,7 +1575,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
15751575
bool CScriptCheck::operator()() {
15761576
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
15771577
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL;
1578-
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) {
1578+
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) {
15791579
return false;
15801580
}
15811581
return true;

src/main.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,23 +356,25 @@ class CScriptCheck
356356
{
357357
private:
358358
CScript scriptPubKey;
359+
CAmount amount;
359360
const CTransaction *ptxTo;
360361
unsigned int nIn;
361362
unsigned int nFlags;
362363
bool cacheStore;
363364
ScriptError error;
364365

365366
public:
366-
CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
367+
CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
367368
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) :
368-
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
369+
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue),
369370
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { }
370371

371372
bool operator()();
372373

373374
void swap(CScriptCheck &check) {
374375
scriptPubKey.swap(check.scriptPubKey);
375376
std::swap(ptxTo, check.ptxTo);
377+
std::swap(amount, check.amount);
376378
std::swap(nIn, check.nIn);
377379
std::swap(nFlags, check.nFlags);
378380
std::swap(cacheStore, check.cacheStore);

src/rpcrawtransaction.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
559559
" \"txid\":\"id\", (string, required) The transaction id\n"
560560
" \"vout\":n, (numeric, required) The output number\n"
561561
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
562-
" \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n"
562+
" \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n"
563+
" \"amount\": value (numeric, required) The amount spent\n"
563564
" }\n"
564565
" ,...\n"
565566
" ]\n"
@@ -697,7 +698,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
697698
if ((unsigned int)nOut >= coins->vout.size())
698699
coins->vout.resize(nOut+1);
699700
coins->vout[nOut].scriptPubKey = scriptPubKey;
700-
coins->vout[nOut].nValue = 0; // we don't know the actual output value
701+
coins->vout[nOut].nValue = 0;
702+
if (prevOut.exists("amount")) {
703+
coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
704+
}
701705
}
702706

703707
// if redeemScript given and not using the local wallet (private keys
@@ -752,21 +756,22 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
752756
continue;
753757
}
754758
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
759+
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
755760

756761
SignatureData sigdata;
757762
// Only sign SIGHASH_SINGLE if there's a corresponding output:
758763
if (!fHashSingle || (i < mergedTx.vout.size()))
759-
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata);
764+
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
760765

761766
// ... and merge in other signatures:
762767
BOOST_FOREACH(const CMutableTransaction& txv, txVariants) {
763-
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i));
768+
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
764769
}
765770

766771
UpdateTransaction(mergedTx, i, sigdata);
767772

768773
ScriptError serror = SCRIPT_ERR_OK;
769-
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) {
774+
if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount), &serror)) {
770775
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
771776
}
772777
}

src/script/bitcoinconsensus.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct ECCryptoClosure
6969
ECCryptoClosure instance_of_eccryptoclosure;
7070
}
7171

72-
int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
72+
int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount,
7373
const unsigned char *txTo , unsigned int txToLen,
7474
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
7575
{
@@ -85,7 +85,8 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i
8585
// Regardless of the verification result, the tx did not error.
8686
set_error(err, bitcoinconsensus_ERR_OK);
8787

88-
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn), NULL);
88+
CAmount am(amount);
89+
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, am), NULL);
8990
} catch (const std::exception&) {
9091
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
9192
}

src/script/bitcoinconsensus.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#ifndef BITCOIN_BITCOINCONSENSUS_H
77
#define BITCOIN_BITCOINCONSENSUS_H
88

9+
#include <stdint.h>
10+
911
#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H)
1012
#include "config/bitcoin-config.h"
1113
#if defined(_WIN32)
@@ -54,7 +56,7 @@ enum
5456
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
5557
/// the additional constraints specified by flags.
5658
/// If not NULL, err will contain an error/success code for the operation
57-
EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
59+
EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount,
5860
const unsigned char *txTo , unsigned int txToLen,
5961
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
6062

src/script/interpreter.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class TransactionSignatureChecker : public BaseSignatureChecker
117117
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
118118

119119
public:
120-
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {}
120+
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : txTo(txToIn), nIn(nInIn) {}
121121
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const;
122122
bool CheckLockTime(const CScriptNum& nLockTime) const;
123123
};
@@ -128,7 +128,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker
128128
const CTransaction txTo;
129129

130130
public:
131-
MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn) : TransactionSignatureChecker(&txTo, nInIn), txTo(*txToIn) {}
131+
MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {}
132132
};
133133

134134
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL);

src/script/sigcache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
2222
bool store;
2323

2424
public:
25-
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {}
25+
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {}
2626

2727
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
2828
};

src/script/sign.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using namespace std;
1818

1919
typedef std::vector<unsigned char> valtype;
2020

21-
TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {}
21+
TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn, amount) {}
2222

2323
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode) const
2424
{
@@ -205,12 +205,12 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur
205205
}
206206
}
207207

208-
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
208+
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
209209
{
210210
assert(nIn < txTo.vin.size());
211211

212212
CTransaction txToConst(txTo);
213-
TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType);
213+
TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType);
214214

215215
SignatureData sigdata;
216216
bool ret = ProduceSignature(creator, fromPubKey, sigdata);
@@ -225,7 +225,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab
225225
assert(txin.prevout.n < txFrom.vout.size());
226226
const CTxOut& txout = txFrom.vout[txin.prevout.n];
227227

228-
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType);
228+
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
229229
}
230230

231231
static vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,

src/script/sign.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class TransactionSignatureCreator : public BaseSignatureCreator {
3838
const TransactionSignatureChecker checker;
3939

4040
public:
41-
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL);
41+
TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn);
4242
const BaseSignatureChecker& Checker() const { return checker; }
4343
bool CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode) const;
4444
};
@@ -47,7 +47,7 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
4747
CTransaction tx;
4848

4949
public:
50-
MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, nHashTypeIn), tx(*txToIn) {}
50+
MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {}
5151
};
5252

5353
/** A signature creator that just produces 72-byte empty signatyres. */
@@ -70,7 +70,7 @@ struct SignatureData {
7070
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
7171

7272
/** Produce a script signature for a transaction. */
73-
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
73+
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType=SIGHASH_ALL);
7474
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
7575

7676
/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */

0 commit comments

Comments
 (0)