btcsuite / btcd

An alternative full node bitcoin implementation written in Go (golang)
https://github.com/btcsuite/btcd/blob/master/README.md
ISC License
6.28k stars 2.38k forks source link

BIP 322 Support #2077

Open trevormil opened 11 months ago

trevormil commented 11 months ago

As BIP322 support becomes more popular with Bitcoin wallets (Phantom, etc), it would be nice to have a Go implementation for message signature verification. To my knowledge, I could not find one. The main one I could find is https://github.com/ACken2/bip322-js.

I particularly need it for my project that I am working on. Is this something that can be completed anytime soon? Or should I find an alternative solution in the meantime?

Roasbeef commented 11 months ago

I particularly need it for my project that I am working on. Is this something that can be completed anytime soon? Or should I find an alternative solution in the meantime?

We'd accept a contribution implementing it. Seems relatively straight forward.

mohamedawnallah commented 11 months ago

I'd like to work on this issue, Thanks!

trevormil commented 11 months ago

I'd like to work on this issue, Thanks!

Awesome. Could you keep me updated on any progress?

mohamedawnallah commented 11 months ago

Okay, I will do :)

trevormil commented 10 months ago

Hey, any progress updates? My project will need a solution by the end of February. Do you think this will be done by then? Or, should I find an alternative?

No worries if not. Just need to know how to move forward.

gamedevod commented 10 months ago

@trevormil check my issue, i started implement it, but I warn you right away - my code does not produce correct results yet, and I cannot fix it https://github.com/btcsuite/btcd/issues/2117

mohamedawnallah commented 10 months ago

Sorry I seem missed this notification @trevormil I am busy now with other tasks Hope I get to it soon. That said please feel free to look for alternatives in the meantime. Thanks for your understanding 🙏

trevormil commented 10 months ago

Sorry I seem missed this notification @trevormil I am busy now with other tasks Hope I get to it soon. That said please feel free to look for alternatives in the meantime. Thanks for your understanding 🙏

No worries. Thank you.

AbhinavMir commented 9 months ago

@trevormil hi! I had a look at how bitcoin core does this and a quick gist would be:

I just got started on this and would be using this as a rough roadmap. A rather trivial question would be: Where does the files for bip322 go? 😅 The repo is HUGE and I'm new here.

yemmyharry commented 9 months ago

@trevormil @Roasbeef pls, I'd like to contribute to this. can I pick it up?

trevormil commented 9 months ago

@yemmyharry Yea, that would be great. I am not actively working on it. I kinda just settled with a workaround calling the JavaScript version from Go code. Could you keep me updated on progress?

Roasbeef commented 9 months ago

I just got started on this and would be using this as a rough roadmap. A rather trivial question would be: Where does the files for bip322 go? 😅 The repo is HUGE and I'm new here.

@AbhinavMir I think we'd add the bip322 code to the txscript package (where all the other Script logic lives). IIUC, BIP 322 is just a way to make a "fake transaction" to show that if an output were created with a given pkScript, you can spend it with a valid witness. The txscript package has all the contents (along with the wire) package needed to do such a construction.

@yemmyharry sure, feel free to use this issue to get feedback on any WIP code you may have.

trevormil commented 6 months ago

cc: @Roasbeef @AbhinavMir @yemmyharry @mohamedawnallah

Recently noticed that Unisat wallet a BIP322 Go package and was able to use it along with btcsuite for my use cases. I have provided the code below and is available via a Gist (https://gist.github.com/trevormil/7c8c3bec3cac94a2af3c1d4edf95ac2b). If you would like to use it or merge it into the library, feel free!

The goal of my code was to simply verify Phantom Wallet Bitcoin signatures in Go. Phantom uses P2WPKH BIP322 verification, so this code is only targeted to that specific algorithm (and not others like P2SH-P2WPKH and single-key-spend P2TR BIP-322 verification).

Behind the scenes, Phantom relies (or is at least compatible with) on the bip322-js npm package. This code is basically the equivalent of

Verifier.verifySignature(address, message, signature)

Code Snippet:

package main

import (
    "encoding/base64"
    "log"

    "github.com/btcsuite/btcd/btcutil"
    "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/txscript"
    "github.com/btcsuite/btcd/wire"

    bip322 "github.com/unisat-wallet/libbrc20-indexer/utils/bip322"
)

func convertAddressToScriptPubkey(address string) []byte {
    btcAddress, err := btcutil.DecodeAddress(address, &chaincfg.MainNetParams)
    if err != nil {
        panic(err)
    }

    outputScript, err := txscript.PayToAddrScript(btcAddress)
    if err != nil {
        panic(err)
    }

    return outputScript
}

func main() {
    //You can replace these with the actual values you want to verify
    //These are in the format used by bip322-js
    //This is for P2WPKH BIP322 verification
    message := "Hello World"
    signature := "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="
    signerAddress := "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l"

    //These will be in the following format:
    //[0x02 or 0x03] [LENGTH_BYTE, ...(LENGTH bytes that go into witness[0])] [0x21, ...(33 byte (len = 0x21) public key that was used to sign the message)]
    //- First byte is either a 0x02 or 0x03 (not sure exactly why but apparently it is - https://github.com/ACken2/bip322-js/blob/f0f9373b3a1da19e017c518b891522eaa4bcccdd/src/helpers/Address.ts#L85)
    //- Next part is the details that go into witness[0] (length of this part is determined by the second byte) - again, not exactly sure what this is
    //- Last part is the public key that was used to sign the message (33 bytes) - There is a length byte 0x21 before this part to denote the length of the public key (0x21 = 33)
    encodedSigBytes, err := base64.StdEncoding.DecodeString(signature)
    if err != nil {
        panic(err)
    }

    //Convert the address to a public key script (I believe this is the denotation for a pay-to-this-address script but not 100% sure)
    pkScript := convertAddressToScriptPubkey(signerAddress)

    //Decode the length of the witness[0] part
    encodedSigLenByte := encodedSigBytes[1]
    encodedSigLen := int(encodedSigLenByte)

    //Extract witness[0] and witness[1] from the encoded signature
    PUB_KEY_LEN := 33
    encodedSig := encodedSigBytes[len(encodedSigBytes)-PUB_KEY_LEN-1-encodedSigLen : len(encodedSigBytes)-PUB_KEY_LEN-1]
    pubKey := encodedSigBytes[len(encodedSigBytes)-33:]

    //Recreate the witness to pass into the VerifySignature function
    witness := wire.TxWitness{
        encodedSig,
        pubKey,
    }

    //This is the BIP322 verification function from the Unisat wallet
    verified := bip322.VerifySignature(witness, pkScript, message)
    log.Println("BIP 322 Verified:", verified)
}