// commitScriptToSelf constructs the public key script for the output on the // commitment transaction paying to the "owner" of said commitment transaction. // If the other party learns of the pre-image to the revocation hash, then they // can claim all the settled funds in the channel, plus the unsettled funds. func commitScriptToSelf(csvTimeout uint32, selfKey, theirKey *btcec.PublicKey, revokeHash []byte) ([]byte, error) { // This script is spendable under two conditions: either the 'csvTimeout' // has passed and we can redeem our funds, or they have the pre-image // to 'revokeHash'. builder := txscript.NewScriptBuilder() // If the pre-image for the revocation hash is presented, then allow a // spend provided the proper signature. builder.AddOp(txscript.OP_HASH160) builder.AddData(revokeHash) builder.AddOp(txscript.OP_EQUAL) builder.AddOp(txscript.OP_IF) builder.AddData(theirKey.SerializeCompressed()) builder.AddOp(txscript.OP_ELSE) // Otherwise, we can re-claim our funds after a CSV delay of // 'csvTimeout' timeout blocks, and a valid signature. builder.AddInt64(int64(csvTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddData(selfKey.SerializeCompressed()) builder.AddOp(txscript.OP_ENDIF) builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() }
// generateKeyPair generates and stores a secp256k1 keypair in a file. func generateKeyPair(filename string) error { key, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { return err } pub := btcec.PublicKey{curve, key.PublicKey.X, key.PublicKey.Y} priv := btcec.PrivateKey{key.PublicKey, key.D} addr, err := address.NewAddressPubKeyHash(Hash160(pub.SerializeCompressed()), PubKeyHashAddrID) if err != nil { return err } privWif := NewWIF(priv) var buf bytes.Buffer buf.WriteString("Address: ") buf.WriteString(addr.EncodeAddress()) buf.WriteString("\n") buf.WriteString("Private key: ") buf.WriteString(privWif.String()) buf.WriteString("\n") err = writeNewFile(filename, buf.Bytes(), 0644) if err != nil { return err } return nil }
// newManagedAddressWithoutPrivKey returns a new managed address based on the // passed account, public key, and whether or not the public key should be // compressed. func newManagedAddressWithoutPrivKey(m *Manager, account uint32, pubKey *btcec.PublicKey, compressed bool) (*managedAddress, error) { // Create a pay-to-pubkey-hash address from the public key. var pubKeyHash []byte if compressed { pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed()) } else { pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed()) } address, err := btcutil.NewAddressPubKeyHash(pubKeyHash, m.chainParams) if err != nil { return nil, err } return &managedAddress{ manager: m, address: address, account: account, imported: false, internal: false, compressed: compressed, pubKey: pubKey, privKeyEncrypted: nil, privKeyCT: nil, }, nil }
// SerializePubKey serializes the associated public key of the imported or // exported private key in compressed format. The serialization format // chosen depends on the value of w.ecType. func (w *WIF) SerializePubKey() []byte { pk := btcec.PublicKey{ Curve: curve, X: w.PrivKey.PublicKey.X, Y: w.PrivKey.PublicKey.Y, } return pk.SerializeCompressed() }
// Exists returns true if an existing entry of 'sig' over 'sigHash' for public // key 'pubKey' is found within the SigCache. Otherwise, false is returned. // // NOTE: This function is safe for concurrent access. Readers won't be blocked // unless there exists a writer, adding an entry to the SigCache. func (s *SigCache) Exists(sigHash wire.ShaHash, sig *btcec.Signature, pubKey *btcec.PublicKey) bool { info := sigInfo{sigHash, string(sig.Serialize()), string(pubKey.SerializeCompressed())} s.RLock() _, ok := s.validSigs[info] s.RUnlock() return ok }
// ComputeBlindingFactor for the next hop given the ephemeral pubKey and // sharedSecret for this hop. The blinding factor is computed as the // sha-256(pubkey || sharedSecret). func computeBlindingFactor(hopPubKey *btcec.PublicKey, hopSharedSecret []byte) [sha256.Size]byte { sha := sha256.New() sha.Write(hopPubKey.SerializeCompressed()) sha.Write(hopSharedSecret) var hash [sha256.Size]byte copy(hash[:], sha.Sum(nil)) return hash }
// commitScriptUnencumbered constructs the public key script on the commitment // transaction paying to the "other" party. This output is spendable // immediately, requiring no contestation period. func commitScriptUnencumbered(key *btcec.PublicKey) ([]byte, error) { // This script goes to the "other" party, and it spendable immediately. builder := txscript.NewScriptBuilder() builder.AddOp(txscript.OP_DUP) builder.AddOp(txscript.OP_HASH160) builder.AddData(btcutil.Hash160(key.SerializeCompressed())) builder.AddOp(txscript.OP_EQUALVERIFY) builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() }
// pubKeyBytes returns bytes for the serialized compressed public key associated // with this extended key in an efficient manner including memoization as // necessary. // // When the extended key is already a public key, the key is simply returned as // is since it's already in the correct form. However, when the extended key is // a private key, the public key will be calculated and memoized so future // accesses can simply return the cached result. func (k *ExtendedKey) pubKeyBytes() []byte { // Just return the key if it's already an extended public key. if !k.isPrivate { return k.key } // This is a private extended key, so calculate and memoize the public // key if needed. if len(k.pubKey) == 0 { pkx, pky := btcec.S256().ScalarBaseMult(k.key) pubKey := btcec.PublicKey{Curve: btcec.S256(), X: pkx, Y: pky} k.pubKey = pubKey.SerializeCompressed() } return k.pubKey }
// generateAddr computes the associated bitcon address from the provided // public key. We compute ripemd160(sha256(b)) of the pubkey and then // shimmy the hashed bytes into btcsuite's AddressPubKeyHash type func generateAddr(pub *btcec.PublicKey) *btcutil.AddressPubKeyHash { net := &chaincfg.MainNetParams // Serialize the public key into bytes and then run ripemd160(sha256(b)) on it b := btcutil.Hash160(pub.SerializeCompressed()) // Convert the hashed public key into the btcsuite type so that the library // will handle the base58 encoding when we call addr.String() addr, err := btcutil.NewAddressPubKeyHash(b, net) if err != nil { log.Fatal(err) } return addr }
//Deriving Child public Key from Public Parent Key func (parent *ExtendedPublicKey) CKDpub(index uint32) (*ExtendedPublicKey, error) { if index >= HardenedKeyIndex { return nil, nil } // let I = HMAC-SHA512(Key = cpar, Data = serP(Kpar) || ser32(i)). //serP(P): serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's //compressed form: (0x02 or 0x03) || ser256(x) <-- done above //, where the header byte depends on the parity of the omitted y coordinate. data := make([]byte, 37) copy(data[0:], parent.PublicKey) binary.BigEndian.PutUint32(data[33:], index) hmac512 := hmac.New(sha512.New, parent.Chaincode) hmac512.Write(data) raw := hmac512.Sum(nil) //child Chaincode is the right 32 bytes of the result childChaincode := raw[32:] // The returned child key Ki is point(parse256(IL)) + Kpar leftX, leftY := Point(raw[:32]) x1 := new(big.Int).SetBytes(leftX) y1 := new(big.Int).SetBytes(leftY) x2 := new(big.Int).SetBytes(parent.X) y2 := new(big.Int).SetBytes(parent.Y) childX, childY := btcec.S256().Add(x1, y1, x2, y2) ParentFingerPrint := parent.GetFingerPrint() depthBytes := []byte{parent.Depth} depthInt := new(big.Int).SetBytes(depthBytes) depthInt.Add(depthInt, big.NewInt(1)) depth := depthInt.Bytes() publicKey := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} childExtendedPublicKey := NewExtendedPublicKey(childX.Bytes(), childY.Bytes(), depth[len(depth)-1], ParentFingerPrint, index, childChaincode, publicKey.SerializeCompressed()) return childExtendedPublicKey, nil }
// senderHTLCScript constructs the public key script for an incoming HTLC // output payment for the receiver's commitment transaction. func receiverHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey, receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) { builder := txscript.NewScriptBuilder() // Was the pre-image to the payment hash presented? builder.AddOp(txscript.OP_HASH160) builder.AddOp(txscript.OP_DUP) builder.AddData(paymentHash) builder.AddOp(txscript.OP_EQUAL) builder.AddOp(txscript.OP_IF) // If so, let the receiver redeem after a relative timeout. This added // delay gives the sender (at this time) an opportunity to re-claim the // pending HTLC in the event that the receiver (at this time) broadcasts // this old commitment transaction after it has been revoked. builder.AddInt64(int64(relativeTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_2DROP) builder.AddData(receiverKey.SerializeCompressed()) // Otherwise, if the sender has the revocation pre-image to the // receiver's commitment transactions, then let them claim the // funds immediately. builder.AddOp(txscript.OP_ELSE) builder.AddData(revokeHash) builder.AddOp(txscript.OP_EQUAL) // If not, then the sender needs to wait for the HTLC timeout. This // clause may be executed if the receiver fails to present the r-value // in time. This prevents the pending funds from being locked up // indefinately. builder.AddOp(txscript.OP_NOTIF) builder.AddInt64(int64(absoluteTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_DROP) builder.AddOp(txscript.OP_ENDIF) builder.AddData(senderKey.SerializeCompressed()) builder.AddOp(txscript.OP_ENDIF) builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() }
// Add adds an entry for a signature over 'sigHash' under public key 'pubKey' // to the signature cache. In the event that the SigCache is 'full', an // existing entry is randomly chosen to be evicted in order to make space for // the new entry. // // NOTE: This function is safe for concurrent access. Writers will block // simultaneous readers until function execution has concluded. func (s *SigCache) Add(sigHash wire.ShaHash, sig *btcec.Signature, pubKey *btcec.PublicKey) { s.Lock() defer s.Unlock() if s.maxEntries <= 0 { return } // If adding this new entry will put us over the max number of allowed // entries, then evict an entry. if uint(len(s.validSigs)+1) > s.maxEntries { // Generate a cryptographically random hash. randHashBytes := make([]byte, wire.HashSize) _, err := rand.Read(randHashBytes) if err != nil { // Failure to read a random hash results in the proposed // entry not being added to the cache since we are // unable to evict any existing entries. return } // Try to find the first entry that is greater than the random // hash. Use the first entry (which is already pseudo random due // to Go's range statement over maps) as a fall back if none of // the hashes in the rejected transactions pool are larger than // the random hash. var foundEntry sigInfo for sigEntry := range s.validSigs { if foundEntry.sig == "" { foundEntry = sigEntry } if bytes.Compare(sigEntry.sigHash.Bytes(), randHashBytes) > 0 { foundEntry = sigEntry break } } delete(s.validSigs, foundEntry) } info := sigInfo{sigHash, string(sig.Serialize()), string(pubKey.SerializeCompressed())} s.validSigs[info] = struct{}{} }
// senderHTLCScript constructs the public key script for an outgoing HTLC // output payment for the sender's commitment transaction. func senderHTLCScript(absoluteTimeout, relativeTimeout uint32, senderKey, receiverKey *btcec.PublicKey, revokeHash, paymentHash []byte) ([]byte, error) { builder := txscript.NewScriptBuilder() // Was the pre-image to the payment hash presented? builder.AddOp(txscript.OP_HASH160) builder.AddOp(txscript.OP_DUP) builder.AddData(paymentHash) builder.AddOp(txscript.OP_EQUAL) // How about the pre-image for our commitment revocation hash? builder.AddOp(txscript.OP_SWAP) builder.AddData(revokeHash) builder.AddOp(txscript.OP_EQUAL) builder.AddOp(txscript.OP_SWAP) builder.AddOp(txscript.OP_ADD) // If either is present, then the receiver can claim immediately. builder.AddOp(txscript.OP_IF) builder.AddData(receiverKey.SerializeCompressed()) // Otherwise, we (the sender) need to wait for an absolute HTLC // timeout, then afterwards a relative timeout before we claim re-claim // the unsettled funds. This delay gives the other party a chance to // present the pre-image to the revocation hash in the event that the // sender (at this time) broadcasts this commitment transaction after // it has been revoked. builder.AddOp(txscript.OP_ELSE) builder.AddInt64(int64(absoluteTimeout)) builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY) builder.AddInt64(int64(relativeTimeout)) builder.AddOp(OP_CHECKSEQUENCEVERIFY) builder.AddOp(txscript.OP_2DROP) builder.AddData(senderKey.SerializeCompressed()) builder.AddOp(txscript.OP_ENDIF) builder.AddOp(txscript.OP_CHECKSIG) return builder.Script() }
// Child returns a derived child extended key at the given index. When this // extended key is a private extended key (as determined by the IsPrivate // function), a private extended key will be derived. Otherwise, the derived // extended key will be also be a public extended key. // // When the index is greater to or equal than the HardenedKeyStart constant, the // derived extended key will be a hardened extended key. It is only possible to // derive a hardended extended key from a private extended key. Consequently, // this function will return ErrDeriveHardFromPublic if a hardened child // extended key is requested from a public extended key. // // A hardened extended key is useful since, as previously mentioned, it requires // a parent private extended key to derive. In other words, normal child // extended public keys can be derived from a parent public extended key (no // knowledge of the parent private key) whereas hardened extended keys may not // be. // // NOTE: There is an extremely small chance (< 1 in 2^127) the specific child // index does not derive to a usable child. The ErrInvalidChild error will be // returned if this should occur, and the caller is expected to ignore the // invalid child and simply increment to the next index. func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) { // There are four scenarios that could happen here: // 1) Private extended key -> Hardened child private extended key // 2) Private extended key -> Non-hardened child private extended key // 3) Public extended key -> Non-hardened child public extended key // 4) Public extended key -> Hardened child public extended key (INVALID!) // Case #4 is invalid, so error out early. // A hardened child extended key may not be created from a public // extended key. isChildHardened := i >= HardenedKeyStart if !k.isPrivate && isChildHardened { return nil, ErrDeriveHardFromPublic } // The data used to derive the child key depends on whether or not the // child is hardened per [BIP32]. // // For hardened children: // 0x00 || ser256(parentKey) || ser32(i) // // For normal children: // serP(parentPubKey) || ser32(i) keyLen := 33 data := make([]byte, keyLen+4) if isChildHardened { // Case #1. // When the child is a hardened child, the key is known to be a // private key due to the above early return. Pad it with a // leading zero as required by [BIP32] for deriving the child. copy(data[1:], k.key) } else { // Case #2 or #3. // This is either a public or private extended key, but in // either case, the data which is used to derive the child key // starts with the secp256k1 compressed public key bytes. copy(data, k.pubKeyBytes()) } binary.BigEndian.PutUint32(data[keyLen:], i) // Take the HMAC-SHA512 of the current key's chain code and the derived // data: // I = HMAC-SHA512(Key = chainCode, Data = data) hmac512 := hmac.New(sha512.New, k.chainCode) hmac512.Write(data) ilr := hmac512.Sum(nil) // Split "I" into two 32-byte sequences Il and Ir where: // Il = intermediate key used to derive the child // Ir = child chain code il := ilr[:len(ilr)/2] childChainCode := ilr[len(ilr)/2:] // Both derived public or private keys rely on treating the left 32-byte // sequence calculated above (Il) as a 256-bit integer that must be // within the valid range for a secp256k1 private key. There is a small // chance (< 1 in 2^127) this condition will not hold, and in that case, // a child extended key can't be created for this index and the caller // should simply increment to the next index. ilNum := new(big.Int).SetBytes(il) if ilNum.Cmp(btcec.S256().N) >= 0 || ilNum.Sign() == 0 { return nil, ErrInvalidChild } // The algorithm used to derive the child key depends on whether or not // a private or public child is being derived. // // For private children: // childKey = parse256(Il) + parentKey // // For public children: // childKey = serP(point(parse256(Il)) + parentKey) var isPrivate bool var childKey []byte if k.isPrivate { // Case #1 or #2. // Add the parent private key to the intermediate private key to // derive the final child key. // // childKey = parse256(Il) + parenKey keyNum := new(big.Int).SetBytes(k.key) ilNum.Add(ilNum, keyNum) ilNum.Mod(ilNum, btcec.S256().N) childKey = ilNum.Bytes() isPrivate = true } else { // Case #3. // Calculate the corresponding intermediate public key for // intermediate private key. ilx, ily := btcec.S256().ScalarBaseMult(il) if ilx.Sign() == 0 || ily.Sign() == 0 { return nil, ErrInvalidChild } // Convert the serialized compressed parent public key into X // and Y coordinates so it can be added to the intermediate // public key. pubKey, err := btcec.ParsePubKey(k.key, btcec.S256()) if err != nil { return nil, err } // Add the intermediate public key to the parent public key to // derive the final child key. // // childKey = serP(point(parse256(Il)) + parentKey) childX, childY := btcec.S256().Add(ilx, ily, pubKey.X, pubKey.Y) pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} childKey = pk.SerializeCompressed() } // The fingerprint of the parent for the derived child is the first 4 // bytes of the RIPEMD160(SHA256(parentPubKey)). parentFP := btcutil.Hash160(k.pubKeyBytes())[:4] return newExtendedKey(k.version, childKey, childChainCode, parentFP, k.depth+1, i, isPrivate), nil }