bitcoinjs / bitcoinjs-lib

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

Segwit: P2WPKH embedded in BIP16 P2SH #851

Closed junderw closed 6 years ago

junderw commented 6 years ago

Is there a current example of this somewhere using current master...?

Also, as an extension of this, what would be an example of P2WSH (which is a 2 of 3 multisig script, let's say) that is also embedded in a BIP16 P2SH.

I knew it wouldn't work like this, and I was right, txb.sign throws an error.

EDIT: I got P2WSH-in-P2SH AND P2WPKH-in-P2SH to work. See below (I overwrote the old code)

P2WPKH-in-P2SH

var bitcoin = require('bitcoinjs-lib')

var keyPair = bitcoin.ECPair.fromWIF('*****', bitcoin.networks.testnet)

var pubKey = keyPair.getPublicKeyBuffer()
var pubKeyHash = bitcoin.crypto.hash160(pubKey)

var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash)
var redeemScriptHash = bitcoin.crypto.hash160(redeemScript)

var scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash)
var P2SHaddress = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)

// console.log(P2SHaddress)

// [
//   {
//     "address": "2NERT3qnEWDuJx8rcgHhf8fuLFhMJrcDEPb",
//     "txid": "5fae347057f0e62106716413218eeb6a79dd6d9de3496156728245c9af43a0de",
//     "vout": 1,
//     "scriptPubKey": "a914e849b44257f9a9589588d51682e06e8b4f58a55787",
//     "amount": 0.001,
//     "satoshis": 100000,
//     "confirmations": 0,
//     "ts": 1503033827
//   },
//   {
//     "address": "2NERT3qnEWDuJx8rcgHhf8fuLFhMJrcDEPb",
//     "txid": "4bfa42b065bc0496d5bd824f630396faead3358cff1176959dae355698ac7a97",
//     "vout": 0,
//     "scriptPubKey": "a914e849b44257f9a9589588d51682e06e8b4f58a55787",
//     "amount": 0.001,
//     "satoshis": 100000,
//     "height": 1178864,
//     "confirmations": 236
//   }
// ]

var txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)

txb.addInput('5fae347057f0e62106716413218eeb6a79dd6d9de3496156728245c9af43a0de',
             1,
             0xffffffff,
             scriptPubKey)
txb.addInput('4bfa42b065bc0496d5bd824f630396faead3358cff1176959dae355698ac7a97',
             0,
             0xffffffff,
             scriptPubKey)

txb.addOutput(scriptPubKey,
              199000)

txb.sign(0, keyPair, redeemScript, null, 100000)
txb.sign(1, keyPair, redeemScript, null, 100000)

var tx = txb.build()

var byteLength = tx.byteLength() // 387 bytes
var weight = tx.weight() // 897 weight units ( / 4 == 224.25 byte cost)
var satoshiPerByte = 100
var satoshiPerWeight = satoshiPerByte / 4 // 1 byte == 4 weight

var oldFees = parseInt(byteLength * satoshiPerByte)
var newFees = parseInt(weight * satoshiPerWeight) // satoshi fees

console.log(oldFees) // 38700 satoshi
console.log(newFees) // 22425 satoshi

console.log(tx.toHex()) // raw transaction

// curl -X POST -d "{\"hex\": \"TTTTTTTTT\"}" https://testnet-api.smartbit.com.au/v1/blockchain/pushtx

P2WSH-in-P2SH (multisig 2-of-2)

var bitcoin = require('bitcoinjs-lib')

var keyPairs = [
  '********',
  '********'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, bitcoin.networks.testnet) })

var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })

var witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys)
var witnessScriptHash = bitcoin.crypto.sha256(witnessScript)

var redeemScript = bitcoin.script.witnessScriptHash.output.encode(witnessScriptHash)
var redeemScriptHash = bitcoin.crypto.hash160(redeemScript)

var scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash)
var P2SHaddress = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)

// console.log(P2SHaddress)
// TEST UTXO
// [
//   {
//     "address": "2N9sBjLU5Qdw7iUH5PPFcP4pAuHqngA5eh6",
//     "txid": "a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529",
//     "vout": 1,
//     "scriptPubKey": "a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87",
//     "amount": 0.001,
//     "satoshis": 100000,
//     "confirmations": 0,
//     "ts": 1503023150
//   }
// ]

var txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)

txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529',
             1,
             0xffffffff,
             scriptPubKey)

txb.addOutput(scriptPubKey,
              99000)

txb.sign(0, keyPairs[0], redeemScript, null, 100000, witnessScript)
txb.sign(0, keyPairs[1], redeemScript, null, 100000, witnessScript)

var tx = txb.build()

var byteLength = tx.byteLength() // 340 bytes
var weight = tx.weight() // 700 weight units ( / 4 == 175 byte cost)
var satoshiPerByte = 100
var satoshiPerWeight = satoshiPerByte / 4 // 1 byte == 4 weight

var oldFees = parseInt(byteLength * satoshiPerByte)
var newFees = parseInt(weight * satoshiPerWeight) // satoshi fees

console.log(oldFees) // 34000 satoshi
console.log(newFees) // 17500 satoshi

console.log(tx.toHex()) // raw transaction

// curl -X POST -d "{\"hex\": \"TTTTTTT\"}" https://testnet-api.smartbit.com.au/v1/blockchain/pushtx
dcousens commented 6 years ago

Excellent example(s) to be added to README.

Will do.

junderw commented 6 years ago

Thanks!

junderw commented 6 years ago

@dcousens I fixed my example. It now works.

dcousens commented 6 years ago

@junderw use .virtualSize instead of / 4?

See https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/transaction.js#L201

dcousens commented 6 years ago

@afk11 virtualSize probably should be virtualByteLength ... for consistency.

junderw commented 6 years ago

re: using virtualSize

The goal of this snippet was to give my colleagues an idea of how fee calculations will be done on segwit transactions and introducing them to the concept of weight in an intuitive readable couple of lines.

In prod we will definitely use virtualSize. ( | virtualByteLength)

dcousens commented 6 years ago

Closing in favour of https://github.com/bitcoinjs/bitcoinjs-lib/pull/852

@junderw it is probably still worth show-casing how to spend the SegWit outputs, which #852 doesn't cover.

Feel like submitting a PR and putting it in test/integration/transactions.js? If not, I'll do it.

lacksfish commented 4 years ago

I know this is a long closed and "resolved" issue, and I would like to say test for p2sh-p2wpkh can be found here, but it is not the clearest example possible, unfortunately. The createPayment() and getInputData() functions make understanding the flow rather... clutterly.

Another example can be found here, but it features the now deprecated TransactionBuilder - and no example for using PSBT can be found elsewhere.

I would say I do comprehend what I am doing, but I'm trying to offload the signing step to a ledger hardware wallet, and struggling a bit with that. If this is the wrong place and I do not find the problem - I will create a separate issue asking for help later. Just wanted to point out there is not really concise documentation apart from the hard-to-follow test cases, and wanted to ask if there will be better examples in the future. :)