Example #1
0
// 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)
			}
		}
	}
}
Example #2
0
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
}