<p>The first thing to remember here is that the entire script that is executed comes in two parts. The first part comes in the output that is being spent. This script fragment is incomplete - if you run it in the script interpreter by itself, you will get an error. The second part comes in the input that spends the output. When verifying a transaction, the input script and the output script are essentially concatenated and executed together as a single script (they aren't actually concatenated but it's easier to think of it that way).</p>
<p>The next thing to remember is that the script interpreter is relatively dumb. It's a simple machine whose only memory is the stack and executes operations as it sees them. It does not know about standard script patterns and so cannot assign any special meaning or treatment to specific script patterns. It also does not know what kind of data a stack element is before it executes an opcode. It will take whatever is on the stack and interpret it as needed for the opcode, even if it's the wrong data type.</p>
<p>If your output script is <code>OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code>, this is just a script fragment that requires an input script to provide the necessary data to satisfy it. The input script you propose would be <code>&lt;push(sig with P1)&gt; &lt;push(P1)&gt; &lt;push(P1)&gt;</code> which results in a combined script of <code>&lt;push(sig with P1)&gt; &lt;push(P1)&gt; &lt;push(P1)&gt; OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code>.</p>
<p>To make it easier to follow the script execution, I'm going to notate the <code>P1</code>s as <code>P1_a</code> and <code>P1_b</code>.</p>
<p>We can walk through how this would verify:</p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Remaining Script</th>
<th>Current Operation</th>
<th>Resulting Stack (top is first)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;push(P1_a)&gt; &lt;push(P1_b)&gt; OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(sig with P1)&gt;</code></td>
<td><code>&lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>&lt;push(P1_b)&gt; OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(P1_a)&gt;</code></td>
<td><code>&lt;P1_a&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(P1_b)&gt;</code></td>
<td><code>&lt;P1_b&gt; &lt;P1_a&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>&lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>OP_HASH160</code></td>
<td><code>&lt;hash(P1_b)&gt; &lt;P1_a&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(hash(P1))&gt;</code></td>
<td><code>&lt;hash(P1)&gt; &lt;hash(P1_b)&gt; &lt;P1_a&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_CHECKSIG</code></td>
<td><code>OP_EQUALVERIFY</code></td>
<td><code>&lt;P1_a&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code> </code></td>
<td><code>OP_CHECKSIG</code></td>
<td><code>&lt;true&gt;</code></td>
</tr>
</tbody>
</table>
</div>
<p>As you can see, we end with <code>P1_a</code> and <code>sig with P1</code> as the two stack elements that go into the <code>OP_CHECKSIG</code>. These two are provided by the person trying to spend the transaction. As mentioned in the previous answer, an input script of <code>&lt;sig with P2&gt; &lt;P2&gt; &lt;P1&gt;</code> can satisfy this output script as well. Let's walk through that to see how it would work:</p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Remaining Script</th>
<th>Current Operation</th>
<th>Resulting Stack (top is first)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;push(P2)&gt; &lt;push(P1)&gt; OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(sig with P2)&gt;</code></td>
<td><code>&lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code>&lt;push(P1)&gt; OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(P2)&gt;</code></td>
<td><code>&lt;P2&gt; &lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code>OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(P1)&gt;</code></td>
<td><code>&lt;P1&gt; &lt;P2&gt; &lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code>&lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>OP_HASH160</code></td>
<td><code>&lt;hash(P1)&gt; &lt;P2&gt; &lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code>OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(hash(P1))&gt;</code></td>
<td><code>&lt;hash(P1)&gt; &lt;hash(P1)&gt; &lt;P2&gt; &lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code>OP_CHECKSIG</code></td>
<td><code>OP_EQUALVERIFY</code></td>
<td><code>&lt;P2&gt; &lt;sig with P2&gt;</code></td>
</tr>
<tr>
<td><code> </code></td>
<td><code>OP_CHECKSIG</code></td>
<td><code>&lt;true&gt;</code></td>
</tr>
</tbody>
</table>
</div>
<p>This results in success as well, hence your script is vulnerable. The reason it works is because <code>P2</code> is never verified to be the public key that is specified by the hash. The script interpreter does not know or care that this could be a standard script, so it does not know to enforce that <code>P2</code> has to be the same as <code>P1</code>. The script itself needs to do that, and this script fails to do that because it does not tell the interpreter to check that the two stack elements are equal.</p>
<p>As for how this could be exploited, anyone who observes the transaction with your intended input script would learn what <code>P1</code> is. Then anyone can copy that <code>P1</code> and produce a transaction that has the second script with <code>P2</code>.</p>
<p>Lastly, let's look at how the actual script with <code>OP_DUP</code> works. The output script is <code>OP_DUP OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code> and the input script is <code>&lt;sig with P1&gt; &lt;P1&gt;</code></p>
<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th>Remaining Script</th>
<th>Current Operation</th>
<th>Resulting Stack (top is first)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;push(P1)&gt; OP_DUP OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(sig with P1)&gt;</code></td>
<td><code>&lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_DUP OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(P1)&gt;</code></td>
<td><code>&lt;P1&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_HASH160 &lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>OP_DUP</code></td>
<td><code>&lt;P1_dup&gt; &lt;P1&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>&lt;push(hash(P1))&gt; OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>OP_HASH160</code></td>
<td><code>&lt;hash(P1_dup)&gt; &lt;P1&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_EQUALVERIFY OP_CHECKSIG</code></td>
<td><code>&lt;push(hash(P1))&gt;</code></td>
<td><code>&lt;hash(P1)&gt; &lt;hash(P1_dup)&gt; &lt;P1&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code>OP_CHECKSIG</code></td>
<td><code>OP_EQUALVERIFY</code></td>
<td><code>&lt;P1&gt; &lt;sig with P1&gt;</code></td>
</tr>
<tr>
<td><code> </code></td>
<td><code>OP_CHECKSIG</code></td>
<td><code>&lt;true&gt;</code></td>
</tr>
</tbody>
</table>
</div>
<p>This is secure because we duplicate <code>P1</code> exactly and then hash it and check the hashes. Because we duplicated it, we know that it must be the same and if the hashes match, then the original also has the same hash. This ensures that the public key that we use in the <code>OP_CHECKSIG</code> is the public key that was intended.</p>
<p>Of course this assumes that <code>OP_DUP</code> duplicates exactly as we expect it to. This is a reasonable assumption - we're assuming that all of the other opcodes work as we expect them to as well. If we didn't, then any script could verify because the opcodes could be doing anything.</p>
<p>Lastly, the script interpreter could have been written to give your script special treatment. It could have been written to behave differently if it saw this particular script template and verify that the top two stack elements were identical. Then this would not be vulnerable. However it is not written that way, and so we cannot rely on this behavior. Furthermore, the script construction that you have described is also inefficient. It's a harder template to match, and it requires actually providing duplicated public keys, which for some transactions, can result in a significant portion of their size. This will then result in increased transaction fees and less block space. So overall, the DUP method is more concise and safer.</p>
<p>It should also be noted that Segwit does introduce a simple single key construction that doesn't actually rely on script and is really just template matching. This is likely more in line with what you are suggesting.</p>

CC BY-SA 4.0
2022-12-26T17:44:43.317Z
First of all thank you for the detailed and timely response. I suppose my follow up is to question WHY the interpreter is written to be so dumb? We do a lot of work in bitcoin to generate valid outputs, verification of those outputs might be more expensive to do with a smarter interpreter but it would be very secure would it not?
Poseidon
CC BY-SA 4.0
2022-12-26T21:28:16.560Z
Well it's an interpreter - its job is to interpret. As for why Satoshi chose this design in the initial Bitcoin protocol: you'll have to ask him to be sure, but it makes sense: it's very flexible, because anyone can design their own spending conditions, rather than be restricted to perhaps just a few standard constructions. Do not however, that this is *not* how Bitcoin commonly works anymore. Since the introduction of segwit, there are specialized constructions for just-a-pubkey-hash that don't involve any explicit opcodes.
Pieter Wuille
CC BY-SA 4.0
2022-12-26T22:42:57.730Z
It's very interesting to think about the evolution of the design behind transactions, I agree it makes tons of sense but it also just makes me wonder how this design was produced and if there is some key concepts behind it that I might be misunderstanding. The cryptography makes tons of sense to me but when it gets into the opcode behaviors I start to have many of these questions, perhaps because computers and computer science was explained very differently at the time of bitcoin's genesis than it is in the modern day of flashy titles on yt videos. Thanks anyways, for all your work!
Poseidon
CC BY-SA 4.0
2022-12-26T17:23:14.530Z
2022-12-26T17:23:14.530Z
Ava Chow
<p>Let's work out the hypothetical scenario. Alice sends coins to Bob, and then to Carol, and the malicious miner Mallory tries to steal the last sent coins.</p>
<p>The honest scenario is this:</p>
<ul>
<li>Bob generates a keypair <code>privB</code> / <code>pubB</code>.</li>
<li>Bob creates a script <code>OP_HASH160 &lt;hash(pubB)&gt; OP_EQUALVERIFY OP_CHECKSIG</code>.</li>
<li>Bob gives this script to Alice by encoding it in an address (in this hypothetical world, there is an address format for such scripts).</li>
<li>Alice constructs a transaction tx1 which sends 0.1 BTC to that <code>OP_HASH160 &lt;hash(pubB)&gt; OP_EQUALVERIFY OP_CHECKSIG</code> script.</li>
<li>The transaction confirms.</li>
<li>Now Bob creates a transaction tx2 that spends the received 0.1 BTC UTXO, using his private key <code>peivB</code>, and sends it Carol (by giving the transaction an output with scriptPubKey equal to Carol's script). The scriptSig for tx2's input would be <code>&lt;sigB&gt; &lt;pubB&gt; &lt;pubB&gt;</code>. Running the spent output's scriptPubKey after this would yield success: the hashed key matched the hash in the script, and the signature matches the public key.</li>
</ul>
<p>Now, Mallory the miner sees this transaction, and steals it:</p>
<ul>
<li>Mallory generates their own keypair <code>privM</code> / <code>pubM</code>.</li>
<li>Mallory sees tx2, but creates an alternative tx2, where the input spending Bob's UTXO is replaced with <code>&lt;sigM&gt; &lt;pubM&gt; &lt;pubB&gt;</code>, and an output sending to himself.</li>
</ul>
<p>This input is <em>also</em> valid! If you work it out, the stack will be:</p>
<ul>
<li>Initially: <code>[sigM, pubM, pubB]</code>.</li>
<li>Execute <code>OP_HASH160</code>: <code>[sigM, pubM, hash(pubB)]</code>.</li>
<li>Execute <code>&lt;hash(pubB)&gt;</code>: <code>[sigM, pubM, hash(pubB), hash(pubB)]</code>.</li>
<li>Execute <code>OP_EQUALVERIFY</code>: <code>[sigM, pubM]</code> (the two last stack elements were equal, so no failure).</li>
<li>Execute <code>OP_CHECKSIG</code>: <code>[1]</code> (the signature sigM is a valid signature made with the private key corresponding to public key pubM, so the signature check succeeds).</li>
<li>Success because end of script is reached and there is a 1 on top of the stack.</li>
</ul>
<p>So despite the fact that the signature and key don't match the hash of pubB, the signature was verified against the miner's own public key, spending the coins.</p>

CC BY-SA 4.0
2022-12-26T17:20:56.330Z
2022-12-26T17:20:56.330Z
Pieter Wuille
<p>Re-reading your question, I realize my other answer may be besides the point.</p>
<p>It is certainly possible that the scripting language's semantics would recognize this scenario and cause failure if someone were to try spending transaction outputs of this form, if the two public keys don't match. After all, the script does see both these keys as input.</p>
<p>The point is simply that that is not what the scripting language does. Even though a large majority of transactions use scripts of this form, it is not the only thing the scripting language is designed for. It's a programming language, and the interpreter does not try to guess what the users' intent was when executing. It simply executes the script, opcode by opcode, with the semantics those opcodes have. And if the outcome is a true value on the stack at the end, the spend is valid.</p>
<p>If we're going to accept making changes to the scripting language with the purpose of making typical scripts shorter, we can go even further. For example, we could say that if the scriptPubKey is just a pubkey hash (and no opcodes whatsoever), then the scriptSig <em>must</em> be exactly a public key (matching that hash) and a signature (valid for the provided public key). That would save another 3 bytes in every output.</p>
<p>This is perfectly feasible with a softfork consensus change, but you have to recognize that this is arguably bypassing the point of the scripting language: allowing users to customize how their outputs can be spent, for the purpose of making a (very) common case shorter.</p>
<p>In fact, such softforks <em>have</em> happened: segwit (BIP141) and taproot (BIP341) both introduced new, very short, scriptPubKey types with special semantics that don't include any opcodes (or just one, which functions as a version number), because they envision users will very commonly want these.</p>

CC BY-SA 4.0
2022-12-26T22:38:33.057Z
This makes a ton of sense thank you for responding so quickly. Do you think it would it be a practical addition to bitcoin script to have this? Or as you said this has basically been implemented with segwit and taproot. I am pretty new to Bitcoin (2020) as you might possibly tell. It was hard for me to imagine how UTXO works without understanding the p2pk scripts. It also explains the added privacy of generating new address' which is often not explained in this context. So the transaction model that would save 3 bytes would be way less private which is just a poor trade-off I suppose.
Poseidon
CC BY-SA 4.0
2022-12-26T22:45:02.603Z
I think there is no point to have something like this, no; segwit already addresses this use case already, in an even cheaper way.
Pieter Wuille
CC BY-SA 4.0
2022-12-26T22:46:40.957Z
That's what I figured, I have never heard of this point of view about segwit, it pretty much explains everything I was wondering.
Poseidon
CC BY-SA 4.0
2022-12-26T22:52:11.143Z
It wasn't the main benefit/purpose of segwit, but as it had a need to introduce effectively a new type of script anyway, that opportunity was used to make some minor efficiency improvements too.
Pieter Wuille
CC BY-SA 4.0
2022-12-26T23:09:19.920Z
The purpose was to enable lightning correct? I suppose it also enables layer-2's that use a similar model to lightning, was this part of the consideration or was it  focused towards working specifically for lightning network? Seems like a very good thing for interoperability with side chains.
Poseidon
CC BY-SA 4.0
2022-12-27T00:27:37.097Z
Its purpose was solving transaction malleability (which is a prerequisite for many higher-level protocols that could be built on top; lightning was just one) and providing a mild capacity increase. It has absolutely nothing to do with sidechains.
Pieter Wuille
CC BY-SA 4.0
2022-12-27T00:35:04.220Z
My mistake, it seems that many articles were essentially presenting lightning as an explanation of why segwit was needed.
Poseidon
CC BY-SA 4.0
2022-12-26T17:33:30.097Z
2022-12-26T17:33:30.097Z
Pieter Wuille
<p>Original Thread: <a href="https://bitcoin.stackexchange.com/questions/107860/why-op-dup-instead-of-providing-the-pubkey-twice-in-p2pkh/116508#116508">Why OP_DUP instead of providing the PubKey twice in P2PKH?</a>
I am struggling to understand the logic in the most popular answer and my &quot;reputation&quot; is &lt; 50 so I cannot comment my response.</p>
<p>If the specification for a different single key type of p2pk transaction required only a single public key there would be no situation in which a miner was able to provide a second key. For example you have explained that the setup for the transaction looks like <code>OP_HASH160 &lt;hash(P1)&gt; OP_EQUALVERIFY OP_CHECKSIG</code> if this is true then the only way in which someone would be able to produce an output that looks like this <code>&lt;sig with P2&gt; &lt;P2&gt; &lt;P1&gt;</code>  (with their own public key) is by modifying the number of public keys used in the transaction. The specification for a transaction with a single public key should not be able to be spent by any other public key, even one that can prove it is derived from the private key which owns the original. I understand that this may not align with the core logic of bitcoin script, if this is the case I would really appreciate an explanation into why. It seems cryptographically secure, is the security issue with the logical implementation of transactions?</p>
<p>I'm also not following how the core logic would not be able to tell that a different public key is not a valid duplicate of the original key (in the situation where we simply provided one or two public keys in a row)? Your answer seems to suggest that the vulnerability is within the ability for anyone to add a key to the transaction and sign for it. However if the key was anything different from the original public key it would not really be the result of a valid OP_DUP right?  You would not be able to verify the signature of that transaction with the original public key, is that not enough to prove it is coming from another key and is therefore an invalid signature?</p>
<p>Overall it seems to me that the vulnerability scenario where a miner provided an alternative public key would rely on the ability for there to be more than one public key variable semantically in the transaction. The OP_DUP semantic in this situation causes this theoretical vulnerability since it is apparently implicitly duplicated and is not verifiably duplicated. If you remove the duplication entirely you have a transaction in which a public key references an output from which it owns and provides the signature proving this owner relationship. So while I understand why removing the OP_DUP could cause vulnerability with the current implementation, I struggle to understand why a much simpler single key transaction could not be possible to remove all of this overhead.</p>

<transactions><p2pkh><opcodes><p2pk>
A question about the role of OP_DUP in the P2PK(H) transaction specification, and possibility of slightly smaller single address transactions
Poseidon
# A question about the role of OP_DUP in the P2PK(H) transaction specification, and possibility of slightly smaller single address transactions

(Asked by: username_0 on 2022-12-26)

Original Thread: [Why OP\_DUP instead of providing the PubKey twice in P2PKH?](https://bitcoin.stackexchange.com/questions/107860/why-op-dup-instead-of-providing-the-pubkey-twice-in-p2pkh/116508#116508)
I am struggling to understand the logic in the most popular answer and my "reputation" is < 50 so I cannot comment my response.

If the specification for a different single key type of p2pk transaction required only a single public key there would be no situation in which a miner was able to provide a second key. For example you have explained that the setup for the transaction looks like `OP_HASH160 <hash(P1)> OP_EQUALVERIFY OP_CHECKSIG` if this is true then the only way in which someone would be able to produce an output that looks like this `<sig with P2> <P2> <P1>` (with their own public key) is by modifying the number of public keys used in the transaction. The specification for a transaction with a single public key should not be able to be spent by any other public key, even one that can prove it is derived from the private key which owns the original. I understand that this may not align with the core logic of bitcoin script, if this is the case I would really appreciate an explanation into why. It seems cryptographically secure, is the security issue with the logical implementation of transactions?

I'm also not following how the core logic would not be able to tell that a different public key is not a valid duplicate of the original key (in the situation where we simply provided one or two public keys in a row)? Your answer seems to suggest that the vulnerability is within the ability for anyone to add a key to the transaction and sign for it. However if the key was anything different from the original public key it would not really be the result of a valid OP\_DUP right? You would not be able to verify the signature of that transaction with the original public key, is that not enough to prove it is coming from another key and is therefore an invalid signature?

Overall it seems to me that the vulnerability scenario where a miner provided an alternative public key would rely on the ability for there to be more than one public key variable semantically in the transaction. The OP\_DUP semantic in this situation causes this theoretical vulnerability since it is apparently implicitly duplicated and is not verifiably duplicated. If you remove the duplication entirely you have a transaction in which a public key references an output from which it owns and provides the signature proving this owner relationship. So while I understand why removing the OP\_DUP could cause vulnerability with the current implementation, I struggle to understand why a much simpler single key transaction could not be possible to remove all of this overhead.

**Tags:** transactions, p2pkh, opcodes, p2pk


---

## 3 answers

### Answer by username_2 on 2022-12-26. Score: 5 | Accepted answer

Re-reading your question, I realize my other answer may be besides the point.

It is certainly possible that the scripting language's semantics would recognize this scenario and cause failure if someone were to try spending transaction outputs of this form, if the two public keys don't match. After all, the script does see both these keys as input.

The point is simply that that is not what the scripting language does. Even though a large majority of transactions use scripts of this form, it is not the only thing the scripting language is designed for. It's a programming language, and the interpreter does not try to guess what the users' intent was when executing. It simply executes the script, opcode by opcode, with the semantics those opcodes have. And if the outcome is a true value on the stack at the end, the spend is valid.

If we're going to accept making changes to the scripting language with the purpose of making typical scripts shorter, we can go even further. For example, we could say that if the scriptPubKey is just a pubkey hash (and no opcodes whatsoever), then the scriptSig *must* be exactly a public key (matching that hash) and a signature (valid for the provided public key). That would save another 3 bytes in every output.

This is perfectly feasible with a softfork consensus change, but you have to recognize that this is arguably bypassing the point of the scripting language: allowing users to customize how their outputs can be spent, for the purpose of making a (very) common case shorter.

In fact, such softforks *have* happened: segwit (BIP141) and taproot (BIP341) both introduced new, very short, scriptPubKey types with special semantics that don't include any opcodes (or just one, which functions as a version number), because they envision users will very commonly want these.

> **Comments:**
> **username_0**: This makes a ton of sense thank you for responding so quickly. Do you think it would it be a practical addition to bitcoin script to have this? Or as you said this has basically been implemented with segwit and taproot. I am pretty new to Bitcoin (2020) as you might possibly tell. It was hard for me to imagine how UTXO works without understanding the p2pk scripts. It also explains the added privacy of generating new address' which is often not explained in this context. So the transaction model that would save 3 bytes would be way less private which is just a poor trade-off I suppose.  
> **username_2**: I think there is no point to have something like this, no; segwit already addresses this use case already, in an even cheaper way.  
> **username_0**: That's what I figured, I have never heard of this point of view about segwit, it pretty much explains everything I was wondering.  
> **username_2**: It wasn't the main benefit/purpose of segwit, but as it had a need to introduce effectively a new type of script anyway, that opportunity was used to make some minor efficiency improvements too.  
> **username_0**: The purpose was to enable lightning correct? I suppose it also enables layer-2's that use a similar model to lightning, was this part of the consideration or was it  focused towards working specifically for lightning network? Seems like a very good thing for interoperability with side chains.  
> **username_2**: Its purpose was solving transaction malleability (which is a prerequisite for many higher-level protocols that could be built on top; lightning was just one) and providing a mild capacity increase. It has absolutely nothing to do with sidechains.  
> **username_0**: My mistake, it seems that many articles were essentially presenting lightning as an explanation of why segwit was needed.  

---

### Answer by username_1 on 2022-12-26. Score: 4

The first thing to remember here is that the entire script that is executed comes in two parts. The first part comes in the output that is being spent. This script fragment is incomplete - if you run it in the script interpreter by itself, you will get an error. The second part comes in the input that spends the output. When verifying a transaction, the input script and the output script are essentially concatenated and executed together as a single script (they aren't actually concatenated but it's easier to think of it that way).

The next thing to remember is that the script interpreter is relatively dumb. It's a simple machine whose only memory is the stack and executes operations as it sees them. It does not know about standard script patterns and so cannot assign any special meaning or treatment to specific script patterns. It also does not know what kind of data a stack element is before it executes an opcode. It will take whatever is on the stack and interpret it as needed for the opcode, even if it's the wrong data type.

If your output script is `OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG`, this is just a script fragment that requires an input script to provide the necessary data to satisfy it. The input script you propose would be `<push(sig with P1)> <push(P1)> <push(P1)>` which results in a combined script of `<push(sig with P1)> <push(P1)> <push(P1)> OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG`.

To make it easier to follow the script execution, I'm going to notate the `P1`s as `P1_a` and `P1_b`.

We can walk through how this would verify:

| Remaining Script | Current Operation | Resulting Stack (top is first) |
| --- | --- | --- |
| `<push(P1_a)> <push(P1_b)> OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(sig with P1)>` | `<sig with P1>` |
| `<push(P1_b)> OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(P1_a)>` | `<P1_a> <sig with P1>` |
| `OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(P1_b)>` | `<P1_b> <P1_a> <sig with P1>` |
| `<push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `OP_HASH160` | `<hash(P1_b)> <P1_a> <sig with P1>` |
| `OP_EQUALVERIFY OP_CHECKSIG` | `<push(hash(P1))>` | `<hash(P1)> <hash(P1_b)> <P1_a> <sig with P1>` |
| `OP_CHECKSIG` | `OP_EQUALVERIFY` | `<P1_a> <sig with P1>` |
|  | `OP_CHECKSIG` | `<true>` |

As you can see, we end with `P1_a` and `sig with P1` as the two stack elements that go into the `OP_CHECKSIG`. These two are provided by the person trying to spend the transaction. As mentioned in the previous answer, an input script of `<sig with P2> <P2> <P1>` can satisfy this output script as well. Let's walk through that to see how it would work:

| Remaining Script | Current Operation | Resulting Stack (top is first) |
| --- | --- | --- |
| `<push(P2)> <push(P1)> OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(sig with P2)>` | `<sig with P2>` |
| `<push(P1)> OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(P2)>` | `<P2> <sig with P2>` |
| `OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(P1)>` | `<P1> <P2> <sig with P2>` |
| `<push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `OP_HASH160` | `<hash(P1)> <P2> <sig with P2>` |
| `OP_EQUALVERIFY OP_CHECKSIG` | `<push(hash(P1))>` | `<hash(P1)> <hash(P1)> <P2> <sig with P2>` |
| `OP_CHECKSIG` | `OP_EQUALVERIFY` | `<P2> <sig with P2>` |
|  | `OP_CHECKSIG` | `<true>` |

This results in success as well, hence your script is vulnerable. The reason it works is because `P2` is never verified to be the public key that is specified by the hash. The script interpreter does not know or care that this could be a standard script, so it does not know to enforce that `P2` has to be the same as `P1`. The script itself needs to do that, and this script fails to do that because it does not tell the interpreter to check that the two stack elements are equal.

As for how this could be exploited, anyone who observes the transaction with your intended input script would learn what `P1` is. Then anyone can copy that `P1` and produce a transaction that has the second script with `P2`.

Lastly, let's look at how the actual script with `OP_DUP` works. The output script is `OP_DUP OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` and the input script is `<sig with P1> <P1>`

| Remaining Script | Current Operation | Resulting Stack (top is first) |
| --- | --- | --- |
| `<push(P1)> OP_DUP OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(sig with P1)>` | `<sig with P1>` |
| `OP_DUP OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `<push(P1)>` | `<P1> <sig with P1>` |
| `OP_HASH160 <push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `OP_DUP` | `<P1_dup> <P1> <sig with P1>` |
| `<push(hash(P1))> OP_EQUALVERIFY OP_CHECKSIG` | `OP_HASH160` | `<hash(P1_dup)> <P1> <sig with P1>` |
| `OP_EQUALVERIFY OP_CHECKSIG` | `<push(hash(P1))>` | `<hash(P1)> <hash(P1_dup)> <P1> <sig with P1>` |
| `OP_CHECKSIG` | `OP_EQUALVERIFY` | `<P1> <sig with P1>` |
|  | `OP_CHECKSIG` | `<true>` |

This is secure because we duplicate `P1` exactly and then hash it and check the hashes. Because we duplicated it, we know that it must be the same and if the hashes match, then the original also has the same hash. This ensures that the public key that we use in the `OP_CHECKSIG` is the public key that was intended.

Of course this assumes that `OP_DUP` duplicates exactly as we expect it to. This is a reasonable assumption - we're assuming that all of the other opcodes work as we expect them to as well. If we didn't, then any script could verify because the opcodes could be doing anything.

Lastly, the script interpreter could have been written to give your script special treatment. It could have been written to behave differently if it saw this particular script template and verify that the top two stack elements were identical. Then this would not be vulnerable. However it is not written that way, and so we cannot rely on this behavior. Furthermore, the script construction that you have described is also inefficient. It's a harder template to match, and it requires actually providing duplicated public keys, which for some transactions, can result in a significant portion of their size. This will then result in increased transaction fees and less block space. So overall, the DUP method is more concise and safer.

It should also be noted that Segwit does introduce a simple single key construction that doesn't actually rely on script and is really just template matching. This is likely more in line with what you are suggesting.

> **Comments:**
> **username_0**: First of all thank you for the detailed and timely response. I suppose my follow up is to question WHY the interpreter is written to be so dumb? We do a lot of work in bitcoin to generate valid outputs, verification of those outputs might be more expensive to do with a smarter interpreter but it would be very secure would it not?  
> **username_2**: Well it's an interpreter - its job is to interpret. As for why Satoshi chose this design in the initial Bitcoin protocol: you'll have to ask him to be sure, but it makes sense: it's very flexible, because anyone can design their own spending conditions, rather than be restricted to perhaps just a few standard constructions. Do not however, that this is *not* how Bitcoin commonly works anymore. Since the introduction of segwit, there are specialized constructions for just-a-pubkey-hash that don't involve any explicit opcodes.  
> **username_0**: It's very interesting to think about the evolution of the design behind transactions, I agree it makes tons of sense but it also just makes me wonder how this design was produced and if there is some key concepts behind it that I might be misunderstanding. The cryptography makes tons of sense to me but when it gets into the opcode behaviors I start to have many of these questions, perhaps because computers and computer science was explained very differently at the time of bitcoin's genesis than it is in the modern day of flashy titles on yt videos. Thanks anyways, for all your work!  

---

### Answer by username_2 on 2022-12-26. Score: 4

Let's work out the hypothetical scenario. Alice sends coins to Bob, and then to Carol, and the malicious miner Mallory tries to steal the last sent coins.

The honest scenario is this:

* Bob generates a keypair `privB` / `pubB`.
* Bob creates a script `OP_HASH160 <hash(pubB)> OP_EQUALVERIFY OP_CHECKSIG`.
* Bob gives this script to Alice by encoding it in an address (in this hypothetical world, there is an address format for such scripts).
* Alice constructs a transaction tx1 which sends 0.1 BTC to that `OP_HASH160 <hash(pubB)> OP_EQUALVERIFY OP_CHECKSIG` script.
* The transaction confirms.
* Now Bob creates a transaction tx2 that spends the received 0.1 BTC UTXO, using his private key `peivB`, and sends it Carol (by giving the transaction an output with scriptPubKey equal to Carol's script). The scriptSig for tx2's input would be `<sigB> <pubB> <pubB>`. Running the spent output's scriptPubKey after this would yield success: the hashed key matched the hash in the script, and the signature matches the public key.

Now, Mallory the miner sees this transaction, and steals it:

* Mallory generates their own keypair `privM` / `pubM`.
* Mallory sees tx2, but creates an alternative tx2, where the input spending Bob's UTXO is replaced with `<sigM> <pubM> <pubB>`, and an output sending to himself.

This input is *also* valid! If you work it out, the stack will be:

* Initially: `[sigM, pubM, pubB]`.
* Execute `OP_HASH160`: `[sigM, pubM, hash(pubB)]`.
* Execute `<hash(pubB)>`: `[sigM, pubM, hash(pubB), hash(pubB)]`.
* Execute `OP_EQUALVERIFY`: `[sigM, pubM]` (the two last stack elements were equal, so no failure).
* Execute `OP_CHECKSIG`: `[1]` (the signature sigM is a valid signature made with the private key corresponding to public key pubM, so the signature check succeeds).
* Success because end of script is reached and there is a 1 on top of the stack.

So despite the fact that the signature and key don't match the hash of pubB, the signature was verified against the miner's own public key, spending the coins.


---

