// newServer creates a new instance of the server which is to listen using the // passed listener address. func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier, bio lnwallet.BlockChainIO, wallet *lnwallet.LightningWallet, chanDB *channeldb.DB) (*server, error) { privKey, err := wallet.GetIdentitykey() if err != nil { return nil, err } listeners := make([]net.Listener, len(listenAddrs)) for i, addr := range listenAddrs { listeners[i], err = brontide.NewListener(privKey, addr) if err != nil { return nil, err } } serializedPubKey := privKey.PubKey().SerializeCompressed() s := &server{ bio: bio, chainNotifier: notifier, chanDB: chanDB, fundingMgr: newFundingManager(wallet), invoices: newInvoiceRegistry(chanDB), lnwallet: wallet, identityPriv: privKey, // TODO(roasbeef): derive proper onion key based on rotation // schedule sphinx: sphinx.NewRouter(privKey, activeNetParams.Params), lightningID: fastsha256.Sum256(serializedPubKey), listeners: listeners, peers: make(map[int32]*peer), newPeers: make(chan *peer, 100), donePeers: make(chan *peer, 100), queries: make(chan interface{}), quit: make(chan struct{}), } // If the debug HTLC flag is on, then we invoice a "master debug" // invoice which all outgoing payments will be sent and all incoming // HTLC's with the debug R-Hash immediately settled. if cfg.DebugHTLC { kiloCoin := btcutil.Amount(btcutil.SatoshiPerBitcoin * 1000) s.invoices.AddDebugInvoice(kiloCoin, *debugPre) srvrLog.Debugf("Debug HTLC invoice inserted, preimage=%x, hash=%x", debugPre[:], debugHash[:]) } s.utxoNursery = newUtxoNursery(notifier, wallet) // Create a new routing manager with ourself as the sole node within // the graph. selfVertex := hex.EncodeToString(serializedPubKey) s.routingMgr = routing.NewRoutingManager(graph.NewID(selfVertex), nil) s.htlcSwitch = newHtlcSwitch(serializedPubKey, s.routingMgr) s.rpcServer = newRpcServer(s) return s, nil }
// newPeer creates a new peer from an establish connection object, and a // pointer to the main server. func newPeer(conn net.Conn, server *server, addr *lnwire.NetAddress, inbound bool) (*peer, error) { nodePub := addr.IdentityKey p := &peer{ conn: conn, lightningID: wire.ShaHash(fastsha256.Sum256(nodePub.SerializeCompressed())), addr: addr, id: atomic.AddInt32(&numNodes, 1), inbound: inbound, server: server, lastNMessages: make(map[lnwire.Message]struct{}), sendQueueSync: make(chan struct{}, 1), sendQueue: make(chan outgoinMsg, 1), outgoingQueue: make(chan outgoinMsg, outgoingQueueLen), barrierInits: make(chan wire.OutPoint), newChanBarriers: make(map[wire.OutPoint]chan struct{}), activeChannels: make(map[wire.OutPoint]*lnwallet.LightningChannel), htlcManagers: make(map[wire.OutPoint]chan lnwire.Message), chanSnapshotReqs: make(chan *chanSnapshotReq), newChannels: make(chan *lnwallet.LightningChannel, 1), localCloseChanReqs: make(chan *closeLinkReq), remoteCloseChanReqs: make(chan *lnwire.CloseRequest), queueQuit: make(chan struct{}), quit: make(chan struct{}), } // Initiate the pending channel identifier properly depending on if this // node is inbound or outbound. This value will be used in an increasing // manner to track pending channels. if inbound { p.nextPendingChannelID = 1 << 63 } else { p.nextPendingChannelID = 0 } // Fetch and then load all the active channels we have with this // remote peer from the database. activeChans, err := server.chanDB.FetchOpenChannels(p.addr.IdentityKey) if err != nil { peerLog.Errorf("unable to fetch active chans "+ "for peer %v: %v", p, err) return nil, err } peerLog.Debugf("Loaded %v active channels from database with peerID(%v)", len(activeChans), p.id) if err := p.loadActiveChannels(activeChans); err != nil { return nil, err } return p, nil }
func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket, i *Invoice, invoiceNum uint32) error { // Create the invoice key which is just the big-endian representation // of the invoice number. var invoiceKey [4]byte byteOrder.PutUint32(invoiceKey[:], invoiceNum) // Increment the num invoice counter index so the next invoice bares // the proper ID. var scratch [4]byte invoiceCounter := invoiceNum + 1 byteOrder.PutUint32(scratch[:], invoiceCounter) if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil { return err } // Add the payment hash to the invoice index. This'll let us quickly // identify if we can settle an incoming payment, and also to possibly // allow a single invoice to have multiple payment installations. paymentHash := fastsha256.Sum256(i.Terms.PaymentPreimage[:]) if err := invoiceIndex.Put(paymentHash[:], invoiceKey[:]); err != nil { return err } // Finally, serialize the invoice itself to be written to the disk. var buf bytes.Buffer if err := serializeInvoice(&buf, i); err != nil { return nil } return invoices.Put(invoiceKey[:], buf.Bytes()) }
// witnessScriptHash generates a pay-to-witness-script-hash public key script // paying to a version 0 witness program paying to the passed redeem script. func witnessScriptHash(witnessScript []byte) ([]byte, error) { bldr := txscript.NewScriptBuilder() bldr.AddOp(txscript.OP_0) scriptHash := fastsha256.Sum256(witnessScript) bldr.AddData(scriptHash[:]) return bldr.Script() }
// authPKH... func (c *LNDConn) authPKH( myId *btcec.PrivateKey, theirPKH, localEphPubBytes []byte) error { if c.Authed { return fmt.Errorf("%s already authed", c.RemotePub) } if len(theirPKH) != 20 { return fmt.Errorf("remote PKH must be 20 bytes, got %d", len(theirPKH)) } // Send 53 bytes: our pubkey, and the remote's pubkey hash. var greetingMsg [53]byte copy(greetingMsg[:33], myId.PubKey().SerializeCompressed()) copy(greetingMsg[:33], theirPKH) if _, err := c.Conn.Write(greetingMsg[:]); err != nil { return err } // Wait for their response. // TODO(tadge): add timeout here // * NOTE(roasbeef): read timeout should be set on the underlying // net.Conn. resp := make([]byte, 53) if _, err := c.Conn.Read(resp); err != nil { return err } // Parse their long-term public key, and generate the DH proof. theirPub, err := btcec.ParsePubKey(resp[:33], btcec.S256()) if err != nil { return err } idDH := fastsha256.Sum256(btcec.GenerateSharedSecret(myId, theirPub)) fmt.Printf("made idDH %x\n", idDH) theirDHproof := btcutil.Hash160(append(localEphPubBytes, idDH[:]...)) // Verify that their DH proof matches the one we just generated. if bytes.Equal(resp[33:], theirDHproof) == false { return fmt.Errorf("Invalid DH proof %x", theirDHproof) } // If their DH proof checks out, then send our own. myDHproof := btcutil.Hash160(append(c.RemotePub.SerializeCompressed(), idDH[:]...)) if _, err = c.Conn.Write(myDHproof); err != nil { return err } // Proof sent, auth complete. c.RemotePub = theirPub theirAdr := btcutil.Hash160(theirPub.SerializeCompressed()) copy(c.RemoteLNId[:], theirAdr[:16]) c.Authed = true return nil }
// UnregisterLink requets the htlcSwitch to unregiser the new active link. An // unregistered link will no longer be considered a candidate to forward // HTLC's. func (h *htlcSwitch) UnregisterLink(remotePub *btcec.PublicKey, chanPoint *wire.OutPoint) { done := make(chan struct{}, 1) rawPub := remotePub.SerializeCompressed() h.linkControl <- &unregisterLinkMsg{ chanInterface: fastsha256.Sum256(rawPub), chanPoint: chanPoint, remoteID: rawPub, done: done, } <-done }
// checkAuthHeader checks the HTTP Basic authentication supplied by a client // in the HTTP request r. It errors with ErrNoAuth if the request does not // contain the Authorization header, or another non-nil error if the // authentication was provided but incorrect. // // This check is time-constant. func (s *Server) checkAuthHeader(r *http.Request) error { authhdr := r.Header["Authorization"] if len(authhdr) == 0 { return ErrNoAuth } authsha := fastsha256.Sum256([]byte(authhdr[0])) cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:]) if cmp != 1 { return errors.New("bad auth") } return nil }
// DeriveKey derives the underlying secret key and ensures it matches the // expected digest. This should only be called after previously calling the // Zero function or on an initial Unmarshal. func (sk *SecretKey) DeriveKey(password *[]byte) error { if err := sk.deriveKey(password); err != nil { return err } // verify password digest := fastsha256.Sum256(sk.Key[:]) if subtle.ConstantTimeCompare(digest[:], sk.Parameters.Digest[:]) != 1 { return ErrInvalidPassword } return nil }
// invalidAuth checks whether a websocket request is a valid (parsable) // authenticate request and checks the supplied username and passphrase // against the server auth. func (s *Server) invalidAuth(req *btcjson.Request) bool { cmd, err := btcjson.UnmarshalCmd(req) if err != nil { return false } authCmd, ok := cmd.(*btcjson.AuthenticateCmd) if !ok { return false } // Check credentials. login := authCmd.Username + ":" + authCmd.Passphrase auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) authSha := fastsha256.Sum256([]byte(auth)) return subtle.ConstantTimeCompare(authSha[:], s.authsha[:]) != 1 }
// authPubKey... func (c *LNDConn) authPubKey( myId *btcec.PrivateKey, remotePubBytes, localEphPubBytes []byte) error { if c.Authed { return fmt.Errorf("%s already authed", c.RemotePub) } // Since we already know their public key, we can immediately generate // the DH proof without an additional round-trip. theirPub, err := btcec.ParsePubKey(remotePubBytes, btcec.S256()) if err != nil { return err } theirPKH := btcutil.Hash160(remotePubBytes) idDH := fastsha256.Sum256(btcec.GenerateSharedSecret(myId, theirPub)) myDHproof := btcutil.Hash160(append(c.RemotePub.SerializeCompressed(), idDH[:]...)) // Send over the 73 byte authentication message: my pubkey, their // pubkey hash, DH proof. var authMsg [73]byte copy(authMsg[:33], myId.PubKey().SerializeCompressed()) copy(authMsg[33:], theirPKH) copy(authMsg[53:], myDHproof) if _, err = c.Conn.Write(authMsg[:]); err != nil { return nil } // Await, their response. They should send only the 20-byte DH proof. resp := make([]byte, 20) _, err = c.Conn.Read(resp) if err != nil { return err } // Verify that their proof matches our locally computed version. theirDHproof := btcutil.Hash160(append(localEphPubBytes, idDH[:]...)) if bytes.Equal(resp, theirDHproof) == false { return fmt.Errorf("invalid DH proof %x", theirDHproof) } // Proof checks out, auth complete. c.RemotePub = theirPub theirAdr := btcutil.Hash160(theirPub.SerializeCompressed()) copy(c.RemoteLNId[:], theirAdr[:16]) c.Authed = true return nil }
// createCipherConn.... func (l *Listener) createCipherConn(lnConn *LNDConn) (*btcec.PrivateKey, error) { var err error var theirEphPubBytes []byte // First, read and deserialize their ephemeral public key. theirEphPubBytes, err = readClear(lnConn.Conn) if err != nil { return nil, err } if len(theirEphPubBytes) != 33 { return nil, fmt.Errorf("Got invalid %d byte eph pubkey %x\n", len(theirEphPubBytes), theirEphPubBytes) } theirEphPub, err := btcec.ParsePubKey(theirEphPubBytes, btcec.S256()) if err != nil { return nil, err } // Once we've parsed and verified their key, generate, and send own // ephemeral key pair for use within this session. myEph, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { return nil, err } if _, err := writeClear(lnConn.Conn, myEph.PubKey().SerializeCompressed()); err != nil { return nil, err } // Now that we have both keys, do non-interactive diffie with ephemeral // pubkeys, sha256 for good luck. sessionKey := fastsha256.Sum256( btcec.GenerateSharedSecret(myEph, theirEphPub), ) lnConn.chachaStream, err = chacha20poly1305.New(sessionKey[:]) // display private key for debug only fmt.Printf("made session key %x\n", sessionKey) lnConn.remoteNonceInt = 1 << 63 lnConn.myNonceInt = 0 lnConn.RemotePub = theirEphPub lnConn.Authed = false return myEph, nil }
// AddInvoice inserts the targeted invoice into the database. If the invoice // has *any* payment hashes which already exists within the database, then the // insertion will be aborted and rejected due to the strict policy banning any // duplicate payment hashes. func (d *DB) AddInvoice(i *Invoice) error { if len(i.Memo) > MaxMemoSize { return fmt.Errorf("max length a memo is %v, and invoice "+ "of length %v was provided", MaxMemoSize, len(i.Memo)) } if len(i.Receipt) > MaxReceiptSize { return fmt.Errorf("max length a receipt is %v, and invoice "+ "of length %v was provided", MaxReceiptSize, len(i.Receipt)) } return d.store.Update(func(tx *bolt.Tx) error { invoices, err := tx.CreateBucketIfNotExists(invoiceBucket) if err != nil { return err } invoiceIndex, err := invoices.CreateBucketIfNotExists(invoiceIndexBucket) if err != nil { return err } // Ensure that an invoice an identical payment hash doesn't // already exist within the index. paymentHash := fastsha256.Sum256(i.Terms.PaymentPreimage[:]) if invoiceIndex.Get(paymentHash[:]) != nil { return ErrDuplicateInvoice } // If the current running payment ID counter hasn't yet been // created, then create it now. var invoiceNum uint32 invoiceCounter := invoiceIndex.Get(numInvoicesKey) if invoiceCounter == nil { var scratch [4]byte byteOrder.PutUint32(scratch[:], invoiceNum) if err := invoiceIndex.Put(numInvoicesKey, scratch[:]); err != nil { return nil } } else { invoiceNum = byteOrder.Uint32(invoiceCounter) } return putInvoice(invoices, invoiceIndex, i, invoiceNum) }) }
// AddInvoice attempts to add a new invoice to the invoice database. Any // duplicated invoices are rejected, therefore all invoices *must* have a // unique payment preimage. func (r *rpcServer) AddInvoice(ctx context.Context, invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) { preImage := invoice.RPreimage preimageLength := len(preImage) if preimageLength != 32 { return nil, fmt.Errorf("payment preimage must be exactly "+ "32 bytes, is instead %v", preimageLength) } if len(invoice.Memo) > channeldb.MaxMemoSize { return nil, fmt.Errorf("memo too large: %v bytes "+ "(maxsize=%v)", len(invoice.Memo), channeldb.MaxMemoSize) } if len(invoice.Receipt) > channeldb.MaxReceiptSize { return nil, fmt.Errorf("receipt too large: %v bytes "+ "(maxsize=%v)", len(invoice.Receipt), channeldb.MaxReceiptSize) } i := &channeldb.Invoice{ CreationDate: time.Now(), Memo: []byte(invoice.Memo), Receipt: invoice.Receipt, Terms: channeldb.ContractTerm{ Value: btcutil.Amount(invoice.Value), }, } copy(i.Terms.PaymentPreimage[:], preImage) rpcsLog.Tracef("[addinvoice] adding new invoice %v", newLogClosure(func() string { return spew.Sdump(i) })) if err := r.server.invoices.AddInvoice(i); err != nil { return nil, err } rHash := fastsha256.Sum256(preImage) return &lnrpc.AddInvoiceResponse{ RHash: rHash[:], }, nil }
// addDebugInvoice adds a debug invoice for the specified amount, identified // by the passed preimage. Once this invoice is added, sub-systems within the // daemon add/forward HTLC's are able to obtain the proper preimage required // for redemption in the case that we're the final destination. func (i *invoiceRegistry) AddDebugInvoice(amt btcutil.Amount, preimage wire.ShaHash) { paymentHash := wire.ShaHash(fastsha256.Sum256(preimage[:])) invoice := &channeldb.Invoice{ CreationDate: time.Now(), Terms: channeldb.ContractTerm{ Value: amt, PaymentPreimage: preimage, }, } i.Lock() i.debugInvoices[paymentHash] = invoice i.Unlock() ltndLog.Debugf("Adding debug invoice %v", newLogClosure(func() string { return spew.Sdump(invoice) })) }
// NewSecretKey returns a SecretKey structure based on the passed parameters. func NewSecretKey(password *[]byte, N, r, p int) (*SecretKey, error) { sk := SecretKey{ Key: (*CryptoKey)(&[KeySize]byte{}), } // setup parameters sk.Parameters.N = N sk.Parameters.R = r sk.Parameters.P = p _, err := io.ReadFull(prng, sk.Parameters.Salt[:]) if err != nil { return nil, err } // derive key err = sk.deriveKey(password) if err != nil { return nil, err } // store digest sk.Parameters.Digest = fastsha256.Sum256(sk.Key[:]) return &sk, nil }
// DoubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes. func DoubleSha256(b []byte) []byte { first := fastsha256.Sum256(b) second := fastsha256.Sum256(first[:]) return second[:] }
// DoubleSha256SH calculates sha256(sha256(b)) and returns the resulting bytes // as a ShaHash. func DoubleSha256SH(b []byte) ShaHash { first := fastsha256.Sum256(b) return ShaHash(fastsha256.Sum256(first[:])) }
func TestRFC6979(t *testing.T) { // Test vectors matching Trezor and CoreBitcoin implementations. // - https://github.com/trezor/trezor-crypto/blob/9fea8f8ab377dc514e40c6fd1f7c89a74c1d8dc6/tests.c#L432-L453 // - https://github.com/oleganza/CoreBitcoin/blob/e93dd71207861b5bf044415db5fa72405e7d8fbc/CoreBitcoin/BTCKey%2BTests.m#L23-L49 tests := []struct { key string msg string nonce string signature string }{ { "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", "sample", "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", "3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124", }, { // This signature hits the case when S is higher than halforder. // If S is not canonicalized (lowered by halforder), this test will fail. "0000000000000000000000000000000000000000000000000000000000000001", "Satoshi Nakamoto", "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15", "3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5", }, { "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "Satoshi Nakamoto", "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90", "3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5", }, { "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", "Alan Turing", "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1", "304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea", }, { "0000000000000000000000000000000000000000000000000000000000000001", "All those moments will be lost in time, like tears in rain. Time to die...", "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3", "30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21", }, { "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d", "3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6", }, } for i, test := range tests { privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), decodeHex(test.key)) hash := fastsha256.Sum256([]byte(test.msg)) // Ensure deterministically generated nonce is the expected value. gotNonce := btcec.TstNonceRFC6979(privKey.D, hash[:]).Bytes() wantNonce := decodeHex(test.nonce) if !bytes.Equal(gotNonce, wantNonce) { t.Errorf("NonceRFC6979 #%d (%s): Nonce is incorrect: "+ "%x (expected %x)", i, test.msg, gotNonce, wantNonce) continue } // Ensure deterministically generated signature is the expected value. gotSig, err := privKey.Sign(hash[:]) if err != nil { t.Errorf("Sign #%d (%s): unexpected error: %v", i, test.msg, err) continue } gotSigBytes := gotSig.Serialize() wantSigBytes := decodeHex(test.signature) if !bytes.Equal(gotSigBytes, wantSigBytes) { t.Errorf("Sign #%d (%s): mismatched signature: %x "+ "(expected %x)", i, test.msg, gotSigBytes, wantSigBytes) continue } } }
// Dial... func (c *Conn) Dial(address string, remoteId []byte) error { var err error if c.conn != nil { return fmt.Errorf("connection already established") } // Before dialing out to the remote host, verify that `remoteId` is either // a pubkey or a pubkey hash. if len(remoteId) != 33 && len(remoteId) != 20 { return fmt.Errorf("must supply either remote pubkey or " + "pubkey hash") } // First, open the TCP connection itself. c.conn, err = net.Dial("tcp", address) if err != nil { return err } // Calc remote LNId; need this for creating pbx connections just because // LNid is in the struct does not mean it's authed! if len(remoteId) == 20 { copy(c.remoteLNId[:], remoteId[:16]) } else { theirAdr := btcutil.Hash160(remoteId) copy(c.remoteLNId[:], theirAdr[:16]) } // Make up an ephemeral keypair for this session. ourEphemeralPriv, err := btcec.NewPrivateKey(btcec.S256()) if err != nil { return err } ourEphemeralPub := ourEphemeralPriv.PubKey() // Sned 1. Send my ephemeral pubkey. Can add version bits. if _, err = writeClear(c.conn, ourEphemeralPub.SerializeCompressed()); err != nil { return err } // Read, then deserialize their ephemeral public key. theirEphPubBytes, err := readClear(c.conn) if err != nil { return err } theirEphPub, err := btcec.ParsePubKey(theirEphPubBytes, btcec.S256()) if err != nil { return err } // Do non-interactive diffie with ephemeral pubkeys. Sha256 for good // luck. sessionKey := fastsha256.Sum256( btcec.GenerateSharedSecret(ourEphemeralPriv, theirEphPub), ) // Now that we've derive the session key, we can initialize the // chacha20poly1305 AEAD instance which will be used for the remainder of // the session. c.chachaStream, err = chacha20poly1305.New(sessionKey[:]) if err != nil { return err } // display private key for debug only fmt.Printf("made session key %x\n", sessionKey) c.myNonceInt = 1 << 63 c.remoteNonceInt = 0 c.remotePub = theirEphPub c.authed = false // Session is now open and confidential but not yet authenticated... // So auth! if len(remoteId) == 20 { // Only know pubkey hash (20 bytes). err = c.authPKH(remoteId, ourEphemeralPub.SerializeCompressed()) } else { // Must be 33 byte pubkey. err = c.authPubKey(remoteId, ourEphemeralPub.SerializeCompressed()) } if err != nil { return err } return nil }
// SendPayment dispatches a bi-directional streaming RPC for sending payments // through the Lightning Network. A single RPC invocation creates a persistent // bi-directional stream allowing clients to rapidly send payments through the // Lightning Network with a single persistent connection. func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) error { const queryTimeout = time.Duration(time.Second * 10) errChan := make(chan error, 1) payChan := make(chan *lnrpc.SendRequest) // Launch a new goroutine to handle reading new payment requests from // the client. This way we can handle errors independently of blocking // and waiting for the next payment request to come through. go func() { for { select { case <-r.quit: errChan <- nil return default: // Receive the next pending payment within the // stream sent by the client. If we read the // EOF sentinel, then the client has closed the // stream, and we can exit normally. nextPayment, err := paymentStream.Recv() if err == io.EOF { errChan <- nil return } else if err != nil { errChan <- err return } payChan <- nextPayment } } }() for { select { case err := <-errChan: return err case nextPayment := <-payChan: // Query the routing table for a potential path to the // destination node. If a path is ultimately // unavailable, then an error will be returned. destNode := hex.EncodeToString(nextPayment.Dest) targetVertex := graph.NewID(destNode) path, err := r.server.routingMgr.FindPath(targetVertex, queryTimeout) if err != nil { return err } rpcsLog.Tracef("[sendpayment] selected route: %v", path) // If we're in debug HTLC mode, then all outgoing // HTLC's will pay to the same debug rHash. Otherwise, // we pay to the rHash specified within the RPC // request. var rHash [32]byte if cfg.DebugHTLC { rHash = debugHash } else { copy(rHash[:], nextPayment.PaymentHash) } // Generate the raw encoded sphinx packet to be // included along with the HTLC add message. We snip // off the first hop from the path as within the // routing table's star graph, we're always the first // hop. sphinxPacket, err := generateSphinxPacket(path[1:], rHash[:]) if err != nil { return err } // Craft an HTLC packet to send to the routing // sub-system. The meta-data within this packet will be // used to route the payment through the network. htlcAdd := &lnwire.HTLCAddRequest{ Amount: lnwire.CreditsAmount(nextPayment.Amt), RedemptionHashes: [][32]byte{rHash}, OnionBlob: sphinxPacket, } firstHopPub, err := hex.DecodeString(path[1].String()) if err != nil { return err } destAddr := wire.ShaHash(fastsha256.Sum256(firstHopPub)) htlcPkt := &htlcPacket{ dest: destAddr, msg: htlcAdd, } // TODO(roasbeef): semaphore to limit num outstanding // goroutines. go func() { // Finally, send this next packet to the // routing layer in order to complete the next // payment. // TODO(roasbeef): this should go through the // L3 router once multi-hop is in place. if err := r.server.htlcSwitch.SendHTLC(htlcPkt); err != nil { errChan <- err return } // TODO(roasbeef): proper responses resp := &lnrpc.SendResponse{} if err := paymentStream.Send(resp); err != nil { errChan <- err return } }() } } return nil }
// NewServer creates a new server for serving legacy RPC client connections, // both HTTP POST and websocket. func NewServer(opts *Options, walletLoader *wallet.Loader, listeners []net.Listener) *Server { serveMux := http.NewServeMux() const rpcAuthTimeoutSeconds = 10 server := &Server{ httpServer: http.Server{ Handler: serveMux, // Timeout connections which don't complete the initial // handshake within the allowed timeframe. ReadTimeout: time.Second * rpcAuthTimeoutSeconds, }, walletLoader: walletLoader, maxPostClients: opts.MaxPOSTClients, maxWebsocketClients: opts.MaxWebsocketClients, listeners: listeners, // A hash of the HTTP basic auth string is used for a constant // time comparison. authsha: fastsha256.Sum256(httpBasicAuth(opts.Username, opts.Password)), upgrader: websocket.Upgrader{ // Allow all origins. CheckOrigin: func(r *http.Request) bool { return true }, }, quit: make(chan struct{}), requestShutdownChan: make(chan struct{}, 1), } serveMux.Handle("/", throttledFn(opts.MaxPOSTClients, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") w.Header().Set("Content-Type", "application/json") r.Close = true if err := server.checkAuthHeader(r); err != nil { log.Warnf("Unauthorized client connection attempt") jsonAuthFail(w) return } server.wg.Add(1) server.postClientRPC(w, r) server.wg.Done() })) serveMux.Handle("/ws", throttledFn(opts.MaxWebsocketClients, func(w http.ResponseWriter, r *http.Request) { authenticated := false switch server.checkAuthHeader(r) { case nil: authenticated = true case ErrNoAuth: // nothing default: // If auth was supplied but incorrect, rather than simply // being missing, immediately terminate the connection. log.Warnf("Disconnecting improperly authorized " + "websocket client") jsonAuthFail(w) return } conn, err := server.upgrader.Upgrade(w, r, nil) if err != nil { log.Warnf("Cannot websocket upgrade client %s: %v", r.RemoteAddr, err) return } wsc := newWebsocketClient(conn, authenticated, r.RemoteAddr) server.websocketClientRPC(wsc) })) for _, lis := range listeners { server.serve(lis) } return server }
// Sha256. The internal tests from secp256k1 are kind of screwy and for // partial signatures call this hash function instead of testSchnorrHash. func testSchnorrSha256Hash(msg []byte) []byte { sha := fastsha256.Sum256(msg) return sha[:] }
// TestHTLCReceiverSpendValidation tests all possible valid+invalid redemption // paths in the script used within the reciever's commitment transaction for an // incoming HTLC. // // The following cases are exercised by this test: // * reciever spends // * HTLC redemption w/ invalid preimage size // * HTLC redemption w/ invalid sequence // * HTLC redemption w/ valid preimage size // * sender spends // * revoke w/ sig // * refund w/ invalid lock time // * refund w/ valid lock time func TestHTLCReceiverSpendValidation(t *testing.T) { // We generate a fake output, and the coresponding txin. This output // doesn't need to exist, as we'll only be validating spending from the // transaction that references this. fundingOut := &wire.OutPoint{ Hash: testHdSeed, Index: 50, } fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil) // Generate a payment and revocation pre-image to be used below. revokePreimage := testHdSeed[:] revokeHash := fastsha256.Sum256(revokePreimage) paymentPreimage := revokeHash paymentPreimage[0] ^= 1 paymentHash := fastsha256.Sum256(paymentPreimage[:]) // We'll also need some tests keys for alice and bob, and meta-data of // the HTLC output. aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) paymentAmt := btcutil.Amount(1 * 10e8) cltvTimeout := uint32(8) csvTimeout := uint32(5) // Generate the raw HTLC redemption scripts, and its p2wsh counterpart. htlcScript, err := receiverHTLCScript(cltvTimeout, csvTimeout, aliceKeyPub, bobKeyPub, revokeHash[:], paymentHash[:]) if err != nil { t.Fatalf("unable to create htlc sender script: %v", err) } htlcWitnessScript, err := witnessScriptHash(htlcScript) if err != nil { t.Fatalf("unable to create p2wsh htlc script: %v", err) } // This will be Bob's commitment transaction. In this scenario Alice // is sending an HTLC to a node she has a a path to (could be Bob, // could be multiple hops down, it doesn't really matter). recieverCommitTx := wire.NewMsgTx() recieverCommitTx.AddTxIn(fakeFundingTxIn) recieverCommitTx.AddTxOut(&wire.TxOut{ Value: int64(paymentAmt), PkScript: htlcWitnessScript, }) prevOut := &wire.OutPoint{ Hash: recieverCommitTx.TxSha(), Index: 0, } sweepTx := wire.NewMsgTx() sweepTx.AddTxIn(wire.NewTxIn(prevOut, nil, nil)) sweepTx.AddTxOut( &wire.TxOut{ PkScript: []byte("doesn't matter"), Value: 1 * 10e8, }, ) testCases := []struct { witness func() wire.TxWitness valid bool }{ { // HTLC redemption w/ invalid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, bytes.Repeat([]byte{1}, 45), csvTimeout, ) }), false, }, { // HTLC redemption w/ invalid sequence makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, paymentPreimage[:], csvTimeout-2, ) }), false, }, { // HTLC redemption w/ valid preimage size makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRedeem(htlcScript, paymentAmt, bobKeyPriv, sweepTx, paymentPreimage[:], csvTimeout, ) }), true, }, { // revoke w/ sig makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendRevoke(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, revokePreimage[:], ) }), true, }, { // refund w/ invalid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendTimeout(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, cltvTimeout-2) }), false, }, { // refund w/ valid lock time makeWitnessTestCase(t, func() (wire.TxWitness, error) { return receiverHtlcSpendTimeout(htlcScript, paymentAmt, aliceKeyPriv, sweepTx, cltvTimeout) }), true, }, } for i, testCase := range testCases { sweepTx.TxIn[0].Witness = testCase.witness() vm, err := txscript.NewEngine(htlcWitnessScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, nil, int64(paymentAmt)) if err != nil { t.Fatalf("unable to create engine: %v", err) } // This buffer will trace execution of the Script, only dumping // out to stdout in the case that a test fails. var debugBuf bytes.Buffer done := false for !done { dis, err := vm.DisasmPC() if err != nil { t.Fatalf("stepping (%v)\n", err) } debugBuf.WriteString(fmt.Sprintf("stepping %v\n", dis)) done, err = vm.Step() if err != nil && testCase.valid { fmt.Println(debugBuf.String()) t.Fatalf("spend test case #%v failed, spend should be valid: %v", i, err) } else if err == nil && !testCase.valid && done { fmt.Println(debugBuf.String()) t.Fatalf("spend test case #%v succeed, spend should be invalid: %v", i, err) } debugBuf.WriteString(fmt.Sprintf("Stack: ", vm.GetStack())) debugBuf.WriteString(fmt.Sprintf("AltStack: ", vm.GetAltStack())) } } }
// htlcForwarder is responsible for optimally forwarding (and possibly // fragmenting) incoming/outgoing HTLC's amongst all active interfaces and // their links. The duties of the forwarder are similar to that of a network // switch, in that it facilitates multi-hop payments by acting as a central // messaging bus. The switch communicates will active links to create, manage, // and tearn down active onion routed payments.Each active channel is modeled // as networked device with meta-data such as the available payment bandwidth, // and total link capacity. func (h *htlcSwitch) htlcForwarder() { // TODO(roasbeef): track pending payments here instead of within each peer? // Examine settles/timeouts from htlcPlex. Add src to htlcPacket, key by // (src, htlcKey). // TODO(roasbeef): cleared vs settled distinction var numUpdates uint64 var satSent, satRecv btcutil.Amount logTicker := time.NewTicker(10 * time.Second) out: for { select { case htlcPkt := <-h.outgoingPayments: dest := htlcPkt.dest h.interfaceMtx.RLock() chanInterface, ok := h.interfaces[dest] h.interfaceMtx.RUnlock() if !ok { err := fmt.Errorf("Unable to locate link %x", dest[:]) hswcLog.Errorf(err.Error()) htlcPkt.err <- err continue } wireMsg := htlcPkt.msg.(*lnwire.HTLCAddRequest) amt := btcutil.Amount(wireMsg.Amount) // Handle this send request in a distinct goroutine in // order to avoid a possible deadlock between the htlc // switch and channel's htlc manager. for _, link := range chanInterface { // TODO(roasbeef): implement HTLC fragmentation // * avoid full channel depletion at higher // level (here) instead of within state // machine? if link.availableBandwidth < int64(amt) { continue } hswcLog.Tracef("Sending %v to %x", amt, dest[:]) go func() { link.linkChan <- htlcPkt }() n := atomic.AddInt64(&link.availableBandwidth, -int64(amt)) hswcLog.Tracef("Decrementing link %v bandwidth to %v", link.chanPoint, n) continue out } hswcLog.Errorf("Unable to send payment, insufficient capacity") htlcPkt.err <- fmt.Errorf("Insufficient capacity") case pkt := <-h.htlcPlex: // TODO(roasbeef): properly account with cleared vs settled numUpdates += 1 hswcLog.Tracef("plex packet: %v", newLogClosure(func() string { return spew.Sdump(pkt) })) switch wireMsg := pkt.msg.(type) { // A link has just forwarded us a new HTLC, therefore // we initiate the payment circuit within our internal // staate so we can properly forward the ultimate // settle message. case *lnwire.HTLCAddRequest: // Create the two ends of the payment circuit // required to ensure completion of this new // payment. nextHop := pkt.onion.NextHop h.onionMtx.RLock() clearLink, ok := h.onionIndex[nextHop] h.onionMtx.RUnlock() if !ok { hswcLog.Errorf("unable to find dest end of "+ "circuit: %x", nextHop) continue } h.chanIndexMtx.RLock() settleLink := h.chanIndex[pkt.srcLink] h.chanIndexMtx.RUnlock() // TODO(roasbeef): examine per-hop info to decide on link? // * check clear has enough available sat circuit := &paymentCircuit{ clear: clearLink[0], settle: settleLink, } cKey := circuitKey(wireMsg.RedemptionHashes[0]) h.paymentCircuits[cKey] = circuit hswcLog.Debugf("Creating onion circuit for %x: %v<->%v", cKey[:], clearLink[0].chanPoint, settleLink.chanPoint) // With the circuit initiated, send the htlcPkt // to the clearing link within the circuit to // continue propagating the HTLC accross the // network. circuit.clear.linkChan <- &htlcPacket{ msg: wireMsg, err: make(chan error, 1), } // Reduce the available bandwidth for the link // as it will clear the above HTLC, increasing // the limbo balance within the channel. n := atomic.AddInt64(&circuit.clear.availableBandwidth, -int64(pkt.amt)) hswcLog.Tracef("Decrementing link %v bandwidth to %v", circuit.clear.chanPoint, n) satRecv += pkt.amt // We've just received a settle message which means we // can finalize the payment circuit by forwarding the // settle msg to the link which initially created the // circuit. case *lnwire.HTLCSettleRequest: rHash := fastsha256.Sum256(wireMsg.RedemptionProofs[0][:]) var cKey circuitKey copy(cKey[:], rHash[:]) // If we initiated the payment then there won't // be an active circuit to continue propagating // the settle over. Therefore, we exit early. circuit, ok := h.paymentCircuits[cKey] if !ok { hswcLog.Debugf("No existing circuit "+ "for %x", rHash[:]) satSent += pkt.amt continue } hswcLog.Debugf("Closing completed onion "+ "circuit for %x: %v<->%v", rHash[:], circuit.clear.chanPoint, circuit.settle.chanPoint) circuit.settle.linkChan <- &htlcPacket{ msg: wireMsg, err: make(chan error, 1), } // Increase the available bandwidth for the // link as it will settle the above HTLC, // subtracting from the limbo balacne and // incrementing its local balance. n := atomic.AddInt64(&circuit.settle.availableBandwidth, int64(pkt.amt)) hswcLog.Tracef("Incrementing link %v bandwidth to %v", circuit.settle.chanPoint, n) satSent += pkt.amt } case <-logTicker.C: if numUpdates == 0 { continue } hswcLog.Infof("Sent %v satoshis, received %v satoshi in "+ "the last 10 seconds (%v tx/sec)", satSent.ToUnit(btcutil.AmountSatoshi), satRecv.ToUnit(btcutil.AmountSatoshi), float64(numUpdates)/10) satSent = 0 satRecv = 0 numUpdates = 0 case <-h.quit: break out } } h.wg.Done() }
// verifyWitnessProgram validates the stored witness program using the passed // witness as input. func (vm *Engine) verifyWitnessProgram(witness [][]byte) error { // All elements within the witness stack must not be greater than the // maximum bytes which are allowed to be pushed onto the stack. for _, witElement := range witness { if len(witElement) > MaxScriptElementSize { return ErrStackElementTooBig } } if vm.witnessVersion == 0 { switch len(vm.witnessProgram) { case payToWitnessPubKeyHashDataSize: // P2WKH // The witness stack should consist of exactly two // items: the signature, and the pubkey. if len(witness) != 2 { return ErrWitnessScriptMismatch } // Now we'll resume execution as if it were a // regular p2pkh transaction. pkScript, err := payToPubKeyHashScript(vm.witnessProgram) if err != nil { return err } pops, err := parseScript(pkScript) if err != nil { return err } // Set the stack to the provided witness // stack, then append the pkScript // generated above as the next script to execute. vm.scripts = append(vm.scripts, pops) vm.SetStack(witness) case payToWitnessScriptHashDataSize: // P2WSH // Additionally, The witness stack MUST NOT be empty at // this point. if len(witness) == 0 { return ErrWitnessProgramEmpty } // Ensure that the serialized pkScript // at the end of the witness stack // matches the witness program. pkScript := witness[len(witness)-1] witnessHash := fastsha256.Sum256(pkScript) if !bytes.Equal(witnessHash[:], vm.witnessProgram) { return fmt.Errorf("witness program mismatch") } pops, err := parseScript(pkScript) if err != nil { return err } // The hash matched successfully, so // use the witness as the stack, and // set the pkScript to be the next script // executed. vm.scripts = append(vm.scripts, pops) vm.SetStack(witness[:len(witness)-1]) default: return ErrWitnessProgramWrongLength } } else if vm.hasFlag(ScriptVerifyDiscourageUpgradeableWitnessProgram) { return fmt.Errorf("new witness program versions invalid: %v", vm.witnessVersion) } else { // If we encounter an unknown witness program version and we // aren't discouraging future unknown witness based soft-forks, // then we de-activate the segwit behavior within the VM for // the remainder of execution. vm.witness = false } return nil }
// AuthListen... func (l *Listener) authenticateConnection( myId *btcec.PrivateKey, lnConn *LNDConn, localEphPubBytes []byte) error { var err error // TODO(roasbeef): should be using read/write clear here? slice := make([]byte, 73) n, err := lnConn.Conn.Read(slice) if err != nil { fmt.Printf("Read error: %s\n", err.Error()) return err } fmt.Printf("read %d bytes\n", n) authmsg := slice[:n] if len(authmsg) != 53 && len(authmsg) != 73 { return fmt.Errorf("got auth message of %d bytes, "+ "expect 53 or 73", len(authmsg)) } myPKH := btcutil.Hash160(l.longTermPriv.PubKey().SerializeCompressed()) if !bytes.Equal(myPKH, authmsg[33:53]) { return fmt.Errorf( "remote host asking for PKH %x, that's not me", authmsg[33:53]) } // do DH with id keys theirPub, err := btcec.ParsePubKey(authmsg[:33], btcec.S256()) if err != nil { return err } idDH := fastsha256.Sum256( btcec.GenerateSharedSecret(l.longTermPriv, theirPub), ) myDHproof := btcutil.Hash160( append(lnConn.RemotePub.SerializeCompressed(), idDH[:]...), ) theirDHproof := btcutil.Hash160(append(localEphPubBytes, idDH[:]...)) // If they already know our public key, then execute the fast path. // Verify their DH proof, and send our own. if len(authmsg) == 73 { // Verify their DH proof. if !bytes.Equal(authmsg[53:], theirDHproof) { return fmt.Errorf("invalid DH proof from %s", lnConn.RemoteAddr().String()) } // Their DH proof checks out, so send ours now. if _, err = lnConn.Conn.Write(myDHproof); err != nil { return err } } else { // Otherwise, they don't yet know our public key. So we'll send // it over to them, so we can both compute the DH proof. msg := append(l.longTermPriv.PubKey().SerializeCompressed(), myDHproof...) if _, err = lnConn.Conn.Write(msg); err != nil { return err } resp := make([]byte, 20) if _, err := lnConn.Conn.Read(resp); err != nil { return err } // Verify their DH proof. if bytes.Equal(resp, theirDHproof) == false { return fmt.Errorf("Invalid DH proof %x", theirDHproof) } } theirAdr := btcutil.Hash160(theirPub.SerializeCompressed()) copy(lnConn.RemoteLNId[:], theirAdr[:16]) lnConn.RemotePub = theirPub lnConn.Authed = true return nil }
// DoubleHashH calculates hash(hash(b)) and returns the resulting bytes as a // Hash. func DoubleHashH(b []byte) Hash { first := fastsha256.Sum256(b) return Hash(fastsha256.Sum256(first[:])) }
// HashH calculates hash(b) and returns the resulting bytes as a Hash. func HashH(b []byte) Hash { return Hash(fastsha256.Sum256(b)) }
// HashB calculates hash(b) and returns the resulting bytes. func HashB(b []byte) []byte { hash := fastsha256.Sum256(b) return hash[:] }
func TestInvoiceWorkflow(t *testing.T) { db, cleanUp, err := makeTestDB() if err != nil { t.Fatalf("unable to make test db: %v", err) } defer cleanUp() // Create a fake invoice which we'll use several times in the tests // below. fakeInvoice := &Invoice{ CreationDate: time.Now(), } fakeInvoice.Memo = []byte("memo") fakeInvoice.Receipt = []byte("recipt") copy(fakeInvoice.Terms.PaymentPreimage[:], rev[:]) fakeInvoice.Terms.Value = btcutil.Amount(10000) // Add the invoice to the database, this should suceed as there aren't // any existing invoices within the database with the same payment // hash. if err := db.AddInvoice(fakeInvoice); err != nil { t.Fatalf("unable to find invoice: %v", err) } // Attempt to retrieve the invoice which was just added to the // database. It should be found, and the invoice returned should be // identical to the one created above. paymentHash := fastsha256.Sum256(fakeInvoice.Terms.PaymentPreimage[:]) dbInvoice, err := db.LookupInvoice(paymentHash) if err != nil { t.Fatalf("unable to find invoice: %v", err) } if !reflect.DeepEqual(fakeInvoice, dbInvoice) { t.Fatalf("invoice fetched from db doesn't match original %v vs %v", spew.Sdump(fakeInvoice), spew.Sdump(dbInvoice)) } // Settle the invoice, the versin retreived from the database should // now have the settled bit toggle to true. if err := db.SettleInvoice(paymentHash); err != nil { t.Fatalf("unable to settle invoice: %v", err) } dbInvoice2, err := db.LookupInvoice(paymentHash) if err != nil { t.Fatalf("unable to fetch invoice: %v", err) } if !dbInvoice2.Terms.Settled { t.Fatalf("invoice should now be settled but isn't") } // Attempt to insert generated above again, this should fail as // duplicates are rejected by the processing logic. if err := db.AddInvoice(fakeInvoice); err != ErrDuplicateInvoice { t.Fatalf("invoice insertion should fail due to duplication, "+ "instead %v", err) } // Attempt to look up a non-existant invoice, this should also fail but // with a "not found" error. var fakeHash [32]byte if _, err := db.LookupInvoice(fakeHash); err != ErrInvoiceNotFound { t.Fatalf("lookup should have failed, instead %v", err) } // Add 100 random invoices. const numInvoices = 10 amt := btcutil.Amount(1000) invoices := make([]*Invoice, numInvoices+1) invoices[0] = dbInvoice2 for i := 1; i < len(invoices)-1; i++ { invoice, err := randInvoice(amt) if err != nil { t.Fatalf("unable to create invoice: %v", err) } if err := db.AddInvoice(invoice); err != nil { t.Fatalf("unable to add invoice %v", err) } invoices[i] = invoice } // Perform a scan to collect all the active invoices. dbInvoices, err := db.FetchAllInvoices(false) if err != nil { t.Fatalf("unable to fetch all invoices: %v", err) } // The retrieve list of invoices should be identical as since we're // using big endian, the invoices should be retrieved in asecending // order (and the primary key should be incremented with each // insertion). for i := 0; i < len(invoices)-1; i++ { if !reflect.DeepEqual(invoices[i], dbInvoices[i]) { t.Fatalf("retrived invoices don't match %v vs %v", spew.Sdump(invoices[i]), spew.Sdump(dbInvoices[i])) } } }