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 }