Ejemplo n.º 1
0
// BUGS:
// - MinimumRecentTransactions is ignored.
// - Wrong error codes when a block height or hash is not recognized
func (s *walletServer) GetTransactions(ctx context.Context, req *pb.GetTransactionsRequest) (
	resp *pb.GetTransactionsResponse, err error) {

	var startBlock, endBlock *wallet.BlockIdentifier
	if req.StartingBlockHash != nil && req.StartingBlockHeight != 0 {
		return nil, errors.New(
			"starting block hash and height may not be specified simultaneously")
	} else if req.StartingBlockHash != nil {
		startBlockHash, err := wire.NewShaHash(req.StartingBlockHash)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "%s", err.Error())
		}
		startBlock = wallet.NewBlockIdentifierFromHash(startBlockHash)
	} else if req.StartingBlockHeight != 0 {
		startBlock = wallet.NewBlockIdentifierFromHeight(req.StartingBlockHeight)
	}

	if req.EndingBlockHash != nil && req.EndingBlockHeight != 0 {
		return nil, grpc.Errorf(codes.InvalidArgument,
			"ending block hash and height may not be specified simultaneously")
	} else if req.EndingBlockHash != nil {
		endBlockHash, err := wire.NewShaHash(req.EndingBlockHash)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "%s", err.Error())
		}
		endBlock = wallet.NewBlockIdentifierFromHash(endBlockHash)
	} else if req.EndingBlockHeight != 0 {
		endBlock = wallet.NewBlockIdentifierFromHeight(req.EndingBlockHeight)
	}

	var minRecentTxs int
	if req.MinimumRecentTransactions != 0 {
		if endBlock != nil {
			return nil, grpc.Errorf(codes.InvalidArgument,
				"ending block and minimum number of recent transactions "+
					"may not be specified simultaneously")
		}
		minRecentTxs = int(req.MinimumRecentTransactions)
		if minRecentTxs < 0 {
			return nil, grpc.Errorf(codes.InvalidArgument,
				"minimum number of recent transactions may not be negative")
		}
	}

	_ = minRecentTxs

	gtr, err := s.wallet.GetTransactions(startBlock, endBlock, ctx.Done())
	if err != nil {
		return nil, translateError(err)
	}
	return marshalGetTransactionsResult(gtr)
}
Ejemplo n.º 2
0
// CloseChannel attempts to close an active channel identified by its channel
// point. The actions of this method can additionally be augmented to attempt
// a force close after a timeout period in the case of an inactive peer.
func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
	updateStream lnrpc.Lightning_CloseChannelServer) error {

	force := in.Force
	index := in.ChannelPoint.OutputIndex
	txid, err := wire.NewShaHash(in.ChannelPoint.FundingTxid)
	if err != nil {
		rpcsLog.Errorf("[closechannel] invalid txid: %v", err)
		return err
	}
	targetChannelPoint := wire.NewOutPoint(txid, index)

	rpcsLog.Tracef("[closechannel] request for ChannelPoint(%v)",
		targetChannelPoint)

	updateChan, errChan := r.server.htlcSwitch.CloseLink(targetChannelPoint, force)

out:
	for {
		select {
		case err := <-errChan:
			rpcsLog.Errorf("[closechannel] unable to close "+
				"ChannelPoint(%v): %v", targetChannelPoint, err)
			return err
		case closingUpdate := <-updateChan:
			rpcsLog.Tracef("[closechannel] sending update: %v",
				closingUpdate)
			if err := updateStream.Send(closingUpdate); err != nil {
				return err
			}

			// If a final channel closing updates is being sent,
			// then we can break out of our dispatch loop as we no
			// longer need to process any further updates.
			switch closeUpdate := closingUpdate.Update.(type) {
			case *lnrpc.CloseStatusUpdate_ChanClose:
				h, _ := wire.NewShaHash(closeUpdate.ChanClose.ClosingTxid)
				rpcsLog.Infof("[closechannel] close completed: "+
					"txid(%v)", h)
				break out
			}
		case <-r.quit:
			return nil
		}
	}

	return nil
}
Ejemplo n.º 3
0
// WaitForChannelClose waits for a notification from the passed channel close
// stream that the node has deemed the channel has been fully closed. If the
// passed context has a timeout, then if the timeout is reached before the
// notification is received then an error is returned.
func (n *networkHarness) WaitForChannelClose(ctx context.Context,
	closeChanStream lnrpc.Lightning_CloseChannelClient) (*wire.ShaHash, error) {

	errChan := make(chan error)
	updateChan := make(chan *lnrpc.CloseStatusUpdate_ChanClose)
	go func() {
		closeResp, err := closeChanStream.Recv()
		if err != nil {
			errChan <- err
			return
		}

		closeFin, ok := closeResp.Update.(*lnrpc.CloseStatusUpdate_ChanClose)
		if !ok {
			errChan <- fmt.Errorf("expected channel close update, "+
				"instead got %v", closeFin)
			return
		}

		updateChan <- closeFin
	}()

	// Wait until either the deadline for the context expires, an error
	// occurs, or the channel close update is received.
	select {
	case <-ctx.Done():
		return nil, fmt.Errorf("timeout reached before update sent")
	case err := <-errChan:
		return nil, err
	case update := <-updateChan:
		return wire.NewShaHash(update.ChanClose.ClosingTxid)
	}
}
Ejemplo n.º 4
0
// openChannelAndAssert attempts to open a channel with the specified
// parameters extended from Alice to Bob. Additionally, two items are asserted
// after the channel is considered open: the funding transaction should be
// found within a block, and that Alice can report the status of the new
// channel.
func openChannelAndAssert(t *harnessTest, net *networkHarness, ctx context.Context,
	alice, bob *lightningNode, amount btcutil.Amount) *lnrpc.ChannelPoint {

	chanOpenUpdate, err := net.OpenChannel(ctx, alice, bob, amount, 1)
	if err != nil {
		t.Fatalf("unable to open channel: %v", err)
	}

	// Mine a block, then wait for Alice's node to notify us that the
	// channel has been opened. The funding transaction should be found
	// within the newly mined block.
	block := mineBlocks(t, net, 1)[0]

	fundingChanPoint, err := net.WaitForChannelOpen(ctx, chanOpenUpdate)
	if err != nil {
		t.Fatalf("error while waiting for channel open: %v", err)
	}
	fundingTxID, err := wire.NewShaHash(fundingChanPoint.FundingTxid)
	if err != nil {
		t.Fatalf("unable to create sha hash: %v", err)
	}
	assertTxInBlock(t, block, fundingTxID)

	// The channel should be listed in the peer information returned by
	// both peers.
	chanPoint := wire.OutPoint{
		Hash:  *fundingTxID,
		Index: fundingChanPoint.OutputIndex,
	}
	if err := net.AssertChannelExists(ctx, alice, &chanPoint); err != nil {
		t.Fatalf("unable to assert channel existence: %v", err)
	}

	return fundingChanPoint
}
Ejemplo n.º 5
0
// CloseChannel close channel attempts to close the channel indicated by the
// passed channel point, initiated by the passed lnNode. If the passed context
// has a timeout, then if the timeout is reached before the channel close is
// pending, then an error is returned.
func (n *networkHarness) CloseChannel(ctx context.Context,
	lnNode *lightningNode, cp *lnrpc.ChannelPoint,
	force bool) (lnrpc.Lightning_CloseChannelClient, error) {

	closeReq := &lnrpc.CloseChannelRequest{
		ChannelPoint: cp,
		Force:        force,
	}
	closeRespStream, err := lnNode.CloseChannel(ctx, closeReq)
	if err != nil {
		return nil, fmt.Errorf("unable to close channel: %v", err)
	}

	errChan := make(chan error)
	fin := make(chan struct{})
	go func() {
		// Consume the "channel close" update in order to wait for the closing
		// transaction to be broadcast, then wait for the closing tx to be seen
		// within the network.
		closeResp, err := closeRespStream.Recv()
		if err != nil {
			errChan <- err
			return
		}
		pendingClose, ok := closeResp.Update.(*lnrpc.CloseStatusUpdate_ClosePending)
		if !ok {
			errChan <- fmt.Errorf("expected channel close update, "+
				"instead got %v", pendingClose)
			return
		}

		closeTxid, err := wire.NewShaHash(pendingClose.ClosePending.Txid)
		if err != nil {
			errChan <- err
			return
		}
		if err := n.WaitForTxBroadcast(ctx, *closeTxid); err != nil {
			errChan <- err
			return
		}

		close(fin)
	}()

	// Wait until either the deadline for the context expires, an error
	// occurs, or the channel close update is received.
	select {
	case <-ctx.Done():
		return nil, fmt.Errorf("timeout reached before channel close " +
			"initiated")
	case err := <-errChan:
		return nil, err
	case <-fin:
		return closeRespStream, nil
	}
}
Ejemplo n.º 6
0
// OpenChannel attempts to open a singly funded channel specified in the
// request to a remote peer.
func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
	updateStream lnrpc.Lightning_OpenChannelServer) error {

	rpcsLog.Tracef("[openchannel] request to peerid(%v) "+
		"allocation(us=%v, them=%v) numconfs=%v", in.TargetPeerId,
		in.LocalFundingAmount, in.RemoteFundingAmount, in.NumConfs)

	localFundingAmt := btcutil.Amount(in.LocalFundingAmount)
	remoteFundingAmt := btcutil.Amount(in.RemoteFundingAmount)
	nodepubKey, err := btcec.ParsePubKey(in.NodePubkey, btcec.S256())
	if err != nil {
		return err
	}

	updateChan, errChan := r.server.OpenChannel(in.TargetPeerId,
		nodepubKey, localFundingAmt, remoteFundingAmt, in.NumConfs)

	var outpoint wire.OutPoint
out:
	for {
		select {
		case err := <-errChan:
			rpcsLog.Errorf("unable to open channel to "+
				"identityPub(%x) nor peerID(%v): %v",
				nodepubKey, in.TargetPeerId, err)
			return err
		case fundingUpdate := <-updateChan:
			rpcsLog.Tracef("[openchannel] sending update: %v",
				fundingUpdate)
			if err := updateStream.Send(fundingUpdate); err != nil {
				return err
			}

			// If a final channel open update is being sent, then
			// we can break out of our recv loop as we no longer
			// need to process any further updates.
			switch update := fundingUpdate.Update.(type) {
			case *lnrpc.OpenStatusUpdate_ChanOpen:
				chanPoint := update.ChanOpen.ChannelPoint
				h, _ := wire.NewShaHash(chanPoint.FundingTxid)
				outpoint = wire.OutPoint{
					Hash:  *h,
					Index: chanPoint.OutputIndex,
				}

				break out
			}
		case <-r.quit:
			return nil
		}
	}

	rpcsLog.Tracef("[openchannel] success peerid(%v), ChannelPoint(%v)",
		in.TargetPeerId, outpoint)
	return nil
}
Ejemplo n.º 7
0
func closeChannel(ctx *cli.Context) error {
	ctxb := context.Background()
	client := getClient(ctx)

	txid, err := wire.NewShaHashFromStr(ctx.String("funding_txid"))
	if err != nil {
		return err
	}

	// TODO(roasbeef): implement time deadline within server
	req := &lnrpc.CloseChannelRequest{
		ChannelPoint: &lnrpc.ChannelPoint{
			FundingTxid: txid[:],
			OutputIndex: uint32(ctx.Int("output_index")),
		},
		Force: ctx.Bool("force"),
	}

	stream, err := client.CloseChannel(ctxb, req)
	if err != nil {
		return err
	}

	if !ctx.Bool("block") {
		return nil
	}

	for {
		resp, err := stream.Recv()
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}

		switch update := resp.Update.(type) {
		case *lnrpc.CloseStatusUpdate_ChanClose:
			closingHash := update.ChanClose.ClosingTxid
			txid, err := wire.NewShaHash(closingHash)
			if err != nil {
				return err
			}

			printRespJson(struct {
				ClosingTXID string `json:"closing_txid"`
			}{
				ClosingTXID: txid.String(),
			})
		}

	}

	return nil
}
Ejemplo n.º 8
0
func fetchChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
	var b bytes.Buffer
	if err := writeOutpoint(&b, channel.ChanID); err != nil {
		return err
	}
	elkremKey := make([]byte, len(elkremStateKey)+b.Len())
	copy(elkremKey[:3], elkremStateKey)
	copy(elkremKey[3:], b.Bytes())

	elkremStateBytes := bytes.NewReader(nodeChanBucket.Get(elkremKey))

	revKeyBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "")
	if err != nil {
		return err
	}
	channel.TheirCurrentRevocation, err = btcec.ParsePubKey(revKeyBytes, btcec.S256())
	if err != nil {
		return err
	}

	if _, err := elkremStateBytes.Read(channel.TheirCurrentRevocationHash[:]); err != nil {
		return err
	}

	// TODO(roasbeef): should be rederiving on fly, or encrypting on disk.
	senderBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "")
	if err != nil {
		return err
	}
	elkremRoot, err := wire.NewShaHash(senderBytes)
	if err != nil {
		return err
	}
	channel.LocalElkrem = elkrem.NewElkremSender(*elkremRoot)

	reciverBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "")
	if err != nil {
		return err
	}
	remoteE, err := elkrem.ElkremReceiverFromBytes(reciverBytes)
	if err != nil {
		return err
	}
	channel.RemoteElkrem = remoteE

	return nil
}
Ejemplo n.º 9
0
	address         = pubKey

	//  Delivery PkScript
	// Privkey: f2c00ead9cbcfec63098dc0a5f152c0165aff40a2ab92feb4e24869a284c32a7
	// PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz
	deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac")

	//  Change PkScript
	// Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f
	// PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc
	changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac")

	// echo -n | openssl sha256
	// This stuff gets reversed!!!
	shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
	shaHash1, _      = wire.NewShaHash(shaHash1Bytes)
	outpoint1        = wire.NewOutPoint(shaHash1, 0)
	// echo | openssl sha256
	// This stuff gets reversed!!!
	shaHash2Bytes, _ = hex.DecodeString("01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b")
	shaHash2, _      = wire.NewShaHash(shaHash2Bytes)
	outpoint2        = wire.NewOutPoint(shaHash2, 1)
	// create inputs from outpoint1 and outpoint2
	inputs = []*wire.TxIn{wire.NewTxIn(outpoint1, nil, nil), wire.NewTxIn(outpoint2, nil, nil)}

	// Commitment Signature
	tx           = wire.NewMsgTx()
	emptybytes   = new([]byte)
	sigStr, _    = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, privKey)
	commitSig, _ = btcec.ParseSignature(sigStr, btcec.S256())
Ejemplo n.º 10
0
func openChannel(ctx *cli.Context) error {
	// TODO(roasbeef): add deadline to context
	ctxb := context.Background()
	client := getClient(ctx)

	if ctx.Int("peer_id") != 0 && ctx.String("node_key") != "" {
		return fmt.Errorf("both peer_id and lightning_id cannot be set " +
			"at the same time, only one can be specified")
	}

	req := &lnrpc.OpenChannelRequest{
		LocalFundingAmount:  int64(ctx.Int("local_amt")),
		RemoteFundingAmount: int64(ctx.Int("remote_amt")),
		NumConfs:            uint32(ctx.Int("num_confs")),
	}

	if ctx.Int("peer_id") != 0 {
		req.TargetPeerId = int32(ctx.Int("peer_id"))
	} else {
		nodePubHex, err := hex.DecodeString(ctx.String("node_key"))
		if err != nil {
			return fmt.Errorf("unable to decode lightning id: %v", err)
		}
		req.NodePubkey = nodePubHex
	}

	stream, err := client.OpenChannel(ctxb, req)
	if err != nil {
		return err
	}

	if !ctx.Bool("block") {
		return nil
	}

	for {
		resp, err := stream.Recv()
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}

		switch update := resp.Update.(type) {
		case *lnrpc.OpenStatusUpdate_ChanOpen:
			channelPoint := update.ChanOpen.ChannelPoint
			txid, err := wire.NewShaHash(channelPoint.FundingTxid)
			if err != nil {
				return err
			}

			index := channelPoint.OutputIndex
			printRespJson(struct {
				ChannelPoint string `json:"channel_point"`
			}{
				ChannelPoint: fmt.Sprintf("%v:%v", txid, index),
			},
			)
		}
	}

	return nil
}
Ejemplo n.º 11
0
// BlockRootOK checks for block self-consistency.
// If the block has no wintess txs, and no coinbase witness commitment,
// it only checks the tx merkle root.  If either a witness commitment or
// any witnesses are detected, it also checks that as well.
// Returns false if anything goes wrong, true if everything is fine.
func BlockOK(blk wire.MsgBlock) bool {
	var txids, wtxids []*wire.ShaHash // txids and wtxids
	// witMode true if any tx has a wintess OR coinbase has wit commit
	var witMode bool

	for _, tx := range blk.Transactions { // make slice of (w)/txids
		txid := tx.TxSha()
		wtxid := tx.WitnessHash()
		if !witMode && !txid.IsEqual(&wtxid) {
			witMode = true
		}
		txids = append(txids, &txid)
		wtxids = append(wtxids, &wtxid)
	}

	var commitBytes []byte
	// try to extract coinbase witness commitment (even if !witMode)
	cb := blk.Transactions[0]                 // get coinbase tx
	for i := len(cb.TxOut) - 1; i >= 0; i-- { // start at the last txout
		if bytes.HasPrefix(cb.TxOut[i].PkScript, WitMagicBytes) &&
			len(cb.TxOut[i].PkScript) > 37 {
			// 38 bytes or more, and starts with WitMagicBytes is a hit
			commitBytes = cb.TxOut[i].PkScript[6:38]
			witMode = true // it there is a wit commit it must be valid
		}
	}

	if witMode { // witmode, so check witness tree
		// first find ways witMode can be disqualified
		if len(commitBytes) != 32 {
			// witness in block but didn't find a wintess commitment; fail
			log.Printf("block %s has witness but no witcommit",
				blk.BlockSha().String())
			return false
		}
		if len(cb.TxIn) != 1 {
			log.Printf("block %s coinbase tx has %d txins (must be 1)",
				blk.BlockSha().String(), len(cb.TxIn))
			return false
		}
		if len(cb.TxIn[0].Witness) != 1 {
			log.Printf("block %s coinbase has %d witnesses (must be 1)",
				blk.BlockSha().String(), len(cb.TxIn[0].Witness))
			return false
		}

		if len(cb.TxIn[0].Witness[0]) != 32 {
			log.Printf("block %s coinbase has %d byte witness nonce (not 32)",
				blk.BlockSha().String(), len(cb.TxIn[0].Witness[0]))
			return false
		}
		// witness nonce is the cb's witness, subject to above constraints
		witNonce, err := wire.NewShaHash(cb.TxIn[0].Witness[0])
		if err != nil {
			log.Printf("Witness nonce error: %s", err.Error())
			return false // not sure why that'd happen but fail
		}

		var empty [32]byte
		wtxids[0].SetBytes(empty[:]) // coinbase wtxid is 0x00...00

		// witness root calculated from wtixds
		witRoot := calcRoot(wtxids)

		calcWitCommit := wire.DoubleSha256SH(
			append(witRoot.Bytes(), witNonce.Bytes()...))

		// witness root given in coinbase op_return
		givenWitCommit, err := wire.NewShaHash(commitBytes)
		if err != nil {
			log.Printf("Witness root error: %s", err.Error())
			return false // not sure why that'd happen but fail
		}
		// they should be the same.  If not, fail.
		if !calcWitCommit.IsEqual(givenWitCommit) {
			log.Printf("Block %s witRoot error: calc %s given %s",
				blk.BlockSha().String(),
				calcWitCommit.String(), givenWitCommit.String())
			return false
		}
	}

	// got through witMode check so that should be OK;
	// check regular txid merkleroot.  Which is, like, trivial.
	return blk.Header.MerkleRoot.IsEqual(calcRoot(txids))
}
Ejemplo n.º 12
0
// readElement is a one-stop utility function to deserialize any datastructure
// encoded using the serialization format of lnwire.
func readElement(r io.Reader, element interface{}) error {
	var err error
	switch e := element.(type) {
	case *uint8:
		var b [1]uint8
		if _, err := r.Read(b[:]); err != nil {
			return err
		}
		*e = b[0]
	case *uint16:
		var b [2]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint16(b[:])
	case *ErrorCode:
		var b [2]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = ErrorCode(binary.BigEndian.Uint16(b[:]))
	case *CreditsAmount:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = CreditsAmount(int64(binary.BigEndian.Uint64(b[:])))
	case *uint32:
		var b [4]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint32(b[:])
	case *uint64:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = binary.BigEndian.Uint64(b[:])
	case *HTLCKey:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = HTLCKey(int64(binary.BigEndian.Uint64(b[:])))
	case *btcutil.Amount:
		var b [8]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
	case **wire.ShaHash:
		var b wire.ShaHash
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = &b
	case **btcec.PublicKey:
		var b [33]byte
		if _, err = io.ReadFull(r, b[:]); err != nil {
			return err
		}

		pubKey, err := btcec.ParsePubKey(b[:], btcec.S256())
		if err != nil {
			return err
		}
		*e = pubKey
	case *[]uint64:
		var numItems uint16
		if err := readElement(r, &numItems); err != nil {
			return err
		}
		// if numItems > 65535 {
		// 	return fmt.Errorf("Too many items in []uint64")
		// }

		// Read the number of items
		var items []uint64
		for i := uint16(0); i < numItems; i++ {
			var item uint64
			err = readElement(r, &item)
			if err != nil {
				return err
			}
			items = append(items, item)
		}
		*e = items
	case *[]*btcec.Signature:
		var numSigs uint8
		err = readElement(r, &numSigs)
		if err != nil {
			return err
		}
		if numSigs > 127 {
			return fmt.Errorf("Too many signatures!")
		}

		// Read that number of signatures
		var sigs []*btcec.Signature
		for i := uint8(0); i < numSigs; i++ {
			sig := new(btcec.Signature)
			err = readElement(r, &sig)
			if err != nil {
				return err
			}
			sigs = append(sigs, sig)
		}
		*e = sigs
		return nil
	case **btcec.Signature:
		sigBytes, err := wire.ReadVarBytes(r, 0, 73, "signature")
		if err != nil {
			return err
		}

		sig, err := btcec.ParseSignature(sigBytes, btcec.S256())
		if err != nil {
			return err
		}
		*e = sig
	case *[][32]byte:
		// How many to read
		var sliceSize uint16
		err = readElement(r, &sliceSize)
		if err != nil {
			return err
		}

		data := make([][32]byte, 0, sliceSize)
		// Append the actual
		for i := uint16(0); i < sliceSize; i++ {
			var element [32]byte
			err = readElement(r, &element)
			if err != nil {
				return err
			}
			data = append(data, element)
		}
		*e = data
	case *[32]byte:
		if _, err = io.ReadFull(r, e[:]); err != nil {
			return err
		}
	case *wire.BitcoinNet:
		var b [4]byte
		if _, err := io.ReadFull(r, b[:]); err != nil {
			return err
		}
		*e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:]))
		return nil
	case *[]byte:
		bytes, err := wire.ReadVarBytes(r, 0, MaxSliceLength, "byte slice")
		if err != nil {
			return err
		}
		*e = bytes
	case *PkScript:
		pkScript, err := wire.ReadVarBytes(r, 0, 25, "pkscript")
		if err != nil {
			return err
		}
		*e = pkScript
	case *string:
		str, err := wire.ReadVarString(r, 0)
		if err != nil {
			return err
		}
		*e = str
	case *[]*wire.TxIn:
		// Read the size (1-byte number of txins)
		var numScripts uint8
		if err := readElement(r, &numScripts); err != nil {
			return err
		}
		if numScripts > 127 {
			return fmt.Errorf("Too many txins")
		}

		// Append the actual TxIns
		txins := make([]*wire.TxIn, 0, numScripts)
		for i := uint8(0); i < numScripts; i++ {
			outpoint := new(wire.OutPoint)
			txin := wire.NewTxIn(outpoint, nil, nil)
			if err := readElement(r, &txin); err != nil {
				return err
			}
			txins = append(txins, txin)
		}
		*e = txins
	case **wire.TxIn:
		// Hash
		var h [32]byte
		if _, err = io.ReadFull(r, h[:]); err != nil {
			return err
		}
		hash, err := wire.NewShaHash(h[:])
		if err != nil {
			return err
		}
		(*e).PreviousOutPoint.Hash = *hash

		// Index
		var idxBytes [4]byte
		_, err = io.ReadFull(r, idxBytes[:])
		if err != nil {
			return err
		}
		(*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:])
		return nil
	case **wire.OutPoint:
		// TODO(roasbeef): consolidate with above
		var h [32]byte
		if _, err = io.ReadFull(r, h[:]); err != nil {
			return err
		}
		hash, err := wire.NewShaHash(h[:])
		if err != nil {
			return err
		}
		// Index
		var idxBytes [4]byte
		_, err = io.ReadFull(r, idxBytes[:])
		if err != nil {
			return err
		}
		index := binary.BigEndian.Uint32(idxBytes[:])

		*e = wire.NewOutPoint(hash, index)
	default:
		return fmt.Errorf("Unknown type in readElement: %T", e)
	}

	return nil
}
Ejemplo n.º 13
0
// testMaxPendingChannels checks that error is returned from remote peer if
// max pending channel number was exceeded and that '--maxpendingchannels' flag
// exists and works properly.
func testMaxPendingChannels(net *networkHarness, t *harnessTest) {
	maxPendingChannels := defaultMaxPendingChannels + 1
	amount := btcutil.Amount(btcutil.SatoshiPerBitcoin)

	timeout := time.Duration(time.Second * 10)
	ctx, _ := context.WithTimeout(context.Background(), timeout)

	// Create a new node (Carol) with greater number of max pending
	// channels.
	args := []string{
		fmt.Sprintf("--maxpendingchannels=%v", maxPendingChannels),
	}
	carol, err := net.NewNode(args)
	if err != nil {
		t.Fatalf("unable to create new nodes: %v", err)
	}

	ctx, _ = context.WithTimeout(context.Background(), timeout)
	if err := net.ConnectNodes(ctx, net.Alice, carol); err != nil {
		t.Fatalf("unable to connect carol to alice: %v", err)
	}

	ctx, _ = context.WithTimeout(context.Background(), timeout)
	carolBalance := btcutil.Amount(maxPendingChannels) * amount
	if err := net.SendCoins(ctx, carolBalance, carol); err != nil {
		t.Fatalf("unable to send coins to carol: %v", err)
	}

	// Send open channel requests without generating new blocks thereby
	// increasing pool of pending channels. Then check that we can't
	// open the channel if the number of pending channels exceed
	// max value.
	openStreams := make([]lnrpc.Lightning_OpenChannelClient, maxPendingChannels)
	for i := 0; i < maxPendingChannels; i++ {
		ctx, _ = context.WithTimeout(context.Background(), timeout)
		stream, err := net.OpenChannel(ctx, net.Alice, carol, amount, 1)
		if err != nil {
			t.Fatalf("unable to open channel: %v", err)
		}
		openStreams[i] = stream
	}

	// Carol exhausted available amount of pending channels, next open
	// channel request should cause ErrorGeneric to be sent back to Alice.
	ctx, _ = context.WithTimeout(context.Background(), timeout)
	_, err = net.OpenChannel(ctx, net.Alice, carol, amount, 1)
	if err == nil {
		t.Fatalf("error wasn't received")
	} else if grpc.Code(err) != OpenChannelFundingError {
		t.Fatalf("not expected error was received : %v", err)
	}

	// For now our channels are in pending state, in order to not
	// interfere with other tests we should clean up - complete opening
	// of the channel and then close it.

	// Mine a block, then wait for node's to notify us that the channel
	// has been opened. The funding transactions should be found within the
	// newly mined block.
	block := mineBlocks(t, net, 1)[0]

	chanPoints := make([]*lnrpc.ChannelPoint, maxPendingChannels)
	for i, stream := range openStreams {
		ctx, _ = context.WithTimeout(context.Background(), timeout)
		fundingChanPoint, err := net.WaitForChannelOpen(ctx, stream)
		if err != nil {
			t.Fatalf("error while waiting for channel open: %v", err)
		}

		fundingTxID, err := wire.NewShaHash(fundingChanPoint.FundingTxid)
		if err != nil {
			t.Fatalf("unable to create sha hash: %v", err)
		}

		assertTxInBlock(t, block, fundingTxID)

		// The channel should be listed in the peer information
		// returned by both peers.
		chanPoint := wire.OutPoint{
			Hash:  *fundingTxID,
			Index: fundingChanPoint.OutputIndex,
		}
		if err := net.AssertChannelExists(ctx, net.Alice, &chanPoint); err != nil {
			t.Fatalf("unable to assert channel existence: %v", err)
		}

		chanPoints[i] = fundingChanPoint
	}

	// Finally close the channel between Alice and Carol, asserting that the
	// channel has been properly closed on-chain.
	for _, chanPoint := range chanPoints {
		ctx, _ = context.WithTimeout(context.Background(), timeout)
		closeChannelAndAssert(t, net, ctx, net.Alice, chanPoint)
	}
}
Ejemplo n.º 14
0
func testMultiHopPayments(net *networkHarness, t *harnessTest) {
	const chanAmt = btcutil.Amount(100000)
	ctxb := context.Background()
	timeout := time.Duration(time.Second * 5)

	// Open a channel with 100k satoshis between Alice and Bob with Alice
	// being the sole funder of the channel.
	ctxt, _ := context.WithTimeout(ctxb, timeout)
	chanPointAlice := openChannelAndAssert(t, net, ctxt, net.Alice,
		net.Bob, chanAmt)

	aliceChanTXID, err := wire.NewShaHash(chanPointAlice.FundingTxid)
	if err != nil {
		t.Fatalf("unable to create sha hash: %v", err)
	}
	aliceFundPoint := wire.OutPoint{
		Hash:  *aliceChanTXID,
		Index: chanPointAlice.OutputIndex,
	}

	// Create a new node (Carol), load her with some funds, then establish
	// a connection between Carol and Alice with a channel that has
	// identical capacity to the one created above.
	//
	// The network topology should now look like: Carol -> Alice -> Bob
	carol, err := net.NewNode(nil)
	if err != nil {
		t.Fatalf("unable to create new nodes: %v", err)
	}
	if err := net.ConnectNodes(ctxb, carol, net.Alice); err != nil {
		t.Fatalf("unable to connect carol to alice: %v", err)
	}
	err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, carol)
	if err != nil {
		t.Fatalf("unable to send coins to carol: %v", err)
	}
	ctxt, _ = context.WithTimeout(ctxb, timeout)
	chanPointCarol := openChannelAndAssert(t, net, ctxt, carol,
		net.Alice, chanAmt)

	carolChanTXID, err := wire.NewShaHash(chanPointCarol.FundingTxid)
	if err != nil {
		t.Fatalf("unable to create sha hash: %v", err)
	}
	carolFundPoint := wire.OutPoint{
		Hash:  *carolChanTXID,
		Index: chanPointCarol.OutputIndex,
	}

	// Create 5 invoices for Bob, which expect a payment from Carol for 1k
	// satoshis with a different preimage each time.
	const numPayments = 5
	const paymentAmt = 1000
	rHashes := make([][]byte, numPayments)
	for i := 0; i < numPayments; i++ {
		preimage := bytes.Repeat([]byte{byte(i)}, 32)
		invoice := &lnrpc.Invoice{
			Memo:      "testing",
			RPreimage: preimage,
			Value:     paymentAmt,
		}
		resp, err := net.Bob.AddInvoice(ctxb, invoice)
		if err != nil {
			t.Fatalf("unable to add invoice: %v", err)
		}

		rHashes[i] = resp.RHash
	}

	// Carol's routing table should show a path from Carol -> Alice -> Bob,
	// with the two channels above recognized as the only links within the
	// network.
	time.Sleep(time.Second)
	req := &lnrpc.ShowRoutingTableRequest{}
	routingResp, err := carol.ShowRoutingTable(ctxb, req)
	if err != nil {
		t.Fatalf("unable to query for carol's routing table: %v", err)
	}
	if len(routingResp.Channels) != 2 {
		t.Fatalf("only two channels should be seen as active in the "+
			"network, instead %v are", len(routingResp.Channels))
	}
	for _, link := range routingResp.Channels {
		switch {
		case link.Outpoint == aliceFundPoint.String():
			switch {
			case link.Id1 == net.Alice.PubKeyStr &&
				link.Id2 == net.Bob.PubKeyStr:
				continue
			case link.Id1 == net.Bob.PubKeyStr &&
				link.Id2 == net.Alice.PubKeyStr:
				continue
			default:
				t.Fatalf("unkown link within routing "+
					"table: %v", spew.Sdump(link))
			}
		case link.Outpoint == carolFundPoint.String():
			switch {
			case link.Id1 == net.Alice.PubKeyStr &&
				link.Id2 == carol.PubKeyStr:
				continue
			case link.Id1 == carol.PubKeyStr &&
				link.Id2 == net.Alice.PubKeyStr:
				continue
			default:
				t.Fatalf("unkown link within routing "+
					"table: %v", spew.Sdump(link))
			}
		default:
			t.Fatalf("unkown channel %v found in routing table, "+
				"only %v and %v should exist", link.Outpoint,
				aliceFundPoint, carolFundPoint)
		}
	}

	// Using Carol as the source, pay to the 5 invoices from Bob created above.
	carolPayStream, err := carol.SendPayment(ctxb)
	if err != nil {
		t.Fatalf("unable to create payment stream for carol: %v", err)
	}

	// Concurrently pay off all 5 of Bob's invoices. Each of the goroutines
	// will unblock on the recv once the HTLC it sent has been fully
	// settled.
	var wg sync.WaitGroup
	for _, rHash := range rHashes {
		sendReq := &lnrpc.SendRequest{
			PaymentHash: rHash,
			Dest:        net.Bob.PubKey[:],
			Amt:         paymentAmt,
		}

		wg.Add(1)
		go func() {
			if err := carolPayStream.Send(sendReq); err != nil {
				t.Fatalf("unable to send payment: %v", err)
			}
			if _, err := carolPayStream.Recv(); err != nil {
				t.Fatalf("unable to recv pay resp: %v", err)
			}
			wg.Done()
		}()
	}

	finClear := make(chan struct{})
	go func() {
		wg.Wait()
		close(finClear)
	}()

	select {
	case <-time.After(time.Second * 10):
		t.Fatalf("HTLC's not cleared after 10 seconds")
	case <-finClear:
	}

	assertAsymmetricBalance := func(node *lightningNode,
		chanPoint wire.OutPoint, localBalance,
		remoteBalance int64) {

		channelName := ""
		switch chanPoint {
		case carolFundPoint:
			channelName = "Carol(local) => Alice(remote)"
		case aliceFundPoint:
			channelName = "Alice(local) => Bob(remote)"
		}

		checkBalance := func() error {
			listReq := &lnrpc.ListChannelsRequest{}
			resp, err := node.ListChannels(ctxb, listReq)
			if err != nil {
				return fmt.Errorf("unable to for node's "+
					"channels: %v", err)
			}
			for _, channel := range resp.Channels {
				if channel.ChannelPoint != chanPoint.String() {
					continue
				}

				if channel.LocalBalance != localBalance {
					return fmt.Errorf("%v: incorrect local "+
						"balances: %v != %v", channelName,
						channel.LocalBalance, localBalance)
				}

				if channel.RemoteBalance != remoteBalance {
					return fmt.Errorf("%v: incorrect remote "+
						"balances: %v != %v", channelName,
						channel.RemoteBalance, remoteBalance)
				}

				return nil
			}
			return fmt.Errorf("channel not found")
		}

		// As far as HTLC inclusion in commitment transaction might be
		// postponed we will try to check the balance couple of
		// times, and then if after some period of time we receive wrong
		// balance return the error.
		// TODO(roasbeef): remove sleep after invoice notification hooks
		// are in place
		var timeover uint32
		go func() {
			<-time.After(time.Second * 20)
			atomic.StoreUint32(&timeover, 1)
		}()

		for {
			isTimeover := atomic.LoadUint32(&timeover) == 1
			if err := checkBalance(); err != nil {
				if isTimeover {
					t.Fatalf("Check balance failed: %v", err)
				}
			} else {
				break
			}
		}
	}

	// At this point all the channels within our proto network should be
	// shifted by 5k satoshis in the direction of Bob, the sink within the
	// payment flow generated above. The order of asserts corresponds to
	// increasing of time is needed to embed the HTLC in commitment
	// transaction, in channel Carol->Alice->Bob, order is Bob,Alice,Carol.
	const sourceBal = int64(95000)
	const sinkBal = int64(5000)
	assertAsymmetricBalance(net.Bob, aliceFundPoint, sinkBal, sourceBal)
	assertAsymmetricBalance(net.Alice, aliceFundPoint, sourceBal, sinkBal)
	assertAsymmetricBalance(net.Alice, carolFundPoint, sinkBal, sourceBal)
	assertAsymmetricBalance(carol, carolFundPoint, sourceBal, sinkBal)

	ctxt, _ = context.WithTimeout(ctxb, timeout)
	closeChannelAndAssert(t, net, ctxt, net.Alice, chanPointAlice)
	ctxt, _ = context.WithTimeout(ctxb, timeout)
	closeChannelAndAssert(t, net, ctxt, carol, chanPointCarol)
}
Ejemplo n.º 15
0
	"time"

	"github.com/btcsuite/fastsha256"
	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

var (
	// debugPre is the default debug preimage which is inserted into the
	// invoice registry if the --debughtlc flag is activated on start up.
	// All nodes initialize with the flag active will immediately settle
	// any incoming HTLC whose rHash is corresponds with the debug
	// preimage.
	debugPre, _ = wire.NewShaHash(bytes.Repeat([]byte{1}, 32))

	debugHash = wire.ShaHash(fastsha256.Sum256(debugPre[:]))
)

// invoiceRegistry is a central registry of all the outstanding invoices
// created by the daemon. The registry is a thin wrapper around a map in order
// to ensure that all updates/reads are thread safe.
type invoiceRegistry struct {
	sync.RWMutex

	cdb *channeldb.DB

	clientMtx           sync.Mutex
	nextClientID        uint32
	notificationClients map[uint32]*invoiceSubscription