// Create a new Acknowledgement. Must be called by a leader. This // call assumes all the pieces are in place to create a new acknowledgement func (s *State) NewAck(msg interfaces.IMsg) interfaces.IMsg { vmIndex := msg.GetVMIndex() msg.SetLeaderChainID(s.IdentityChainID) ack := new(messages.Ack) ack.DBHeight = s.LLeaderHeight ack.VMIndex = vmIndex ack.Minute = byte(s.ProcessLists.Get(s.LLeaderHeight).VMs[vmIndex].LeaderMinute) ack.Timestamp = s.GetTimestamp() ack.SaltNumber = s.GetSalt(ack.Timestamp) copy(ack.Salt[:8], s.Salt.Bytes()[:8]) ack.MessageHash = msg.GetMsgHash() ack.LeaderChainID = s.IdentityChainID listlen := len(s.LeaderPL.VMs[vmIndex].List) if listlen == 0 { ack.Height = 0 ack.SerialHash = ack.MessageHash } else { last := s.LeaderPL.GetAckAt(vmIndex, listlen-1) ack.Height = last.Height + 1 ack.SerialHash, _ = primitives.CreateHash(last.MessageHash, ack.MessageHash) } ack.Sign(s) return ack }
func (s *State) executeMsg(vm *VM, msg interfaces.IMsg) (ret bool) { _, ok := s.Replay.Valid(constants.INTERNAL_REPLAY, msg.GetRepeatHash().Fixed(), msg.GetTimestamp(), s.GetTimestamp()) if !ok { return } s.SetString() msg.ComputeVMIndex(s) switch msg.Validate(s) { case 1: if s.RunLeader && s.Leader && !s.Saving && vm != nil && int(vm.Height) == len(vm.List) && (!s.Syncing || !vm.Synced) && (msg.IsLocal() || msg.GetVMIndex() == s.LeaderVMIndex) && s.LeaderPL.DBHeight+1 >= s.GetHighestKnownBlock() { if len(vm.List) == 0 { s.SendDBSig(s.LLeaderHeight, s.LeaderVMIndex) s.XReview = append(s.XReview, msg) } else { msg.LeaderExecute(s) } } else { msg.FollowerExecute(s) } ret = true case 0: s.Holding[msg.GetMsgHash().Fixed()] = msg default: s.Holding[msg.GetMsgHash().Fixed()] = msg if !msg.SentInvlaid() { msg.MarkSentInvalid(true) s.networkInvalidMsgQueue <- msg } } return }
// When we process the directory Signature, and we are the leader for said signature, it // is then that we push it out to the rest of the network. Otherwise, if we are not the // leader for the signature, it marks the sig complete for that list func (s *State) ProcessDBSig(dbheight uint32, msg interfaces.IMsg) bool { dbs := msg.(*messages.DirectoryBlockSignature) // Don't process if syncing an EOM if s.Syncing && !s.DBSig { return false } pl := s.ProcessLists.Get(dbheight) vm := s.ProcessLists.Get(dbheight).VMs[msg.GetVMIndex()] if uint32(pl.System.Height) >= dbs.SysHeight { s.DBSigSys = true } // If we are done with DBSigs, and this message is processed, then we are done. Let everything go! if s.DBSigSys && s.DBSig && s.DBSigDone { s.DBSigProcessed-- if s.DBSigProcessed <= 0 { s.DBSig = false s.Syncing = false } vm.Signed = true //s.LeaderPL.AdminBlock return true } // Put the stuff that only executes once at the start of DBSignatures here if !s.DBSig { s.DBSigLimit = len(pl.FedServers) s.DBSigProcessed = 0 s.DBSig = true s.Syncing = true s.DBSigDone = false for _, vm := range pl.VMs { vm.Synced = false } pl.ResetDiffSigTally() } // Put the stuff that executes per DBSignature here if !dbs.Processed { if dbs.VMIndex == 0 { s.SetLeaderTimestamp(dbs.GetTimestamp()) } dbstate := s.GetDBState(dbheight - 1) if dbstate == nil || !dbs.DirectoryBlockHeader.GetBodyMR().IsSameAs(dbstate.DirectoryBlock.GetHeader().GetBodyMR()) { //fmt.Println(s.FactomNodeName, "JUST COMPARED", dbs.DirectoryBlockHeader.GetBodyMR().String()[:10], " : ", dbstate.DirectoryBlock.GetHeader().GetBodyMR().String()[:10]) pl.IncrementDiffSigTally() } // Adds DB Sig to be added to Admin block if passes sig checks allChecks := false data, err := dbs.DirectoryBlockHeader.MarshalBinary() if err != nil { fmt.Println("Debug: DBSig Signature Error, Marshal binary errored") } else { if !dbs.DBSignature.Verify(data) { fmt.Println("Debug: DBSig Signature Error, Verify errored") } else { if valid, err := s.VerifyAuthoritySignature(data, dbs.DBSignature.GetSignature(), dbs.DBHeight); err == nil && valid == 1 { allChecks = true } } } if allChecks { dbs.Matches = true s.AddDBSig(dbheight, dbs.ServerIdentityChainID, dbs.DBSignature) } dbs.Processed = true s.DBSigProcessed++ vm.Synced = true } allfaults := s.LeaderPL.System.Height >= s.LeaderPL.SysHighest // Put the stuff that executes once for set of DBSignatures (after I have them all) here if allfaults && !s.DBSigDone && s.DBSigProcessed >= s.DBSigLimit { fails := 0 for i := range pl.FedServers { vm := pl.VMs[i] tdbsig, ok := vm.List[0].(*messages.DirectoryBlockSignature) if !ok || !tdbsig.Matches { fails++ vm.List[0] = nil vm.Height = 0 s.DBSigProcessed-- } } if fails > len(pl.FedServers)/2 { //s.DoReset() return false } else if fails > 0 { return false } dbstate := s.DBStates.Get(int(dbheight - 1)) // TODO: check signatures here. Count what match and what don't. Then if a majority // disagree with us, null our entry out. Otherwise toss our DBState and ask for one from // our neighbors. if s.KeepMismatch || pl.CheckDiffSigTally() { if !dbstate.Saved { dbstate.ReadyToSave = true s.DBStates.SaveDBStateToDB(dbstate) //s.LeaderPL.AddDBSig(dbs.ServerIdentityChainID, dbs.DBSignature) } } else { s.DBSigFails++ s.Reset() msg := messages.NewDBStateMissing(s, uint32(dbheight-1), uint32(dbheight-1)) if msg != nil { s.RunLeader = false s.StartDelay = s.GetTimestamp().GetTimeMilli() s.NetworkOutMsgQueue() <- msg } } s.ReviewHolding() s.Saving = false s.DBSigDone = true } return false /* err := s.LeaderPL.AdminBlock.AddDBSig(dbs.ServerIdentityChainID, dbs.DBSignature) if err != nil { fmt.Printf("Error in adding DB sig to admin block, %s\n", err.Error()) } */ }
// TODO: Should fault the server if we don't have the proper sequence of EOM messages. func (s *State) ProcessEOM(dbheight uint32, msg interfaces.IMsg) bool { e := msg.(*messages.EOM) if s.Syncing && !s.EOM { return false } if s.EOM && int(e.Minute) > s.EOMMinute { return false } pl := s.ProcessLists.Get(dbheight) vm := s.ProcessLists.Get(dbheight).VMs[msg.GetVMIndex()] if uint32(pl.System.Height) >= e.SysHeight { s.EOMSys = true } // If I have done everything for all EOMs for all VMs, then and only then do I // let processing continue. if s.EOMDone && s.EOMSys { s.EOMProcessed-- if s.EOMProcessed <= 0 { s.EOM = false s.EOMDone = false s.ReviewHolding() s.Syncing = false } s.SendHeartBeat() return true } // What I do once for all VMs at the beginning of processing a particular EOM if !s.EOM { s.EOMSys = false s.Syncing = true s.EOM = true s.EOMLimit = len(s.LeaderPL.FedServers) s.EOMMinute = int(e.Minute) s.EOMsyncing = true s.EOMProcessed = 0 s.Newblk = false for _, vm := range pl.VMs { vm.Synced = false } s.AddStatus("EOM Syncing") return false } // What I do for each EOM if !e.Processed { vm.LeaderMinute++ s.EOMProcessed++ e.Processed = true vm.Synced = true if s.LeaderPL.SysHighest < int(e.SysHeight) { s.LeaderPL.SysHighest = int(e.SysHeight) } s.AddStatus("EOM Processed") return false } allfaults := s.LeaderPL.System.Height >= s.LeaderPL.SysHighest // After all EOM markers are processed, Claim we are done. Now we can unwind if allfaults && s.EOMProcessed == s.EOMLimit && !s.EOMDone { s.AddStatus("EOM All Done") s.EOMDone = true for _, eb := range pl.NewEBlocks { eb.AddEndOfMinuteMarker(byte(e.Minute + 1)) } s.FactoidState.EndOfPeriod(int(e.Minute)) ecblk := pl.EntryCreditBlock ecbody := ecblk.GetBody() mn := entryCreditBlock.NewMinuteNumber(e.Minute + 1) ecbody.AddEntry(mn) if !s.Leader { s.CurrentMinute = int(e.Minute) } s.CurrentMinute++ switch { case s.CurrentMinute < 10: s.LeaderPL = s.ProcessLists.Get(s.LLeaderHeight) s.Leader, s.LeaderVMIndex = s.LeaderPL.GetVirtualServers(s.CurrentMinute, s.IdentityChainID) case s.CurrentMinute == 10: eBlocks := []interfaces.IEntryBlock{} entries := []interfaces.IEBEntry{} for _, v := range pl.NewEBlocks { eBlocks = append(eBlocks, v) } for _, v := range pl.NewEntries { entries = append(entries, v) } dbstate := s.AddDBState(true, s.LeaderPL.DirectoryBlock, s.LeaderPL.AdminBlock, s.GetFactoidState().GetCurrentBlock(), s.LeaderPL.EntryCreditBlock, eBlocks, entries) if dbstate == nil { dbstate = s.DBStates.Get(int(s.LeaderPL.DirectoryBlock.GetHeader().GetDBHeight())) } dbht := int(dbstate.DirectoryBlock.GetHeader().GetDBHeight()) if dbht > 0 { prev := s.DBStates.Get(dbht - 1) s.DBStates.FixupLinks(prev, dbstate) } s.DBStates.ProcessBlocks(dbstate) s.CurrentMinute = 0 s.LLeaderHeight++ s.LeaderPL = s.ProcessLists.Get(s.LLeaderHeight) s.Leader, s.LeaderVMIndex = s.LeaderPL.GetVirtualServers(0, s.IdentityChainID) s.DBSigProcessed = 0 // Note about dbsigs.... If we processed the previous minute, then we generate the DBSig for the next block. // But if we didn't process the preivious block, like we start from scratch, or we had to reset the entire // network, then no dbsig exists. This code doesn't execute, and so we have no dbsig. In that case, on // the next EOM, we see the block hasn't been signed, and we sign the block (Thats the call to SendDBSig() // above). if s.Leader { dbstate := s.DBStates.Get(int(s.LLeaderHeight - 1)) dbs := new(messages.DirectoryBlockSignature) db := dbstate.DirectoryBlock dbs.DirectoryBlockHeader = db.GetHeader() //dbs.DirectoryBlockKeyMR = dbstate.DirectoryBlock.GetKeyMR() dbs.ServerIdentityChainID = s.GetIdentityChainID() dbs.DBHeight = s.LLeaderHeight dbs.Timestamp = s.GetTimestamp() dbs.SetVMHash(nil) dbs.SetVMIndex(s.LeaderVMIndex) dbs.SetLocal(true) dbs.Sign(s) err := dbs.Sign(s) if err != nil { panic(err) } dbs.LeaderExecute(s) } s.Saving = true } for k := range s.Commits { vs := s.Commits[k] if len(vs) == 0 { delete(s.Commits, k) continue } v, ok := vs[0].(interfaces.IMsg) if ok { _, ok := s.Replay.Valid(constants.TIME_TEST, v.GetRepeatHash().Fixed(), v.GetTimestamp(), s.GetTimestamp()) if !ok { copy(vs, vs[1:]) vs[len(vs)-1] = nil s.Commits[k] = vs[:len(vs)-1] } } } for k := range s.Acks { v := s.Acks[k].(*messages.Ack) if v.DBHeight < s.LLeaderHeight { delete(s.Acks, k) } } } return false }