decred.org/dcrdex@v1.0.5/tatanka/tatanka_test.go (about)

     1  package tatanka
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"testing"
     9  	"time"
    10  
    11  	"decred.org/dcrdex/dex"
    12  	"decred.org/dcrdex/dex/encode"
    13  	"decred.org/dcrdex/dex/msgjson"
    14  	"decred.org/dcrdex/tatanka/mj"
    15  	"decred.org/dcrdex/tatanka/tanka"
    16  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    17  )
    18  
    19  type tSender struct {
    20  	id           tanka.PeerID
    21  	disconnected bool
    22  	responseErr  error
    23  	responses    []*msgjson.Message
    24  	recvd        []*msgjson.Message
    25  	sendErr      error
    26  }
    27  
    28  func tNewSender(peerID tanka.PeerID) *tSender {
    29  	return &tSender{id: peerID}
    30  }
    31  
    32  func (s *tSender) queueResponse(resp *msgjson.Message) {
    33  	s.responses = append(s.responses, resp)
    34  }
    35  
    36  func (s *tSender) received() *msgjson.Message {
    37  	if len(s.recvd) == 0 {
    38  		return nil
    39  	}
    40  	msg := s.recvd[0]
    41  	s.recvd = s.recvd[1:]
    42  	return msg
    43  }
    44  
    45  func (s *tSender) Send(msg *msgjson.Message) error {
    46  	s.recvd = append(s.recvd, msg)
    47  	return s.sendErr
    48  }
    49  func (s *tSender) SendRaw(rawMsg []byte) error {
    50  	msg, err := msgjson.DecodeMessage(rawMsg)
    51  	if err != nil {
    52  		return fmt.Errorf("tSender DecodeMessage error: %w", err)
    53  	}
    54  	s.recvd = append(s.recvd, msg)
    55  	return s.sendErr
    56  }
    57  func (s *tSender) Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error {
    58  	return s.RequestRaw(0, nil, respHandler)
    59  }
    60  func (s *tSender) RequestRaw(msgID uint64, rawMsg []byte, respHandler func(*msgjson.Message)) error {
    61  	if s.responseErr != nil {
    62  		return s.responseErr
    63  	}
    64  	if len(s.responses) == 0 {
    65  		return errors.New("not test responses queued")
    66  	}
    67  	resp := s.responses[0]
    68  	s.responses = s.responses[1:]
    69  	go respHandler(resp) // goroutine to simulate Sender impls
    70  	return nil
    71  }
    72  func (s *tSender) SetPeerID(tanka.PeerID) {}
    73  
    74  func (s *tSender) PeerID() tanka.PeerID {
    75  	return s.id
    76  }
    77  func (s *tSender) Disconnect() {
    78  	s.disconnected = true
    79  }
    80  
    81  func tNewTatanka(id byte) (*Tatanka, func()) {
    82  	priv, _ := secp256k1.GeneratePrivateKey()
    83  	var peerID tanka.PeerID
    84  	peerID[tanka.PeerIDLength-1] = id
    85  
    86  	ctx, cancel := context.WithCancel(context.Background())
    87  
    88  	srv := &Tatanka{
    89  		ctx: ctx,
    90  		net: dex.Simnet,
    91  		log: dex.StdOutLogger("T", dex.LevelTrace),
    92  		// db:            db,
    93  		priv: priv,
    94  		id:   peerID,
    95  		// chains:        chains,
    96  		tatankas:      make(map[tanka.PeerID]*remoteTatanka),
    97  		clients:       make(map[tanka.PeerID]*client),
    98  		remoteClients: make(map[tanka.PeerID]map[tanka.PeerID]struct{}),
    99  		topics:        make(map[tanka.Topic]*Topic),
   100  		recentRelays:  make(map[[32]byte]time.Time),
   101  		clientJobs:    make(chan *clientJob, 128),
   102  	}
   103  
   104  	go srv.runRemoteClientsLoop(ctx)
   105  
   106  	return srv, cancel
   107  }
   108  
   109  func tNewPeer(id byte) (*peer, *tSender) {
   110  	var peerID tanka.PeerID
   111  	peerID[tanka.PeerIDLength-1] = id
   112  	p := &tanka.Peer{ID: peerID}
   113  	s := tNewSender(peerID)
   114  	return &peer{Peer: p, Sender: s, rrs: make(map[tanka.PeerID]*mj.RemoteReputation)}, s
   115  }
   116  
   117  func tNewRemoteTatanka(id byte) (*remoteTatanka, *tSender) {
   118  	p, s := tNewPeer(id)
   119  	return &remoteTatanka{peer: p}, s
   120  }
   121  
   122  func tNewClient(id byte) (*client, *tSender) {
   123  	p, s := tNewPeer(id)
   124  	return &client{peer: p}, s
   125  }
   126  
   127  func TestTankagrams(t *testing.T) {
   128  	srv, shutdown := tNewTatanka(0)
   129  	defer shutdown()
   130  
   131  	tt, tts := tNewRemoteTatanka(1)
   132  	srv.tatankas[tt.ID] = tt
   133  
   134  	c0, c0s := tNewClient(2)
   135  	srv.clients[c0.ID] = c0
   136  
   137  	c1, c1s := tNewClient(3)
   138  	srv.clients[c1.ID] = c1
   139  
   140  	gram := &mj.Tankagram{
   141  		From: c0.ID,
   142  		To:   c1.ID,
   143  	}
   144  	msg := mj.MustRequest(mj.RouteTankagram, gram)
   145  	var r mj.TankagramResult
   146  	checkResponse := func(expResult mj.TankagramResultType) {
   147  		t.Helper()
   148  		if msgErr := srv.handleTankagram(c0, msg); msgErr != nil {
   149  			t.Fatalf("Initial handleTankagram error: %v", msgErr)
   150  		}
   151  		respMsg := c0s.received()
   152  		if respMsg == nil {
   153  			t.Fatalf("No response received")
   154  		}
   155  
   156  		respMsg.UnmarshalResult(&r)
   157  		if r.Result != expResult {
   158  			t.Fatalf("Expected result %q, got %q", expResult, r.Result)
   159  		}
   160  	}
   161  
   162  	// tankagram to locally connected client success
   163  	responseB := dex.Bytes(encode.RandomBytes(10))
   164  	resp := mj.MustResponse(msg.ID, responseB, nil)
   165  	c1s.queueResponse(resp)
   166  	checkResponse(mj.TRTTransmitted)
   167  	if !bytes.Equal(r.Response, responseB) {
   168  		t.Fatalf("wrong response")
   169  	}
   170  
   171  	// Bad response from client.
   172  	resp, _ = msgjson.NewResponse(msg.ID, "zz", nil)
   173  	c1s.queueResponse(resp)
   174  	checkResponse(mj.TRTErrBadClient)
   175  
   176  	// Client not responsive locally, but is remotely.
   177  	c1s.responseErr = errors.New("test error")
   178  	resp, _ = msgjson.NewResponse(msg.ID, &mj.TankagramResult{Result: mj.TRTTransmitted, Response: responseB}, nil)
   179  	tts.queueResponse(resp)
   180  	srv.remoteClients[c1.ID] = map[tanka.PeerID]struct{}{tt.ID: {}}
   181  	checkResponse(mj.TRTTransmitted)
   182  	if !bytes.Equal(r.Response, responseB) {
   183  		t.Fatalf("wrong response")
   184  	}
   185  	c1s.responseErr = nil
   186  
   187  	// Client not known locally, and is erroneous remotely.
   188  	delete(srv.clients, c1.ID)
   189  	resp, _ = msgjson.NewResponse(msg.ID, &mj.TankagramResult{Result: mj.TRTErrBadClient}, nil)
   190  	tts.queueResponse(resp)
   191  	checkResponse(mj.TRTErrBadClient)
   192  
   193  	// Client not known locally or remotely
   194  	delete(srv.remoteClients, c1.ID)
   195  	checkResponse(mj.TRTNoPath)
   196  
   197  	// Now we're the remote.
   198  
   199  	// We know the client and transmit the relayed tankagram successfully.
   200  	srv.clients[c1.ID] = c1
   201  	resp, _ = msgjson.NewResponse(msg.ID, responseB, nil)
   202  	c1s.queueResponse(resp)
   203  	msg, _ = msgjson.NewRequest(mj.NewMessageID(), mj.RouteRelayTankagram, gram)
   204  	srv.handleRelayedTankagram(tt, msg)
   205  	respMsg := tts.received()
   206  	if respMsg == nil {
   207  		t.Fatalf("No response received")
   208  	}
   209  	respMsg.UnmarshalResult(&r)
   210  	if r.Result != mj.TRTTransmitted {
   211  		t.Fatalf("Expected result %q, got %q", mj.TRTTransmitted, r.Result)
   212  	}
   213  }