Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support compressed keys. #13

Closed
afk11 opened this issue May 17, 2014 · 8 comments
Closed

Support compressed keys. #13

afk11 opened this issue May 17, 2014 · 8 comments

Comments

@afk11
Copy link

afk11 commented May 17, 2014

At the moment there are several issues which stem from Coinb.in not supporting compressed keys.

  • When asked to sign a partially signed transaction, where the signature was added by a key that is compressed in the redeemScript, Coinb.in ignores this signature, and leaves it out when creating the signed transaction.
  • Bitcoin-qt verifyaddress directions do not work #5

So just to explain how annoying this is:
Bitcoin Core is the only client which supports multisig.
People can't use keys taken from Bitcoin Core and sign transactions using Coinbin.
People can't take transactions signed by someone using Bitcoin Core, and complete them using Coinbin.
Uncompressed keys are about twice as long as compressed - meaning you're bloating the blockchain by forcing these.
Electrum will soon only support compressed keys, as this is in the formal specification of BIP32.
Coinbin is the only web utility for signing these tx's, but it isn't compatible with ANYONE else. Please fix this, your tool is very important for multisigs usability right now.

I've implemented support for signing P2SH transactions in a PHP library of mine: https://github.com/Bit-Wasp/bitcoin-lib-php/Raw_transaction.php check out some of the examples. This wallet structure contains enough info to allow signature verification of sigs that are there, and also to add sigs where possible.

    array(
        'hash160 of public key' => 
            array(
                'type' => 'pubkeyhash',
                'private_key' => '5...',
                'public_key' => '04....',
                'uncompressed_key' => '04...',
                'is_compressed' => 'false',
                'address' => '1A',
            ),
    'hash160 of public key' =>  array(
                'type' => 'pubkeyhash',
                'private_key' => 'K....',
                'public_key' => '03......',
                'uncompressed_key' => '04......',
                'is_compressed' => 'true',
                'address' => '1B',
        )
    'hash160 of SCRIPT' =>  array(
                'type' => 'scripthash',
                'script' => '52........ae',
                'required_signature_count' => m,
                'public_keys' => array( as given in redeem script ),
                'keys' => array with references to above entries of keys, identified by the hash160 of the public key in the redeem script
        )

If you don't know PHP feel free to email me on thomas@bitwasp.co and I'll try explain how you might change your code - I can read JS, but don't know it well enough to fix this.

@OutCast3k
Copy link
Owner

Hey,

Thanks for the info.

Your link to https://github.com/Bit-Wasp/bitcoin-lib-php/Raw_transaction.php isn't working...

@OutCast3k OutCast3k reopened this May 17, 2014
@afk11
Copy link
Author

afk11 commented May 17, 2014

Sorry, right link is https://github.com/Bit-Wasp/bitcoin-lib-php/blob/master/src/RawTransaction.php
Check this for the relevant functions for building up the wallet structure:
https://github.com/Bit-Wasp/bitcoin-lib-php/blob/master/src/RawTransaction.php#L1243-1309

The signing function is here: https://github.com/Bit-Wasp/bitcoin-lib-php/blob/master/src/RawTransaction.php#L920-988

If you decode the scriptPubKey of the Tx you're trying to redeem, you learn the script hash. This identifies the redeemScript in your wallet (line935) It then looks if it's scriptHash or pay-to-pubkeyhash.

If its scriptHash, extract any signatures. Function for that is L987. Just decode the scriptSig['hex'], and check for anything that looks like a signature, or passes IsCanonicalSignature.
Attempt to validate the signature against the $wallet[$scriptHash]['public_keys'] array. You should check the length, and decompress if you see a shorter one. There probably is a function in bitcoinjs to decompress a public key. Pass back an array of signatures, indexed by the hash160 of the public key used to create them. There could be several signatures already on the transaction.
array('hash160_pk1' => sig, 'hash160_pk2' => sig2);

At this stage, you have learned that there are already count($signatures) valid signatures. But you still want to apply whatever signatures you can. Loop through the $wallet[$scriptHash]['keys'] array to get the private keys you have for that address, creating signatures as you go, and adding them to $signatures. It will overwrite previous signatures you added, but only with another valid siganture, so thats fine.

Now, to fix your scriptSig generation function (L1055). Start off adding the null byte to your scriptLoop through the $wallet[$scriptHash]['public_keys'] array, which could have uncompressed/compressed keys, create the hash160, and see if a signature exists for that hash160. If so, prefix with length in bytes.
Once all sigs are added here, you need to add the redeemScript. Add '4c' for PUSHDATA, then prefix redeemScript with length in bytes.

Now that your scriptSig is correctly encoded, just serialize the whole transaction and you're done.
You've validated every signature that is there, and left out only invalid ones. You've also added new signatures, which maybe overwrote older ones, but that's fine. That should be it!

@OutCast3k
Copy link
Owner

Quote "There probably is a function in bitcoinjs to decompress a public key"

I do not believe there is, I could probably figure it out but it might take a bit of time and some testing. Are you aware of any javascript functions that allows you to decompress a compressed public key?

@afk11
Copy link
Author

afk11 commented May 23, 2014

You could try look at BitCore for ideas, I have a gist explaining the process (albeit with PHP to follow), but it should show you what you need to research https://gist.github.com/afk11/a3f1174f30e1e8d9ed2d

@afk11
Copy link
Author

afk11 commented May 24, 2014

https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/eckey.js#L24-29
So when you generate private keys, you should append a 0x01 byte to force them to be compressed (leading to compressed public keys, and the new address for this hashed compressed key)

I really don't know where to start with js.. but I'm skimming through, it looks as if it just learns the string length of the given public key (and private key as you see above), and just sets a 'compressed' object attribute to TRUE.

How this links with your code, I'm not quite sure yet :P However, bitcoinjs also has support for BIP32, which uses compressed keys by specification. Check out there to see how they do it? BitCore also should support this.

I see that there IS a function to extract the public key from the signature. Surely whatever that extracts will be usable for signature verification?

My thoughts are, when you take a TX as input, look for signatures on each input by doing validation against the current Tx, with the right SigHash value, and by using bitcoinjs' extract pubkey from signature method. Although you still need a comparison against the keys in the redeemScript, implying you'll either decompress a key in one, or the other.
Or, you could just instantiate an ECPubKey class for both, and find a way to directly compare them?

(I refactored the repo above recently, updated the links to signing, and wallet fxns now)

@prusnak
Copy link

prusnak commented Jun 6, 2014

Maybe I am missing something here: but why would you need to decompress a public key? Keys in the redeemScript need to stay compressed. Is it just for the signature verification in BitcoinJS that does not understand compressed key?

@afk11
Copy link
Author

afk11 commented Jun 9, 2014

Indeed they need to stay uncompressed in the redeemScript. But when it comes to signature verification, this script doesn't handle compressed keys. Creating a temporary variable with the decompressed key around this step could be a simple fix.

@OutCast3k
Copy link
Owner

Please see http://coinb.in and https://github.com/OutCast3k/coinbin for a newer version which supports compressed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants