package lockstep import ( "ripple/state" "ripple/transport" "ripple/types" ) type Lockstep struct { st *state.State queues map[types.UserIdentifier][]types.Instruction sender *transport.AccountTransport cmdHandlers [128]CommandHandler interlockstep *Interlockstep FailsafeCh chan struct{} } func NewLockstep( st *state.State, accSender *transport.AccountTransport, cptSender *transport.CounterpartTransport, h [128]CommandHandler, ) *Lockstep { l := &Lockstep{ st: st, queues: make(map[types.UserIdentifier][]types.Instruction), sender: accSender, cmdHandlers: h, FailsafeCh: make(chan struct{}), } l.interlockstep = &Interlockstep{Lockstep: l, CptSender: cptSender} go l.RunFailsafe() return l } func (l *Lockstep) PreviewAccount(id types.UserIdentifier) *state.State { stateCopy := l.st.Clone() queue := l.mustGetAccount(id) for i := 0; i < len(queue); { instr := queue[i] if _, err := l.dispatch(&stateCopy, id, instr); err != nil { queue = append(queue[:i], queue[i+1:]...) } else { i++ } } l.queues[id] = queue return &stateCopy } func (l *Lockstep) stateTransition(userID types.UserIdentifier, instr types.Instruction) error { stateCopy := l.st.Clone() interLockstepCb, err := l.dispatch(&stateCopy, userID, instr) if err != nil { return err } account := stateCopy.Storage.MustGetAccount(userID) account.LastValidated = instr account.TurnCounter++ stateCopy.Storage.Accounts[userID] = account if err := stateCopy.Save(); err != nil { return err } l.st.Apply(stateCopy) if interLockstepCb != nil { interLockstepCb(l.interlockstep) } return nil } func (l *Lockstep) inTurn(id types.UserIdentifier) bool { acc := l.st.Storage.MustGetAccount(id) tb := acc.TurnBit tc := acc.TurnCounter return calculateInTurn(tb, tc) } func (l *Lockstep) inTurnFromTC(id types.UserIdentifier, tc uint32) bool { acc := l.st.Storage.MustGetAccount(id) tb := acc.TurnBit return calculateInTurn(tb, tc) } func (l *Lockstep) doTurn(id types.UserIdentifier) { if l.inTurn(id) { d, ok := l.dequeue(id) if !ok { return } if err := l.stateTransition(id, d); err != nil { return } l.sendValidated(id, d) } d, ok := l.peek(id) if !ok { return } l.sendProposal(id, d) }