Skip to content

Commit

Permalink
Generic TransactionSignatureCreator works with both CTransaction and …
Browse files Browse the repository at this point in the history
…CMutableTransaction

Templated version so that no copying of CMutableTransaction into a CTransaction is
necessary. This speeds up the test case transaction_tests/test_big_witness_transaction
from 7.9 seconds to 3.1 seconds on my machine.
  • Loading branch information
martinus committed May 30, 2018
1 parent d792e47 commit 6b8b63a
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 46 deletions.
48 changes: 35 additions & 13 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1089,17 +1089,19 @@ namespace {
* Wrapper that serializes like CTransaction, but with the modifications
* required for the signature hash done in-place
*/
class CTransactionSignatureSerializer {
template <class T>
class CTransactionSignatureSerializer
{
private:
const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized)
const T& txTo; //!< reference to the spending transaction (the one being serialized)
const CScript& scriptCode; //!< output script being consumed
const unsigned int nIn; //!< input index of txTo being signed
const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set
const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE
const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE

public:
CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
Expand Down Expand Up @@ -1180,23 +1182,29 @@ class CTransactionSignatureSerializer {
}
};

uint256 GetPrevoutHash(const CTransaction& txTo) {
template <class T>
uint256 GetPrevoutHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.prevout;
}
return ss.GetHash();
}

uint256 GetSequenceHash(const CTransaction& txTo) {
template <class T>
uint256 GetSequenceHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.nSequence;
}
return ss.GetHash();
}

uint256 GetOutputsHash(const CTransaction& txTo) {
template <class T>
uint256 GetOutputsHash(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : txTo.vout) {
ss << txout;
Expand All @@ -1206,7 +1214,8 @@ uint256 GetOutputsHash(const CTransaction& txTo) {

} // namespace

PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
// Cache is calculated only for transactions with witness
if (txTo.HasWitness()) {
Expand All @@ -1217,7 +1226,12 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
}
}

uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
// explicit instantiation
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
assert(nIn < txTo.vin.size());

Expand Down Expand Up @@ -1278,20 +1292,22 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
}

// Wrapper to serialize only the necessary parts of the transaction being signed
CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);

// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;
return ss.GetHash();
}

bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
template <class T>
bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}

bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
Expand All @@ -1312,7 +1328,8 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch
return true;
}

bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const
template <class T>
bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
{
// There are two kinds of nLockTime: lock-by-blockheight
// and lock-by-blocktime, distinguished by whether
Expand Down Expand Up @@ -1348,7 +1365,8 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
return true;
}

bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const
template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const
{
// Relative lock times are supported by comparing the passed
// in operand to the sequence number of the input.
Expand Down Expand Up @@ -1394,6 +1412,10 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con
return true;
}

// explicit instantiation
template class GenericTransactionSignatureChecker<CTransaction>;
template class GenericTransactionSignatureChecker<CMutableTransaction>;

static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
std::vector<std::vector<unsigned char> > stack;
Expand Down
25 changes: 11 additions & 14 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ struct PrecomputedTransactionData
uint256 hashPrevouts, hashSequence, hashOutputs;
bool ready = false;

explicit PrecomputedTransactionData(const CTransaction& tx);
template <class T>
explicit PrecomputedTransactionData(const T& tx);
};

enum class SigVersion
Expand All @@ -137,7 +138,8 @@ enum class SigVersion
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;

uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);

class BaseSignatureChecker
{
Expand All @@ -160,10 +162,11 @@ class BaseSignatureChecker
virtual ~BaseSignatureChecker() {}
};

class TransactionSignatureChecker : public BaseSignatureChecker
template <class T>
class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
private:
const CTransaction* txTo;
const T* txTo;
unsigned int nIn;
const CAmount amount;
const PrecomputedTransactionData* txdata;
Expand All @@ -172,21 +175,15 @@ class TransactionSignatureChecker : public BaseSignatureChecker
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;

public:
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
};

class MutableTransactionSignatureChecker : public TransactionSignatureChecker
{
private:
const CTransaction txTo;

public:
MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {}
};
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;

bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
Expand Down
7 changes: 3 additions & 4 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

typedef std::vector<unsigned char> valtype;

TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}

bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
if (!provider.GetKey(address, key))
Expand Down Expand Up @@ -209,8 +209,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C
{
assert(nIn < txTo.vin.size());

CTransaction txToConst(txTo);
TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType);
MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);

SignatureData sigdata;
bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
Expand Down
15 changes: 4 additions & 11 deletions src/script/sign.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,19 @@ class BaseSignatureCreator {
};

/** A signature creator for transactions. */
class TransactionSignatureCreator : public BaseSignatureCreator {
const CTransaction* txTo;
class MutableTransactionSignatureCreator : public BaseSignatureCreator {
const CMutableTransaction* txTo;
unsigned int nIn;
int nHashType;
CAmount amount;
const TransactionSignatureChecker checker;
const MutableTransactionSignatureChecker checker;

public:
TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};

class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
CTransaction tx;

public:
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {}
};

/** A signature creator that just produces 72-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;

Expand Down
6 changes: 2 additions & 4 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2608,7 +2608,6 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
AssertLockHeld(cs_wallet); // mapWallet

// sign the new tx
CTransaction txNewConst(tx);
int nIn = 0;
for (const auto& input : tx.vin) {
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
Expand All @@ -2618,7 +2617,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
SignatureData sigdata;
if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
return false;
}
UpdateTransaction(tx, nIn, sigdata);
Expand Down Expand Up @@ -3040,14 +3039,13 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac

if (sign)
{
CTransaction txNewConst(txNew);
int nIn = 0;
for (const auto& coin : selected_coins)
{
const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;

if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
Expand Down

0 comments on commit 6b8b63a

Please sign in to comment.