// Create and initialize a state object. Set everything to default. func CreateState(messageRouter common.MessageRouter) (s *State, err error) { // check that we have a non-nil messageSender if messageRouter == nil { err = fmt.Errorf("Cannot initialize with a nil messageRouter") return } // create a signature keypair for this state pubKey, secKey, err := crypto.CreateKeyPair() if err != nil { return } // initialize State with default values and keypair s = &State{ messageRouter: messageRouter, self: &Participant{ index: 255, address: messageRouter.Address(), publicKey: pubKey, }, secretKey: secKey, currentStep: 1, } // register State and store our assigned ID s.self.address.ID = messageRouter.RegisterHandler(s) // a call to joinSia() may be placed here... behavior not fully defined return }
// Create and initialize a state object. func CreateState(messageRouter common.MessageRouter) (s *State, err error) { s = new(State) // check that we have a non-nil messageSender if messageRouter == nil { err = fmt.Errorf("Cannot initialize with a nil messageRouter") return } // create a signature keypair for this state pubKey, secKey, err := crypto.CreateKeyPair() if err != nil { return } // calculate the value of an empty hash (default for storedEntropyStage2 on all hosts is a blank array) emptyHash, err := crypto.CalculateTruncatedHash(s.storedEntropyStage2[:]) if err != nil { return } // set state variables to their defaults s.messageRouter = messageRouter s.self = new(participant) s.self.address = messageRouter.AddMessageHandler(s) s.self.publicKey = pubKey s.secretKey = secKey for i := range s.previousEntropyStage1 { s.previousEntropyStage1[i] = emptyHash } s.participantIndex = 255 s.currentStep = 1 s.wallets = make(map[string]uint64) return }
func TestParticipantEncoding(t *testing.T) { // Try nil values var p *Participant _, err := p.GobEncode() if err == nil { t.Error("Encoded nil participant without error") } p = new(Participant) _, err = p.GobEncode() if err == nil { t.Fatal("Should not be able to encode nil values") } // Make a bootstrap participant pubKey, _, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } p.publicKey = pubKey p.address = bootstrapAddress up := new(Participant) ep, err := p.GobEncode() if err != nil { t.Fatal(err) } err = up.GobDecode(ep) if err != nil { t.Fatal(err) } if up.address != p.address { t.Error("up.address != p.address") } compare := up.publicKey.Compare(p.publicKey) if compare != true { t.Error("up.PublicKey != p.PublicKey") } // try to decode into nil participant up = nil err = up.GobDecode(ep) if err == nil { t.Error("decoded into nil participant without error") } }
// Create and initialize a state object func CreateState(messageSender common.MessageSender, participantIndex participantIndex) (s State, err error) { // check that we have a non-nil messageSender if messageSender == nil { err = fmt.Errorf("Cannot initialize with a nil messageSender") return } // check that participantIndex is legal if int(participantIndex) >= common.QuorumSize { err = fmt.Errorf("Invalid participant index!") return } // initialize crypto keys pubKey, secKey, err := crypto.CreateKeyPair() if err != nil { return } // create and fill out the participant object self := new(Participant) self.Address = messageSender.Address() self.Address.Id = common.Identifier(participantIndex) self.PublicKey = pubKey // calculate the value of an empty hash (default for storedEntropyStage2 on all hosts is a blank array) emptyHash, err := crypto.CalculateTruncatedHash(s.storedEntropyStage2[:]) if err != nil { return } // set state variables to their defaults s.messageSender = messageSender s.AddParticipant(self, participantIndex) s.secretKey = secKey for i := range s.previousEntropyStage1 { s.previousEntropyStage1[i] = emptyHash } s.participantIndex = participantIndex s.currentStep = 1 s.wallets = make(map[string]uint64) return }
func TestParticipantCompare(t *testing.T) { var p0 *Participant var p1 *Participant // compare nil values compare := p0.compare(p1) if compare == true { t.Error("Comparing any nil participant should return false") } // compare when one is nil p0 = new(Participant) compare = p0.compare(p1) if compare == true { t.Error("Comparing a zero participant to a nil participant should return false") } compare = p1.compare(p0) if compare == true { t.Error("Comparing a zero participant to a nil participant should return false") } // initialize each participant with a public key p1 = new(Participant) pubKey, _, err := crypto.CreateKeyPair() if err != nil { t.Fatal(err) } p1.publicKey = pubKey p0.publicKey = new(crypto.PublicKey) *p0.publicKey = *p1.publicKey // compare initialized participants compare = p0.compare(p1) if !compare { t.Error("Comparing two zero participants should return true") } compare = p1.compare(p0) if !compare { t.Error("Comparing two zero participants should return true") } // compare when address are not equal p1.address.Port = 9987 compare = p0.compare(p1) if compare { t.Error("Comparing two participants with different addresses should return false") } compare = p1.compare(p0) if compare { t.Error("Comparing two zero participants with different addresses should return false") } // compare when public keys are not equivalent pubKey, _, err = crypto.CreateKeyPair() if err != nil { t.Fatal(err) } p1.publicKey = pubKey compare = p0.compare(p1) if compare == true { t.Error("Comparing two participants with different public keys should return false") } compare = p1.compare(p0) if compare == true { t.Error("Comparing two participants with different public keys should return false") } }
// 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) }
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) }