2021-03-19 11:50:05 +01:00
|
|
|
package pool
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-05-21 20:13:47 -07:00
|
|
|
"fmt"
|
2021-10-27 15:34:10 +02:00
|
|
|
"sync"
|
2021-03-19 11:50:05 +01:00
|
|
|
"testing"
|
2021-05-21 20:13:47 -07:00
|
|
|
"time"
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2022-03-11 14:19:30 -08:00
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
2021-05-21 20:13:47 -07:00
|
|
|
"github.com/lightninglabs/pool/account"
|
2021-03-19 16:49:01 +01:00
|
|
|
"github.com/lightninglabs/pool/auctioneer"
|
2021-03-19 11:50:05 +01:00
|
|
|
"github.com/lightninglabs/pool/clientdb"
|
|
|
|
|
"github.com/lightninglabs/pool/internal/test"
|
2021-05-21 20:13:47 -07:00
|
|
|
"github.com/lightninglabs/pool/order"
|
2021-03-19 11:50:05 +01:00
|
|
|
"github.com/lightninglabs/pool/sidecar"
|
2021-05-21 20:13:47 -07:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2021-05-23 18:48:23 -07:00
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
2021-05-21 20:13:47 -07:00
|
|
|
"github.com/stretchr/testify/assert"
|
2021-03-19 11:50:05 +01:00
|
|
|
"github.com/stretchr/testify/require"
|
2024-05-31 09:43:00 +02:00
|
|
|
gomock "go.uber.org/mock/gomock"
|
2021-03-19 11:50:05 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2022-03-11 14:19:30 -08:00
|
|
|
_, providerPubKey = btcec.PrivKeyFromBytes([]byte{0x02})
|
|
|
|
|
_, ourNodePubKey = btcec.PrivKeyFromBytes([]byte{0x03})
|
|
|
|
|
testOfferSig = test.NewSignatureFromInt(44, 22)
|
2021-03-19 11:50:05 +01:00
|
|
|
)
|
|
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
func getVerifyParameters(ticket *sidecar.Ticket) ([]byte, []byte, [33]byte) {
|
|
|
|
|
var offerPubKeyRaw [33]byte
|
|
|
|
|
copy(offerPubKeyRaw[:], ticket.Offer.SignPubKey.SerializeCompressed())
|
|
|
|
|
|
|
|
|
|
// Make sure the provider's signature over the offer is valid.
|
|
|
|
|
offerDigest, _ := ticket.OfferDigest()
|
|
|
|
|
sigOfferDigest := ticket.Offer.SigOfferDigest.Serialize()
|
|
|
|
|
|
|
|
|
|
return offerDigest[:], sigOfferDigest, offerPubKeyRaw
|
2021-03-19 11:50:05 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
var registerSidecarTestCases = []struct {
|
|
|
|
|
name string
|
|
|
|
|
ticket *sidecar.Ticket
|
|
|
|
|
expectedErr string
|
|
|
|
|
mockSetter func(ticket *sidecar.Ticket, sc *test.MockSignerClient,
|
|
|
|
|
wc *test.MockWalletKitClient, store *sidecar.MockStore,
|
2021-03-19 11:50:05 +01:00
|
|
|
)
|
2021-11-08 22:21:13 -08:00
|
|
|
check func(t *testing.T, ticket *sidecar.Ticket)
|
|
|
|
|
}{{
|
|
|
|
|
name: "unable to register sidecar if ticket does not have a " +
|
|
|
|
|
"valid state",
|
|
|
|
|
ticket: &sidecar.Ticket{},
|
|
|
|
|
expectedErr: "ticket is in invalid state",
|
|
|
|
|
}, {
|
|
|
|
|
name: "unable to register sidecar if signature is missing",
|
|
|
|
|
ticket: &sidecar.Ticket{
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
},
|
|
|
|
|
expectedErr: "offer in ticket is not signed",
|
|
|
|
|
}, {
|
|
|
|
|
name: "unable to register sidecar if signature is invalid",
|
|
|
|
|
ticket: &sidecar.Ticket{
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
Offer: sidecar.Offer{
|
2022-03-11 14:19:30 -08:00
|
|
|
SignPubKey: providerPubKey,
|
|
|
|
|
SigOfferDigest: test.NewSignatureFromInt(33, 33),
|
2021-03-19 11:50:05 +01:00
|
|
|
},
|
2021-11-08 22:21:13 -08:00
|
|
|
},
|
|
|
|
|
expectedErr: "signature not valid for public key",
|
|
|
|
|
mockSetter: func(ticket *sidecar.Ticket, sc *test.MockSignerClient,
|
|
|
|
|
wc *test.MockWalletKitClient, store *sidecar.MockStore,
|
|
|
|
|
) {
|
|
|
|
|
p1, p2, p3 := getVerifyParameters(ticket)
|
|
|
|
|
sc.EXPECT().
|
|
|
|
|
VerifyMessage(
|
|
|
|
|
gomock.Any(),
|
|
|
|
|
p1,
|
|
|
|
|
p2,
|
|
|
|
|
p3,
|
|
|
|
|
).
|
|
|
|
|
Return(false, nil)
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
name: "unable to register sidecar if ticket already exists",
|
|
|
|
|
ticket: &sidecar.Ticket{
|
|
|
|
|
ID: [8]byte{1, 2, 3, 4},
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SignPubKey: providerPubKey,
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
2021-03-19 11:50:05 +01:00
|
|
|
},
|
2021-11-08 22:21:13 -08:00
|
|
|
},
|
|
|
|
|
expectedErr: "already exists",
|
|
|
|
|
mockSetter: func(ticket *sidecar.Ticket, sc *test.MockSignerClient,
|
|
|
|
|
wc *test.MockWalletKitClient, store *sidecar.MockStore,
|
|
|
|
|
) {
|
|
|
|
|
p1, p2, p3 := getVerifyParameters(ticket)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
sc.EXPECT().
|
|
|
|
|
VerifyMessage(
|
|
|
|
|
gomock.Any(),
|
|
|
|
|
p1,
|
|
|
|
|
p2,
|
|
|
|
|
p3,
|
|
|
|
|
).
|
|
|
|
|
Return(true, nil)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
store.EXPECT().
|
|
|
|
|
Sidecar(
|
|
|
|
|
[8]byte{1, 2, 3, 4},
|
|
|
|
|
providerPubKey,
|
|
|
|
|
).
|
|
|
|
|
Return(nil, nil)
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
name: "register sidecar happy path",
|
|
|
|
|
ticket: &sidecar.Ticket{
|
|
|
|
|
ID: [8]byte{1, 2, 3, 4},
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SignPubKey: providerPubKey,
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
2021-03-19 11:50:05 +01:00
|
|
|
},
|
2021-11-08 22:21:13 -08:00
|
|
|
},
|
|
|
|
|
mockSetter: func(ticket *sidecar.Ticket, sc *test.MockSignerClient,
|
|
|
|
|
wc *test.MockWalletKitClient, store *sidecar.MockStore,
|
|
|
|
|
) {
|
|
|
|
|
p1, p2, p3 := getVerifyParameters(ticket)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
sc.EXPECT().
|
|
|
|
|
VerifyMessage(
|
|
|
|
|
gomock.Any(),
|
|
|
|
|
p1,
|
|
|
|
|
p2,
|
|
|
|
|
p3,
|
|
|
|
|
).
|
|
|
|
|
Return(true, nil)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
store.EXPECT().
|
|
|
|
|
Sidecar(
|
|
|
|
|
[8]byte{1, 2, 3, 4},
|
|
|
|
|
providerPubKey,
|
|
|
|
|
).
|
|
|
|
|
Return(nil, clientdb.ErrNoSidecar)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
index := 7
|
|
|
|
|
_, pubkey := test.CreateKey(int32(index))
|
|
|
|
|
wc.EXPECT().
|
|
|
|
|
DeriveNextKey(gomock.Any(), gomock.Any()).
|
|
|
|
|
Return(&keychain.KeyDescriptor{
|
|
|
|
|
KeyLocator: keychain.KeyLocator{
|
|
|
|
|
Index: uint32(index),
|
|
|
|
|
},
|
|
|
|
|
PubKey: pubkey,
|
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
|
|
store.EXPECT().
|
|
|
|
|
AddSidecar(gomock.Any())
|
|
|
|
|
},
|
|
|
|
|
check: func(t *testing.T, ticket *sidecar.Ticket) {
|
|
|
|
|
index := 7
|
|
|
|
|
_, pubkey := test.CreateKey(int32(index))
|
|
|
|
|
|
|
|
|
|
require.Equal(t, ticket.State, sidecar.StateRegistered)
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
require.Equal(t, ticket.Recipient.NodePubKey, ourNodePubKey)
|
|
|
|
|
require.Equal(t, ticket.Recipient.MultiSigPubKey, pubkey)
|
|
|
|
|
require.Equal(
|
|
|
|
|
t, ticket.Recipient.MultiSigKeyIndex, uint32(index),
|
2021-04-27 20:42:24 -07:00
|
|
|
)
|
2021-11-08 22:21:13 -08:00
|
|
|
},
|
|
|
|
|
}}
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
// TestRegisterSidecar makes sure that registering a sidecar ticket verifies the
|
|
|
|
|
// offer signature contained within, adds the recipient node's information to
|
|
|
|
|
// the ticket and stores it to the local database.
|
|
|
|
|
func TestRegisterSidecar(t *testing.T) {
|
|
|
|
|
for _, tc := range registerSidecarTestCases {
|
|
|
|
|
tc := tc
|
2021-03-19 11:50:05 +01:00
|
|
|
|
2021-11-08 22:21:13 -08:00
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
|
|
|
|
signer := test.NewMockSignerClient(mockCtrl)
|
|
|
|
|
wallet := test.NewMockWalletKitClient(mockCtrl)
|
|
|
|
|
store := sidecar.NewMockStore(mockCtrl)
|
|
|
|
|
|
2022-08-03 19:51:34 +02:00
|
|
|
if tc.mockSetter != nil {
|
|
|
|
|
tc.mockSetter(tc.ticket, signer, wallet, store)
|
|
|
|
|
}
|
2021-11-08 22:21:13 -08:00
|
|
|
|
|
|
|
|
acceptor := NewSidecarAcceptor(&SidecarAcceptorConfig{
|
|
|
|
|
SidecarDB: store,
|
|
|
|
|
AcctDB: nil,
|
|
|
|
|
Signer: signer,
|
|
|
|
|
Wallet: wallet,
|
|
|
|
|
NodePubKey: ourNodePubKey,
|
|
|
|
|
ClientCfg: auctioneer.Config{},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ticket, err := acceptor.RegisterSidecar(
|
|
|
|
|
context.Background(), *tc.ticket,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if tc.expectedErr != "" {
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(
|
|
|
|
|
t, err.Error(), tc.expectedErr,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
2022-08-03 19:51:34 +02:00
|
|
|
|
|
|
|
|
if tc.check != nil {
|
|
|
|
|
tc.check(t, ticket)
|
|
|
|
|
}
|
2021-11-08 22:21:13 -08:00
|
|
|
})
|
2021-03-19 11:50:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-21 20:13:47 -07:00
|
|
|
|
|
|
|
|
type mockMailBox struct {
|
2021-05-23 18:48:23 -07:00
|
|
|
providerChan chan *sidecar.Ticket
|
|
|
|
|
providerMsgAck chan struct{}
|
|
|
|
|
providerDel chan struct{}
|
|
|
|
|
providerDropChan chan struct{}
|
|
|
|
|
|
|
|
|
|
receiverChan chan *sidecar.Ticket
|
|
|
|
|
receiverMsgAck chan struct{}
|
|
|
|
|
receiverDel chan struct{}
|
|
|
|
|
receiverDropChan chan struct{}
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newMockMailBox() *mockMailBox {
|
|
|
|
|
return &mockMailBox{
|
2021-05-23 18:48:23 -07:00
|
|
|
providerChan: make(chan *sidecar.Ticket),
|
|
|
|
|
providerMsgAck: make(chan struct{}),
|
|
|
|
|
providerDel: make(chan struct{}),
|
|
|
|
|
providerDropChan: make(chan struct{}, 1),
|
|
|
|
|
|
|
|
|
|
receiverChan: make(chan *sidecar.Ticket),
|
|
|
|
|
receiverMsgAck: make(chan struct{}),
|
|
|
|
|
receiverDel: make(chan struct{}),
|
|
|
|
|
receiverDropChan: make(chan struct{}, 1),
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) RecvSidecarPkt(ctx context.Context, pkt *sidecar.Ticket,
|
|
|
|
|
provider bool) (*sidecar.Ticket, error) {
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
recvChan chan *sidecar.Ticket
|
2021-05-23 18:48:23 -07:00
|
|
|
dropChan chan struct{}
|
2021-05-21 20:13:47 -07:00
|
|
|
ackChan chan struct{}
|
|
|
|
|
)
|
|
|
|
|
if provider {
|
|
|
|
|
recvChan = m.providerChan
|
|
|
|
|
ackChan = m.providerMsgAck
|
2021-05-23 18:48:23 -07:00
|
|
|
dropChan = m.providerDropChan
|
2021-05-21 20:13:47 -07:00
|
|
|
} else {
|
|
|
|
|
recvChan = m.receiverChan
|
|
|
|
|
ackChan = m.receiverMsgAck
|
2021-05-23 18:48:23 -07:00
|
|
|
dropChan = m.receiverDropChan
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
recvMsg:
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
return nil, fmt.Errorf("mailbox shutting down")
|
|
|
|
|
|
|
|
|
|
case tkt := <-recvChan:
|
2021-05-23 18:48:23 -07:00
|
|
|
tktCopy := *tkt
|
2021-05-21 20:13:47 -07:00
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
select {
|
|
|
|
|
case ackChan <- struct{}{}:
|
2021-05-21 20:13:47 -07:00
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
// If we get a signal to drop the message, then we'll just go
|
|
|
|
|
// back to receiving as normal.
|
|
|
|
|
case <-dropChan:
|
|
|
|
|
goto recvMsg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &tktCopy, nil
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) SendSidecarPkt(ctx context.Context, pkt *sidecar.Ticket,
|
|
|
|
|
provider bool) error {
|
2022-08-03 19:51:34 +02:00
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
var sendChan chan *sidecar.Ticket
|
|
|
|
|
if provider {
|
|
|
|
|
sendChan = m.providerChan
|
|
|
|
|
} else {
|
|
|
|
|
sendChan = m.receiverChan
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
case sendChan <- pkt:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) InitSidecarMailbox(streamID [64]byte, ticket *sidecar.Ticket) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) InitAcctMailbox(streamID [64]byte, pubKey *keychain.KeyDescriptor) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) DelSidecarMailbox(streamID [64]byte, ticket *sidecar.Ticket) error {
|
|
|
|
|
m.receiverDel <- struct{}{}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockMailBox) DelAcctMailbox(streamID [64]byte, pubKey *keychain.KeyDescriptor) error {
|
|
|
|
|
m.providerDel <- struct{}{}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type mockDriver struct {
|
|
|
|
|
stateUpdates chan sidecar.State
|
|
|
|
|
bidSubmitted chan struct{}
|
|
|
|
|
ticketValidated chan struct{}
|
|
|
|
|
channelExpected chan struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newMockDriver() *mockDriver {
|
|
|
|
|
return &mockDriver{
|
|
|
|
|
stateUpdates: make(chan sidecar.State),
|
|
|
|
|
bidSubmitted: make(chan struct{}),
|
|
|
|
|
ticketValidated: make(chan struct{}),
|
|
|
|
|
channelExpected: make(chan struct{}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockDriver) ValidateOrderedTicket(tkt *sidecar.Ticket) error {
|
|
|
|
|
if tkt.State != sidecar.StateOrdered {
|
|
|
|
|
return fmt.Errorf("sidecar not in state ordered: %v", tkt.State)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m.ticketValidated <- struct{}{}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockDriver) ExpectChannel(ctx context.Context, tkt *sidecar.Ticket) error {
|
|
|
|
|
tkt.State = sidecar.StateExpectingChannel
|
|
|
|
|
|
|
|
|
|
m.channelExpected <- struct{}{}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockDriver) UpdateSidecar(tkt *sidecar.Ticket) error {
|
|
|
|
|
m.stateUpdates <- tkt.State
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *mockDriver) SubmitSidecarOrder(tkt *sidecar.Ticket, bid *order.Bid,
|
|
|
|
|
acct *account.Account) (*sidecar.Ticket, error) {
|
|
|
|
|
|
|
|
|
|
tkt.State = sidecar.StateOrdered
|
|
|
|
|
|
|
|
|
|
m.bidSubmitted <- struct{}{}
|
|
|
|
|
|
|
|
|
|
return tkt, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type sidecarTestCtx struct {
|
|
|
|
|
t *testing.T
|
|
|
|
|
|
|
|
|
|
provider *SidecarNegotiator
|
|
|
|
|
providerDriver *mockDriver
|
|
|
|
|
|
|
|
|
|
recipient *SidecarNegotiator
|
|
|
|
|
recipientDriver *mockDriver
|
|
|
|
|
|
|
|
|
|
mailbox *mockMailBox
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) startNegotiators() error {
|
|
|
|
|
if err := s.provider.Start(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s.recipient.Start()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) restartAllNegotiators() error {
|
|
|
|
|
s.provider.Stop()
|
|
|
|
|
s.recipient.Stop()
|
|
|
|
|
|
|
|
|
|
s.provider.quit = make(chan struct{})
|
2021-10-27 15:34:10 +02:00
|
|
|
s.provider.stopOnce = sync.Once{}
|
2021-05-21 20:13:47 -07:00
|
|
|
s.recipient.quit = make(chan struct{})
|
2021-10-27 15:34:10 +02:00
|
|
|
s.recipient.stopOnce = sync.Once{}
|
2021-05-21 20:13:47 -07:00
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
s.provider.cfg.StartingPkt.CurrentState = sidecar.State(s.provider.currentState)
|
|
|
|
|
s.recipient.cfg.StartingPkt.CurrentState = sidecar.State(s.recipient.currentState)
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
if err := s.provider.Start(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s.recipient.Start()
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
func (s *sidecarTestCtx) restartProvider() error {
|
|
|
|
|
s.provider.Stop()
|
|
|
|
|
|
|
|
|
|
s.provider.quit = make(chan struct{})
|
|
|
|
|
|
|
|
|
|
// When we restart the provider in isolation, we'll have their state be
|
|
|
|
|
// mapped to the _created_ state (as the SidecarAcceptor would),
|
|
|
|
|
// which'll cause them to retransmit their last message.
|
|
|
|
|
s.provider.cfg.StartingPkt.CurrentState = sidecar.StateCreated
|
|
|
|
|
|
|
|
|
|
return s.provider.Start()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) restartRecipient() error {
|
|
|
|
|
s.recipient.Stop()
|
|
|
|
|
|
|
|
|
|
s.recipient.quit = make(chan struct{})
|
|
|
|
|
|
|
|
|
|
s.recipient.cfg.StartingPkt.CurrentState = sidecar.State(s.recipient.currentState)
|
|
|
|
|
|
|
|
|
|
return s.recipient.Start()
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
func (s *sidecarTestCtx) assertProviderMsgRecv() {
|
2021-05-23 18:48:23 -07:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.providerMsgAck:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
2021-05-23 18:48:23 -07:00
|
|
|
s.t.Fatalf("no provider msg received")
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientMsgRecv() {
|
2021-05-23 18:48:23 -07:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.receiverMsgAck:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
2021-05-23 18:48:23 -07:00
|
|
|
s.t.Fatalf("no recipient msg received")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) dropReceiverMessage() {
|
|
|
|
|
s.mailbox.receiverDropChan <- struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) dropProviderMessage() {
|
|
|
|
|
s.mailbox.providerDropChan <- struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertNoProviderMsgsRecvd() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.providerMsgAck:
|
|
|
|
|
s.t.Fatalf("provider should've received no messages")
|
|
|
|
|
case <-time.After(time.Second * 1):
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertNoReceiverMsgsRecvd() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.receiverMsgAck:
|
|
|
|
|
s.t.Fatalf("receiver should've received no messages")
|
|
|
|
|
case <-time.After(time.Second * 1):
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertProviderTicketUpdated(expectedState sidecar.State) {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case stateUpdate := <-s.providerDriver.stateUpdates:
|
|
|
|
|
|
|
|
|
|
if stateUpdate != expectedState {
|
|
|
|
|
s.t.Fatalf("expected state=%v, got: %v", expectedState,
|
|
|
|
|
stateUpdate)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("provider ticket never updated")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertBidSubmited() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.providerDriver.bidSubmitted:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("provider bid never submitted")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientTicketValidated() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.recipientDriver.ticketValidated:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("recipient ticket never validated")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientExpectsChannel() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.recipientDriver.channelExpected:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("recipient channel never expected")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientTicketUpdated(expectedState sidecar.State) {
|
2021-05-25 19:11:39 -07:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case stateUpdate := <-s.recipientDriver.stateUpdates:
|
|
|
|
|
|
|
|
|
|
if stateUpdate != expectedState {
|
|
|
|
|
s.t.Fatalf("expected state=%v, got: %v", expectedState,
|
|
|
|
|
stateUpdate)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("recipient ticket never updated")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) confirmSidecarBatch() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.provider.TicketExecuted(sidecar.StateCompleted, false)
|
2021-05-21 20:13:47 -07:00
|
|
|
|
2021-10-27 15:34:12 +02:00
|
|
|
s.recipient.TicketExecuted(sidecar.StateCompleted, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) cancelTicket(fromProvider bool) {
|
|
|
|
|
if fromProvider {
|
|
|
|
|
s.provider.TicketExecuted(sidecar.StateCanceled, false)
|
|
|
|
|
} else {
|
|
|
|
|
s.recipient.TicketExecuted(sidecar.StateCanceled, false)
|
|
|
|
|
}
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertProviderMailboxDel() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.providerDel:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("provider mailbox not deleted")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientMailboxDel() {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
select {
|
|
|
|
|
case <-s.mailbox.receiverDel:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Fatalf("recipient mailbox not deleted")
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
func (s *sidecarTestCtx) assertNegotiatorStates(providerState, recepientState sidecar.State) {
|
2021-10-27 15:34:12 +02:00
|
|
|
s.t.Helper()
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
err := wait.Predicate(func() bool {
|
|
|
|
|
return s.provider.CurrentState() == providerState
|
|
|
|
|
}, time.Second*5)
|
|
|
|
|
assert.NoError(s.t, err)
|
|
|
|
|
err = wait.Predicate(func() bool {
|
|
|
|
|
return s.recipient.CurrentState() == recepientState
|
|
|
|
|
}, time.Second*5)
|
|
|
|
|
assert.NoError(s.t, err)
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 15:34:10 +02:00
|
|
|
func (s *sidecarTestCtx) assertProviderShutdown() {
|
|
|
|
|
s.t.Helper()
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-s.provider.quit:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("provider not shut down")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
waitDone := make(chan struct{})
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(waitDone)
|
|
|
|
|
|
|
|
|
|
s.provider.wg.Wait()
|
|
|
|
|
}()
|
|
|
|
|
select {
|
|
|
|
|
case <-waitDone:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("provider not finished")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *sidecarTestCtx) assertRecipientShutdown() {
|
|
|
|
|
s.t.Helper()
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-s.recipient.quit:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("recipient not shut down")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
waitDone := make(chan struct{})
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(waitDone)
|
|
|
|
|
|
|
|
|
|
s.recipient.wg.Wait()
|
|
|
|
|
}()
|
|
|
|
|
select {
|
|
|
|
|
case <-waitDone:
|
|
|
|
|
case <-time.After(time.Second * 5):
|
|
|
|
|
s.t.Fatalf("recipient not finished")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
func newSidecarTestCtx(t *testing.T) *sidecarTestCtx {
|
|
|
|
|
mailBox := newMockMailBox()
|
|
|
|
|
providerDriver := newMockDriver()
|
|
|
|
|
recipientDriver := newMockDriver()
|
|
|
|
|
|
|
|
|
|
ticketID := [8]byte{1}
|
|
|
|
|
|
|
|
|
|
provider := NewSidecarNegotiator(AutoAcceptorConfig{
|
|
|
|
|
Provider: true,
|
|
|
|
|
ProviderBid: &order.Bid{
|
|
|
|
|
Kit: order.Kit{
|
|
|
|
|
Version: order.VersionSelfChanBalance,
|
|
|
|
|
LeaseDuration: 144,
|
|
|
|
|
MaxBatchFeeRate: 253,
|
|
|
|
|
MinUnitsMatch: 1,
|
|
|
|
|
Amt: 0,
|
|
|
|
|
UnitsUnfulfilled: 0,
|
|
|
|
|
},
|
|
|
|
|
SelfChanBalance: 1,
|
|
|
|
|
},
|
|
|
|
|
ProviderAccount: &account.Account{},
|
|
|
|
|
StartingPkt: &SidecarPacket{
|
|
|
|
|
CurrentState: sidecar.StateOffered,
|
|
|
|
|
ProviderTicket: &sidecar.Ticket{
|
|
|
|
|
ID: ticketID,
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
ReceiverTicket: &sidecar.Ticket{
|
|
|
|
|
ID: ticketID,
|
|
|
|
|
State: sidecar.StateOffered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Driver: providerDriver,
|
|
|
|
|
MailBox: mailBox,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
recipient := NewSidecarNegotiator(AutoAcceptorConfig{
|
|
|
|
|
Provider: false,
|
|
|
|
|
StartingPkt: &SidecarPacket{
|
|
|
|
|
CurrentState: sidecar.StateRegistered,
|
|
|
|
|
ProviderTicket: &sidecar.Ticket{
|
|
|
|
|
ID: ticketID,
|
|
|
|
|
State: sidecar.StateRegistered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
ReceiverTicket: &sidecar.Ticket{
|
|
|
|
|
ID: ticketID,
|
|
|
|
|
State: sidecar.StateRegistered,
|
|
|
|
|
Offer: sidecar.Offer{
|
|
|
|
|
SigOfferDigest: testOfferSig,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Driver: recipientDriver,
|
|
|
|
|
MailBox: mailBox,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return &sidecarTestCtx{
|
|
|
|
|
t: t,
|
|
|
|
|
provider: provider,
|
|
|
|
|
providerDriver: providerDriver,
|
|
|
|
|
recipient: recipient,
|
|
|
|
|
recipientDriver: recipientDriver,
|
|
|
|
|
mailbox: mailBox,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestAutoSidecarNegotiation tests the routine sidecar negotiation process
|
|
|
|
|
// including that both sides are able to properly handle retransmissions and
|
|
|
|
|
// also restarts assuming persistent storage is durable.
|
|
|
|
|
func TestAutoSidecarNegotiation(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
testCtx := newSidecarTestCtx(t)
|
|
|
|
|
|
|
|
|
|
// First, we'll start both negotiators. The provider should no-op, but
|
|
|
|
|
// then the receiver should send over the ticket and complete
|
|
|
|
|
// execution. At the end of the exchange, we expect that both sides are
|
|
|
|
|
// waiting for the channel in its expectation state.
|
|
|
|
|
err := testCtx.startNegotiators()
|
|
|
|
|
assert.NoError(t, err, fmt.Errorf("unable to start negotiators: %v", err))
|
|
|
|
|
|
|
|
|
|
// The recipient should send a new message to the provider with their
|
|
|
|
|
// ticket in the registered state.
|
|
|
|
|
testCtx.assertProviderMsgRecv()
|
|
|
|
|
|
|
|
|
|
// Upon receiving the new ticket, the provider should write the new
|
2021-10-27 15:34:10 +02:00
|
|
|
// registered state to disk, submit the bid, then send the new ticket
|
2021-05-21 20:13:47 -07:00
|
|
|
// over to the recipient.
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateRegistered)
|
|
|
|
|
testCtx.assertBidSubmited()
|
|
|
|
|
testCtx.assertRecipientMsgRecv()
|
|
|
|
|
|
|
|
|
|
// After sending the ticket, the provider should update the ticket in
|
|
|
|
|
// its database as it waits in the expected state.
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateExpectingChannel)
|
|
|
|
|
|
|
|
|
|
// The recipient, should now validate the ticket, then wait and expect
|
|
|
|
|
// the channel.
|
|
|
|
|
testCtx.assertRecipientTicketValidated()
|
|
|
|
|
testCtx.assertRecipientExpectsChannel()
|
|
|
|
|
|
|
|
|
|
// At this point, both sides should be waiting for the channel in its
|
|
|
|
|
// expected state.
|
2021-05-23 18:48:23 -07:00
|
|
|
testCtx.assertNegotiatorStates(
|
|
|
|
|
sidecar.StateExpectingChannel, sidecar.StateExpectingChannel,
|
2021-05-21 20:13:47 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// We'll now simulate a restart on both sides by signalling their
|
|
|
|
|
// goroutines to exit, then re-starting them anew with their persisted
|
|
|
|
|
// state.
|
|
|
|
|
err = testCtx.restartAllNegotiators()
|
|
|
|
|
assert.NoError(t, err, fmt.Errorf("unable to restart negotiators: %v", err))
|
|
|
|
|
|
|
|
|
|
// After the start, both sides should still show that they're expecting
|
|
|
|
|
// the channel
|
|
|
|
|
assert.Equal(
|
|
|
|
|
t, sidecar.StateExpectingChannel, testCtx.provider.CurrentState(),
|
|
|
|
|
)
|
|
|
|
|
assert.Equal(
|
|
|
|
|
t, sidecar.StateExpectingChannel, testCtx.recipient.CurrentState(),
|
|
|
|
|
)
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
// Finally there should be no additional message sent either since both
|
|
|
|
|
// sides should now be in a terminal state
|
|
|
|
|
testCtx.assertNoProviderMsgsRecvd()
|
|
|
|
|
testCtx.assertNoReceiverMsgsRecvd()
|
|
|
|
|
|
2021-05-25 19:11:39 -07:00
|
|
|
// The recipient of the ticket should re-expect the channel to
|
|
|
|
|
// re-register with the auctioneer to ensure the channel can be
|
|
|
|
|
// executed amidst their restarts.
|
|
|
|
|
testCtx.assertRecipientExpectsChannel()
|
|
|
|
|
|
2021-05-21 20:13:47 -07:00
|
|
|
// We'll now signal to both goroutines that the channel has been
|
|
|
|
|
// finalized, at this point, we expect both ticket to transition to the
|
|
|
|
|
// terminal state and the goroutines to exit.
|
|
|
|
|
go testCtx.confirmSidecarBatch()
|
|
|
|
|
|
|
|
|
|
// We expect that both sides now update their state one last time to
|
|
|
|
|
// transition the ticket to a completed state, afterwards, they should
|
|
|
|
|
// move clean up their mailboxes.
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateCompleted)
|
|
|
|
|
testCtx.assertProviderMailboxDel()
|
|
|
|
|
|
|
|
|
|
testCtx.assertRecipientTicketUpdated(sidecar.StateCompleted)
|
|
|
|
|
testCtx.assertRecipientMailboxDel()
|
|
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
// Once again, no messages should be received by either side.
|
|
|
|
|
testCtx.assertNoProviderMsgsRecvd()
|
|
|
|
|
testCtx.assertNoReceiverMsgsRecvd()
|
2021-10-27 15:34:10 +02:00
|
|
|
|
|
|
|
|
// Both negotiators should be properly shut down.
|
|
|
|
|
testCtx.assertProviderShutdown()
|
|
|
|
|
testCtx.assertRecipientShutdown()
|
2021-05-23 18:48:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestAutoSidecarNegotiationRetransmission tests that if either side restarts,
|
|
|
|
|
// then the proper message is sent in order to ensure the negotiation state
|
|
|
|
|
// machine continues to be progressed.
|
|
|
|
|
func TestAutoSidecarNegotiationRetransmission(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
testCtx := newSidecarTestCtx(t)
|
|
|
|
|
|
|
|
|
|
// We'll start our negotiators as usual, however before we start them
|
|
|
|
|
// we'll make sure that the message sent by the receiver is never
|
|
|
|
|
// received by the provider.
|
|
|
|
|
testCtx.dropProviderMessage()
|
|
|
|
|
|
|
|
|
|
err := testCtx.startNegotiators()
|
|
|
|
|
assert.NoError(t, err, fmt.Errorf("unable to start negotiators: %v", err))
|
2021-05-21 20:13:47 -07:00
|
|
|
|
2021-05-23 18:48:23 -07:00
|
|
|
// At this point, both sides should still be in their starting state as
|
|
|
|
|
// the initial message was never received.
|
|
|
|
|
testCtx.assertNegotiatorStates(
|
|
|
|
|
sidecar.StateOffered, sidecar.StateRegistered,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// We'll now restart only the provider. This should cause the provider
|
|
|
|
|
// to retransmit a message of their offered ticket, which should cause
|
|
|
|
|
// the recipient to re-send their registered ticket.
|
|
|
|
|
//
|
|
|
|
|
// In order to test the other retransmission case, we'll drop the
|
|
|
|
|
// provider's message which carries the ticket in the ordered state.
|
|
|
|
|
require.NoError(t, testCtx.restartProvider())
|
|
|
|
|
testCtx.assertRecipientMsgRecv()
|
|
|
|
|
|
|
|
|
|
// The provider receive the ticket, then update their local state as
|
|
|
|
|
// normal.
|
|
|
|
|
testCtx.assertProviderMsgRecv()
|
|
|
|
|
testCtx.dropReceiverMessage()
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateRegistered)
|
|
|
|
|
testCtx.assertBidSubmited()
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateExpectingChannel)
|
|
|
|
|
|
|
|
|
|
// The provider should now have transitioned to the final state,
|
|
|
|
|
// however the recipient should still be in their initial registered
|
|
|
|
|
// state as they haven't received any messages yet.
|
|
|
|
|
testCtx.assertNegotiatorStates(
|
|
|
|
|
sidecar.StateExpectingChannel, sidecar.StateRegistered,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// We'll now restart the recipient, which should cause them to re-send
|
|
|
|
|
// their registered ticket that'll cause the provider to re-send
|
|
|
|
|
// _their_ ticket which should conclude the process with the ticket
|
|
|
|
|
// being fully finalized.
|
|
|
|
|
require.NoError(t, testCtx.restartRecipient())
|
|
|
|
|
testCtx.assertProviderMsgRecv()
|
|
|
|
|
|
|
|
|
|
testCtx.assertRecipientMsgRecv()
|
|
|
|
|
testCtx.assertRecipientTicketValidated()
|
|
|
|
|
testCtx.assertRecipientExpectsChannel()
|
2021-05-21 20:13:47 -07:00
|
|
|
}
|
2021-10-27 15:34:12 +02:00
|
|
|
|
|
|
|
|
// TestAutoSidecarNegotiationCancellation tests that if either side cancels,
|
|
|
|
|
// then the proper message is sent in order to ensure the negotiation state
|
|
|
|
|
// machine properly stops on both ends.
|
|
|
|
|
func TestAutoSidecarNegotiationCancellation(t *testing.T) {
|
|
|
|
|
t.Run("provider cancels", func(tt *testing.T) {
|
|
|
|
|
runAutoSidecarNegotiationCancellation(tt, true)
|
|
|
|
|
})
|
|
|
|
|
t.Run("recipient cancels", func(tt *testing.T) {
|
|
|
|
|
runAutoSidecarNegotiationCancellation(tt, false)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runAutoSidecarNegotiationCancellation(t *testing.T, providerCancels bool) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
testCtx := newSidecarTestCtx(t)
|
|
|
|
|
|
|
|
|
|
// First, we'll start both negotiators. The provider should no-op, but
|
|
|
|
|
// then the receiver should send over the ticket and complete
|
|
|
|
|
// execution. At the end of the exchange, we expect that both sides are
|
|
|
|
|
// waiting for the channel in its expectation state.
|
|
|
|
|
err := testCtx.startNegotiators()
|
|
|
|
|
assert.NoError(t, err, fmt.Errorf("unable to start negotiators: %v", err))
|
|
|
|
|
|
|
|
|
|
// The recipient should send a new message to the provider with their
|
|
|
|
|
// ticket in the registered state.
|
|
|
|
|
testCtx.assertProviderMsgRecv()
|
|
|
|
|
|
|
|
|
|
// Upon receiving the new ticket, the provider should write the new
|
|
|
|
|
// registered state to disk, submit the bid, then send the new ticket
|
|
|
|
|
// over to the recipient.
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateRegistered)
|
|
|
|
|
testCtx.assertBidSubmited()
|
|
|
|
|
testCtx.assertRecipientMsgRecv()
|
|
|
|
|
|
|
|
|
|
// After sending the ticket, the provider should update the ticket in
|
|
|
|
|
// its database as it waits in the expected state.
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateExpectingChannel)
|
|
|
|
|
|
|
|
|
|
// The recipient, should now validate the ticket, then wait and expect
|
|
|
|
|
// the channel.
|
|
|
|
|
testCtx.assertRecipientTicketValidated()
|
|
|
|
|
testCtx.assertRecipientExpectsChannel()
|
|
|
|
|
|
|
|
|
|
// At this point, both sides should be waiting for the channel in its
|
|
|
|
|
// expected state.
|
|
|
|
|
testCtx.assertNegotiatorStates(
|
|
|
|
|
sidecar.StateExpectingChannel, sidecar.StateExpectingChannel,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// We now cancel the ticket on the requested side. This cancellation
|
|
|
|
|
// would normally come in as an RPC request, so from its own goroutine.
|
|
|
|
|
go testCtx.cancelTicket(providerCancels)
|
|
|
|
|
|
|
|
|
|
// The other side should now receive a message.
|
|
|
|
|
if providerCancels {
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateCanceled)
|
|
|
|
|
testCtx.assertRecipientMsgRecv()
|
|
|
|
|
testCtx.assertRecipientTicketUpdated(sidecar.StateCanceled)
|
|
|
|
|
testCtx.assertRecipientMailboxDel()
|
|
|
|
|
} else {
|
|
|
|
|
testCtx.assertRecipientTicketUpdated(sidecar.StateCanceled)
|
|
|
|
|
testCtx.assertProviderMsgRecv()
|
|
|
|
|
testCtx.assertProviderTicketUpdated(sidecar.StateCanceled)
|
|
|
|
|
testCtx.assertProviderMailboxDel()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Once again, no messages should be received by either side.
|
|
|
|
|
testCtx.assertNoProviderMsgsRecvd()
|
|
|
|
|
testCtx.assertNoReceiverMsgsRecvd()
|
|
|
|
|
}
|