Rewrite time locks, coinbase validation, and script functions in C++ for easier integration

This commit is contained in:
Michael Toutonghi
2018-04-20 18:38:39 -07:00
parent f0e0d9e560
commit 29bd53a102
11 changed files with 339 additions and 164 deletions

View File

@@ -1466,6 +1466,140 @@ bool CWallet::IsMine(const CTransaction& tx) const
return false;
}
// special case handling for CLTV scripts, this does not error check to ensure the script is CLTV and is
// only internal to the wallet for that reason. if it is the first time we see this script, we add it to the wallet.
isminetype CWallet::IsCLTVMine(CScriptExt &script, CScriptID &scriptID, int64_t locktime)
{
uint8_t pushOp = script.data()[0];
uint32_t scriptStart = pushOp + 2;
// check post CLTV script
CScriptExt postfix = CScriptExt(script.size() > scriptStart ? script.begin() + scriptStart : script.end(), script.end());
// check again with postfix subscript
isminetype ret = ::IsMine(*this, postfix);
if (ret != ISMINE_NO)
{
// once we get here, we should have this script in our
// wallet, either as watch only if still time locked, or spendable
if (!chainActive.Tip()->nHeight >= locktime)
{
ret = ISMINE_WATCH_ONLY;
if (!this->HaveWatchOnly(script))
{
this->AddWatchOnly(script);
}
} else
{
if (this->HaveWatchOnly(script))
this->RemoveWatchOnly(script);
if (!this->HaveCScript(scriptID))
this->AddCScript(script);
}
}
return ret;
}
typedef vector<unsigned char> valtype;
unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore);
// special case handling for non-standard/Verus OP_RETURN script outputs, which need the transaction
// to determine ownership
isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum)
{
vector<valtype> vSolutions;
txnouttype whichType;
const CScriptExt scriptPubKey = CScriptExt(tx.vout[voutNum].scriptPubKey);
if (!Solver(scriptPubKey, whichType, vSolutions)) {
if (this->HaveWatchOnly(scriptPubKey))
return ISMINE_WATCH_ONLY;
return ISMINE_NO;
}
CKeyID keyID;
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScriptExt subscript;
int voutNext = voutNum + 1;
switch (whichType)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
if (this->HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
if (this->HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_SCRIPTHASH:
if (this->GetCScript(scriptID, subscript))
{
// if this is a CLTV, handle it differently
int64_t lockTime;
if (subscript.IsCheckLockTimeVerify(&lockTime))
{
return this->IsCLTVMine(subscript, scriptID, lockTime);
}
else
{
isminetype ret = ::IsMine(*this, subscript);
if (ret == ISMINE_SPENDABLE)
return ret;
}
}
else if (tx.vout.size() > (voutNext = voutNum + 1) &&
tx.vout[voutNext].scriptPubKey.size() > 7 &&
tx.vout[voutNext].scriptPubKey.data()[0] == OP_RETURN)
{
// get the opret script from next vout, verify that the front is CLTV and hash matches
// if so, remove it and use the solver
opcodetype op;
std::vector<uint8_t> opretData;
CScript::const_iterator it = tx.vout[voutNext].scriptPubKey.begin() + 1;
if (tx.vout[voutNext].scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData.data()[0] == OPRETTYPE_TIMELOCK)
{
int64_t unlocktime;
CScriptExt opretScript = CScriptExt(opretData.begin() + 1, opretData.end());
if (CScriptID(opretScript) == scriptID &&
opretScript.IsCheckLockTimeVerify(&unlocktime))
{
return this->IsCLTVMine(opretScript, scriptID, unlocktime);
}
}
}
}
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);
if (HaveKeys(keys, *this) == keys.size())
return ISMINE_SPENDABLE;
break;
}
if (this->HaveWatchOnly(scriptPubKey))
return ISMINE_WATCH_ONLY;
return ISMINE_NO;
}
bool CWallet::IsFromMe(const CTransaction& tx) const
{
if (GetDebit(tx, ISMINE_ALL) > 0) {