significant optimizations to Solver and CWalletTx::GetAmounts

This commit is contained in:
Casey Fleser 2014-02-19 16:43:41 -06:00
parent c26399bc56
commit d0640f940d
4 changed files with 132 additions and 37 deletions

View file

@ -31,6 +31,7 @@ static const CBigNum bnFalse(0);
static const CBigNum bnTrue(1);
static const size_t nMaxNumSize = 4;
#undef printf
CBigNum CastToBigNum(const valtype& vch)
{
@ -1119,27 +1120,40 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubK
typedef struct {
txnouttype txType;
CScript *tScript;
} SScriptPairRec;
//
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{
// Templates
static map<txnouttype, CScript> mTemplates;
if (mTemplates.empty())
{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
}
static SScriptPairRec *sTemplates = NULL;
SScriptPairRec *curTemplate;
if (sTemplates == NULL) {
CScript *tScript;
sTemplates = (SScriptPairRec *)malloc(sizeof(SScriptPairRec) * 4);
// order templates such that most common transaction types are checked first
tScript = new CScript();
*tScript << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG;
sTemplates[0].txType = TX_PUBKEYHASH;
sTemplates[0].tScript = tScript;
tScript = new CScript();
*tScript << OP_PUBKEY << OP_CHECKSIG;
sTemplates[1].txType = TX_PUBKEY;
sTemplates[1].tScript = tScript;
tScript = new CScript();
*tScript << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG;
sTemplates[2].txType = TX_MULTISIG;
sTemplates[2].tScript = tScript;
sTemplates[3].txType = (txnouttype)-1;
sTemplates[3].tScript = NULL;
}
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
@ -1150,26 +1164,29 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
vSolutionsRet.push_back(hashBytes);
return true;
}
// Scan templates
const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates)
{
const CScript& script2 = tplate.second;
vSolutionsRet.clear();
curTemplate = &sTemplates[0];
while (curTemplate->tScript != NULL) {
const CScript *testScript = curTemplate->tScript;
opcodetype opcode1, opcode2;
vector<unsigned char> vch1;
opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2;
vSolutionsRet.clear();
// Compare
CScript::const_iterator pc1 = script1.begin();
CScript::const_iterator pc2 = script2.begin();
CScript::const_iterator pc2 = testScript->begin();
CScript::const_iterator end1 = script1.end();
CScript::const_iterator end2 = testScript->end();
loop
{
if (pc1 == script1.end() && pc2 == script2.end())
if (pc1 == end1 && pc2 == end2)
{
// Found a match
typeRet = tplate.first;
typeRet = curTemplate->txType;
if (typeRet == TX_MULTISIG)
{
// Additional checks for TX_MULTISIG:
@ -1182,7 +1199,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
if (!testScript->GetOp(pc2, opcode2)) // templates push no data, no need to get vch
break;
// Template matching opcodes:
@ -1194,7 +1211,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
if (!script1.GetOp(pc1, opcode1, vch1))
break;
}
if (!script2.GetOp(pc2, opcode2, vch2))
if (!testScript->GetOp(pc2, opcode2))
break;
// Normal situation is to fall through
// to other if/else statements
@ -1223,16 +1240,18 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
else
break;
}
else if (opcode1 != opcode2 || vch1 != vch2)
else if (opcode1 != opcode2)
{
// Others must match exactly
break;
}
}
}
curTemplate++;
}
vSolutionsRet.clear();
typeRet = TX_NONSTANDARD;
return false;
}
@ -1446,6 +1465,67 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
return false;
}
// ExtractDestinationAndMine is an amalgam of ExtractDestination and IsMine. Since they do very
// similar work and are both called from CWalletTx::GetAmounts we can reduce kill two birds with
// one stone by combining them and speed CWalletTx::GetAmounts considerably.
bool ExtractDestinationAndMine(const CKeyStore &keystore, const CScript& scriptPubKey, CTxDestination& addressRet, bool *outIsMine)
{
vector<valtype> vSolutions;
txnouttype whichType;
bool hasDestination = false;
*outIsMine = false;
if (Solver(scriptPubKey, whichType, vSolutions)) {
CKeyID keyID;
switch (whichType) {
case TX_NONSTANDARD:
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
addressRet = keyID;
*outIsMine = keystore.HaveKey(keyID);
hasDestination = true;
break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
addressRet = keyID;
*outIsMine = keystore.HaveKey(keyID);
hasDestination = true;
break;
case TX_SCRIPTHASH: {
CScript subscript;
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
addressRet = scriptID;
hasDestination = true;
if (keystore.GetCScript(scriptID, subscript))
*outIsMine = IsMine(keystore, subscript);
}
break;
case TX_MULTISIG:
{
// Only consider transactions "mine" if we own ALL the
// keys involved. multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
*outIsMine = HaveKeys(keys, keystore) == keys.size();
}
}
}
return hasDestination;
}
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet)
{
addressRet.clear();
@ -1732,7 +1812,7 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const
return subscript.GetSigOpCount(true);
}
bool CScript::IsPayToScriptHash() const
inline bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:
return (this->size() == 23 &&

View file

@ -676,6 +676,7 @@ bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinationAndMine(const CKeyStore &keystore, const CScript& scriptPubKey, CTxDestination& addressRet, bool *outMine);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);

View file

@ -570,6 +570,17 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
return 0;
}
bool CWallet::HasAddress(const CTxDestination &txDest) const
{
bool hasAddress = false;
LOCK(cs_wallet);
if (mapAddressBook.count(txDest))
hasAddress = true;
return hasAddress;
}
bool CWallet::IsChange(const CTxOut& txout) const
{
CTxDestination address;
@ -656,20 +667,22 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived,
{
CTxDestination address;
vector<unsigned char> vchPubKey;
if (!ExtractDestination(txout.scriptPubKey, address))
bool isMine;
if (!ExtractDestinationAndMine(*pwallet, txout.scriptPubKey, address, &isMine))
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
}
// Don't report 'change' txouts
if (nDebit > 0 && pwallet->IsChange(txout))
if (nDebit > 0 && isMine && !pwallet->HasAddress(address)) // equivalent to CWallet::IsChange, but avoids an additional call to Solver
continue;
if (nDebit > 0)
listSent.push_back(make_pair(address, txout.nValue));
if (pwallet->IsMine(txout))
if (isMine)
listReceived.push_back(make_pair(address, txout.nValue));
}

View file

@ -213,6 +213,7 @@ public:
throw std::runtime_error("CWallet::GetCredit() : value out of range");
return (IsMine(txout) ? txout.nValue : 0);
}
bool HasAddress(const CTxDestination &txDest) const;
bool IsChange(const CTxOut& txout) const;
int64 GetChange(const CTxOut& txout) const
{