github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/wallet.go (about)

     1  package btc
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"errors"
     7  	"math/big"
     8  
     9  	"github.com/piotrnar/gocoin/lib/secp256k1"
    10  )
    11  
    12  // PublicFromPrivate gets the ECDSA public key in Bitcoin protocol format, from the give private key.
    13  func PublicFromPrivate(priv_key []byte, compressed bool) (res []byte) {
    14  	if compressed {
    15  		res = make([]byte, 33)
    16  	} else {
    17  		res = make([]byte, 65)
    18  	}
    19  
    20  	if !secp256k1.BaseMultiply(priv_key, res) {
    21  		res = nil
    22  	}
    23  	return
    24  }
    25  
    26  // VerifyKeyPair verifies the secret key's range and if a test message signed with it verifies OK.
    27  // Returns nil if everything looks OK.
    28  func VerifyKeyPair(priv []byte, publ []byte) error {
    29  	var sig Signature
    30  
    31  	const TestMessage = "Just some test message..."
    32  	hash := Sha2Sum([]byte(TestMessage))
    33  
    34  	D := new(big.Int).SetBytes(priv)
    35  
    36  	if D.Cmp(big.NewInt(0)) == 0 {
    37  		return errors.New("pubkey value is zero")
    38  	}
    39  
    40  	if D.Cmp(&secp256k1.TheCurve.Order.Int) != -1 {
    41  		return errors.New("pubkey value is too big")
    42  	}
    43  
    44  	r, s, e := EcdsaSign(priv, hash[:])
    45  	if e != nil {
    46  		return errors.New("EcdsaSign failed: " + e.Error())
    47  	}
    48  
    49  	sig.R.Set(r)
    50  	sig.S.Set(s)
    51  	ok := EcdsaVerify(publ, sig.Bytes(), hash[:])
    52  	if !ok {
    53  		return errors.New("EcdsaVerify failed")
    54  	}
    55  
    56  	// Now the same using a schnorr sign/verify
    57  	randata := Sha2Sum(append(hash[:], []byte(TestMessage)...))
    58  	ssig := secp256k1.SchnorrSign(hash[:], priv, randata[:])
    59  	if len(ssig) != 64 {
    60  		return errors.New("SchnorrSign failed: " + hex.EncodeToString(ssig))
    61  	}
    62  
    63  	ok = secp256k1.SchnorrVerify(publ[1:33], ssig, hash[:])
    64  	if !ok {
    65  		return errors.New("SchnorrVerify failed")
    66  	}
    67  
    68  	return nil
    69  }
    70  
    71  // DeriveNextPrivate is used for implementing Type-2 determinitic keys.
    72  // B_private_key = ( A_private_key + secret ) % N
    73  func DeriveNextPrivate(p, s []byte) (toreturn []byte) {
    74  	var prv, secret big.Int
    75  	prv.SetBytes(p)
    76  	secret.SetBytes(s)
    77  	res := new(big.Int).Mod(new(big.Int).Add(&prv, &secret), &secp256k1.TheCurve.Order.Int).Bytes()
    78  	toreturn = make([]byte, 32)
    79  	copy(toreturn[32-len(res):], res)
    80  	return
    81  }
    82  
    83  // DeriveNextPublic is used for implementing Type-2 determinitic keys.
    84  // B_public_key = G * secret + A_public_key
    85  func DeriveNextPublic(public, secret []byte) (out []byte) {
    86  	out = make([]byte, len(public))
    87  	secp256k1.BaseMultiplyAdd(public, secret, out)
    88  	return
    89  }
    90  
    91  // NewSpendOutputs returns one TxOut record.
    92  func NewSpendOutputs(addr *BtcAddr, amount uint64, testnet bool) ([]*TxOut, error) {
    93  	out := new(TxOut)
    94  	out.Value = amount
    95  	out.Pk_script = addr.OutScript()
    96  	return []*TxOut{out}, nil
    97  }
    98  
    99  // PrivateAddr is a Base58 encoded private address with checksum and its corresponding public key/address.
   100  type PrivateAddr struct {
   101  	Version byte
   102  	Key     []byte
   103  	*BtcAddr
   104  }
   105  
   106  func NewPrivateAddr(key []byte, ver byte, compr bool) (ad *PrivateAddr) {
   107  	ad = new(PrivateAddr)
   108  	ad.Version = ver
   109  	ad.Key = key
   110  	pub := PublicFromPrivate(key, compr)
   111  	if pub == nil {
   112  		panic("PublicFromPrivate error")
   113  	}
   114  	ad.BtcAddr = NewAddrFromPubkey(pub, ver-0x80)
   115  	return
   116  }
   117  
   118  func DecodePrivateAddr(s string) (*PrivateAddr, error) {
   119  	pkb := Decodeb58(s)
   120  
   121  	if pkb == nil {
   122  		return nil, errors.New("Decodeb58 failed")
   123  	}
   124  
   125  	if len(pkb) < 37 {
   126  		return nil, errors.New("Decoded data too short")
   127  	}
   128  
   129  	if len(pkb) > 38 {
   130  		return nil, errors.New("Decoded data too long")
   131  	}
   132  
   133  	var sh [32]byte
   134  	ShaHash(pkb[:len(pkb)-4], sh[:])
   135  	if !bytes.Equal(sh[:4], pkb[len(pkb)-4:]) {
   136  		return nil, errors.New("Checksum error")
   137  	}
   138  
   139  	return NewPrivateAddr(pkb[1:33], pkb[0], len(pkb) == 38 && pkb[33] == 1), nil
   140  }
   141  
   142  // String returns the Base58 encoded private key (with checksum).
   143  func (ad *PrivateAddr) String() string {
   144  	var ha [32]byte
   145  	buf := new(bytes.Buffer)
   146  	buf.WriteByte(ad.Version)
   147  	buf.Write(ad.Key)
   148  	if ad.BtcAddr.IsCompressed() {
   149  		buf.WriteByte(1)
   150  	}
   151  	ShaHash(buf.Bytes(), ha[:])
   152  	buf.Write(ha[:4])
   153  	return Encodeb58(buf.Bytes())
   154  }