github.com/decred/dcrlnd@v0.7.6/watchtower/wtwire/wtwire_test.go (about)

     1  package wtwire_test
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  	"reflect"
     7  	"testing"
     8  	"testing/quick"
     9  	"time"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  	"github.com/decred/dcrd/chaincfg/chainhash"
    13  	"github.com/decred/dcrlnd/lnwire"
    14  	"github.com/decred/dcrlnd/watchtower/wtwire"
    15  )
    16  
    17  func randRawFeatureVector(r *rand.Rand) *lnwire.RawFeatureVector {
    18  	featureVec := lnwire.NewRawFeatureVector()
    19  	for i := 0; i < 10000; i++ {
    20  		if r.Int31n(2) == 0 {
    21  			featureVec.Set(lnwire.FeatureBit(i))
    22  		}
    23  	}
    24  	return featureVec
    25  }
    26  
    27  func randChainHash(r *rand.Rand) chainhash.Hash {
    28  	var hash chainhash.Hash
    29  	r.Read(hash[:])
    30  	return hash
    31  }
    32  
    33  // TestWatchtowerWireProtocol uses the testing/quick package to create a series
    34  // of fuzz tests to attempt to break a primary scenario which is implemented as
    35  // property based testing scenario.
    36  func TestWatchtowerWireProtocol(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	// mainScenario is the primary test that will programmatically be
    40  	// executed for all registered wire messages. The quick-checker within
    41  	// testing/quick will attempt to find an input to this function, s.t
    42  	// the function returns false, if so then we've found an input that
    43  	// violates our model of the system.
    44  	mainScenario := func(msg wtwire.Message) bool {
    45  		// Give a new message, we'll serialize the message into a new
    46  		// bytes buffer.
    47  		var b bytes.Buffer
    48  		if _, err := wtwire.WriteMessage(&b, msg, 0); err != nil {
    49  			t.Fatalf("unable to write msg: %v", err)
    50  			return false
    51  		}
    52  
    53  		// Next, we'll ensure that the serialized payload (subtracting
    54  		// the 2 bytes for the message type) is _below_ the specified
    55  		// max payload size for this message.
    56  		payloadLen := uint32(b.Len()) - 2
    57  		if payloadLen > msg.MaxPayloadLength(0) {
    58  			t.Fatalf("msg payload constraint violated: %v > %v",
    59  				payloadLen, msg.MaxPayloadLength(0))
    60  			return false
    61  		}
    62  
    63  		// Finally, we'll deserialize the message from the written
    64  		// buffer, and finally assert that the messages are equal.
    65  		newMsg, err := wtwire.ReadMessage(&b, 0)
    66  		if err != nil {
    67  			t.Fatalf("unable to read msg: %v", err)
    68  			return false
    69  		}
    70  		if !reflect.DeepEqual(msg, newMsg) {
    71  			t.Fatalf("messages don't match after re-encoding: %v "+
    72  				"vs %v", spew.Sdump(msg), spew.Sdump(newMsg))
    73  			return false
    74  		}
    75  
    76  		return true
    77  	}
    78  
    79  	customTypeGen := map[wtwire.MessageType]func([]reflect.Value, *rand.Rand){
    80  		wtwire.MsgInit: func(v []reflect.Value, r *rand.Rand) {
    81  			req := wtwire.NewInitMessage(
    82  				randRawFeatureVector(r),
    83  				randChainHash(r),
    84  			)
    85  
    86  			v[0] = reflect.ValueOf(*req)
    87  		},
    88  	}
    89  
    90  	// With the above types defined, we'll now generate a slice of
    91  	// scenarios to feed into quick.Check. The function scans in input
    92  	// space of the target function under test, so we'll need to create a
    93  	// series of wrapper functions to force it to iterate over the target
    94  	// types, but re-use the mainScenario defined above.
    95  	tests := []struct {
    96  		msgType  wtwire.MessageType
    97  		scenario interface{}
    98  	}{
    99  		{
   100  			msgType: wtwire.MsgInit,
   101  			scenario: func(m wtwire.Init) bool {
   102  				return mainScenario(&m)
   103  			},
   104  		},
   105  		{
   106  			msgType: wtwire.MsgCreateSession,
   107  			scenario: func(m wtwire.CreateSession) bool {
   108  				return mainScenario(&m)
   109  			},
   110  		},
   111  		{
   112  			msgType: wtwire.MsgCreateSessionReply,
   113  			scenario: func(m wtwire.CreateSessionReply) bool {
   114  				return mainScenario(&m)
   115  			},
   116  		},
   117  		{
   118  			msgType: wtwire.MsgStateUpdate,
   119  			scenario: func(m wtwire.StateUpdate) bool {
   120  				return mainScenario(&m)
   121  			},
   122  		},
   123  		{
   124  			msgType: wtwire.MsgStateUpdateReply,
   125  			scenario: func(m wtwire.StateUpdateReply) bool {
   126  				return mainScenario(&m)
   127  			},
   128  		},
   129  		{
   130  			msgType: wtwire.MsgDeleteSession,
   131  			scenario: func(m wtwire.DeleteSession) bool {
   132  				return mainScenario(&m)
   133  			},
   134  		},
   135  		{
   136  			msgType: wtwire.MsgDeleteSessionReply,
   137  			scenario: func(m wtwire.DeleteSessionReply) bool {
   138  				return mainScenario(&m)
   139  			},
   140  		},
   141  		{
   142  			msgType: wtwire.MsgError,
   143  			scenario: func(m wtwire.Error) bool {
   144  				return mainScenario(&m)
   145  			},
   146  		},
   147  	}
   148  	for _, test := range tests {
   149  		var config *quick.Config
   150  
   151  		// If the type defined is within the custom type gen map above,
   152  		// the we'll modify the default config to use this Value
   153  		// function that knows how to generate the proper types.
   154  		if valueGen, ok := customTypeGen[test.msgType]; ok {
   155  			config = &quick.Config{
   156  				Values: valueGen,
   157  			}
   158  		}
   159  
   160  		t.Logf("Running fuzz tests for msgType=%v", test.msgType)
   161  		if err := quick.Check(test.scenario, config); err != nil {
   162  			t.Fatalf("fuzz checks for msg=%v failed: %v",
   163  				test.msgType, err)
   164  		}
   165  	}
   166  
   167  }
   168  
   169  func init() {
   170  	rand.Seed(time.Now().Unix())
   171  }