// 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 }
// Create a new Acknowledgement. This Acknowledgement func NewAck(state interfaces.IState, hash interfaces.IHash) (iack interfaces.IMsg, err error) { var last *Ack if state.GetLastAck() != nil { last = state.GetLastAck().(*Ack) } ack := new(Ack) ack.Timestamp = state.GetTimestamp() ack.MessageHash = hash if last == nil { ack.Height = 0 ack.SerialHash = ack.MessageHash } else { ack.Height = last.Height + 1 ack.SerialHash, err = primitives.CreateHash(last.MessageHash, ack.MessageHash) if err != nil { return nil, err } } state.SetLastAck(ack) // TODO: Add the signature. return ack, nil }
// Return the serial height for this Full Fault message. Can return nil if there is // no process list at this dbheight, or if we are missing a preceeding Full Fault message. func (m *FullServerFault) GetSerialHash() interfaces.IHash { if m.SSerialHash == nil { sh, err := primitives.CreateHash(m.SSerialHash, m.GetCoreHash()) if err != nil { panic(err.Error()) } m.SSerialHash = sh } return m.SSerialHash }
// Process messages and update our state. func (p *ProcessList) Process(state *State) (progress bool) { dbht := state.GetHighestCompletedBlock() if dbht >= p.DBHeight { return true } state.PLProcessHeight = p.DBHeight p.AskDBState(0, p.VMs[0].Height) // Look for a possible dbstate at this height. if len(p.System.List) > 0 { systemloop: for i, f := range p.System.List[p.System.Height:] { fault, ok := f.(*messages.FullServerFault) if ok { vm := p.VMs[fault.VMIndex] if vm.Height < int(fault.Height) { break systemloop } if !fault.Process(p.DBHeight, p.State) { return false } p.System.Height++ progress = true } if fault == nil { p.Ask(-1, i, 10, 100) } } } for i := 0; i < len(p.FedServers); i++ { vm := p.VMs[i] if !p.State.Syncing { vm.whenFaulted = 0 p.Unfault() } else { if !vm.Synced { eomFault(p, vm, i, len(vm.List), 0) } } if vm.Height == len(vm.List) && p.State.Syncing && !vm.Synced { // means that we are missing an EOM p.Ask(i, vm.Height, 0, 1) } // If we haven't heard anything from a VM, ask for a message at the last-known height if vm.Height == len(vm.List) { p.Ask(i, vm.Height, 20, 2) } if vm.whenFaulted > 0 && vm.Height > vm.faultHeight { if p.AmINegotiator && i == p.NegotiatorVMIndex { p.AmINegotiator = false } vm.faultHeight = -1 vm.whenFaulted = 0 p.Unfault() } VMListLoop: for j := vm.Height; j < len(vm.List); j++ { if vm.List[j] == nil { p.Ask(i, j, 0, 3) break VMListLoop } thisAck := vm.ListAck[j] var expectedSerialHash interfaces.IHash var err error if vm.Height == 0 { expectedSerialHash = thisAck.SerialHash } else { last := vm.ListAck[vm.Height-1] expectedSerialHash, err = primitives.CreateHash(last.MessageHash, thisAck.MessageHash) if err != nil { p.Ask(i, j, 3, 4) break VMListLoop } // compare the SerialHash of this acknowledgement with the // expected serialHash (generated above) if !expectedSerialHash.IsSameAs(thisAck.SerialHash) { fmt.Printf("dddd %20s %10s --- %10s %10x %10s %10x \n", "Conflict", p.State.FactomNodeName, "expected", expectedSerialHash.Bytes()[:3], "This", thisAck.Bytes()[:3]) fmt.Printf("dddd Error detected on %s\nSerial Hash failure: Fed Server %d Leader ID %x List Ht: %d \nDetected on: %s\n", state.GetFactomNodeName(), i, p.FedServers[i].GetChainID().Bytes()[:3], j, vm.List[j].String()) fmt.Printf("dddd Last Ack: %6x Last Serial: %6x\n", last.GetHash().Bytes()[:3], last.SerialHash.Bytes()[:3]) fmt.Printf("dddd This Ack: %6x This Serial: %6x\n", thisAck.GetHash().Bytes()[:3], thisAck.SerialHash.Bytes()[:3]) fmt.Printf("dddd Expected: %6x\n", expectedSerialHash.Bytes()[:3]) fmt.Printf("dddd The message that didn't work: %s\n\n", vm.List[j].String()) // the SerialHash of this acknowledgment is incorrect // according to this node's processList //fault(p, i, 0, vm, 0, j, 2) p.State.Reset() return } } // So here is the deal. After we have processed a block, we have to allow the DirectoryBlockSignatures a chance to save // to disk. Then we can insist on having the entry blocks. diff := p.DBHeight - state.EntryBlockDBHeightComplete _, dbsig := vm.List[j].(*messages.DirectoryBlockSignature) // Keep in mind, the process list is processing at a height one greater than the database. 1 is caught up. 2 is one behind. // Until the signatures are processed, we will be 2 behind. if (dbsig && diff <= 2) || diff <= 1 { // If we can't process this entry (i.e. returns false) then we can't process any more. p.NextHeightToProcess[i] = j + 1 if vm.List[j].Process(p.DBHeight, state) { // Try and Process this entry vm.heartBeat = 0 vm.Height = j + 1 // Don't process it again if the process worked. //p.Unfault() progress = true } else { break VMListLoop // Don't process further in this list, go to the next. } } else { // If we don't have the Entry Blocks (or we haven't processed the signatures) we can't do more. break VMListLoop } } } return }