Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

address: add toScript method and expose to api #269

Closed
wants to merge 3 commits into from

Conversation

tynes
Copy link
Contributor

@tynes tynes commented Oct 9, 2019

An address is a commitment to some program that will run in the Script interpreter. Every address has a mapping to a program. Depending on the type of address, it may or may not need an opening to the commitment. For example, p2pkh does not need an opening while p2sh does (the program preimage).

The abstract function that maps an address to a program accepts a version, data (hash in this codebase) and an opening to the commitment (top of Witness stack in this codebase). I find reasoning about addresses in this way to be useful. The hsd codebase calls things Script when in reality I think they should be called Program. There is a lot of confusion between what is a script/program/redeem script/witness etc.

This PR addresses this mapping by:

  • Adding a toScript method to the Address class that encompasses the functionality of converting an address to a script that can be ran in the Script interpreter. See codepath mentioned above here:

if (addr.version === 0) {

This method must accept the witness as an argument in the case of pay to witness script hash, as it is impossible to know the program that will be ran without the preimage. In the case of pay to pubkey hash, you do not need the witness.

For each of Coin, Input and Output the getJSON method additionally renders program and asm. The program is the address.toScript where the program is the raw hex bytecode of the program and the asm is the assembly printed for human consumption.

This will allow block explorers to better visualize the locking scripts of addresses. It will also allow block explorers to display the redeem script (program preimage) when p2sh outputs are spent. @kilpatty

Mark Tyneway added 2 commits October 9, 2019 13:22
This converts the Address into a Script.
This same functionality occurs in Script.exeucte,
as the Address.hash needs to be templated into
a Bitcoin Script to run through the interpreter.
In reality, Address.hash does not need to be
a hash. To know what the script will be,
the size of Address.hash must be observed. In
the case of 20 bytes, it is known to be a pay
to pubkey hash, which can be created with
Script.fromPubkeyHash. If the Address.hash
is 32 bytes, the witness must be present
to build the script correctly. In the case
of Address.version === 31, it is an OP_RETURN
script. Unknown Address types will return `null`.
Seeing an address alone isn't particularly
helpful in understanding what it does unless
you can tell the size difference between the
pay to pubkeyhash and pay to script hash.
This will be useful for block explorers.
@codecov-io
Copy link

codecov-io commented Oct 9, 2019

Codecov Report

Merging #269 into master will increase coverage by 0.1%.
The diff coverage is 96.29%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master     #269     +/-   ##
=========================================
+ Coverage   53.12%   53.22%   +0.1%     
=========================================
  Files         129      129             
  Lines       35751    35776     +25     
  Branches     6023     6035     +12     
=========================================
+ Hits        18993    19043     +50     
+ Misses      16758    16733     -25
Impacted Files Coverage Δ
lib/primitives/input.js 87.85% <100%> (+0.35%) ⬆️
lib/primitives/coin.js 84.55% <100%> (+0.34%) ⬆️
lib/primitives/output.js 89.69% <100%> (+0.32%) ⬆️
lib/primitives/address.js 84.73% <93.75%> (+0.82%) ⬆️
lib/covenants/rules.js 73.04% <0%> (-0.15%) ⬇️
lib/net/pool.js 24.22% <0%> (+0.04%) ⬆️
lib/net/peer.js 20.06% <0%> (+0.3%) ⬆️
lib/script/script.js 60.36% <0%> (+1.04%) ⬆️
lib/script/witness.js 64.82% <0%> (+1.37%) ⬆️
lib/script/opcode.js 49.83% <0%> (+1.67%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7c17227...11e4c34. Read the comment docs.

@tynes
Copy link
Contributor Author

tynes commented Oct 9, 2019

See example TX.toJSON with these changes applied:

{
  "hash": "7a03af60884104ef9f27ce0ba11b8f59754bb2585743cbcf76ecd9cc72aafe86",
  "witnessHash": "91a1360e61826b95531c8ec89d3d62768526125a4737390a16a79a6e272aee51",
  "mtime": 1570653688,
  "version": 0,
  "inputs": [
    {
      "prevout": {
        "hash": "2dbdf026de53fdde97a932e5cf23f401fb671157666ce1bc1a3c1640071c48b7",
        "index": 1
      },
      "witness": [
        "654c3fa70b5e3e54b925b97f9cf516066fe2719a83074842b778d1d62501f69a2b59ed279821d8c0e43fc1222e1e7df32dc2c234cff114bf04b0e8688a16ec9401",
        "035bd7808c83826cad9ab1997c88be3564117e27a083ea442e359779c3b4d84675"
      ],
      "sequence": 4294967295,
      "address": "hs1qhrc264e234wxqhl5tugvxp7lvllwc2phsmfdp6",
      "program": "76c014b8f0ad572a8d5c605ff45f10c307df67feec283788ac",
      "asm": "OP_DUP OP_BLAKE160 b8f0ad572a8d5c605ff45f10c307df67feec2837 OP_EQUALVERIFY OP_CHECKSIG"
    }
  ],
  "outputs": [
    {
      "value": 0,
      "address": "hs1qrsmnssdez3mp6kadvvwegfzh8h6fe06y6fhwjk",
      "program": "76c0141c373841b914761d5bad631d9424573df49cbf4488ac",
      "asm": "OP_DUP OP_BLAKE160 1c373841b914761d5bad631d9424573df49cbf44 OP_EQUALVERIFY OP_CHECKSIG",
      "covenant": {
        "type": 3,
        "action": "BID",
        "items": [
          "bbd9e905510ec6ca554591336a8ef507390c1fdb3200ea00a3af637535acfb63",
          "9d5e0000",
          "74687265652d68756e647265642d646f6d61696e732d74656e2d626964732d332d3634",
          "342714d9ea2c4eeb81209ebcedbc7d760cbff00a4edea5e67f297848b1f8051d"
        ],
        "asm": "TYPE:BID NAMEHASH:bbd9e905510ec6ca554591336a8ef507390c1fdb3200ea00a3af637535acfb63 HEIGHT:24221 NAME:74687265652d68756e647265642d646f6d61696e732d74656e2d626964732d332d3634 HASH:342714d9ea2c4eeb81209ebcedbc7d760cbff00a4edea5e67f297848b1f8051d"
      }
    },
    {
      "value": 59998932140,
      "address": "hs1q2a6yys0p22eennza42awfhtzd0wg6prczyaehc",
      "program": "76c01457744241e152b399cc5daabae4dd626bdc8d047888ac",
      "asm": "OP_DUP OP_BLAKE160 57744241e152b399cc5daabae4dd626bdc8d0478 OP_EQUALVERIFY OP_CHECKSIG",
      "covenant": {
        "type": 0,
        "action": "NONE",
        "items": [],
        "asm": "TYPE:NONE"
      }
    }
  ],
  "locktime": 0,
  "hex": "00000000012dbdf026de53fdde97a932e5cf23f401fb671157666ce1bc1a3c1640071c48b701000000ffffffff02000000000000000000141c373841b914761d5bad631d9424573df49cbf44030420bbd9e905510ec6ca554591336a8ef507390c1fdb3200ea00a3af637535acfb63049d5e00002374687265652d68756e647265642d646f6d61696e732d74656e2d626964732d332d363420342714d9ea2c4eeb81209ebcedbc7d760cbff00a4edea5e67f297848b1f8051dac0c37f80d000000001457744241e152b399cc5daabae4dd626bdc8d04780000000000000241654c3fa70b5e3e54b925b97f9cf516066fe2719a83074842b778d1d62501f69a2b59ed279821d8c0e43fc1222e1e7df32dc2c234cff114bf04b0e8688a16ec940121035bd7808c83826cad9ab1997c88be3564117e27a083ea442e359779c3b4d84675"
}

Need to test the p2wsh case before ready to merge.

Looking for acks

@tynes
Copy link
Contributor Author

tynes commented Oct 10, 2019

Updated with tests

@tynes tynes changed the title address: add toProgram method and expose to api address: add toScript method and expose to api Oct 10, 2019
@tynes tynes marked this pull request as ready for review October 10, 2019 18:23
@tynes tynes requested a review from boymanjor October 10, 2019 18:24
@pinheadmz
Copy link
Member

I think this may be more problematic than its worth.

This method must accept the witness as an argument, as it is impossible to know the program that will be ran without the preimage.

I think this is a pretty good sign that this method doesn't belong in Address, I think Script.decode() is good where it is, and doesn't need to be linked here. Addresses, as a concept, are for the sender, who only needs minimal information about the spending conditions (pubkey vs script -- even easier than bitcoin 😄 ). If you know the witness, you are already one step past the address.

@tynes
Copy link
Contributor Author

tynes commented Oct 10, 2019

There is no good way to distinguish what actual bitcoin script will be executed without knowing the protocol. People are not going to know to look at line 2200+ in lib/script/script.js to understand how addresses correspond to the actual bitcoin script that executes.

I also updated what you quoted above, as that applies in the p2wsh case. In the pay to pubkey hash case, you can always get back the correct script.

@pinheadmz
Copy link
Member

Actually, probably a more important point is that when spending from a witness pubkey hash output - there is no script at all. Script.fromPubKeyHash() is just used internally. SegWit removed the need for anyone to ever write OPDUP HASH160 etc on the chain...

@tynes
Copy link
Contributor Author

tynes commented Oct 10, 2019

The program is the script that runs to determine if it is a valid state transition. The witness is the initial stack for the VM execution. This happens for every transaction, and just seeing the address does not give insight into what is happening under the hood. You have the witness for the input so you are guaranteed to have a correctly rendered program.

For outputs, It is not guaranteed that you will have the program in this case, depending on the type of UTXO it is. If it is pay to pubkey hash, then you do have the program, it is simply filling a template. If it is a pay to script hash, you will likely not have the script. For a project like Bitcoin, you generally want to keep you script preimages hidden because you want all BTC to be fungible. I do not think that Handshake is money, so I think that in some cases it is ok to reveal your script preimages. Based on a counterparty learning the script and then hashing it and checking it against the hash encoded in the address, (which should be accessible as a hash and not just as an address because that is an additional encoding/decoding step which is annoying) they can determine their level of trust when transacting with you. This is how the non interactive nameswap scheme works - the counterparty must know that the name holder cannot back out of the deal by spending to a UPDATE and cancelling the atomic swap.

@@ -192,6 +194,38 @@ class Address extends bio.Struct {
return bech32.encode(hrp, version, hash);
}

/**
* Create Script from Address.
* @param {Witness} witness
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument is optional {Witness?}

@nodech nodech added enhancement general - improving existing feature primitives part of the codebase tests part of the codebase breaking-minor Backwards compatible - Release version labels Dec 10, 2021
@nodech nodech closed this Oct 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-minor Backwards compatible - Release version enhancement general - improving existing feature primitives part of the codebase tests part of the codebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants