[ad_1]
When a receiver asks to be sent money, they specify the conditions under which they want to be able to spend the funds in an output script. Later when the receiver wants to spend their funds, they need to provide an input script that satisfies the output script of the output they are spending. In transaction validation, the input script is evaluated first, then the resulting stack is used as the starting point to evaluate the output script.
For example with P2PKH, the input script contains a signature and a public key, the output script contains OP_DUP OP_HASH160 pubkeyhash OP_EQUALVERIFY OP_CHECKSIG
.
In evaluation the input script pushes first the signature then the pubkey on the stack. The stack is then passed to the output script which:
- duplicates the pubkey
- replaces the first of the two pubkey copies with a hash of the pubkey
- pushes the pubkeyhash to the stack
- Verifies that the pubkeyhash pushed from the output script and the pubkeyhash hashed from the pubkey in the input are equal
- Checks that the remaining pubkey and signature amount to a valid signature of the transaction.
There are a number of output scripts that are standardized for frequent use. Some of these cover single-sig usecases, but there are also multiple standard output types for complex scripts. Addresses are a convenient shorthand to communicate the receiver’s output scripts to the sender for standard output script types. Even before P2SH was introduced, a receiver could define arbitrary conditions by writing out the corresponding output script using the opcodes defined in Bitcoin Script. These bare scripts are uncommon, since their arbitrary content does not lend itself to an address standard. The UX is horrible: instead of an address with a checksum, the receiver and sender have to exchange the actual script, and the sender needs to create a raw transaction manually specifying the output script. (P2SH was introduced to improve the UX around defining your own spending conditions while allowing for an address standard.)
The transaction you are looking at contains such a bare script: instead of following one of the standard output schemes, the receiver defined their own output script and satisfied it accordingly in the succeeding input.
The output script specified in the output a601…0e0c:0
of the preceding transaction is:
OP_DUP
OP_0
OP_LESSTHAN
OP_VERIFY
OP_ABS
OP_PUSHNUM_1
OP_PUSHNUM_16
OP_WITHIN
OP_TOALTSTACK
OP_PUSHBYTES_33 0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71
OP_CHECKSIGVERIFY
OP_FROMALTSTACK
The input script in the first input of 54fa…814f
is:
OP_PUSHBYTES_72
3045022100d92e4b61452d91a473a43cde4b469a472467c0ba0cbd5ebba0834e4f4762810402204802b76b7783db57ac1f61d2992799810e173e91055938750815b6d8a675902e01
OP_PUSHNUM_NEG1
The script essentially amounts to an obfuscated version of a P2PK output as can be seen by evaluating the script execution:
- The input script pushes a signature onto the stack.
Current Stack (left is bottom):SIG
- The number
-1
is pushed onto the stack
Stack:SIG -1
- The stack is passed to output script validation
- The number -1 is duplicated
Stack:SIG -1 -1
- A
0
is pushed onto the stack
Stack:SIG -1 -1 0
OP_LESSTHAN
consumes two items (a, b) from the stack returns a1
to the stack because a (-1
) is less than b (0
).
Stack:SIG -1 1
OP_VERIFY
consumes the1
on top of the stack and succeeds
Stack:SIG -1
OP_ABS
replaces the top stack item with its absolute value
Stack:SIG 1
- A
1
is pushed to the stack
Stack:SIG 1 1
- A
16
is pushed to the stack
Stack:SIG 1 1 16
OP_WITHIN
consumes three values (x min max) and returns a1
because x is greater than or equal to the minimum and smaller than the maximum
Stack:SIG 1
OP_TOALTSTACK
removes the top element from the stack and puts it on the alternative stack.
Stack:SIG
, Altstack:1
- A pubkey is pushed on the stack:
Stack:SIG PUBKEY
, Altstack:1
OP_CHECKSIGVERIFY
consumes the signature and pubkey and verifies that the signature is valid in the context of the transaction and pubkey.
Stack:<empty>
, Altstack:1
OP_FROMALTSTACK
removes the top value of the alt stack and places it on the stack:
Stack:1
, Altstack:<empty>
- The script succeeds because it ends with a single truthy value
1
on the stack.
These transactions may break some block explorers in the sense that some block explorers may only have support for standard scripts and would not properly display bare outputs. It seems to me that modern block explorers no longer suffer from that:
e.g. mempool.space shows the output script in the preceding transaction…
… and the spending transaction just fine.
In case “breaking block explorers” was understood as a privacy benefit, this transaction is not more private. In Bitcoin transactions do not spend funds from addresses: addresses merely specify the conditions under which funds can be spent, but each input must specify exactly which transaction output it is spending.
The preceding transaction a601…0e0c
created a single output a601…0e0c:0
with the mentioned bare output script that could be spent by the owner of that script, and the first input of 54fa…814f explicitly spent that a601…0e0c:0
, to create another transaction output 54fa…814f:0
that can be spent by the receiver in control of the address 1GMaxweLLbo8mdXvnnC19Wt2wigiYUKgEB
.
I.e. every UTXO is uniquely identifiable and the transaction graph is public information. The absence of an address has no privacy benefit.
[ad_2]
Source link