Merge pull request #274 from somegeekintn/optim2-1.5

Wallet/RPC Optimizations
This commit is contained in:
ummjackson 2014-02-24 17:28:01 +11:00
commit d41600067d
5 changed files with 171 additions and 56 deletions

View File

@ -28,6 +28,7 @@
#endif
#if MAC_OSX
#undef MSG_NOSIGNAL // undef prior to redefinition eliminates warnings
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif

View File

@ -31,7 +31,6 @@ static const CBigNum bnFalse(0);
static const CBigNum bnTrue(1);
static const size_t nMaxNumSize = 4;
CBigNum CastToBigNum(const valtype& vch)
{
if (vch.size() > nMaxNumSize)
@ -1119,27 +1118,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 +1162,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:
@ -1180,9 +1195,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
}
return true;
}
if (!script1.GetOp(pc1, opcode1, vch1))
if (!script1.GetOp2(pc1, opcode1, &vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
if (!testScript->GetOp2(pc2, opcode2, NULL)) // templates push no data, no need to get vch
break;
// Template matching opcodes:
@ -1191,10 +1206,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
while (vch1.size() >= 33 && vch1.size() <= 120)
{
vSolutionsRet.push_back(vch1);
if (!script1.GetOp(pc1, opcode1, vch1))
if (!script1.GetOp2(pc1, opcode1, &vch1))
break;
}
if (!script2.GetOp(pc2, opcode2, vch2))
if (!testScript->GetOp2(pc2, opcode2, NULL))
break;
// Normal situation is to fall through
// to other if/else statements
@ -1223,16 +1238,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 +1463,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();

View File

@ -426,14 +426,14 @@ public:
bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const
{
const_iterator scriptEnd = end();
opcodeRet = OP_INVALIDOPCODE;
if (pvchRet)
pvchRet->clear();
if (pc >= end())
return false;
// Read instruction
if (end() - pc < 1)
if (scriptEnd - pc < 1)
return false;
unsigned int opcode = *pc++;
@ -447,13 +447,13 @@ public:
}
else if (opcode == OP_PUSHDATA1)
{
if (end() - pc < 1)
if (scriptEnd - pc < 1)
return false;
nSize = *pc++;
}
else if (opcode == OP_PUSHDATA2)
{
if (end() - pc < 2)
if (scriptEnd - pc < 2)
return false;
nSize = 0;
memcpy(&nSize, &pc[0], 2);
@ -461,12 +461,12 @@ public:
}
else if (opcode == OP_PUSHDATA4)
{
if (end() - pc < 4)
if (scriptEnd - pc < 4)
return false;
memcpy(&nSize, &pc[0], 4);
pc += 4;
}
if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize)
if (scriptEnd - pc < 0 || (unsigned int)(scriptEnd - pc) < nSize)
return false;
if (pvchRet)
pvchRet->assign(pc, pc + nSize);
@ -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;
@ -638,6 +649,8 @@ int CWalletTx::GetRequestCount() const
void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived,
list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const
{
CTxDestination address;
nFee = 0;
listReceived.clear();
listSent.clear();
@ -651,28 +664,43 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived,
nFee = nDebit - nValueOut;
}
if (!fMineCached)
vfMine.resize(vout.size());
// Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
CTxDestination address;
vector<unsigned char> vchPubKey;
if (!ExtractDestination(txout.scriptPubKey, address))
{
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str());
for (unsigned int i = 0; i < vout.size(); i++) {
const CTxOut &txout = vout[i];
bool isMine = false;
bool warnUnkownTX = false;
if (!fMineCached) {
address = CNoDestination();
warnUnkownTX = !ExtractDestinationAndMine(*pwallet, txout.scriptPubKey, address, &isMine);
vfMine[i] = isMine;
}
else {
if (vfMine[i]) {
isMine = true; // already know this is ours, just fetch address
address = CNoDestination();
warnUnkownTX = !ExtractDestination(txout.scriptPubKey, address);
}
}
if (warnUnkownTX) {
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))
continue;
if (nDebit > 0) {
if (isMine && !pwallet->HasAddress(address)) // Don't report 'change' txouts
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));
}
fMineCached = true;
}
void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived,

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
{
@ -383,6 +384,8 @@ public:
int64 nOrderPos; // position in ordered transaction list
// memory only
mutable std::vector<char> vfMine; // which outputs are mine
mutable bool fMineCached;
mutable bool fDebitCached;
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
@ -426,6 +429,8 @@ public:
fFromMe = false;
strFromAccount.clear();
vfSpent.clear();
vfMine.clear();
fMineCached = false;
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
@ -519,6 +524,8 @@ public:
// make sure balances are recalculated
void MarkDirty()
{
vfMine.clear();
fMineCached = false;
fCreditCached = false;
fAvailableCreditCached = false;
fDebitCached = false;