You need to parse the script and get to the script type by examining the opcodes.
If you examine the rpc/rawtransaction.cpp source file on Bitcoin Core you'll see the RPC call that outputs this is getrawtransaction(). Tracing it further it leads us to ScriptPubKeyToUniv in core_write.cpp which calls so let's take a look at that:
const char* GetTxnOutputType(txnouttype t)
{
switch (t)
{
case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey";
case TX_PUBKEYHASH: return "pubkeyhash";
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
case TX_NULL_DATA: return "nulldata";
case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
case TX_WITNESS_UNKNOWN: return "witness_unknown";
}
return nullptr;
}
This subroutine maps script types to the string name which you asked about. In your case you got a TX_WITNESS_V0_SCRIPTHASH whose value is 0x06 per its definition in script/standard.h :
enum txnouttype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
We now know how to extract the scriptPubKey type name from the value, but we don't know how to extract that value from the raw bytes, so let's take a look at that next.
txnouttype is extracted from scriptPubkey by ExtractDestinations which is defined in script/standard.cpp, the relevant line being typeRet = Solver(scriptPubKey, vSolutions);. So let's step into Solver.
Here's the answer to your question:
txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
{
vSolutionsRet.clear();
// 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
if (scriptPubKey.IsPayToScriptHash())
{
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
vSolutionsRet.push_back(hashBytes);
return TX_SCRIPTHASH;
}
int witnessversion;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
return TX_WITNESS_V0_KEYHASH;
}
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
return TX_WITNESS_V0_SCRIPTHASH;
}
if (witnessversion != 0) {
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
return TX_WITNESS_UNKNOWN;
}
return TX_NONSTANDARD;
}
// Provably prunable, data-carrying output
//
// So long as script passes the IsUnspendable() test and all but the first
// byte passes the IsPushOnly() test we don't care what exactly is in the
// script.
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
return TX_NULL_DATA;
}
std::vector<unsigned char> data;
if (MatchPayToPubkey(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
return TX_PUBKEY;
}
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
return TX_PUBKEYHASH;
}
unsigned int required;
std::vector<std::vector<unsigned char>> keys;
if (MatchMultisig(scriptPubKey, required, keys)) {
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
return TX_MULTISIG;
}
vSolutionsRet.clear();
return TX_NONSTANDARD;
}
It returns the txnouttype and here's how it determines each one:
TX_SCRIPTHASH
this->size() == 23 &&
(*this)[0] == OP_HASH160 &&
(*this)[1] == 0x14 &&
(*this)[22] == OP_EQUAL
If the script size is 23 bytes, the first script byte is OP_HASH160, second byte is 0x14 and byte 22 is OP_EQUAL then it's a TX_SCRIPTHASH.
Witness Program
The following tests if the script is a witness program:
this->size() == 34 &&
(*this)[0] == OP_0 &&
(*this)[1] == 0x20
So, if the script is 34 bytes long, the first OP is 0 and the second byte is 0x20 then it's a witness program (This is the case in your question.).
If it's a witness program, then there are 4 possible script types:
First Witness Script type: TX_WITNESS_V0_KEYHASH
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
return TX_WITNESS_V0_KEYHASH;
}
If witness version is zero and the witness program is WITNESS_V0_KEYHASH_SIZE (20 bytes) long then it's a TX_WITNESS_V0_KEYHASH.
Second Witness Script type: TX_WITNESS_V0_SCRIPTHASH
If witness version is zer and the witness program is WITNESS_V0_SCRIPTHASH_SIZE (32 bytes) long then it's a TX_WITNESS_V0_SCRIPTHASH. This is the case in your question.
Note: We now know the specific answer to your question : you know if it's a witness_v0_scripthash if the script is 34 bytes long, begins with 00 20 and then there are 32 bytes following it.
Let's proceed for completeness.
Third Witness Script type: TX_WITNESS_UNKNOWN
If the witness version is different from zero, it's unknown.
Fourth and last Witness script type is TX_NONSTANDARD, which means it's not recognized by Bitcon Core.
Now onto the other script types.
TX_NULL_DATA
If the scriptPubKey size is more than one byte long and the first OP is a RETURN, then everything that follows is NULL data.
TX_PUBKEY
If the script is CPubKey::PUBLIC_KEY_SIZE (65) bytes long and the first byte on the script is 65 (CPubKey::PUBLIC_KEY_SIZE) and if the last OP on the script is OP_CHECKSIG then it's a legacy TX_PUBKEY script.
TX_PUBKEYHASH
If the script is 25 bytes long (this length is harcoded in script/standard.cpp), the first operation is OP_DUP, the second operation is OP_HASH160 and script[23] is OP_EQUALVERIFY and script[24] is OP_CHECKSIG then it's the most common type of script of all: TX_PUBKEYHASH.
TX_MULTISIG
Multisig is solved by MatchMultisig in script/standard.cpp. What it does is parse the script looping through it and calling GetOp (script/script.h) to parse the sig out and CPubKey::ValidSize(data) which checks if each signature is the right size. If at the end of this check we are at the end of the script, verified by (it + 1 == script.end()), then it's a valid multisig script.
TX_NONSTANDARD
Lastly if none of the above rules match, then it's a nonstandard script containing undefined contents. Most nodes would probably reject this script but miners and pools are able to insert stuff here if they want to.
These are all the script types in Bitcoin. The rules presented for each one is how you get the type out of scriptPubKey. To turn it into a string you use the enum map in GetTxnOutputType.