bitcoinjs / bitcoinjs-lib

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

How to recover bech32 address from Script.? #1104

Closed Overtorment closed 6 years ago

Overtorment commented 6 years ago

Im sorry if tihs is a dumb question. I already looked at #669 but still cant figure out how to recover a bech32 address from transaction input witness script

Given:

30450221009f696a5c1bb1735bb3f1f64276ac6838c4e04b6c89b5eda84e86d24022d4f7ab02207caae352a19fe830dea60be2cd0a73af27e40124ca7f57ff9451887e81a7569d01
035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8

Can you please help me? Thanks!

fanatid commented 6 years ago

You can restore address only if spent output was P2WPKH.

const bitcoin = require('bitcoinjs-lib')
const pubKey = Buffer.from('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8', 'hex')
const pubKeyHash = bitcoin.crypto.hash160(pubKey)
const scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash)
const address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.bitcoin)
console.log(address) // => bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv

Please fix me if I'm wrong.

junderw commented 6 years ago

@fanatid is right if it is bech32 P2WPKH, but if it is base58 P2SH-P2WPKH (aka it had a redeemscript in the scriptSig) it would be the below:

const bitcoin = require('bitcoinjs-lib')
const pubKey = Buffer.from('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8', 'hex')
const pubKeyHash = bitcoin.crypto.hash160(pubKey)
const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash)
const address1 = bitcoin.address.fromOutputScript(redeemScript, bitcoin.networks.bitcoin)
console.log(address1) // => bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv
// P2WPKH

const redeemScriptHash = bitcoin.crypto.hash160(redeemScript)
const scriptPubkey = bitcoin.script.scriptHash.output.encode(redeemScriptHash)
const address2 = bitcoin.address.fromOutputScript(scriptPubkey, bitcoin.networks.bitcoin)
console.log(address2) // 34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7
// P2SH-P2WPKH
junderw commented 6 years ago

https://api.smartbit.com.au/v1/blockchain/tx/21b1ad37e8c7d66ee25cdd3429ea1ef52823e0e706462497ba1335786d1a4f18

So if you want to parse a segwit input, taking the above data from a transaction api, it would be like:

const tx = JSON.parse(apiResponseAbove)

let inputAddresses = []
let inputs = tx.transaction.inputs

inputs.forEach(input => {

  let scriptSigChunks = input.script_sig.asm.split(/\s+/)
  if (scriptSigChunks[0] === '') scriptSigChunks = []

  if(input.witness === null) {
    console.log('not segwit')
    return
  } else if(scriptSigChunks.length > 1) {
    console.log('unknown input type: scriptSig is not P2SH but has witness')
    return
  } else if(scriptSigChunks.length === 0) {

    // is not P2SH
    let witnessScript = input.witness[input.witness.length - 1]
    let witnessType
    let pubKey
    if (witnessScript.match(/^0[23][0-9a-fA-F]{64}$/)) {
      pubKey = witnessScript
      witnessType = 'P2WPKH'
    } else {
      witnessType = 'P2WSH'
    }

    // get address from the hash160 of pubKey OR the sha256 of witnessScript depending on P2WPKH or P2WSH respectively

  } else {

    // is P2SH
    let redeemScript = scriptSigChunks[0]
    let witnessScript = input.witness[input.witness.length - 1]
    let witnessType
    if (witnessScript.match(/^0[23][0-9a-fA-F]{64}$/)) {
      witnessType = 'P2SH-P2WPKH'
    } else {
      witnessType = 'P2SH-P2WSH'
    }

    // get address from redeemScript hash160
  }

})
Overtorment commented 6 years ago

thanks all!

dcousens commented 6 years ago

Or using the new payments API (https://github.com/bitcoinjs/bitcoinjs-lib/pull/1096), aka 4.0.0:

let bjs = require('bitcoinjs-lib')
let witness = [
  Buffer.from('30450221009f696a5c1bb1735bb3f1f64276ac6838c4e04b6c89b5eda84e86d24022d4f7ab02207caae352a19fe830dea60be2cd0a73af27e40124ca7f57ff9451887e81a7569d01', 'hex'),
  Buffer.from('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8', 'hex')
]

let obj = bjs.payments.p2wpkh({ witness })

console.log(obj.address)
// bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv
dcousens commented 6 years ago

If it was p2sh(p2wpkh):

let bjs = require('bitcoinjs-lib')

let input = Buffer.from('160014e5e6cc9c155da3c9df725c3c4d9987c374e29987', 'hex')
let witness = [
  Buffer.from('30450221009f696a5c1bb1735bb3f1f64276ac6838c4e04b6c89b5eda84e86d24022d4f7ab02207caae352a19fe830dea60be2cd0a73af27e40124ca7f57ff9451887e81a7569d01', 'hex'),
  Buffer.from('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8', 'hex')
]

let p2sh = bjs.payments.p2sh({ input, witness })
console.log(p2sh.address)
// 34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7

let redeem = bjs.payments.p2wpkh(p2sh.redeem)
console.log(redeem.address)
// bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv

:heart_eyes: