From 08783e5cb46f3c07afab2ea1a74048b6a1c7ee28 Mon Sep 17 00:00:00 2001 From: GG <863867759@qq.com> Date: Mon, 8 Apr 2024 16:56:17 +0800 Subject: [PATCH] feat: support bitcoin func `signMessage` --- core/btc/account.go | 50 ++++++++++++++++++++++++++++++++++++++++ core/btc/account_test.go | 17 ++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/core/btc/account.go b/core/btc/account.go index 94cdae8..7f56fc5 100644 --- a/core/btc/account.go +++ b/core/btc/account.go @@ -1,13 +1,18 @@ package btc import ( + "bytes" + "encoding/base64" "fmt" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/centrifuge/go-substrate-rpc-client/v4/types" "github.com/coming-chat/wallet-SDK/core/base" "github.com/tyler-smith/go-bip39" @@ -251,6 +256,51 @@ func (a *Account) SignHex(messageHex string, password string) (*base.OptionalStr return nil, base.ErrUnsupportedFunction } +// SignMessage +// https://developer.bitcoin.org/reference/rpc/signmessage.html +// @param msg The message to create a signature of. +// @return The signature of the message encoded in base64. +func (a *Account) SignMessage(msg string) (*base.OptionalString, error) { + msgHash := messageHash(msg) + + signbytes, err := ecdsa.SignCompact(a.privateKey, msgHash, true) + if err != nil { + return nil, err + } + signature := base64.StdEncoding.EncodeToString(signbytes) + return base.NewOptionalString(signature), nil +} + +func VerifySignature(pubkey, message, signature string) bool { + signBytes, err := base64.StdEncoding.DecodeString(signature) + if err != nil { + return false + } + pubBytes, err := types.HexDecodeString(pubkey) + if err != nil { + return false + } + pub, err := btcec.ParsePubKey(pubBytes) + if err != nil { + return false + } + + msgHash := messageHash(message) + recoverPub, ok, err := ecdsa.RecoverCompact(signBytes, msgHash) + if err != nil || ok == false { + return false + } + + return pub.IsEqual(recoverPub) +} + +func messageHash(msg string) []byte { + var buf bytes.Buffer + _ = wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n") + _ = wire.WriteVarString(&buf, 0, msg) + return chainhash.DoubleHashB(buf.Bytes()) +} + // MARK - Implement the protocol AddressUtil // @param publicKey can start with 0x or not. diff --git a/core/btc/account_test.go b/core/btc/account_test.go index 463cce9..21b9eeb 100644 --- a/core/btc/account_test.go +++ b/core/btc/account_test.go @@ -325,3 +325,20 @@ func TestIsValidPrivateKey(t *testing.T) { valid := IsValidPrivateKey("cTkZaPpb1pDdor36V5VY4uu5LE6tgzrjRADvrEXimEqWqvwRbfXY") t.Log(valid) } + +func TestAccount_SignMessage(t *testing.T) { + acc, err := NewAccountWithMnemonic(testcase.M1, ChainMainnet) + require.Nil(t, err) + acc.AddressType = AddressTypeTaproot + t.Log(acc.Address()) + + // sign message + message := "hello world~" + signature, err := acc.SignMessage(message) + require.Nil(t, err) + t.Log("sign message result: ", signature.Value) + + // check signature + valid := VerifySignature(acc.PublicKeyHex(), message, signature.Value) + t.Log("signature is valid: ", valid) +}