bitpay / bitcore

A full stack for bitcoin and blockchain-based applications
https://bitcore.io/
MIT License
4.88k stars 2.09k forks source link

How to parse partially signed transaction from multisig by bitcore-lib #2372

Open Mihail74 opened 5 years ago

Mihail74 commented 5 years ago

I'm trying to create transaction to spend coins from multisig (2-of-2) address.

I sign transaction by pk1, serialize tx and sent transaction to other signer.

On the other side signer tries to restore transaction by bitcore-lib and sign transaction. He gets an error: Invalid state: Not all utxo information is available to sign the transaction.

There is my code snippet:

(async () => {
  const bitcore = require('bitcore-lib')
  const axios = require('axios')
  const network = bitcore.Networks.testnet
  const api = axios.create({
    baseURL: 'https://test-insight.bitpay.com/'
  })

  const to = 'mgNM1RiqDMka1MgZKzXF11NjBLAB9CsF4E'
  const amount = 1500000

  const wif1 = 'wif1'
  const wif2 = 'wif2'
  const pk1 = new bitcore.PrivateKey(wif1, network)
  const pk2 = new bitcore.PrivateKey(wif2, network)

  const publicKeys = [
    pk1.toPublicKey().toString(),
    pk2.toPublicKey().toString()
  ]
  const requiredSignatures = 2;

  const from = new bitcore.Address.createMultisig(publicKeys, requiredSignatures, network).toString();
  const { data } = await api.get(`/api/addr/${from}/utxo`)

  const utxo = data.map(e => ({
    txId: e.txid,
    outputIndex: e.vout,
    address: e.address,
    script: e.scriptPubKey || e.script,
    satoshis: e.satoshis
  }))

  const tx = new bitcore.Transaction()
    .from(utxo, publicKeys, requiredSignatures)
    .to(to, amount)
    .change(from)
    .feePerByte(55)
    .sign(pk1)

  console.log('inputs:', tx.inputs);
  const partiallySignedRawTx = tx.uncheckedSerialize()

  const tx2 = new bitcore.Transaction(partiallySignedRawTx)
  console.log('inputs:', tx2.inputs)
  tx2.sign(pk2)
})()

When create transaction by utxo inputs has a lot of information and looks like this:

inputs: [ MultiSigScriptHashInput {
    witnesses: [],
    output:
     <Output (2000000 sats) <Script: OP_HASH160 20 0xb63729157c4358983bc1c9932c7e2477d778abf0 OP_EQUAL>>,
    prevTxId:
     <Buffer a2 c1 61 7a 89 4d 71 51 bd ab 9d c7 ec 86 0f 98 9a 82 f2 9a 74 e1 8e 21 da d6 49 1c d1 64 f6 05>,
    outputIndex: 0,
    sequenceNumber: 4294967295,
    _script:
     <Script: OP_0 71 0x30440220030afcc77b51a89fa8ffc41b95b8b7b18b62298844b94f9bdf46e8ab32a4f6e0022021f2703350a87ff582fd20e5851dcea372a5725b259d84935171955787b4068f01 71 0x522102d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de832103bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a52ae>,
    _scriptBuffer:
     <Buffer 00 47 30 44 02 20 03 0a fc c7 7b 51 a8 9f a8 ff c4 1b 95 b8 b7 b1 8b 62 29 88 44 b9 4f 9b df 46 e8 ab 32 a4 f6 e0 02 20 21 f2 70 33 50 a8 7f f5 82 fd ... >,
    nestedWitness: false,
    publicKeys:
     [ '02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83',
       '03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a' ],
    redeemScript:
     <Script: OP_2 33 0x02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83 33 0x03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a OP_2 OP_CHECKMULTISIG>,
    publicKeyIndex:
     { '02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83': 0,
       '03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a': 1 },
    threshold: 2,
    signatures: [ <1 empty item>, [TransactionSignature] ] },
  MultiSigScriptHashInput {
    witnesses: [],
    output:
     <Output (2000000 sats) <Script: OP_HASH160 20 0xb63729157c4358983bc1c9932c7e2477d778abf0 OP_EQUAL>>,
    prevTxId:
     <Buffer 26 e1 89 ba 65 fb d4 86 d1 b5 fb b7 e8 3a 56 05 7d 29 1f 69 82 4e 69 93 93 be 01 83 40 b6 71 0c>,
    outputIndex: 0,
    sequenceNumber: 4294967295,
    _script:
     <Script: OP_0 72 0x3045022100d4f5fe377dd1f06f8da09f2e98ff4f097a7145049d7e536a4ac6205e9f08db2402202e706017a5af0fb710989299fc109fedbf699bfb1b9ea44149b7447998767ad201 71 0x522102d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de832103bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a52ae>,
    _scriptBuffer:
     <Buffer 00 48 30 45 02 21 00 d4 f5 fe 37 7d d1 f0 6f 8d a0 9f 2e 98 ff 4f 09 7a 71 45 04 9d 7e 53 6a 4a c6 20 5e 9f 08 db 24 02 20 2e 70 60 17 a5 af 0f b7 10 ... >,
    nestedWitness: false,
    publicKeys:
     [ '02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83',
       '03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a' ],
    redeemScript:
     <Script: OP_2 33 0x02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83 33 0x03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a OP_2 OP_CHECKMULTISIG>,
    publicKeyIndex:
     { '02d51e4e5a1f10e8fa0b93a40322251e82c6827603709759bc2d6657bacaa1de83': 0,
       '03bf72f7cfac00258f21c505429de4aad8ab4a662d73e31d163e6124b4d348746a': 1 },
    threshold: 2,
    signatures: [ <1 empty item>, [TransactionSignature] ] } ]

But after restoring tx from uncheckedSerialize inputs look like this:

inputs: [ Input {
    prevTxId:
     <Buffer a2 c1 61 7a 89 4d 71 51 bd ab 9d c7 ec 86 0f 98 9a 82 f2 9a 74 e1 8e 21 da d6 49 1c d1 64 f6 05>,
    outputIndex: 0,
    _scriptBuffer:
     <Buffer 00 47 30 44 02 20 03 0a fc c7 7b 51 a8 9f a8 ff c4 1b 95 b8 b7 b1 8b 62 29 88 44 b9 4f 9b df 46 e8 ab 32 a4 f6 e0 02 20 21 f2 70 33 50 a8 7f f5 82 fd ... >,
    sequenceNumber: 4294967295 },
  Input {
    prevTxId:
     <Buffer 26 e1 89 ba 65 fb d4 86 d1 b5 fb b7 e8 3a 56 05 7d 29 1f 69 82 4e 69 93 93 be 01 83 40 b6 71 0c>,
    outputIndex: 0,
    _scriptBuffer:
     <Buffer 00 48 30 45 02 21 00 d4 f5 fe 37 7d d1 f0 6f 8d a0 9f 2e 98 ff 4f 09 7a 71 45 04 9d 7e 53 6a 4a c6 20 5e 9f 08 db 24 02 20 2e 70 60 17 a5 af 0f b7 10 ... >,
    sequenceNumber: 4294967295 } ]

And sign on the restored transaction doesn't work. How should i restore transaction?

Mihail74 commented 5 years ago

In additional: i can't use toObject() how described here because in my case rawTx consumed from other client and can be made by other libs