package htlcswitch import ( "bytes" "encoding/binary" "testing" "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/tlv" ) // TestSphinxHopIteratorForwardingInstructions tests that we're able to // properly decode an onion payload, no matter the payload type, into the // original set of forwarding instructions. func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { t.Parallel() // First, we'll make the hop data that the sender would create to send // an HTLC through our imaginary route. hopData := sphinx.HopData{ ForwardAmount: 100000, OutgoingCltv: 4343, } copy(hopData.NextAddress[:], bytes.Repeat([]byte("a"), 8)) // Next, we'll make the hop forwarding information that we should // extract each type, no matter the payload type. nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:]) expectedFwdInfo := ForwardingInfo{ NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt), AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount), OutgoingCTLV: hopData.OutgoingCltv, } // For our TLV payload, we'll serialize the hop into into a TLV stream // as we would normally in the routing network. var b bytes.Buffer tlvRecords := []tlv.Record{ tlv.MakeDynamicRecord( tlv.AmtOnionType, &hopData.ForwardAmount, func() uint64 { return tlv.SizeTUint64(hopData.ForwardAmount) }, tlv.ETUint64, tlv.DTUint64, ), tlv.MakeDynamicRecord( tlv.LockTimeOnionType, &hopData.OutgoingCltv, func() uint64 { return tlv.SizeTUint32(hopData.OutgoingCltv) }, tlv.ETUint32, tlv.DTUint32, ), tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &nextAddrInt), } tlvStream, err := tlv.NewStream(tlvRecords...) if err != nil { t.Fatalf("unable to create stream: %v", err) } if err := tlvStream.Encode(&b); err != nil { t.Fatalf("unable to encode stream: %v", err) } var testCases = []struct { sphinxPacket *sphinx.ProcessedPacket expectedFwdInfo ForwardingInfo }{ // A regular legacy payload that signals more hops. { sphinxPacket: &sphinx.ProcessedPacket{ Payload: sphinx.HopPayload{ Type: sphinx.PayloadLegacy, }, Action: sphinx.MoreHops, ForwardingInstructions: &hopData, }, expectedFwdInfo: expectedFwdInfo, }, // A TLV payload, we can leave off the action as we'll always // read the cid encoded. { sphinxPacket: &sphinx.ProcessedPacket{ Payload: sphinx.HopPayload{ Type: sphinx.PayloadTLV, Payload: b.Bytes(), }, }, expectedFwdInfo: expectedFwdInfo, }, } // Finally, we'll test that we get the same set of // ForwardingInstructions for each payload type. iterator := sphinxHopIterator{} for i, testCase := range testCases { iterator.processedPacket = testCase.sphinxPacket fwdInfo, err := iterator.ForwardingInstructions() if err != nil { t.Fatalf("#%v: unable to extract forwarding "+ "instructions: %v", i, err) } if fwdInfo != testCase.expectedFwdInfo { t.Fatalf("#%v: wrong fwding info: expected %v, got %v", i, spew.Sdump(testCase.expectedFwdInfo), spew.Sdump(fwdInfo)) } } }