Ejemplo n.º 1
0
Pathfinder::Pathfinder (RippleLineCache::ref cache,
                        const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID,
                        const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid)
    :   mSrcAccountID (uSrcAccountID.getAccountID ()),
        mDstAccountID (uDstAccountID.getAccountID ()),
        mDstAmount (saDstAmount),
        mSrcCurrencyID (uSrcCurrencyID),
        mSrcIssuerID (uSrcIssuerID),
        mSrcAmount (uSrcCurrencyID, uSrcIssuerID, 1u, 0, true),
        mLedger (cache->getLedger ()), mRLCache (cache)
{

    if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency ())) || mDstAmount.isZero ())
    {
        // no need to send to same account with same currency, must send non-zero
        bValid = false;
        mLedger.reset ();
        return;
    }

    bValid = true;

    m_loadEvent = getApp().getJobQueue ().getLoadEvent (jtPATH_FIND, "FindPath");

    bool bIssuer = mSrcCurrencyID.isNonZero() && mSrcIssuerID.isNonZero() && (mSrcIssuerID != mSrcAccountID);
    mSource = STPathElement(                       // Where does an empty path start?
        bIssuer ? mSrcIssuerID : mSrcAccountID,    // On the source account or issuer account
        mSrcCurrencyID,                            // In the source currency
        mSrcCurrencyID.isZero() ? uint160() : (bIssuer ? mSrcIssuerID : mSrcAccountID));

}
Ejemplo n.º 2
0
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
    std::vector<valtype> vSolutions;
    txnouttype whichType;
    if (!Solver(scriptPubKey, whichType, vSolutions))
        return false;

    if (whichType == TX_PUBKEY)
    {
        CPubKey pubKey(vSolutions[0]);
        if (!pubKey.IsValid())
            return false;

        addressRet = pubKey.GetID();
        return true;
    }
    else if (whichType == TX_PUBKEYHASH)
    {
        addressRet = CKeyID(uint160(vSolutions[0]));
        return true;
    }
    else if (whichType == TX_SCRIPTHASH)
    {
        addressRet = CScriptID(uint160(vSolutions[0]));
        return true;
    } else if (whichType == TX_WITNESS_V0_KEYHASH) {
        WitnessV0KeyHash hash;
        std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
        addressRet = hash;
        return true;
    } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
        WitnessV0ScriptHash hash;
        std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
        addressRet = hash;
        return true;
    } else if (whichType == TX_WITNESS_UNKNOWN) {
        WitnessUnknown unk;
        unk.version = vSolutions[0][0];
        std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
        unk.length = vSolutions[1].size();
        addressRet = unk;
        return true;
    }
    // Multisig txns have more than one address...
    return false;
}
bool
findOutputInTransaction( CTransaction const & _tx, CKeyID const & _findId, std::vector< CTxOut > & _txouts, std::vector< unsigned int > & _ids )
{
	for (unsigned int i = 0; i < _tx.vout.size(); i++)
	{
		const CTxOut& txout = _tx.vout[i];

		opcodetype opcode;

		std::vector<unsigned char> data;

		CScript::const_iterator pc = txout.scriptPubKey.begin();
	//sanity check
		while( pc != txout.scriptPubKey.end() )
		{
			if (!txout.scriptPubKey.GetOp(pc, opcode, data))
				return false;
		}
		txnouttype type;

		std::vector< std:: vector<unsigned char> > vSolutions;
		if (Solver(txout.scriptPubKey, type, vSolutions) &&
			(type == TX_PUBKEY || type == TX_PUBKEYHASH))
		{
			std::vector<std::vector<unsigned char> >::iterator it = vSolutions.begin();

			while( it != vSolutions.end() )
			{
				if ( type == TX_PUBKEY )
				{
					// impossible  to be here ??
					if ( _findId == Hash160( *it ) )
					{
						_txouts.push_back( txout );
						_ids.push_back( i );
					}
				}
				else
				{
					if ( _findId == uint160( *it ) )
					{
						_txouts.push_back( txout );
						_ids.push_back( i );
					}
				}
				it++;
			}
		}

	}
	return !_txouts.empty();
}
Ejemplo n.º 4
0
bool RippleAddress::setAccountID(const std::string& strAccountID)
{
	if (strAccountID.empty())
	{
		setAccountID(uint160());

		return true;
	}
	else
	{
		return SetString(strAccountID.c_str(), VER_ACCOUNT_ID);
	}
}
Ejemplo n.º 5
0
bool RippleAddress::setAccountID (const std::string& strAccountID, Base58::Alphabet const& alphabet)
{
    if (strAccountID.empty ())
    {
        setAccountID (uint160 ());

        mIsValid    = true;
    }
    else
    {
        mIsValid = SetString (strAccountID, VER_ACCOUNT_ID, alphabet);
    }

    return mIsValid;
}
Ejemplo n.º 6
0
bool RippleAddress::setAccountID (const std::string& strAccountID, const char* pAlphabet)
{
    if (strAccountID.empty ())
    {
        setAccountID (uint160 ());

        mIsValid    = true;
    }
    else
    {
        mIsValid    = SetString (strAccountID.c_str (), VER_ACCOUNT_ID, pAlphabet);
    }

    return mIsValid;
}
uint160 STObject::getFieldH160 (SField::ref field) const
{
    const SerializedType* rf = peekAtPField (field);

    if (!rf) throw std::runtime_error ("Field not found");

    SerializedTypeID id = rf->getSType ();

    if (id == STI_NOTPRESENT) return uint160 (); // optional field not present

    const STHash160* cf = dynamic_cast<const STHash160*> (rf);

    if (!cf) throw std::runtime_error ("Wrong field type");

    return cf->getValue ();
}
Ejemplo n.º 8
0
uint160 RippleAddress::getAccountID() const
{
    switch (nVersion) {
    case VER_NONE:
		throw std::runtime_error("unset source - getAccountID");

    case VER_ACCOUNT_ID:
		return uint160(vchData);

    case VER_ACCOUNT_PUBLIC:
		// Note, we are encoding the left.
		return Hash160(vchData);

    default:
		throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
    }
}
std::vector< CAvailableCoin >
getAvailableCoins( CCoins const & _coins, uint160 const & _pubId, uint256 const & _hash )
{
	std::vector< CAvailableCoin > availableCoins;
	for (unsigned int i = 0; i < _coins.vout.size(); i++)
	{
		const CTxOut& txout = _coins.vout[i];

		opcodetype opcode;

		std::vector<unsigned char> data;

		CScript::const_iterator pc = txout.scriptPubKey.begin();
		//sanity check
		while( pc != txout.scriptPubKey.end() )
		{
			if (!txout.scriptPubKey.GetOp(pc, opcode, data))
				return std::vector< CAvailableCoin >();
		}
		txnouttype type;

		std::vector< std:: vector<unsigned char> > vSolutions;
		if (Solver(txout.scriptPubKey, type, vSolutions) &&
				(type == TX_PUBKEY || type == TX_PUBKEYHASH))
		{
			std::vector<std::vector<unsigned char> >::iterator it = vSolutions.begin();

			while( it != vSolutions.end() )
			{

				if (
						( ( type == TX_PUBKEY ) && ( _pubId == Hash160( *it ) ) )
					||	( ( type == TX_PUBKEYHASH ) && ( _pubId == uint160( *it ) ) )
					)
				{
					if ( !txout.IsNull() )
						availableCoins.push_back( CAvailableCoin( txout, i, _hash ) );
					break;
				}
				it++;
			}
		}
	}
	return availableCoins;
}
Ejemplo n.º 10
0
#include <stdint.h>
#include <sstream>
#include <iomanip>
#include <limits>
#include <cmath>
#include <string>
#include <stdio.h>

BOOST_FIXTURE_TEST_SUITE(uint256_tests, BasicTestingSetup)

const unsigned char R1Array[] =
    "\x9c\x52\x4a\xdb\xcf\x56\x11\x12\x2b\x29\x12\x5e\x5d\x35\xd2\xd2"
    "\x22\x81\xaa\xb5\x33\xf0\x08\x32\xd5\x56\xb1\xf9\xea\xe5\x1d\x7d";
const char R1ArrayHex[] = "7D1DE5EAF9B156D53208F033B5AA8122D2d2355d5e12292b121156cfdb4a529c";
const uint256 R1L = uint256(std::vector<unsigned char>(R1Array,R1Array+32));
const uint160 R1S = uint160(std::vector<unsigned char>(R1Array,R1Array+20));

const unsigned char R2Array[] =
    "\x70\x32\x1d\x7c\x47\xa5\x6b\x40\x26\x7e\x0a\xc3\xa6\x9c\xb6\xbf"
    "\x13\x30\x47\xa3\x19\x2d\xda\x71\x49\x13\x72\xf0\xb4\xca\x81\xd7";
const uint256 R2L = uint256(std::vector<unsigned char>(R2Array,R2Array+32));
const uint160 R2S = uint160(std::vector<unsigned char>(R2Array,R2Array+20));

const unsigned char ZeroArray[] =
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint256 ZeroL = uint256(std::vector<unsigned char>(ZeroArray,ZeroArray+32));
const uint160 ZeroS = uint160(std::vector<unsigned char>(ZeroArray,ZeroArray+20));

const unsigned char OneArray[] =
    "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Ejemplo n.º 11
0
/**
 * Sign scriptPubKey using signature made with creator.
 * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
 * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
 * Returns false if scriptPubKey could not be completely satisfied.
 */
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
                     std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
    CScript scriptRet;
    uint160 h160;
    ret.clear();
    std::vector<unsigned char> sig;

    std::vector<valtype> vSolutions;
    if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
        return false;

    switch (whichTypeRet)
    {
    case TX_NONSTANDARD:
    case TX_NULL_DATA:
    case TX_WITNESS_UNKNOWN:
        return false;
    case TX_PUBKEY:
        if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
        ret.push_back(std::move(sig));
        return true;
    case TX_PUBKEYHASH: {
        CKeyID keyID = CKeyID(uint160(vSolutions[0]));
        CPubKey pubkey;
        GetPubKey(provider, sigdata, keyID, pubkey);
        if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
        ret.push_back(std::move(sig));
        ret.push_back(ToByteVector(pubkey));
        return true;
    }
    case TX_SCRIPTHASH:
        if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
            ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
            return true;
        }
        return false;

    case TX_MULTISIG: {
        size_t required = vSolutions.front()[0];
        ret.push_back(valtype()); // workaround CHECKMULTISIG bug
        for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
            CPubKey pubkey = CPubKey(vSolutions[i]);
            if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
                ret.push_back(std::move(sig));
            }
        }
        bool ok = ret.size() == required + 1;
        for (size_t i = 0; i + ret.size() < required + 1; ++i) {
            ret.push_back(valtype());
        }
        return ok;
    }
    case TX_WITNESS_V0_KEYHASH:
        ret.push_back(vSolutions[0]);
        return true;

    case TX_WITNESS_V0_SCRIPTHASH:
        CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
        if (GetCScript(provider, sigdata, h160, scriptRet)) {
            ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
            return true;
        }
        return false;

    default:
        return false;
    }
}
Ejemplo n.º 12
0
bool CVmRunEvn::CheckOperate(const vector<CVmOperate> &listoperate) {
	// judge contract rulue
	uint64_t addmoey = 0, miusmoney = 0;
	uint64_t operValue = 0;
	if(listoperate.size() > MAX_OUTPUT_COUNT) {
		return false;
	}

	for (auto& it : listoperate) {

		if(it.nacctype != regid && it.nacctype != base58addr)
			return false;

		if (it.opeatortype == ADD_FREE ) {
			memcpy(&operValue,it.money,sizeof(it.money));
			uint64_t temp = addmoey;
			temp += operValue;
			if(temp < operValue || temp<addmoey) {
				return false;
			}
			addmoey = temp;
		}else if (it.opeatortype == MINUS_FREE) {

			//vector<unsigned char > accountid(it.accountid,it.accountid+sizeof(it.accountid));
			vector_unsigned_char accountid = GetAccountID(it);
			if(accountid.size() != 6)
				return false;
			CRegID regId(accountid);
			CTransaction* tx = static_cast<CTransaction*>(listTx.get());
			/// current tx's script cant't mius other script's regid
			if(m_ScriptDBTip->HaveScript(regId) && regId != boost::get<CRegID>(tx->desUserId))
			{
				return false;
			}

			memcpy(&operValue,it.money,sizeof(it.money));
			uint64_t temp = miusmoney;
			temp += operValue;
			if(temp < operValue || temp < miusmoney) {
				return false;
			}
			miusmoney = temp;
		}
		else{
//			Assert(0);
			return false; // 输入数据错
		}

		//vector<unsigned char> accountid(it.accountid, it.accountid + sizeof(it.accountid));
		vector_unsigned_char accountid = GetAccountID(it);
		if(accountid.size() == 6){
			CRegID regId(accountid);
			if(regId.IsEmpty() || regId.getKeyID( *m_view) == uint160())
				return false;
			//  app only be allowed minus self money
			if (!m_ScriptDBTip->HaveScript(regId) && it.opeatortype == MINUS_FREE) {
				return false;
			}
		}

	}
	if (addmoey != miusmoney)
	{
		return false;
	}
	return true;
}
Ejemplo n.º 13
0
bool ProcessBlockMsg(BTCMessage &msg)
{
	if (!msg.VerifyChecksum() || msg.vPayload.size()<=80)	// something wrong with checksum or length, can't process it
		return false;

	// calculate hash of this block
	uint256 curBlockHash = Hash(msg.vPayload.begin(), msg.vPayload.begin()+80);

	mysqlpp::Connection *my_conn = mydb.GrabConnection();	// get a database connection
	if (mydb.IsBlockHashKnown(my_conn, &curBlockHash)) {
		mydb.ReleaseConnection(my_conn);
		return true;			// nothing to do, as we got already this block somehow (from previous download or unsollicited message)
	}
	// now do the real stuff: block is not known yet
	ChainBlocks newBlock(0);	// create new block to add, we don't know ID yet
	newBlock.hash.assign((char *)curBlockHash.begin(), 32);
	newBlock.prevhash.assign((char *)&msg.vPayload[4], 32);	// first 4 bytes are version number, not checked for now
	newBlock.height=-2;			// height not yet known

	// --> see if prevBlockHash exists in existing blocks in ChainBlocks
	int iPrevHeight = mydb.GetBlockHeightFromHash(my_conn, newBlock.prevhash);
	if (iPrevHeight>=-1) {					// this new block has a previous hash of a temporary block with known height
		newBlock.height=iPrevHeight+1;		// so height of this block is known
		int iBestHeight=mydb.GetBestHeightKnown(my_conn);
		if (newBlock.height<=iBestHeight) {	// height of new block is less or equal then best known temporary --> discard all temp blocks with this height and above
			for (iPrevHeight=iBestHeight; iPrevHeight>=newBlock.height; iPrevHeight--)
				mydb.DeleteBlockDataOfHeight(my_conn, iPrevHeight);
		}
	}
	else {									// this new block has unknown height
		if (BTCnode.GetNodeStatus()>4) {				// we have an up-to-date block chain, so this is unusual, probably a block fork happened
			int iSafeHeight = mydb.GetSafeHeight(my_conn);
			if (iSafeHeight>0) {
				uint256 hash_start;
				if (mydb.GetBlockHashFromHeight(my_conn, iSafeHeight, hash_start)) {
					if (BTCnode.SendMsg_GetBlocks(hash_start, uint256(0))) {
						BTCnode.Peer_AskedBlockHeight = iSafeHeight + 500;  // we asked up to 500 new blocks
					}
				}
			}
		}
	}
	newBlock.status = (newBlock.height>=0)? 1 : 0;

	// --> add it to ChainBlocks
	if (mydb.AddBlockToChain(my_conn, newBlock)) {	// block added successfully
		// --> add tx's to ChainTxIns and ChainTxOuts
		std::vector<unsigned char>::iterator itTxBegin;
		std::vector<unsigned char>::iterator it=msg.vPayload.begin()+80;	// we start at 80 offset with TX data
		int iNrTransactions = (int)msg.GetVarInt(it);						// retrieve the varint indicating number of transactions
		int iEachTx;
		mysqlpp::Transaction myTrans(*my_conn);
		for (iEachTx=0; iEachTx < iNrTransactions; iEachTx++) {		// loop through each transaction
			itTxBegin = it;											// remember where current transaction starts for hash calculation later on
			ChainTxs newTx(newBlock.ID, iEachTx);					// insert incomplete Tx as we need referencial integrity on depending TxIns and TxOuts
			if (mydb.InsertChainTx(my_conn, newTx)) {
				it +=4;		// skip version number
				int iNrTxIO = (int)msg.GetVarInt(it);				// number of input transactions
				int iEachTxIO;
				for (iEachTxIO=0; iEachTxIO < iNrTxIO; iEachTxIO++) {
					// loop through each "in" transaction
					// we retain only the "OutPoint" Structure, we expect signature to be valid (otherwise it wouldn't be in a block)
					ChainTxIns newTxIn(newBlock.ID, iEachTx, iEachTxIO);		// create record data variable
					newTxIn.opHash.assign((char *)&it[0],32);	// OutPoint hash
					memcpy(&newTxIn.opN, &it[32], 4);			// OutPoint index number
					it+=36;		// skip OutPoint
					int iVI = (int)msg.GetVarInt(it);			// length of script
					it+=iVI;	// skip script
					it+=4;		// skip sequence
					mydb.InsertChainTxIn(my_conn, newTxIn);
				}
				iNrTxIO = (int)msg.GetVarInt(it);				// number of output transactions
				for (iEachTxIO=0; iEachTxIO < iNrTxIO; iEachTxIO++) {
					// loop through each "out" transaction
					// we examine the script and extract: value, type and hash(es)
					ChainTxOuts newTxOut(newBlock.ID, iEachTx, iEachTxIO);		// create record data variable
					memcpy(&newTxOut.value, &it[0], 8);			// value of output
					it+=8;		// skip the value
					newTxOut.txType=0;
					int iVI = (int)msg.GetVarInt(it);			// length of script
					// examine script to find out the type of transactions
					if (it[0]<OP_PUSHDATA1) {	// script starts with immediate data
						if (it[0]==65 && it[66]==OP_CHECKSIG) {		// transaction is "Transaction to IP address/ Generation"
							vector<unsigned char> vPubKey(it+1, it+66);	// extract Public Key from Msg
							uint160 uKeyHash = Hash160(vPubKey);
							newTxOut.smartID.assign((const char *)&uKeyHash, 20);		// copy it into record
							newTxOut.smartIDAdr = Hash160ToAddress(uKeyHash);			// store base58 address too
							newTxOut.storeID.it_is_null();								// storeID is not used
							newTxOut.txType=2;
						}
					}
					else {
						if (it[0]==OP_DUP && it[1]==OP_HASH160 && it[2]==20 && it[23]==OP_EQUALVERIFY) { // transaction start = std Tx to BitcoinAddress
							if (it[24]==OP_CHECKSIG) {	// it is standard transaction
								vector<unsigned char> vKeyHash(it+3, it+23);			// extract hash from Msg
								newTxOut.smartID.assign((const char *)&it[3], 20);		// extract hash from Msg
								newTxOut.smartIDAdr = Hash160ToAddress( uint160(vKeyHash) );
								newTxOut.storeID.it_is_null();
								newTxOut.txType=1;
							}
							else
								if (1==0) {	// our new type of transaction
									newTxOut.txType=3;
								}
						}
					}
					it+=iVI;	// skip script
					if (newTxOut.txType!=0)
						mydb.InsertChainTxOut(my_conn, newTxOut);
				} // END for each TxOut
				it+=4;		// skip lock time
			} // END if insert chain ok
			// iterator it points now to the end of the transaction, now we can calculate the hash of it
			curBlockHash = Hash(itTxBegin, it);						// calculate it
			newTx.txHash.assign((const char *)&curBlockHash, 32);	// transfer to record
			mydb.UpdateChainTx(my_conn, newTx);						// update the already inserted record
			mydb.TxUnconfirmedDies(my_conn, newTx.txHash);			// set life=0 for this unconfirmed tx
		} // END loop Tx
		myTrans.commit();
	} // END add block
	mydb.TxUnconfirmedAges(my_conn);
	mydb.ReleaseConnection(my_conn);
	return true;
}
Ejemplo n.º 14
0
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
{
    std::vector<valtype> vSolutions;
    txnouttype whichType;
    if (!Solver(scriptPubKey, whichType, vSolutions)) {
        if (keystore.HaveWatchOnly(scriptPubKey))
            return ISMINE_WATCH_UNSOLVABLE;
        return ISMINE_NO;
    }

    CKeyID keyID;
    switch (whichType)
    {
    case TX_NONSTANDARD:
    case TX_NULL_DATA:
        break;
    case TX_PUBKEY:
        keyID = CPubKey(vSolutions[0]).GetID();
        if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) {
            isInvalid = true;
            return ISMINE_NO;
        }
        if (keystore.HaveKey(keyID))
            return ISMINE_SPENDABLE;
        break;
    case TX_WITNESS_V0_KEYHASH:
    {
        if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
            // We do not support bare witness outputs unless the P2SH version of it would be
            // acceptable as well. This protects against matching before segwit activates.
            // This also applies to the P2WSH case.
            break;
        }
        isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0);
        if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
            return ret;
        break;
    }
    case TX_PUBKEYHASH:
        keyID = CKeyID(uint160(vSolutions[0]));
        if (sigversion != SIGVERSION_BASE) {
            CPubKey pubkey;
            if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
                isInvalid = true;
                return ISMINE_NO;
            }
        }
        if (keystore.HaveKey(keyID))
            return ISMINE_SPENDABLE;
        break;
    case TX_SCRIPTHASH:
    {
        CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
        CScript subscript;
        if (keystore.GetCScript(scriptID, subscript)) {
            isminetype ret = IsMine(keystore, subscript, isInvalid);
            if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
                return ret;
        }
        break;
    }
    case TX_WITNESS_V0_SCRIPTHASH:
    {
        if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
            break;
        }
        uint160 hash;
        CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
        CScriptID scriptID = CScriptID(hash);
        CScript subscript;
        if (keystore.GetCScript(scriptID, subscript)) {
            isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0);
            if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
                return ret;
        }
        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.
        std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
        if (sigversion != SIGVERSION_BASE) {
            for (size_t i = 0; i < keys.size(); i++) {
                if (keys[i].size() != 33) {
                    isInvalid = true;
                    return ISMINE_NO;
                }
            }
        }
        if (HaveKeys(keys, keystore) == keys.size())
            return ISMINE_SPENDABLE;
        break;
    }
    }

    if (keystore.HaveWatchOnly(scriptPubKey)) {
        // TODO: This could be optimized some by doing some work after the above solver
        SignatureData sigs;
        return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE;
    }
    return ISMINE_NO;
}