bitcoinjs / bitcoinjs-lib

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

Getting keys.getPublicKeyBuffer is not a function #1399

Closed brandb12 closed 5 years ago

brandb12 commented 5 years ago

Im trying to sign a transaction for blockcypher described here, but I have run into this error and when searching around I cannot find a solution. Im trying to sign a transaction for the bitcoin testnet, here is the code im using, does anyone know what the problem could be?

var bitcoin = require("bitcoinjs-lib");
var buffer  = require('buffer');
var keys    = new bitcoin.ECPair.fromWIF('cMvPQZiG5mLARSjxbBwMxKwzhTHaxgpTsXB6ymx7SGAeYUqF8HAT', bitcoin.networks.testnet);

var newtx = {
  inputs: [{addresses: ['ms9ySK54aEC2ykDviet9jo4GZE6GxEZMzf']}],
  outputs: [{addresses: ['msWccFYm5PPCn6TNPbNEnprA4hydPGadBN'], value: 1000}]
};
// calling the new endpoint, same as above
$.post('https://api.blockcypher.com/v1/btc/test3/txs/new', JSON.stringify(newtx))
  .then(function(tmptx) {
    // signing each of the hex-encoded string required to finalize the transaction
    tmptx.pubkeys = [];
    tmptx.signatures = tmptx.tosign.map(function(tosign, n) {
      tmptx.pubkeys.push(keys.getPublicKeyBuffer().toString("hex"));
      return keys.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex");
    });
    // sending back the transaction with all the signatures to broadcast
    $.post('https://api.blockcypher.com/v1/btc/test3/txs/send', tmptx).then(function(finaltx) {
      console.log(finaltx);
    }).catch(function (response) {
   console.log(response.responseText);
});
  }).catch(function (response) {
   console.log(response.responseText);
});

Edit: In fact it provided 'undefined' which I didnt notice before. So the full error i get is:

jQuery.Deferred exception: keys.getPublicKeyBuffer is not a function undefined

junderw commented 5 years ago
const publicKey = keys.publicKey;

It's not a function, just treat it like an attribute.

keys.getPublicKeyBuffer() is old code for v3.x.x the current version is v5.x.x

brandb12 commented 5 years ago
const publicKey = keys.publicKey;

It's not a function, just treat it like an attribute.

keys.getPublicKeyBuffer() is old code for v3.x.x the current version is v5.x.x

Thanks for your reply, that part seems to be working now but on the last bit it gives me this: jQuery.Deferred exception: keys.sign(...).toDER is not a function undefined

Is this old aswell and something I need to change?

junderw commented 5 years ago

ECPair.sign returns a Buffer now.

look at bitcoin.script.signature for some function about DER encoding.

junderw commented 5 years ago

it's called encode.

you need to pass 1 for the second arg for the sighash for SIGHASH_ALL

brandb12 commented 5 years ago

ECPair.sign returns a Buffer now.

look at bitcoin.script.signature for some function about DER encoding.

Would you be able to show me what I need to edit on that line? Im quite new to this a am not too clear on what I need to add. I keep getting the error {"error": "Couldn't deserialize request: invalid character 'x' in literal true (expecting 'r')"}

junderw commented 5 years ago

Im quite new to this

I hope no one puts a lot of money in your wallet then :-P

jk

This should work.

const SIGHASH_ALL = 0x01;
return bitcoin.script.signature.encode(
  keys.sign(new buffer.Buffer(tosign, "hex")),
  SIGHASH_ALL,
).toString("hex");
brandb12 commented 5 years ago

I hope no one puts a lot of money in your wallet then :-P

jk

Ha! I know but im learning as I go. But I still get that error {"error": "Couldn't deserialize request: invalid character 'x' in literal true (expecting 'r')"}

Here is the full updated code im using, can you see anything ive done wrong?

var bitcoin = require("bitcoinjs-lib");
var buffer  = require('buffer');
var keys    = new bitcoin.ECPair.fromWIF('cMvPQZiG5mLARSjxbBwMxKwzhTHaxgpTsXB6ymx7SGAeYUqF8HAT', bitcoin.networks.testnet);
const publicKey = keys.publicKey;

var newtx = {
  inputs: [{addresses: ['ms9ySK54aEC2ykDviet9jo4GZE6GxEZMzf']}],
  outputs: [{addresses: ['msWccFYm5PPCn6TNPbNEnprA4hydPGadBN'], value: 1000}]
};
// calling the new endpoint, same as above
$.post('https://api.blockcypher.com/v1/btc/test3/txs/new', JSON.stringify(newtx))
  .then(function(tmptx) {
    // signing each of the hex-encoded string required to finalize the transaction
    tmptx.pubkeys = [];
    tmptx.signatures = tmptx.tosign.map(function(tosign, n) {
      tmptx.pubkeys.push(publicKey.toString("hex"));

const SIGHASH_ALL = 0x01;
return bitcoin.script.signature.encode(
  keys.sign(new buffer.Buffer(tosign, "hex")),
  SIGHASH_ALL,
).toString("hex");

    });
    // sending back the transaction with all the signatures to broadcast
    $.post('https://api.blockcypher.com/v1/btc/test3/txs/send', tmptx).then(function(finaltx) {
      console.log(finaltx);
    }).catch(function (response) {
   console.log(response.responseText);
});
  }).catch(function (response) {
   console.log(response.responseText);
});
junderw commented 5 years ago

That's not an error on our end... looks like something on the API end...

brandb12 commented 5 years ago

That's not an error on our end... looks like something on the API end...

Ok thanks for all your help, ill give them an email.

brandb12 commented 5 years ago

Still no response from blockcypher, I guess they don't care about their users.

brandb12 commented 5 years ago

Finally I seemed to have solved the problem, but as usual that brings another problem. I now get the error when trying to send:

Error building input: Error generating scriptsig when building transaction: Invalid signature: Non-canonical signature: wrong length marker

Does this have something to do with this code?

const SIGHASH_ALL = 0x01;
return bitcoin.script.signature.encode(
  keys.sign(new buffer.Buffer(tosign, "hex")),
  SIGHASH_ALL,
).toString("hex");
junderw commented 5 years ago

I have no clue. I have never used blockcypher.

vineettyagi28 commented 4 years ago

@brandb12 How did you solve your last mentioned issue in this thread? Thanks in advance

fedecaccia commented 4 years ago

Finally I seemed to have solved the problem, but as usual that brings another problem. I now get the error when trying to send:

Error building input: Error generating scriptsig when building transaction: Invalid signature: Non-canonical signature: wrong length marker

Does this have something to do with this code?

const SIGHASH_ALL = 0x01;
return bitcoin.script.signature.encode(
  keys.sign(new buffer.Buffer(tosign, "hex")),
  SIGHASH_ALL,
).toString("hex");

I'm getting the same problem. Have you solved it? Any response from blockcypher?

quentinlesceller commented 4 years ago

Hi @fedecaccia, @vineettyagi28, @brandb12 apologies if no one answered you at BlockCypher. The reason why it fails is because BlockCypher adds the SIGHASH_ALL automatically. Here is a quick way to fix it.

                            return bitcoin.script.signature.encode(
                                keys.sign(new buffer.Buffer(tosign, "hex")),
                                0x01,
                                ).toString("hex").slice(0, -2);

I'll update the doc accordingly.

fedecaccia commented 4 years ago

Hi @quentinlesceller thanks!

asimjb1200 commented 4 years ago

The final error that this produces as of 2020 is

Error: TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'TLSSocket'
    --- property '_httpMessage' closes the circle

here is my code:

const keyBuffer = Buffer.from(privKey, 'hex')
var keys = bitcoin.ECPair.fromPrivateKey(keyBuffer, currentNetwork)

axios.post('https://api.blockcypher.com/v1/bcy/test/txs/new', JSON.stringify(newtx))
    .then(function (tmptx) {
        console.log(tmptx.data)
        // signing each of the hex-encoded string required to finalize the transaction
        tmptx.pubkeys = [];
        tmptx.signatures = tmptx.data.tosign.map(function (tosign, n) {
            tmptx.pubkeys.push(keys.publicKey.toString('hex'));

            const SIGHASH_ALL = 0x01;

            return bitcoin.script.signature.encode(
                keys.sign(Buffer.from(tosign, "hex")),
                0x01,
            ).toString("hex").slice(0, -2);
        });
        // sending back the transaction with all the signatures to broadcast
        axios.post('https://api.blockcypher.com/v1/bcy/test/txs/send', tmptx).then(function (finaltx) {
            console.log(finaltx);
            res.end('Transaction has began')
        }).catch(err => {
            console.log('Error: ' + err)
        });
    }).catch(err => {
        console.log('Error: ' + err)
    });
junderw commented 4 years ago

tmptx contains a cyclical reference, so you can't stringify it. axios.post tries to stringify it in the background.

Remove the cyclical reference (the error tells you how) OR create a new object and only include the data you need.

asimjb1200 commented 4 years ago

I see, so include that property '_httpMessage'? @junderw Now I just have to track it down in the object lol