bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.66k stars 2.09k forks source link

Differences between segwit bech32 and p2sh addresses? #1452

Closed Mr-Mondragon closed 5 years ago

Mr-Mondragon commented 5 years ago

Given a private key, I can generate its corresponding P2WPKH address as well as its segwit P2SH address like this:

const bitcoin = require('bitcoinjs-lib');

const wif = 'KwDieuoz4S9kHeGCgjhw3L9G6EqbS3knZgn5XLSKCmpeDqnp5ozH';
const keyPair = bitcoin.ECPair.fromWIF(wif);

var address_p2wpkh = null;
var address_segwit_p2sh = null;

{
  const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey })
  address_p2wpkh = address;
}

{
  const { address } = bitcoin.payments.p2sh({
    redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey })
  })
  address_segwit_p2sh = address;
}

console.log("bech32 p2wpkh address: "+address_p2wpkh);
// result: bc1qmrfd6fzfya6kc2q6k5rfvycjlvqsmymmqfs8m6

console.log("segwit p2sh address: "+address_segwit_p2sh);
// result: 3BhuyDBgp6yXfkHkvF7wayybPwTkqS6LLV

Three questions:

  1. The P2WPKH address is by default bech32 encoded, whereas the P2SH version is by default Base58Check encoded. As far as I understand, bech32 and Base58Check are just two different ways of encoding data. Is it just convention or common practice to use bech32 for P2WPKH addresses and Base58Check for P2SH addresses? Would it also be technically OK (although most wallets probably won't support it) to do the opposite, i.e. use Base58Check encoding for a P2WPKH address, and bech32 encoding for a P2SH address, or is there a more fundamental reason to this convention?

  2. Besides the difference in encoding, am I correct that the P2WPKH address actually contains different data than the P2SH address? More specifically, the P2WPKH would be a hash of a public key, and the P2SH is a hash of a script (a segwit script to be exact). This also means one cannot be derived from the other, i.e. I cannot convert a P2WPKH address into a P2SH one or vice versa, correct?

  3. If someone sends me bitcoins, does it matter if they send it to the P2WPKH or P2SH address? As long as I have the private key, I can always generate either address and thus spend from the one it was sent to, right?

junderw commented 5 years ago
  1. Standards. Specifically BIP173 dictate that segwit witness programs v0-v16 use bech32. Since P2SH (BIP16) specifies a version byte based on bs58check, it is implied that this is the only way to encode it because bech32 does not have a full byte for versioning info. Yes. you can technically encode the address data any way you want. You can embed the raw data into a cat jpeg if you want. But because that is a non-standard way of doing it no other wallets will be able to read your method of encoding. P2WPKH encoded in base58 will at best error out, at worst LOSE MONEY. And depends on how the wallet software handles the unexpected nature of your non-standard format.
  2. You can convert a P2WPKH address into P2SH, but you can not convert P2SH into P2WPKH. This is because the P2SH is a hash of the P2WPKH witness program, which can be decoded from its bech32 address, but obviously you can't reverse a hash to get that same data.
  3. Does it matter? That's subjective. What differs? Fees, for one. P2WPKH is slightly cheaper than P2SH-P2WPKH. Also, bech32 vs. base58 addresses. Many exchanges still haven't supported bech32 so you may have trouble withdrawing to your wallet if your wallet ONLY uses bech32... which is one good reason to use a wallet that is P2SH-P2WPKH. But this decision is up to you to decide if it "matters"
junderw commented 5 years ago

For 2, in the p2sh example try replacing p2wpkh({ pubkey: keyPair.publicKey }) in your source with p2wpkh({ address: address_p2wpkh }) and see what happens

Mr-Mondragon commented 5 years ago

Crystal clear, thank you so much Jonathan! Very happy user of Bitcoin-js here! 😃