Bitcoinjs-lib: Create a 1-to-1 Transaction

Created on 30 May 2020  路  10Comments  路  Source: bitcoinjs/bitcoinjs-lib

i want to Create a 1-to-1 Transaction

when like this

    const alice = bitcoin.ECPair.fromWIF(
      'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr',
    );
    const psbt = new bitcoin.Psbt();
    psbt.setVersion(2); // These are defaults. This line is not needed.
    psbt.setLocktime(0); // These are defaults. This line is not needed.
    psbt.addInput({
      hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
      index: 0,
      sequence: 0xffffffff, // These are defaults. This line is not needed.
      nonWitnessUtxo: Buffer.from(
        '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' +
          '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' +
          'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' +
          '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' +
          '631e5e1e66009ce3710ceea5b1ad13ffffffff01' +
          // value in satoshis (Int64LE) = 0x015f90 = 90000
          '905f010000000000' +
          // scriptPubkey length
          '19' +
          // scriptPubkey
          '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' +
          // locktime
          '00000000',
        'hex',
      ),
    });

How do I get hash and nonWitnessUtxo? Is it through bitcoinjs?

Hope someone can help me

Most helpful comment

The hash/index is the hash of the transaction which contains the utx you are going to spend, while the index is the utx index in that tx. Same for the nonWitnessUtxo, is the full transaction containing the utx.

To retrieve these data, you could use a blockexplorer and its api; personally I suggest to use blockstream explorer, api docs are available here: https://github.com/Blockstream/esplora/blob/master/API.md

Endpoints are [testnet ? 'https://blockstream.info/testnet/api' : 'https://blockstream.info/api']

All 10 comments

The hash/index is the hash of the transaction which contains the utx you are going to spend, while the index is the utx index in that tx. Same for the nonWitnessUtxo, is the full transaction containing the utx.

To retrieve these data, you could use a blockexplorer and its api; personally I suggest to use blockstream explorer, api docs are available here: https://github.com/Blockstream/esplora/blob/master/API.md

Endpoints are [testnet ? 'https://blockstream.info/testnet/api' : 'https://blockstream.info/api']

Piggybacking on this question. I try to sweep all uxtos from one testnet address.

First I get the uxtos of this address:

https://blockstream.info/testnet/api/address/mfs8TLBd62Pusu9dsjCugFGkop7qveLASn/utxo

This gives me a list of transaction ids. I guess it's these I have to use as hash in addInput?

Then I find every raw transaction for these ids, e.g. for this tx:

https://blockstream.info/testnet/api/tx/36503c549d044dd64de1e2a5f1ecd3472f2ac6a1d11902ac3b5c5f01f5d61474/hex

Result is


But then, when I try to add this as input

const input = {
  hash: u.txid, // The txid from above
  index,  // A sequential number I'm giving
  nonWitnessUtxo: Buffer.from(rawTx.data), // The hex data from above
  network: TESTNET, //bitcoin.networks.testnet
}

I get this error:

UnhandledPromiseRejectionWarning: RangeError [ERR_OUT_OF_RANGE]: The value of "offset" is out of range. It must be >= 0 and <= 2666. Received 2700
    at boundsError (internal/buffer.js:77:9)
    at Buffer.readUInt32LE (internal/buffer.js:211:5)
    at readUInt32 (/node_modules/bitcoinjs-lib/src/transaction.js:56:24)
    at Function.fromBuffer (/node_modules/bitcoinjs-lib/src/transaction.js:102:19)
    at addNonWitnessTxCache (/node_modules/bitcoinjs-lib/src/psbt.js:1137:40)
    at Psbt.addInput (/node_modules/bitcoinjs-lib/src/psbt.js:165:7)
    at /node_modules/bitcoinjs-lib/src/psbt.js:142:42
    at Array.forEach (<anonymous>)
    at Psbt.addInputs (/node_modules/bitcoinjs-lib/src/psbt.js:142:16)
    at createTransaction (file:///createtx.mjs:41:7)

I'm missing something, but I don't know where to start...

You need to tell Buffer.from that the data you have is a hex string. Otherwise it expects a BufferLike Array of numbers from 0 to 255. (ie. Uint8Array etc)
Buffer.from(rawTx.data, 'hex')

Also, network is not needed for the inputs. network is passed to the new Psbt({ network: TESTNET }) call

You need to tell Buffer.from that the data you have is a hex string. Otherwise it expects a BufferLike Array of numbers from 0 to 255. (ie. Uint8Array etc)
Buffer.from(rawTx.data, 'hex')

I knew it had to be something simple, small and easy to overlook :) Thanks!

Going furter:

function createTransaction(inputs, address, value, keyPair) {
    const psbt = new bitcoin.Psbt({ network: TESTNET });

    psbt.addInputs(inputs); // the inputs are generated from the utxos from before
    psbt.addOutput({
        address,  // The address that will be receiving the sats
        value,    // The combined value of the utxos minus a transaction fee
    });
    psbt.signAllInputs(keyPair);  // Generated from WIF: keyPair = bitcoin.ECPair.fromWIF(<privateWIFhere>, TESTNET);
    psbt.validateSignaturesOfAllInputs();
    psbt.finalizeAllInputs();
    return psbt.extractTransaction().toHex();
}

Gives this error:

UnhandledPromiseRejectionWarning: Error: No signatures to validate
    at Psbt.validateSignaturesOfInput (/node_modules/bitcoinjs-lib/src/psbt.js:265:13)
    at /node_modules/bitcoinjs-lib/src/psbt.js:257:12
    at Array.map (<anonymous>)
    at Psbt.validateSignaturesOfAllInputs (/node_modules/bitcoinjs-lib/src/psbt.js:256:52)
    at createTransaction (file:///createtx.mjs:49:7)
    at file:///createtx.mjs:32:18

signAllInputs() seems to work, it doesn't complain, but then validateSignaturesOfAllInputs() says that there are no signatures?

signAllInputs works if it signs ANY input.

validateSignaturesOfAllInputs works only if ALL inputs are signed.

So some of your inputs are not signed properly.

Are you sure every single input is spending an output for the same private key, and are you sure you have the correct info in inputs? (ie. your example above is correct for P2PKH addresses, but that's it)

ahh, you are giving the wrong index.

the index you pass to addInput is the index of the output in the nonWitnessUtxo transaction that you are spending.

Aha, is that the vout?

utxos: [
  {
    txid: 'e6dc8972311082f41cb104f1d3c1c90ffc7a6a7f671d00db48072ae7b8e6c031',
    vout: 0,
    status: {
      confirmed: true,
      block_height: 1744094,
      block_hash: '0000000000000a2017aebef5be7748d5dedebd686f0ba932738da4fcfc5af096',
      block_time: 1589414266
    },
    value: 22000
  },
  {
    txid: 'fa241e9bb394b4054e638d2da5e8f60e2a6c09a861f8b0e3924d7d513d9f0143',
    vout: 0,
    status: {
      confirmed: true,
      block_height: 1744094,
      block_hash: '0000000000000a2017aebef5be7748d5dedebd686f0ba932738da4fcfc5af096',
      block_time: 1589414266
    },
    value: 1000000
  },
  {
    txid: '05349143b730137c397ec0dcbbb818ce5badd208062d77c8e0a3814bf9915789',
    vout: 0,
    status: {
      confirmed: true,
      block_height: 1746439,
      block_hash: '00000000ab90d50bcecbc7929137e7da5f17f69f8eaea2c277985e9b99103db6',
      block_time: 1590332162
    },
    value: 1000000
  },
  {
    txid: 'e4038d0134c99775a55d64b201458f26631cb8668e2935f2ed0bd1714a531e8a',
    vout: 1,
    status: {
      confirmed: true,
      block_height: 1740147,
      block_hash: '000000000000b015c4a714d4cea2ccbbbe99b825fccd8ee5717867082e1dfb61',
      block_time: 1589294874
    },
    value: 1950926
  },
  {
    txid: 'a6fbb47d84c6d88c9a6dace1eccf4cb41f3d17d87d0e056b70f0a626031ac973',
    vout: 1,
    status: {
      confirmed: true,
      block_height: 1740136,
      block_hash: '00000000000075b2ad9575d09cf0896521fbbcaf3118606c04196fa17900110d',
      block_time: 1589294722
    },
    value: 1000000
  },
  {
    txid: 'e8ada2814008b4e756cca76cc7eb39e0c991d9ec693c236619154d3f07de1a78',
    vout: 20,
    status: {
      confirmed: true,
      block_height: 1742536,
      block_hash: '0000000000002f733faf73003c67bfc94a4e8bdc82630d40b1a7d4c8ae16ae36',
      block_time: 1589343510
    },
    value: 74762844
  },
  {
    txid: '36503c549d044dd64de1e2a5f1ecd3472f2ac6a1d11902ac3b5c5f01f5d61474',
    vout: 1,
    status: {
      confirmed: true,
      block_height: 1740141,
      block_hash: '0000000000002a73be9dc6680c6e54fe1ac2509b9ee655f81087995ae8f4a115',
      block_time: 1589294776
    },
    value: 92000
  }
]

It was vout. Thanks for all your help @junderw! Some parts of this low-level bitcoin stuff are still difficult to wrap my head around, but I feel like I'm learning a lot.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hoshsadiq picture hoshsadiq  路  3Comments

yakitorifoodie picture yakitorifoodie  路  3Comments

coingeek picture coingeek  路  4Comments

silence-may picture silence-may  路  3Comments

ishwarchandratiwari picture ishwarchandratiwari  路  3Comments