// Get multiplexes all messages from TCPHost using application logic func (sn *Node) ProcessMessages() error { dbg.Lvl4(sn.Name(), "getting") defer dbg.Lvl4(sn.Name(), "done getting") sn.UpdateTimeout() dbg.Lvl4("Going to get", sn.Name()) msgchan := sn.Host.GetNetworkMessg() // heartbeat for intiating viewChanges, allows intial 500s setup time /* sn.hbLock.Lock() sn.heartbeat = time.NewTimer(500 * time.Second) sn.hbLock.Unlock() */ // gossip to make sure we are up to date sn.StartGossip() errReset := syscall.ECONNRESET.Error() for { select { case <-sn.closed: dbg.Lvl3("Received closed-message through channel") sn.StopHeartbeat() return nil default: dbg.Lvl4(sn.Name(), "waiting for message") nm, ok := <-msgchan err := nm.Err errStr := "" if err != nil { errStr = err.Error() } // One of the errors doesn't have an error-number applied, so we need // to check for the string - will probably be fixed in go 1.6 if !ok || err == coconet.ErrClosed || err == io.EOF || err == io.ErrClosedPipe { dbg.Lvl3(sn.Name(), "getting from closed host") sn.Close() return coconet.ErrClosed } // if it is a non-fatal error try again if err != nil { if strings.Contains(errStr, errReset) { dbg.Lvl2(sn.Name(), "connection reset error") return coconet.ErrClosed } dbg.Lvl1(sn.Name(), "error getting message (still continuing)", err) continue } // interpret network message as Signing Message sm := nm.Data.(*SigningMessage) sm.From = nm.From dbg.Lvlf4("Message on %s is type %s and %+v", sn.Name(), sm.Type, sm) switch sm.Type { // if it is a bad message just ignore it default: continue case Announcement: dbg.Lvl3(sn.Name(), "got announcement") sn.ReceivedHeartbeat(sm.ViewNbr) var err error if sm.Am.Vote != nil { err = sn.Propose(sm.ViewNbr, sm.RoundNbr, sm.Am, sm.From) dbg.Lvl4(sn.Name(), "done proposing") } else { if !sn.IsParent(sm.ViewNbr, sm.From) { log.Fatalln(sn.Name(), "received announcement from non-parent on view", sm.ViewNbr) continue } err = sn.Announce(sm) } if err != nil { dbg.Error(sn.Name(), "announce error:", err) } // if it is a commitment or response it is from the child case Commitment: dbg.Lvl3(sn.Name(), "got commitment") if !sn.IsChild(sm.ViewNbr, sm.From) { log.Fatalln(sn.Name(), "received commitment from non-child on view", sm.ViewNbr) continue } var err error if sm.Com.Vote != nil { err = sn.Promise(sm.ViewNbr, sm.RoundNbr, sm) } else { err = sn.Commit(sm) } if err != nil { dbg.Error(sn.Name(), "commit error:", err) } case Challenge: dbg.Lvl3(sn.Name(), "got challenge") if !sn.IsParent(sm.ViewNbr, sm.From) { log.Fatalln(sn.Name(), "received challenge from non-parent on view", sm.ViewNbr) continue } sn.ReceivedHeartbeat(sm.ViewNbr) var err error if sm.Chm.Vote != nil { err = sn.Accept(sm.ViewNbr, sm.RoundNbr, sm.Chm) } else { err = sn.Challenge(sm) } if err != nil { dbg.Error(sn.Name(), "challenge error:", err) } case Response: dbg.Lvl3(sn.Name(), "received response from", sm.From) if !sn.IsChild(sm.ViewNbr, sm.From) { log.Fatalln(sn.Name(), "received response from non-child on view", sm.ViewNbr) continue } var err error if sm.Rm.Vote != nil { err = sn.Accepted(sm.ViewNbr, sm.RoundNbr, sm) } else { err = sn.Respond(sm) } if err != nil { dbg.Error(sn.Name(), "response error:", err) } case SignatureBroadcast: dbg.Lvl3(sn.Name(), "received SignatureBroadcast", sm.From) sn.ReceivedHeartbeat(sm.ViewNbr) err = sn.SignatureBroadcast(sm) case StatusReturn: sn.StatusReturn(sm.ViewNbr, sm) case CatchUpReq: v := sn.VoteLog.Get(sm.Cureq.Index) ctx := context.TODO() sn.PutTo(ctx, sm.From, &SigningMessage{ Suite: sn.Suite().String(), From: sn.Name(), Type: CatchUpResp, //LastSeenVote: int(atomic.LoadInt64(&sn.LastSeenVote)), Curesp: &CatchUpResponse{Vote: v}}) case CatchUpResp: if sm.Curesp.Vote == nil || sn.VoteLog.Get(sm.Curesp.Vote.Index) != nil { continue } vi := sm.Curesp.Vote.Index // put in votelog to be streamed and applied sn.VoteLog.Put(vi, sm.Curesp.Vote) // continue catching up sn.CatchUp(vi+1, sm.From) case GroupChange: if sm.ViewNbr == -1 { sm.ViewNbr = sn.ViewNo if sm.Vrm.Vote.Type == AddVT { sn.AddPeerToPending(sm.From) } } // TODO sanity checks: check if view is == sn.ViewNo if sn.RootFor(sm.ViewNbr) == sn.Name() { dbg.Fatal("Group change not implementekd. BTH") //go sn.StartVotingRound(sm.Vrm.Vote) continue } sn.PutUp(context.TODO(), sm.ViewNbr, sm) case GroupChanged: if !sm.Gcm.V.Confirmed { dbg.Lvl4(sn.Name(), " received attempt to group change not confirmed") continue } if sm.Gcm.V.Type == RemoveVT { dbg.Lvl4(sn.Name(), " received removal notice") } else if sm.Gcm.V.Type == AddVT { dbg.Lvl4(sn.Name(), " received addition notice") sn.NewView(sm.ViewNbr, sm.From, nil, sm.Gcm.HostList) } else { log.Errorln(sn.Name(), "received GroupChanged for unacceptable action") } case StatusConnections: sn.ReceivedHeartbeat(sm.ViewNbr) err = sn.StatusConnections(sm.ViewNbr, sm.Am) case CloseAll: sn.ReceivedHeartbeat(sm.ViewNbr) err = sn.CloseAll(sm.ViewNbr) return nil case Error: dbg.Lvl4("Received Error Message:", errors.New("received message of unknown type"), sm, sm.Err) } } } }
func (sn *Node) Announce(sm *SigningMessage) error { view := sm.ViewNbr RoundNbr := sm.RoundNbr am := sm.Am dbg.Lvl4(sn.Name(), "received announcement on", view) var round Round round = sn.Rounds[RoundNbr] if round == nil { if am == nil { return fmt.Errorf("Got a nil announcement on a non root nde?") } sn.LastSeenRound = max(sn.LastSeenRound, RoundNbr) rtype := am.RoundType // create the new round and save it dbg.Lvl3(sn.Name(), "Creating new round-type", rtype) r, err := NewRoundFromType(rtype, sn) if err != nil { dbg.Lvl3(sn.Name(), "Error getting new round in announcement") return err } sn.Rounds[RoundNbr] = r round = r } nChildren := sn.NChildren(view) out := make([]*SigningMessage, nChildren) for i := range out { out[i] = &SigningMessage{ Suite: sn.Suite().String(), Type: Announcement, ViewNbr: sn.ViewNo, //LastSeenVote: int(atomic.LoadInt64(&sn.LastSeenVote)), RoundNbr: RoundNbr, Am: &AnnouncementMessage{ Message: make([]byte, 0), RoundType: sm.Am.RoundType, }, } } err := round.Announcement(view, RoundNbr, sm, out) if err != nil { dbg.Lvl3(sn.Name(), "Error on announcement", err) return err } if len(sn.Children(view)) == 0 { // If we are a leaf, start the commit phase process sn.Commit(&SigningMessage{ Suite: sn.Suite().String(), Type: Commitment, RoundNbr: RoundNbr, ViewNbr: view, }) } else { // Transform the AnnouncementMessages to SigningMessages to send to the // Children msgs_bm := make([]coconet.BinaryMarshaler, nChildren) for i := range msgs_bm { msgs_bm[i] = out[i] } // And sending to all our children-nodes dbg.Lvlf4("%s sending to all children", sn.Name()) ctx := context.TODO() if err := sn.PutDown(ctx, view, msgs_bm); err != nil { return err } } return nil }