Last active
February 21, 2020 19:44
-
-
Save champo/8e90ce1ef38f2dd546814ff7898988c3 to your computer and use it in GitHub Desktop.
Virtual link consumer example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"context" | |
"crypto/rand" | |
"encoding/hex" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"time" | |
"github.com/btcsuite/btcd/btcec" | |
"github.com/btcsuite/btcd/chaincfg" | |
sphinx "github.com/lightningnetwork/lightning-onion" | |
"github.com/lightningnetwork/lnd/htlcswitch/hop" | |
v "github.com/lightningnetwork/lnd/lnrpc/virtualchannelsrpc" | |
"github.com/lightningnetwork/lnd/lntypes" | |
"github.com/lightningnetwork/lnd/lnwire" | |
"github.com/lightningnetwork/lnd/macaroons" | |
"github.com/lightningnetwork/lnd/netann" | |
"github.com/lightningnetwork/lnd/zpay32" | |
"google.golang.org/grpc" | |
"google.golang.org/grpc/credentials" | |
"gopkg.in/macaroon.v2" | |
) | |
func main() { | |
cc, err := connection() | |
if err != nil { | |
panic(err) | |
} | |
c := v.NewVirtualChannelsClient(cc) | |
_ = c | |
// newPriv() | |
priv, pub := keys() | |
_ = pub | |
_ = priv | |
fundingId, _ := hex.DecodeString("f5e1d1f4b72ee88f3443e2f56983c393287760b618375faa1bbd0de64dd3ba96") | |
_ = fundingId | |
registerChannel(c, pub.SerializeCompressed(), fundingId) | |
preimage := invoice(priv) | |
stream, err := c.SubscribeVirtualForward(context.Background(), &v.SubscribeVirtualForwardRequest{}) | |
if err != nil { | |
panic(err) | |
} | |
router := sphinx.NewRouter(priv, &chaincfg.RegressionNetParams, sphinx.NewMemoryReplayLog()) | |
if err := router.Start(); err != nil { | |
panic(err) | |
} | |
onionProcessor := hop.NewOnionProcessor(router) | |
onionProcessor.Start() | |
for { | |
forward, err := stream.Recv() | |
if err == io.EOF { | |
fmt.Println("Stream ended") | |
return | |
} | |
fmt.Println("Received fwd for %v", hex.EncodeToString(forward.ChannelId)) | |
if !bytes.Equal(forward.ChannelId, fundingId) { | |
continue | |
} | |
addHTLC := lnwire.NewUpdateAddHTLC() | |
err = addHTLC.Decode(bytes.NewBuffer(forward.Htlc), 0) | |
if err != nil { | |
fmt.Println("Failed to decoded incoming packet: %v", err) | |
continue | |
} | |
fmt.Println("Amount", addHTLC.Amount) | |
if !preimage.Matches(addHTLC.PaymentHash) { | |
fmt.Println("Got unknown payment hash %v", hex.EncodeToString(addHTLC.PaymentHash[:])) | |
continue | |
} | |
iterator, code := onionProcessor.DecodeHopIterator( | |
bytes.NewReader(addHTLC.OnionBlob[:]), | |
addHTLC.PaymentHash[:], | |
addHTLC.Expiry) | |
if code != lnwire.CodeNone { | |
fmt.Println("Failed decode sphinx due to %v", code.String()) | |
continue | |
} | |
payload, err := iterator.HopPayload() | |
if err != nil { | |
fmt.Println("Failed sth", err) | |
} | |
fwdInfo := payload.FwdInfo | |
fmt.Printf("Onion info:\nHop %v\nAmt %v\nCLTV %v\n", | |
fwdInfo.NextHop, | |
fwdInfo.AmountToForward, | |
fwdInfo.OutgoingCTLV) | |
if fwdInfo.AmountToForward != addHTLC.Amount { | |
fmt.Printf("Last hop tried to steal from us: %v vs %v\n", fwdInfo.AmountToForward, addHTLC.Amount) | |
continue | |
} | |
// TODO: Signal we use payment_secret and extract it | |
settle(c, fundingId, preimage) | |
} | |
onionProcessor.Stop() | |
router.Stop() | |
} | |
func settle(c v.VirtualChannelsClient, chanId []byte, preimage lntypes.Preimage) { | |
c.SettleVirtualForward(context.Background(), &v.SettleVirtualForwardRequest{ | |
ChannelId: chanId, | |
Preimage: preimage[:], | |
}) | |
} | |
func invoice(priv *btcec.PrivateKey) lntypes.Preimage { | |
target := pubFromHex("020d105ad99776e163baf7854b25e142d1415e32761f5458642409d4a5f3b0f1c9") | |
var preimage lntypes.Preimage | |
rand.Read(preimage[:]) | |
fmt.Printf("Preimage = %v\n", preimage.String()) | |
fmt.Printf("Payment hash = %v\n", preimage.Hash().String()) | |
hint := zpay32.RouteHint([]zpay32.HopHint{ | |
zpay32.HopHint{ | |
NodeID: target, | |
ChannelID: 1099511693312, | |
FeeBaseMSat: 1, | |
FeeProportionalMillionths: 1, | |
CLTVExpiryDelta: 144, | |
}, | |
}) | |
invoice, err := zpay32.NewInvoice( | |
&chaincfg.RegressionNetParams, | |
preimage.Hash(), | |
time.Now(), | |
zpay32.Description("Test it so hard"), | |
hint) | |
if err != nil { | |
panic(err) | |
} | |
signer := netann.NewNodeSigner(priv) | |
encoded, err := invoice.Encode(zpay32.MessageSigner{SignCompact: signer.SignDigestCompact}) | |
if err != nil { | |
panic(err) | |
} | |
fmt.Println(encoded) | |
return preimage | |
} | |
func registerChannel(c v.VirtualChannelsClient, pubKey []byte, fundingId []byte) { | |
_, err := c.CreateVirtualChannel(context.Background(), &v.CreateVirtualChannelRequest{ | |
NodePubKey: pubKey, | |
ChannelId: fundingId, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
} | |
func keys() (*btcec.PrivateKey, *btcec.PublicKey) { | |
privHex := "e5186c406375b7b9779aa3361c7751ba5e0abcd53f9302cf17a25e25e71a0bb3" | |
privBytes, _ := hex.DecodeString(privHex) | |
return btcec.PrivKeyFromBytes(btcec.S256(), privBytes) | |
} | |
func pubFromHex(str string) *btcec.PublicKey { | |
bytes, _ := hex.DecodeString(str) | |
key, _ := btcec.ParsePubKey(bytes, btcec.S256()) | |
return key | |
} | |
func newPriv() { | |
priv, _ := btcec.NewPrivateKey(btcec.S256()) | |
fmt.Printf(hex.EncodeToString(priv.Serialize())) | |
panic("") | |
} | |
func connection() (*grpc.ClientConn, error) { | |
creds, err := credentials.NewClientTLSFromFile("~/Library/Application Support/Lnd/tls.cert", "") | |
if err != nil { | |
return nil, err | |
} | |
macaroonBytes, err := ioutil.ReadFile("~/Library/Application Support/Lnd/data/chain/bitcoin/regtest/virtualchannels.macaroon") | |
if err != nil { | |
return nil, err | |
} | |
mac := &macaroon.Macaroon{} | |
if err = mac.UnmarshalBinary(macaroonBytes); err != nil { | |
return nil, err | |
} | |
log.Printf("attempting connection to node") | |
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) | |
defer cancel() | |
opts := []grpc.DialOption{ | |
grpc.WithBlock(), | |
grpc.WithTransportCredentials(creds), | |
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)), | |
} | |
return grpc.DialContext(ctx, "localhost:10000", opts...) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment