htlcswitch/test: hodl invoice test
This commit is contained in:
parent
32f2b047e8
commit
0823c79e4e
@ -5589,3 +5589,169 @@ func TestChannelLinkCanceledInvoice(t *testing.T) {
|
|||||||
t.Fatalf("expected unknown payment hash, but got %v", err)
|
t.Fatalf("expected unknown payment hash, but got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hodlInvoiceTestCtx struct {
|
||||||
|
n *twoHopNetwork
|
||||||
|
startBandwidthAlice lnwire.MilliSatoshi
|
||||||
|
startBandwidthBob lnwire.MilliSatoshi
|
||||||
|
hash lntypes.Hash
|
||||||
|
preimage lntypes.Preimage
|
||||||
|
amount lnwire.MilliSatoshi
|
||||||
|
errChan chan error
|
||||||
|
|
||||||
|
cleanUp func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHodlInvoiceTestCtx(t *testing.T) (*hodlInvoiceTestCtx, error) {
|
||||||
|
// Setup a alice-bob network.
|
||||||
|
aliceChannel, bobChannel, cleanUp, err := createTwoClusterChannels(
|
||||||
|
btcutil.SatoshiPerBitcoin*3,
|
||||||
|
btcutil.SatoshiPerBitcoin*5,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create channel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := newTwoHopNetwork(t, aliceChannel, bobChannel, testStartingHeight)
|
||||||
|
if err := n.start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()
|
||||||
|
bobBandwidthBefore := n.bobChannelLink.Bandwidth()
|
||||||
|
|
||||||
|
debug := false
|
||||||
|
if debug {
|
||||||
|
// Log message that alice receives.
|
||||||
|
n.aliceServer.intersect(
|
||||||
|
createLogFunc("alice", n.aliceChannelLink.ChanID()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log message that bob receives.
|
||||||
|
n.bobServer.intersect(
|
||||||
|
createLogFunc("bob", n.bobChannelLink.ChanID()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
||||||
|
htlcAmt, totalTimelock, hops := generateHops(
|
||||||
|
amount, testStartingHeight, n.bobChannelLink,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate hold invoice preimage.
|
||||||
|
r, err := generateRandomBytes(sha256.Size)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
preimage, err := lntypes.MakePreimage(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hash := preimage.Hash()
|
||||||
|
|
||||||
|
// Have alice pay the hodl invoice, wait for bob's commitment state to
|
||||||
|
// be updated and the invoice state to be updated.
|
||||||
|
receiver := n.bobServer
|
||||||
|
receiver.registry.settleChan = make(chan lntypes.Hash)
|
||||||
|
firstHop := n.bobChannelLink.ShortChanID()
|
||||||
|
errChan := n.makeHoldPayment(
|
||||||
|
n.aliceServer, receiver, firstHop, hops, amount, htlcAmt,
|
||||||
|
totalTimelock, preimage,
|
||||||
|
)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
t.Fatalf("no payment result expected: %v", err)
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("timeout")
|
||||||
|
case h := <-receiver.registry.settleChan:
|
||||||
|
if hash != h {
|
||||||
|
t.Fatal("unexpect invoice settled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &hodlInvoiceTestCtx{
|
||||||
|
n: n,
|
||||||
|
startBandwidthAlice: aliceBandwidthBefore,
|
||||||
|
startBandwidthBob: bobBandwidthBefore,
|
||||||
|
preimage: preimage,
|
||||||
|
hash: hash,
|
||||||
|
amount: amount,
|
||||||
|
errChan: errChan,
|
||||||
|
|
||||||
|
cleanUp: func() {
|
||||||
|
cleanUp()
|
||||||
|
n.stop()
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelLinkHoldInvoiceSettle asserts that a hodl invoice can be settled.
|
||||||
|
func TestChannelLinkHoldInvoiceSettle(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, err := newHodlInvoiceTestCtx(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer ctx.cleanUp()
|
||||||
|
|
||||||
|
err = ctx.n.bobServer.registry.SettleHodlInvoice(ctx.preimage)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for payment to succeed.
|
||||||
|
select {
|
||||||
|
case err := <-ctx.errChan:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for Bob to receive the revocation.
|
||||||
|
if ctx.startBandwidthAlice-ctx.amount !=
|
||||||
|
ctx.n.aliceChannelLink.Bandwidth() {
|
||||||
|
|
||||||
|
t.Fatal("alice bandwidth should have decrease on payment " +
|
||||||
|
"amount")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.startBandwidthBob+ctx.amount !=
|
||||||
|
ctx.n.bobChannelLink.Bandwidth() {
|
||||||
|
|
||||||
|
t.Fatalf("bob bandwidth isn't match: expected %v, got %v",
|
||||||
|
ctx.startBandwidthBob+ctx.amount,
|
||||||
|
ctx.n.bobChannelLink.Bandwidth())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestChannelLinkHoldInvoiceSettle asserts that a hodl invoice can be canceled.
|
||||||
|
func TestChannelLinkHoldInvoiceCancel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, err := newHodlInvoiceTestCtx(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer ctx.cleanUp()
|
||||||
|
|
||||||
|
err = ctx.n.bobServer.registry.CancelInvoice(ctx.hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for payment to succeed.
|
||||||
|
select {
|
||||||
|
case err := <-ctx.errChan:
|
||||||
|
if !strings.Contains(err.Error(),
|
||||||
|
lnwire.CodeUnknownPaymentHash.String()) {
|
||||||
|
|
||||||
|
t.Fatal("expected unknown payment hash")
|
||||||
|
}
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -520,19 +520,12 @@ func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
|
|||||||
return chanID, nil
|
return chanID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generatePayment generates the htlc add request by given path blob and
|
// generateHoldPayment generates the htlc add request by given path blob and
|
||||||
// invoice which should be added by destination peer.
|
// invoice which should be added by destination peer.
|
||||||
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
||||||
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC, error) {
|
timelock uint32, blob [lnwire.OnionPacketSize]byte,
|
||||||
|
preimage, rhash [32]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC,
|
||||||
var preimage [sha256.Size]byte
|
error) {
|
||||||
r, err := generateRandomBytes(sha256.Size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
copy(preimage[:], r)
|
|
||||||
|
|
||||||
rhash := fastsha256.Sum256(preimage[:])
|
|
||||||
|
|
||||||
invoice := &channeldb.Invoice{
|
invoice := &channeldb.Invoice{
|
||||||
CreationDate: time.Now(),
|
CreationDate: time.Now(),
|
||||||
@ -552,6 +545,24 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
|||||||
return invoice, htlc, nil
|
return invoice, htlc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generatePayment generates the htlc add request by given path blob and
|
||||||
|
// invoice which should be added by destination peer.
|
||||||
|
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
|
||||||
|
blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC, error) {
|
||||||
|
|
||||||
|
var preimage [sha256.Size]byte
|
||||||
|
r, err := generateRandomBytes(sha256.Size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
copy(preimage[:], r)
|
||||||
|
|
||||||
|
rhash := fastsha256.Sum256(preimage[:])
|
||||||
|
return generatePaymentWithPreimage(
|
||||||
|
invoiceAmt, htlcAmt, timelock, blob, preimage, rhash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// generateRoute generates the path blob by given array of peers.
|
// generateRoute generates the path blob by given array of peers.
|
||||||
func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) {
|
func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) {
|
||||||
var blob [lnwire.OnionPacketSize]byte
|
var blob [lnwire.OnionPacketSize]byte
|
||||||
@ -742,7 +753,8 @@ func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check who is last in the route and add invoice to server registry.
|
// Check who is last in the route and add invoice to server registry.
|
||||||
if err := receiver.registry.AddInvoice(*invoice, htlc.PaymentHash); err != nil {
|
hash := invoice.Terms.PaymentPreimage.Hash()
|
||||||
|
if err := receiver.registry.AddInvoice(*invoice, hash); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1132,7 +1144,7 @@ func newTwoHopNetwork(t testing.TB,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start starts the three hop network alice,bob,carol servers.
|
// start starts the two hop network alice,bob servers.
|
||||||
func (n *twoHopNetwork) start() error {
|
func (n *twoHopNetwork) start() error {
|
||||||
if err := n.aliceServer.Start(); err != nil {
|
if err := n.aliceServer.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1162,3 +1174,48 @@ func (n *twoHopNetwork) stop() {
|
|||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
|
||||||
|
firstHop lnwire.ShortChannelID, hops []ForwardingInfo,
|
||||||
|
invoiceAmt, htlcAmt lnwire.MilliSatoshi,
|
||||||
|
timelock uint32, preimage lntypes.Preimage) chan error {
|
||||||
|
|
||||||
|
paymentErr := make(chan error, 1)
|
||||||
|
|
||||||
|
sender := sendingPeer.(*mockServer)
|
||||||
|
receiver := receivingPeer.(*mockServer)
|
||||||
|
|
||||||
|
// Generate route convert it to blob, and return next destination for
|
||||||
|
// htlc add request.
|
||||||
|
blob, err := generateRoute(hops...)
|
||||||
|
if err != nil {
|
||||||
|
paymentErr <- err
|
||||||
|
return paymentErr
|
||||||
|
}
|
||||||
|
|
||||||
|
rhash := preimage.Hash()
|
||||||
|
|
||||||
|
// Generate payment: invoice and htlc.
|
||||||
|
invoice, htlc, err := generatePaymentWithPreimage(invoiceAmt, htlcAmt, timelock, blob,
|
||||||
|
channeldb.UnknownPreimage, rhash)
|
||||||
|
if err != nil {
|
||||||
|
paymentErr <- err
|
||||||
|
return paymentErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check who is last in the route and add invoice to server registry.
|
||||||
|
if err := receiver.registry.AddInvoice(*invoice, rhash); err != nil {
|
||||||
|
paymentErr <- err
|
||||||
|
return paymentErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send payment and expose err channel.
|
||||||
|
go func() {
|
||||||
|
_, err := sender.htlcSwitch.SendHTLC(
|
||||||
|
firstHop, htlc, newMockDeobfuscator(),
|
||||||
|
)
|
||||||
|
paymentErr <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
return paymentErr
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user