I'm working on a bitcoin block explorer in golang, in which I'd like to gather as much informations as possible about transactions.
I am able to extract addresses from every type of output, and mostly every type of input, except I am struggling to find clear P2SH documentation.
BIP16 doesn't say much about non-SegWit P2SH byte structure. I read many examples where OP_0 is the first byte, as opposed to P2WPKH in P2SH and P2WSH in P2SH where the first field is scriptSig's total length followed by 0x0/OP_0.
From what I understand, a non-SegWit P2PH address is obtained by hashing the redeem script, but I'd need something to anchor to, like for example 1st byte is OP_0, then comes the size of the data (probably a signature), loop until finding OP_TRUE followed by OP_PUSHDATA1?
But is this the unique, standard format?

2 Answers 2


A P2SH locking script on an output has the following structure:

         Output (scriptPubKey)
OP_HASH160 <scripthash> OP_EQUAL

The address comes for this comes from the <scripthash>, creating a "3address". This is the standard format for a P2SH, so you can identify it and work out the address from there.

That's all you need to get the address, but explaining a little further...

Interestingly, the <scripthash> can be the hash of any kind of script you like. However, it's most commonly used for putting the hash of a P2MS in there. So when you come to unlock this script, the scriptSig may look something like this:

          Input (scriptSig)
OP_0 <signature> <signature> <redeemscript>

What you're seeing here is basically a complete script for what would have been a classic P2MS scriptPubKey and scriptSig, where the <redeemscript> is a data push of a normal script with opcodes.

          Input (scriptSig)
              scriptSig       scriptPubKey (as a data push)
---------------------------- -------------- 
OP_0 <signature> <signature> <redeemscript>

With P2SH it's like you have a complete scriptSig+scriptPubKey contained inside the scriptSig, and it can contain any script so it doesn't have a standard form.

Anyway, this complete P2MS script starts with a OP_O, because all P2MS scriptSigs have to start with that due to the off-by-one bug that the OP_CHECKMULTISIG opcode. This is nothing to do with the OP_0 you get in Segwit scriptPubKeys.

When this script executes, the <redeemscript> will firstly be hashed and compared with the <scripthash> in the output to make sure it matches. If it does, the <redeemscript> is deserialized to see the opcodes:

      Input (scriptSig)
              scriptSig       scriptPubKey
---------------------------- -------------- 
OP_0 <signature> <signature> OP_2 <pubkey> <pubkey> <pubkey> OP_3 OP_CHECKMULTISIG

So this is what you would see on a classic P2MS output, but this just happened to be wrapped in a P2SH script because it's cheaper for the sender. So this is the standard format for a P2MS: (OP_N <pubkeys...> OP_M OP_CHECKMULTISIG).

This particular scriptPubKey does not actually have an address. There are <pubkeys> in there, but it wouldn't be technically correct to say those <pubkeys> have an "address", because an address refers to a specific locking script pattern, and is not just the friendlier version of any <pubkey>.



  • Thank you for this great answer! One puzzle piece is missing for me: How does the validator decide where the redeem script starts so that it can hash it?
    – schmijos
    Jan 17, 2021 at 15:52
  • 1
    The <redeemscript> is the last item in the scriptSig (it's actually a data push and not individual opcodes), so you can just look at that last item.
    – inersha
    Jan 18, 2021 at 22:57
  • Ok, I repeat to see if I understood it: So the the <redeemscript> is pushed bytecode. So it's on top of the stack when the <scriptSig> has been executed (pushes only). So by convention we interpret the top stack element as Bitcoin Script and load it into a new script stack for execution if the corresponding UXTO was a P2SH?
    – schmijos
    Jan 19, 2021 at 8:43
  • 1
    @schmijos Yes that's correct.
    – inersha
    Jan 20, 2021 at 14:50

From what I understand, a non-SegWit P2PH address is obtained by hashing the redeem script


I had the same problem and I have resolved it with this C++ code, and I think this code is self-describing. If you have the ScriptPubKey extract from the blk file, this is code calculate the correct address P2SH.

string opcode = hex.substr(0, 2);
int32_t optValue = std::stoul(opcode, nullptr, 16);
auto optMap = bitcoinOpCode.opCodeList.find(optValue);
string optCode = optMap->second;
if (optCode == "OP_HASH160") {
 cout << "Finded the P2SH";
 string scriptHash = hex.substr(4, hex.length() - 6);
 Bytes bytes = hexBytes(scriptHash.c_str());
 bytes.insert(bytes.begin(), 1, 5);

 string address = EncodeBase58Check(bytes);
 cout << "P2SH addresss is " << address;
 string endOpCode = hex.substr(hex.length() - 2, 2);
 int32_t optValueEnd = std::stoul(endOpCode, nullptr, 16);
 auto optMapEnd = bitcoinOpCode.opCodeList.find(optValueEnd);
 string optCodeEnd = optMapEnd->second;
 if(optCodeEnd == "OP_EQUAL"){
   cout << "\n\t----------------------------------------| Results |-----------------------------------" << endl;
   cout << "\t                               ###  Script PUB KEY HASH  ###                            " << endl;
   cout << "\t" << optCode << " "  << scriptHash << " " << optCodeEnd << endl;
   cout << "\t                                                                                      " << endl;
   cout << "\t The public key: " << address << "                                   " << endl;
   cout << endl;
   cout << "\t https://blockstream.info/address/" << address << endl;
   cout << "\t______________________________________________________________________________________"<< endl;

how I do calculate the correct address?

You know the complete P2SH, an example this

OP_0 <A Signature> <B Signature> OP_2 <Public key A> <Public key B>
OP_HASH160 <ScriptSig Hash> OP_EQUAL

Inside the this code you can find the scriptSig, so this:

OP_0 <A Signature> <B Signature> OP_2 <Public key A> <Public key B>

and the ScriptPubKey, so this:

OP_HASH160 <ScriptSig Hash> OP_EQUAL

With my code, I work only with the scriptPubKey and I found the dimension with the following mode

OP_HASH160 = hex.substr(0, 2);
<ScriptSig Hash> = hex.substr(4, hex.length() - 6);
OP_EQUAL = hex.substr(hex.length() - 2, 2);

on the data hex <ScriptSig Hash> I can run the EncodeBase58Check encoding, I have to change the code of bitcoin core(this) for change the hash library. Inside the bitcoin core code you can find the correct execution of EncodeBase58 (here)

Another important code is this to insert the mainet flag inside the byte.

bytes.insert(bytes.begin(), 1, 5);


The scriptSig inside is composed to two component, so:

  • the Sighnature: OP_0 <A Signature> <B Signature>

  • the program control: OP_2 <Public key A> <Public key B> <Public key C> OP_3 OP_CHECKMULTISIG

I didn't have to try to decode with code the script sig but I think this is more difficult to others because the dimension is not standard because the program control is a script P2MS and this script has more combination. I can suggest you create a decompiler intelligent for scriptSig because if you get the OP_2 hex inside the script you can read 2 signature but it is possible to get the OP_14 and your parser must be read 14 signature. I can suggest you to start decompiler the ScriptSig to end because you know the dimension end hexadecimal.

When you have extracted the pubkey and if you want an address bitcoin you can read my question here

  • Thanks a lot for the detailed algorithm explanation, but my question was more about the standard ScriptSig follows, creating a loop that extracts the redeem script is not hard, yet I must be sure the format is always of the form: <OP_0><{data1 size}{data1}><{data2 size}{data2}>...<OP_1><OP-PUSHDATA1><redeemScript>
    – iMil
    Nov 21, 2019 at 11:15
  • 1
    @iMil No because the signature depends on the combination of P2MS script, I have mentioned this argument in the ScriptSig section. Nov 21, 2019 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.