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
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>
.
Examples:
- P2SH with a P2MS inside it: https://learnmeabitcoin.com/explorer/transaction/d3adb18d5e118bb856fbea4b1af936602454b44a98fc6c823aedc858b491fc13/output/0#scriptPubKey
- P2SH with a non-standard script inside it: https://learnmeabitcoin.com/explorer/transaction/7d46745463a28049cb93d2c964320912d9c296c1038cb24cc3716255c397a87f/output/1#scriptPubKey
Links:
-
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?– schmijosJan 17, 2021 at 15:52
-
1The <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.– inershaJan 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?– schmijosJan 19, 2021 at 8:43
-
1
From what I understand, a non-SegWit P2PH address is obtained by hashing the redeem script
ScriptPubKey
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>
<Public key C> OP_3 OP_CHECKMULTISIG
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>
<Public key C> OP_3 OP_CHECKMULTISIG
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);
ScriptSig
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>– iMilNov 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