// Check that valid heartbeats are accepted and invalid heartbeats are rejected func TestProcessHeartbeat(t *testing.T) { // create states and add them to each other s0, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } s1, err := CreateState(common.NewZeroNetwork(), 1) if err != nil { t.Fatal(err) } s0.AddParticipant(s1.Self(), 1) s1.AddParticipant(s0.Self(), 0) // check that a valid heartbeat passes hb0, err := s0.newHeartbeat() if err != nil { t.Fatal(err) } returnCode := s1.processHeartbeat(hb0, 0) if returnCode != 0 { t.Fatal("processHeartbeat threw out a valid heartbeat") } // check that invalid entropy fails hb1, err := s1.newHeartbeat() if err != nil { t.Fatal(err) } hb1.entropyStage2[0] = 1 returnCode = s0.processHeartbeat(hb1, 1) if returnCode != 1 { t.Fatal("processHeartbeat accepted an invalid heartbeat") } }
// Create a state, check the defaults func TestCreateState(t *testing.T) { // does a state create without errors? s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } // check that previousEntropyStage1 is initialized correctly var emptyEntropy common.Entropy emptyHash, err := crypto.CalculateTruncatedHash(emptyEntropy[:]) if err != nil { t.Fatal(err) } for i := range s.previousEntropyStage1 { if s.previousEntropyStage1[i] != emptyHash { t.Error("previousEntropyStage1 initialized incorrectly at index ", i) } } // sanity check the default values if s.participantIndex != 255 { t.Error("s.participantIndex initialized to ", s.participantIndex) } if s.currentStep != 1 { t.Error("s.currentStep should be initialized to 1!") } if s.wallets == nil { t.Error("s.wallets was not initialized") } }
// Marshalling and Unmarshalling should result in equivalent Heartbeats func TestHeartbeatMarshalling(t *testing.T) { s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // verify that a heartbeat, once unmarshalled, is identical to the original hbOriginal, err := s.newHeartbeat() if err != nil { t.Fatal(err) } hbMarshalled := hbOriginal.marshal() hbUnmarshalled, err := unmarshalHeartbeat(hbMarshalled) if err != nil { t.Fatal(err) } if hbOriginal.entropyStage1 != hbUnmarshalled.entropyStage1 { t.Fatal("EntropyStage1 not identical upon umarshalling") } if hbOriginal.entropyStage2 != hbUnmarshalled.entropyStage2 { t.Fatal("EntropyStage1 not identical upon umarshalling") } // verify that input is being checked for UnmarshalHeartbeat _, err = unmarshalHeartbeat(hbMarshalled[1:]) if err == nil { t.Fatal("Heartbeat unmarshalling succeded with a short input") } _, err = unmarshalHeartbeat(append(hbMarshalled, hbMarshalled...)) if err == nil { t.Fatal("Heartbeat unmarshalling succeded with a long input") } }
// verify that one state can add another func TestAddParticipant(t *testing.T) { s0, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } s1, err := CreateState(common.NewZeroNetwork(), 1) if err != nil { t.Fatal(err) } err = s0.AddParticipant(s1.Self(), 1) if err != nil { t.Fatal(err) } // check that participant 1 was added to state 0 if s1.participants[s1.participantIndex].PublicKey != s0.participants[1].PublicKey { t.Fatal("AddParticipant failed!") } }
// Bootstrap a state to the network, then another func TestJoinQuorum(t *testing.T) { // Make a new state and network; start bootstrapping z := common.NewZeroNetwork() s0, err := CreateState(z) if err != nil { t.Fatal(err) } s0.JoinSia() // Verify the message for correctness // Forward message to bootstrap State (ourselves, as it were) s0.HandleMessage(z.RecentMessage(0).Payload) // Verify that a broadcast message went out indicating a new participant // Forward message to recipient s0.HandleMessage(z.RecentMessage(1).Payload) // Verify that we started ticking s0.tickingLock.Lock() if !s0.ticking { t.Error("Bootstrap state not ticking after joining Sia") } s0.tickingLock.Unlock() // Create a new state to bootstrap s1, err := CreateState(z) if err != nil { t.Fatal(err) } s1.JoinSia() // Verify message for correctness // Deliver message to bootstrap s0.HandleMessage(z.RecentMessage(2).Payload) // Deliver the broadcasted messages s0.HandleMessage(z.RecentMessage(3).Payload) s1.HandleMessage(z.RecentMessage(4).Payload) // Verify the messages made it s1.tickingLock.Lock() if !s1.ticking { t.Error("s1 did not start ticking") } // both swarms should be aware of each other... maybe test their ongoing interactions? }
// Check that valid heartbeats are accepted and invalid heartbeats are rejected func TestProcessHeartbeat(t *testing.T) { // create states and add them to each other s0, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } s1, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } s0.AddNewParticipant(*s1.self, nil) s1.AddNewParticipant(*s0.self, nil) // check that a valid heartbeat passes hb0, err := s0.newHeartbeat() if err != nil { t.Fatal(err) } err = s1.processHeartbeat(hb0, 0) if err != nil { t.Error("processHeartbeat threw out a valid heartbeat: ", err) } }
// Create a state, check the defaults func TestCreateState(t *testing.T) { // make sure CreateState does not cause errors s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } // sanity check the default values if s.self.index != 255 { t.Error("s.self.index initialized to ", s.self.index) } if s.currentStep != 1 { t.Error("s.currentStep should be initialized to 1!") } }
// check general case, check corner cases, and then do some fuzzing func TestRandInt(t *testing.T) { s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } // check that it works in the vanilla case previousEntropy := s.currentEntropy randInt, err := s.randInt(0, 5) if err != nil { t.Fatal(err) } if randInt < 0 || randInt >= 5 { t.Fatal("randInt returned but is not between the bounds") } // check that s.CurrentEntropy flipped to next value if previousEntropy == s.currentEntropy { t.Error(previousEntropy) t.Error(s.currentEntropy) t.Fatal("When calling randInt, s.CurrentEntropy was not changed") } // check the zero value randInt, err = s.randInt(0, 0) if err == nil { t.Fatal("Randint(0,0) should return a bounds error") } // fuzzing, skip for short tests if testing.Short() { t.Skip() } low := 0 high := common.QuorumSize for i := 0; i < 100000; i++ { randInt, err = s.randInt(low, high) if err != nil { t.Fatal("randInt fuzzing error: ", err, " low: ", low, " high: ", high) } if randInt < low || randInt >= high { t.Fatal("randInt fuzzing: ", randInt, " produced, expected number between ", low, " and ", high) } } }
// quick sanity check func TestCreateState(t *testing.T) { // create a state s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // verify that the keys can sign and be verified err = crypto.CheckKeys(s.participants[s.participantIndex].PublicKey, s.secretKey) if err != nil { t.Fatal(err) } // sanity check CurrentStep if s.currentStep != 1 { t.Fatal("Current step should be initialized to 1!") } }
// Verify that newHeartbeat() produces valid heartbeats func TestnewHeartbeat(t *testing.T) { // create a state, and then a heartbeat s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } hb, err := s.newHeartbeat() if err != nil { t.Fatal(err) } // verify that entropy is being properly generated when making the heartbeat storedEntropyHash, err := crypto.CalculateTruncatedHash(s.storedEntropyStage2[:]) if err != nil { t.Fatal(err) } else if hb.entropyStage1 != storedEntropyHash { t.Fatal("newHeartbeat() incorrectly producing EntropyStage1 from s.StoredEntropyStage2") } }
// Ensures that Tick() updates CurrentStep func TestRegularTick(t *testing.T) { // test takes common.StepDuration seconds; skip for short testing if testing.Short() { t.Skip() } s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // verify that tick is updating CurrentStep s.currentStep = 1 go s.tick() time.Sleep(common.StepDuration) time.Sleep(time.Second) s.lock.Lock() if s.currentStep != 2 { t.Fatal("s.currentStep failed to update correctly: ", s.currentStep) } s.lock.Unlock() }
// ensures Tick() calles compile() and then resets the counter to step 1 func TestCompilationTick(t *testing.T) { // test takes common.StepDuration seconds; skip for short testing if testing.Short() { t.Skip() } // create state and give it a heartbeat to prevent it from pruning itself s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } hb, err := s.newHeartbeat() if err != nil { return } heartbeatHash, err := crypto.CalculateTruncatedHash([]byte(hb.marshal())) s.heartbeats[s.participantIndex][heartbeatHash] = hb // remember entropy to verify that compile() gets called currentEntropy := s.currentEntropy // verify that tick is wrapping around properly s.currentStep = common.QuorumSize go s.tick() time.Sleep(common.StepDuration) time.Sleep(time.Second) s.lock.Lock() if s.currentStep != 1 { t.Fatal("s.currentStep failed to roll over: ", s.currentStep) } // check if s.compile() got called if currentEntropy == s.currentEntropy { t.Fatal("Entropy did not change after tick wrapped around") } s.lock.Unlock() }
func TestSignedHeartbeatEncoding(t *testing.T) { // Test for bad inputs var bad *SignedHeartbeat bad = nil _, err := bad.GobEncode() if err == nil { t.Error("Should not encode a nil signedHeartbeat") } err = bad.GobDecode(nil) if err == nil { t.Error("Should not be able to decode a nil byte slice") } // Test the encoding and decoding of a simple signed heartbeat s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } hb, err := s.newHeartbeat() if err != nil { t.Fatal(err) } sh, err := s.signHeartbeat(hb) if err != nil { t.Fatal(err) } msh, err := sh.GobEncode() if err != nil { t.Fatal(err) } ush := new(SignedHeartbeat) err = ush.GobDecode(msh) if err != nil { t.Fatal(err) } // check encoding and decoding of a signedHeartbeat with many signatures }
// ensures Tick() calles compile() and then resets the counter to step 1 func TestCompilationTick(t *testing.T) { // test takes common.StepDuration seconds; skip for short testing if testing.Short() { t.Skip() } // create state, set values for compile s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } s.currentStep = common.QuorumSize go s.tick() // verify that tick is wrapping around properly time.Sleep(common.StepDuration) time.Sleep(time.Second) s.stepLock.Lock() if s.currentStep != 1 { t.Error("s.currentStep failed to roll over: ", s.currentStep) } s.stepLock.Unlock() }
// TestTickLock verifies that only one instance of Tick() can run at a time func TestTickLock(t *testing.T) { // test takes common.StepDuration seconds; skip for short testing if testing.Short() { t.Skip() } // create state s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // call tick twice go s.tick() go s.tick() time.Sleep(common.StepDuration) time.Sleep(time.Second) // if two instances of Tick() are running, s.CurrentStep will update twice s.lock.Lock() if s.currentStep != 2 { t.Fatal("Double tick failed: ", s.currentStep) } s.lock.Unlock() }
// a SignedHeartbeat should be the same after marshalling and unmarshalling func TestSignedHeartbeatMarshalling(t *testing.T) { s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // create a SignedHeartbeat, marshall and unmarshall it, and test equivalency hb, err := s.newHeartbeat() if err != nil { t.Fatal(err) } originalSignedHeartbeat, err := s.signHeartbeat(hb) if err != nil { t.Fatal(err) } marshalledSH, err := originalSignedHeartbeat.marshal() if err != nil { t.Fatal(err) } unmarshalledSH, err := unmarshalSignedHeartbeat(marshalledSH) if string(unmarshalledSH.heartbeat.marshal()) != string(originalSignedHeartbeat.heartbeat.marshal()) { t.Fatal("Heartbeat changed after being marshalled and unmarshalled") } if unmarshalledSH.heartbeatHash != originalSignedHeartbeat.heartbeatHash { t.Fatal("HeartbeatHash changed after being marshalled and unmarshalled") } if len(unmarshalledSH.signatures) != len(originalSignedHeartbeat.signatures) { t.Fatal("Length of SignedHeartbeat.Signatures changed after being marshalled and unmarshalled") } if len(unmarshalledSH.signatories) != len(originalSignedHeartbeat.signatories) { t.Fatal("Length of SignedHeartbeat.Signatories changed after being marshalled and unmarshalled") } for i := 0; i < len(unmarshalledSH.signatures); i++ { if unmarshalledSH.signatures[i] != originalSignedHeartbeat.signatures[i] { t.Fatal("For i=", i, ", unmarshalledSH.Signatures[i] did not equal originalSignedHeartbeat.Signatures[i]") } if unmarshalledSH.signatories[i] != originalSignedHeartbeat.signatories[i] { t.Fatal("For i=", i, ", unmarshalledSH.Signatories[i] did not equal originalSignedHeartbeat.Signatories[i]") } } // verify that input is being checked for SignedHeartbeat.Marshal() originalSignedHeartbeat.signatures = make([]crypto.Signature, 2*common.QuorumSize) _, err = originalSignedHeartbeat.marshal() if err == nil { t.Fatal("SignedHeartbeat.Marshal() needs to check the length of msh.Signatures") } originalSignedHeartbeat.signatures = make([]crypto.Signature, 2) if err == nil { t.Fatal("SignedHeartbeat.Marshal() needs to check that msh.Signatures and msh.Signatories are equal in length") } // verify that input is being checked for UnmarshalSignedHeartbeat() _, err = unmarshalSignedHeartbeat(marshalledSH[:marshalledHeartbeatLen()]) if err == nil { t.Fatal("UnmarshalSignedHeartbeat not checking input length") } _, err = unmarshalSignedHeartbeat(marshalledSH[1:]) if err == nil { t.Fatal("UnmarshalSignedHeartbeat succeeded when input was too short") } _, err = unmarshalSignedHeartbeat(append(marshalledSH, marshalledSH...)) if err == nil { t.Fatal("UnmarshalSignedHeartbeat succeded when input was too long") } }
// TestCompile should probably be reviewed and rehashed func TestCompile(t *testing.T) { // Create states and add them to eachother as participants s0, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } s1, err := CreateState(common.NewZeroNetwork(), 1) if err != nil { t.Fatal(err) } s2, err := CreateState(common.NewZeroNetwork(), 2) if err != nil { t.Fatal(err) } s0.AddParticipant(s1.Self(), 1) s0.AddParticipant(s2.Self(), 2) // fetch legal heartbeat for s0 hb0, err := s0.newHeartbeat() if err != nil { t.Fatal(err) } shb0, err := s0.signHeartbeat(hb0) if err != nil { t.Fatal(err) } // fetch legal heartbeat for s2 hb2a, err := s2.newHeartbeat() if err != nil { t.Fatal(err) } shb2a, err := s2.signHeartbeat(hb2a) if err != nil { t.Fatal(err) } // create a second illegal heartbeat for s2 var hb2b heartbeat hb2b.entropyStage2 = hb2a.entropyStage2 shb2b, err := s2.signHeartbeat(&hb2b) if err != nil { t.Fatal(err) } // send the SignedHeartbeats to s0 mshb0, err := shb0.marshal() if err != nil { t.Fatal(err) } returnCode := s0.handleSignedHeartbeat(mshb0) if returnCode != 0 { t.Fatal("Expecting shb0 to be valid: ", returnCode) } mshb2a, err := shb2a.marshal() if err != nil { t.Fatal(err) } returnCode = s0.handleSignedHeartbeat(mshb2a) if returnCode != 0 { t.Fatal("Expecting shb2a to be valid: ", returnCode) } mshb2b, err := shb2b.marshal() if err != nil { t.Fatal(err) } returnCode = s0.handleSignedHeartbeat(mshb2b) if returnCode != 0 { t.Fatal("Expecting shb2b to be valid: ", returnCode) } s0.compile() // check that hosts arrive at the same participantOrdering participantOrdering1 := s1.participantOrdering() participantOrdering2 := s2.participantOrdering() if participantOrdering1 != participantOrdering2 { t.Fatal("partcipantOrderings for s1 and s2 are not identical!") } // verify that upon processing, s0 is not thrown from s0, and is processed correctly if s0.participants[0] == nil { t.Fatal("s0 thrown from s0 despite having a fair heartbeat") } // verify that upon processing, s1 is thrown from s0 (doesn't have heartbeat) if s0.participants[1] != nil { t.Fatal("s1 not thrown from s0 despite having no heartbeats") } // verify that upon processing, s3 is thrown from s0 (too many heartbeats) if s0.participants[2] != nil { t.Fatal("s2 not thrown from s0 despite having multiple heartbeats") } // verify that a new heartbeat was made, formatted into a SignedHeartbeat, and sent off }
// TestHandleSignedHeartbeat should probably be reviewed and rehashed func TestHandleSignedHeartbeat(t *testing.T) { // create a state and populate it with the signatories as participants s, err := CreateState(common.NewZeroNetwork(), 0) if err != nil { t.Fatal(err) } // create keypairs pubKey1, secKey1, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } pubKey2, secKey2, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } // create participants and add them to s p1 := new(Participant) p2 := new(Participant) p1.PublicKey = pubKey1 p2.PublicKey = pubKey2 s.AddParticipant(p1, 1) s.AddParticipant(p2, 2) // create SignedHeartbeat var sh signedHeartbeat sh.heartbeat, err = s.newHeartbeat() if err != nil { t.Fatal(err) } sh.heartbeatHash, err = crypto.CalculateTruncatedHash([]byte(sh.heartbeat.marshal())) if err != nil { t.Fatal(err) } sh.signatures = make([]crypto.Signature, 2) sh.signatories = make([]participantIndex, 2) // Create a set of signatures for the SignedHeartbeat signature1, err := crypto.Sign(secKey1, string(sh.heartbeatHash[:])) if err != nil { t.Fatal("error signing HeartbeatHash") } signature2, err := crypto.Sign(secKey2, signature1.CombinedMessage()) if err != nil { t.Fatal("error with second signing") } // build a valid SignedHeartbeat sh.signatures[0] = signature1.Signature sh.signatures[1] = signature2.Signature sh.signatories[0] = 1 sh.signatories[1] = 2 // handle the signed heartbeat, expecting code 0 msh, err := sh.marshal() if err != nil { t.Fatal(err) } returnCode := s.handleSignedHeartbeat(msh) if returnCode != 0 { t.Fatal("expected heartbeat to succeed:", returnCode) } // verify that a repeat heartbeat gets ignored msh, err = sh.marshal() if err != nil { t.Fatal(err) } returnCode = s.handleSignedHeartbeat(msh) if returnCode != 8 { t.Fatal("expected heartbeat to get ignored as a duplicate:", returnCode) } // create a different heartbeat, this will be used to test the fail conditions sh.heartbeat, err = s.newHeartbeat() if err != nil { t.Fatal(err) } sh.heartbeatHash, err = crypto.CalculateTruncatedHash([]byte(sh.heartbeat.marshal())) if err != nil { t.Fatal(err) } // verify a heartbeat with bad signatures is rejected msh, err = sh.marshal() if err != nil { t.Fatal(err) } returnCode = s.handleSignedHeartbeat(msh) if returnCode != 6 { t.Fatal("expected heartbeat to get ignored as having invalid signatures: ", returnCode) } // give heartbeat repeat signatures signature1, err = crypto.Sign(secKey1, string(sh.heartbeatHash[:])) if err != nil { t.Fatal("error with third signing") } signature2, err = crypto.Sign(secKey1, signature1.CombinedMessage()) if err != nil { t.Fatal("error with fourth signing") } // adjust signatories slice sh.signatures[0] = signature1.Signature sh.signatures[1] = signature2.Signature sh.signatories[0] = 1 sh.signatories[1] = 1 // verify repeated signatures are rejected msh, err = sh.marshal() if err != nil { t.Fatal(err) } returnCode = s.handleSignedHeartbeat(msh) if returnCode != 5 { t.Fatal("expected heartbeat to be rejected for duplicate signatures: ", returnCode) } // remove second signature sh.signatures = sh.signatures[:1] sh.signatories = sh.signatories[:1] // handle heartbeat when tick is larger than num signatures s.currentStep = 2 msh, err = sh.marshal() if err != nil { t.Fatal(err) } returnCode = s.handleSignedHeartbeat(msh) if returnCode != 2 { t.Fatal("expected heartbeat to be rejected as out-of-sync: ", returnCode) } // send a heartbeat right at the edge of a new block // test takes time; skip in short tests if testing.Short() { t.Skip() } // put block at edge s.currentStep = common.QuorumSize // submit heartbeat in separate thread go func() { msh, err = sh.marshal() if err != nil { t.Fatal(err) } returnCode = s.handleSignedHeartbeat(msh) if returnCode != 0 { t.Fatal("expected heartbeat to succeed!: ", returnCode) } }() time.Sleep(time.Second) }
// Bootstrap a state to the network, then another func TestJoinQuorum(t *testing.T) { // Make a new state and network; start bootstrapping z := common.NewZeroNetwork() s0, err := CreateState(z) if err != nil { t.Fatal(err) } err = s0.JoinSia() if err != nil { t.Fatal(err) } // Verify the message for correctness // Forward message to bootstrap State (ourselves, as it were) m := z.RecentMessage(0) if m == nil { t.Fatal("message 0 never received") } s0.HandleJoinSia(m.Args.(Participant), nil) // Verify that a broadcast message went out indicating a new participant // Forward message to recipient m = z.RecentMessage(1) if m == nil { t.Fatal("message 1 never received") } s0.AddNewParticipant(m.Args.(Participant), nil) // Verify that we started ticking s0.tickingLock.Lock() if !s0.ticking { t.Fatal("Bootstrap state not ticking after joining Sia") } s0.tickingLock.Unlock() // Verify that s0.self.index updated if s0.self.index == 255 { t.Error("Bootstrapping failed to update State.self.index") } // Create a new state to bootstrap s1, err := CreateState(z) if err != nil { t.Fatal(err) } s1.JoinSia() // Verify message for correctness // Deliver message to bootstrap m = z.RecentMessage(2) s0.HandleJoinSia(m.Args.(Participant), nil) // Deliver the broadcasted messages m = z.RecentMessage(3) s0.AddNewParticipant(m.Args.(Participant), nil) m = z.RecentMessage(4) s1.AddNewParticipant(m.Args.(Participant), nil) // Verify the messages made it s1.tickingLock.Lock() if !s1.ticking { t.Error("s1 did not start ticking") } // both swarms should be aware of each other... maybe test their ongoing interactions? }
func TestHandleSignedHeartbeat(t *testing.T) { // create a state and populate it with the signatories as participants s, err := CreateState(common.NewZeroNetwork()) if err != nil { t.Fatal(err) } // create keypairs pubKey1, secKey1, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } pubKey2, secKey2, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } // create participants and add them to s var p1 Participant var p2 Participant p1.index = 1 p2.index = 2 p1.publicKey = pubKey1 p2.publicKey = pubKey2 err = s.AddNewParticipant(p1, nil) if err != nil { t.Fatal(err) } s.AddNewParticipant(p2, nil) if err != nil { t.Fatal(err) } // create SignedHeartbeat var sh SignedHeartbeat sh.heartbeat, err = s.newHeartbeat() if err != nil { t.Fatal(err) } esh, err := sh.heartbeat.GobEncode() if err != nil { t.Fatal(err) } sh.heartbeatHash, err = crypto.CalculateTruncatedHash(esh) if err != nil { t.Fatal(err) } sh.signatures = make([]crypto.Signature, 2) sh.signatories = make([]byte, 2) // Create a set of signatures for the SignedHeartbeat signature1, err := secKey1.Sign(sh.heartbeatHash[:]) if err != nil { t.Fatal(err) } combinedMessage, err := signature1.CombinedMessage() if err != nil { t.Fatal(err) } signature2, err := secKey2.Sign(combinedMessage) if err != nil { t.Fatal(err) } // build a valid SignedHeartbeat sh.signatures[0] = signature1.Signature sh.signatures[1] = signature2.Signature sh.signatories[0] = 1 sh.signatories[1] = 2 // delete existing heartbeat from state; makes the remaining tests easier s.heartbeats[sh.signatories[0]] = make(map[crypto.TruncatedHash]*heartbeat) // handle the signed heartbeat, expecting nil error err = s.HandleSignedHeartbeat(sh, nil) if err != nil { t.Fatal(err) } // verify that a repeat heartbeat gets ignored err = s.HandleSignedHeartbeat(sh, nil) if err != hsherrHaveHeartbeat { t.Error("expected heartbeat to get ignored as a duplicate:", err) } // create a different heartbeat, this will be used to test the fail conditions sh.heartbeat, err = s.newHeartbeat() if err != nil { t.Fatal(err) } ehb, err := sh.heartbeat.GobEncode() if err != nil { t.Fatal(err) } sh.heartbeatHash, err = crypto.CalculateTruncatedHash(ehb) if err != nil { t.Fatal(err) } // verify a heartbeat with bad signatures is rejected err = s.HandleSignedHeartbeat(sh, nil) if err != hsherrInvalidSignature { t.Error("expected heartbeat to get ignored as having invalid signatures: ", err) } // verify that a non-participant gets rejected sh.signatories[0] = 3 err = s.HandleSignedHeartbeat(sh, nil) if err != hsherrNonParticipant { t.Error("expected non-participant to be rejected: ", err) } // give heartbeat repeat signatures signature1, err = secKey1.Sign(sh.heartbeatHash[:]) if err != nil { t.Fatal(err) } combinedMessage, err = signature1.CombinedMessage() if err != nil { t.Fatal(err) } signature2, err = secKey1.Sign(combinedMessage) if err != nil { t.Error(err) } // adjust signatories slice sh.signatures[0] = signature1.Signature sh.signatures[1] = signature2.Signature sh.signatories[0] = 1 sh.signatories[1] = 1 // verify repeated signatures are rejected err = s.HandleSignedHeartbeat(sh, nil) if err != hsherrDoubleSigned { t.Error("expected heartbeat to be rejected for duplicate signatures: ", err) } // remove second signature sh.signatures = sh.signatures[:1] sh.signatories = sh.signatories[:1] // handle heartbeat when tick is larger than num signatures s.stepLock.Lock() s.currentStep = 2 s.stepLock.Unlock() err = s.HandleSignedHeartbeat(sh, nil) if err != hsherrNoSync { t.Error("expected heartbeat to be rejected as out-of-sync: ", err) } // remaining tests require sleep if testing.Short() { t.Skip() } // send a heartbeat right at the edge of a new block s.stepLock.Lock() s.currentStep = common.QuorumSize s.stepLock.Unlock() // submit heartbeat in separate thread go func() { err = s.HandleSignedHeartbeat(sh, nil) if err != nil { t.Fatal("expected heartbeat to succeed!: ", err) } // need some way to verify with the test that the funcion gets here }() s.stepLock.Lock() s.currentStep = 1 s.stepLock.Unlock() time.Sleep(time.Second) time.Sleep(common.StepDuration) }