Documentation ¶
Index ¶
- Constants
- Variables
- func CompareHRS(h1, r1 int, s1 RoundStepType, h2, r2 int, s2 RoundStepType) int
- func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console bool)
- type BlockPartMessage
- type CommitStepMessage
- type ConsensusMessage
- type ConsensusReactor
- func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer)
- func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor
- func (conR *ConsensusReactor) OnStart() error
- func (conR *ConsensusReactor) OnStop()
- func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte)
- func (conR *ConsensusReactor) RemovePeer(peer *p2p.Peer, reason interface{})
- func (conR *ConsensusReactor) SetEventSwitch(evsw types.EventSwitch)
- func (conR *ConsensusReactor) String() string
- func (conR *ConsensusReactor) StringIndented(indent string) string
- func (conR *ConsensusReactor) SwitchToConsensus(state *sm.State)
- type ConsensusState
- func (cs *ConsensusState) AddProposalBlockPart(height, round int, part *types.Part, peerKey string) error
- func (cs *ConsensusState) AddVote(vote *types.Vote, peerKey string) (added bool, err error)
- func (cs *ConsensusState) GetRoundState() *RoundState
- func (cs *ConsensusState) GetState() *sm.State
- func (cs *ConsensusState) GetValidators() (int, []*types.Validator)
- func (cs *ConsensusState) LoadCommit(height int) *types.Commit
- func (cs *ConsensusState) OnStart() error
- func (cs *ConsensusState) OnStop()
- func (cs *ConsensusState) OpenWAL(walFile string) (err error)
- func (cs *ConsensusState) ReplayFile(file string, console bool) error
- func (cs *ConsensusState) SetEventSwitch(evsw types.EventSwitch)
- func (cs *ConsensusState) SetLogger(l log.Logger)
- func (cs *ConsensusState) SetPrivValidator(priv PrivValidator)
- func (cs *ConsensusState) SetProposal(proposal *types.Proposal, peerKey string) error
- func (cs *ConsensusState) SetProposalAndBlock(proposal *types.Proposal, block *types.Block, parts *types.PartSet, ...) error
- func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker)
- func (cs *ConsensusState) String() string
- func (cs *ConsensusState) Wait()
- type Handshaker
- type HasVoteMessage
- type HeightVoteSet
- func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerKey string) (added bool, err error)
- func (hvs *HeightVoteSet) Height() int
- func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID)
- func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet
- func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet
- func (hvs *HeightVoteSet) Reset(height int, valSet *types.ValidatorSet)
- func (hvs *HeightVoteSet) Round() int
- func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID string, blockID types.BlockID)
- func (hvs *HeightVoteSet) SetRound(round int)
- func (hvs *HeightVoteSet) String() string
- func (hvs *HeightVoteSet) StringIndented(indent string) string
- type NewRoundStepMessage
- type PeerRoundState
- type PeerState
- func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage)
- func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage)
- func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage)
- func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage)
- func (ps *PeerState) ApplyVoteSetBitsMessage(msg *VoteSetBitsMessage, ourVotes *cmn.BitArray)
- func (ps *PeerState) EnsureVoteBitArrays(height int, numValidators int)
- func (ps *PeerState) GetHeight() int
- func (ps *PeerState) GetRoundState() *PeerRoundState
- func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool
- func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote, ok bool)
- func (ps *PeerState) SetHasProposal(proposal *types.Proposal)
- func (ps *PeerState) SetHasProposalBlockPart(height int, round int, index int)
- func (ps *PeerState) SetHasVote(vote *types.Vote)
- func (ps *PeerState) String() string
- func (ps *PeerState) StringIndented(indent string) string
- type PrivValidator
- type ProposalMessage
- type ProposalPOLMessage
- type RoundState
- type RoundStepType
- type RoundVoteSet
- type TimedWALMessage
- type TimeoutTicker
- type VoteMessage
- type VoteSetBitsMessage
- type VoteSetMaj23Message
- type WAL
- type WALMessage
Constants ¶
const ( StateChannel = byte(0x20) DataChannel = byte(0x21) VoteChannel = byte(0x22) VoteSetBitsChannel = byte(0x23) )
const ( RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout RoundStepCommit = RoundStepType(0x08) // Entered commit state machine )
Variables ¶
var ( ErrPeerStateHeightRegression = errors.New("Error peer state height regression") ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime") )
var ( ErrInvalidProposalSignature = errors.New("Error invalid proposal signature") ErrInvalidProposalPOLRound = errors.New("Error invalid proposal POL round") ErrAddingVote = errors.New("Error adding vote") ErrVoteHeightMismatch = errors.New("Error vote height mismatch") )
var Major = "0" //
var Minor = "2" // replay refactor
var Revision = "2" // validation -> commit
var Spec = "1" // async
kind of arbitrary
var Version = Fmt("v%s/%s.%s.%s", Spec, Major, Minor, Revision)
Functions ¶
func CompareHRS ¶
func CompareHRS(h1, r1 int, s1 RoundStepType, h2, r2 int, s2 RoundStepType) int
func RunReplayFile ¶ added in v0.9.0
func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console bool)
Types ¶
type BlockPartMessage ¶
BlockPartMessage is sent when gossipping a piece of the proposed block.
func (*BlockPartMessage) String ¶
func (m *BlockPartMessage) String() string
String returns a string representation.
type CommitStepMessage ¶
type CommitStepMessage struct { Height int BlockPartsHeader types.PartSetHeader BlockParts *cmn.BitArray }
CommitStepMessage is sent when a block is committed.
func (*CommitStepMessage) String ¶
func (m *CommitStepMessage) String() string
String returns a string representation.
type ConsensusMessage ¶
type ConsensusMessage interface{}
ConsensusMessage is a message that can be sent and received on the ConsensusReactor
func DecodeMessage ¶
func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error)
DecodeMessage decodes the given bytes into a ConsensusMessage. TODO: check for unnecessary extra bytes at the end.
type ConsensusReactor ¶
type ConsensusReactor struct { p2p.BaseReactor // BaseService + p2p.Switch // contains filtered or unexported fields }
ConsensusReactor defines a reactor for the consensus service.
func NewConsensusReactor ¶
func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *ConsensusReactor
NewConsensusReactor returns a new ConsensusReactor with the given consensusState.
func (*ConsensusReactor) AddPeer ¶
func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer)
AddPeer implements Reactor
func (*ConsensusReactor) GetChannels ¶
func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor
GetChannels implements Reactor
func (*ConsensusReactor) OnStart ¶
func (conR *ConsensusReactor) OnStart() error
OnStart implements BaseService.
func (*ConsensusReactor) OnStop ¶
func (conR *ConsensusReactor) OnStop()
OnStop implements BaseService
func (*ConsensusReactor) Receive ¶
func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte)
Receive implements Reactor NOTE: We process these messages even when we're fast_syncing. Messages affect either a peer state or the consensus state. Peer state updates can happen in parallel, but processing of proposals, block parts, and votes are ordered by the receiveRoutine NOTE: blocks on consensus state for proposals, block parts, and votes
func (*ConsensusReactor) RemovePeer ¶
func (conR *ConsensusReactor) RemovePeer(peer *p2p.Peer, reason interface{})
RemovePeer implements Reactor
func (*ConsensusReactor) SetEventSwitch ¶
func (conR *ConsensusReactor) SetEventSwitch(evsw types.EventSwitch)
SetEventSwitch implements events.Eventable
func (*ConsensusReactor) String ¶ added in v0.8.0
func (conR *ConsensusReactor) String() string
String returns a string representation of the ConsensusReactor. NOTE: For now, it is just a hard-coded string to avoid accessing unprotected shared variables. TODO: improve!
func (*ConsensusReactor) StringIndented ¶ added in v0.8.0
func (conR *ConsensusReactor) StringIndented(indent string) string
StringIndented returns an indented string representation of the ConsensusReactor
func (*ConsensusReactor) SwitchToConsensus ¶
func (conR *ConsensusReactor) SwitchToConsensus(state *sm.State)
SwitchToConsensus switches from fast_sync mode to consensus mode. It resets the state, turns off fast_sync, and starts the consensus state-machine
type ConsensusState ¶
type ConsensusState struct { cmn.BaseService RoundState // contains filtered or unexported fields }
Tracks consensus state across block heights and rounds.
func NewConsensusState ¶
func NewConsensusState(config *cfg.ConsensusConfig, state *sm.State, proxyAppConn proxy.AppConnConsensus, blockStore types.BlockStore, mempool types.Mempool) *ConsensusState
func (*ConsensusState) AddProposalBlockPart ¶
func (cs *ConsensusState) AddProposalBlockPart(height, round int, part *types.Part, peerKey string) error
May block on send if queue is full.
func (*ConsensusState) GetRoundState ¶
func (cs *ConsensusState) GetRoundState() *RoundState
func (*ConsensusState) GetState ¶
func (cs *ConsensusState) GetState() *sm.State
func (*ConsensusState) GetValidators ¶ added in v0.7.3
func (cs *ConsensusState) GetValidators() (int, []*types.Validator)
func (*ConsensusState) LoadCommit ¶ added in v0.8.0
func (cs *ConsensusState) LoadCommit(height int) *types.Commit
func (*ConsensusState) OnStart ¶
func (cs *ConsensusState) OnStart() error
func (*ConsensusState) OnStop ¶
func (cs *ConsensusState) OnStop()
func (*ConsensusState) OpenWAL ¶
func (cs *ConsensusState) OpenWAL(walFile string) (err error)
Open file to log all consensus messages and timeouts for deterministic accountability
func (*ConsensusState) ReplayFile ¶ added in v0.9.0
func (cs *ConsensusState) ReplayFile(file string, console bool) error
Replay msgs in file or start the console
func (*ConsensusState) SetEventSwitch ¶
func (cs *ConsensusState) SetEventSwitch(evsw types.EventSwitch)
SetEventSwitch implements events.Eventable
func (*ConsensusState) SetLogger ¶ added in v0.10.0
func (cs *ConsensusState) SetLogger(l log.Logger)
SetLogger implements Service.
func (*ConsensusState) SetPrivValidator ¶
func (cs *ConsensusState) SetPrivValidator(priv PrivValidator)
Sets our private validator account for signing votes.
func (*ConsensusState) SetProposal ¶
func (cs *ConsensusState) SetProposal(proposal *types.Proposal, peerKey string) error
May block on send if queue is full.
func (*ConsensusState) SetProposalAndBlock ¶
func (cs *ConsensusState) SetProposalAndBlock(proposal *types.Proposal, block *types.Block, parts *types.PartSet, peerKey string) error
May block on send if queue is full.
func (*ConsensusState) SetTimeoutTicker ¶ added in v0.8.0
func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker)
Set the local timer
func (*ConsensusState) String ¶
func (cs *ConsensusState) String() string
func (*ConsensusState) Wait ¶ added in v0.8.0
func (cs *ConsensusState) Wait()
NOTE: be sure to Stop() the event switch and drain any event channels or this may deadlock
type Handshaker ¶ added in v0.9.0
type Handshaker struct {
// contains filtered or unexported fields
}
func NewHandshaker ¶ added in v0.9.0
func NewHandshaker(state *sm.State, store types.BlockStore) *Handshaker
func (*Handshaker) Handshake ¶ added in v0.9.0
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error
TODO: retry the handshake/replay if it fails ?
func (*Handshaker) NBlocks ¶ added in v0.9.0
func (h *Handshaker) NBlocks() int
func (*Handshaker) ReplayBlocks ¶ added in v0.9.0
func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, proxyApp proxy.AppConns) ([]byte, error)
Replay all blocks since appBlockHeight and ensure the result matches the current state. Returns the final AppHash or an error
func (*Handshaker) SetLogger ¶ added in v0.10.0
func (h *Handshaker) SetLogger(l log.Logger)
type HasVoteMessage ¶
HasVoteMessage is sent to indicate that a particular vote has been received.
func (*HasVoteMessage) String ¶
func (m *HasVoteMessage) String() string
String returns a string representation.
type HeightVoteSet ¶
type HeightVoteSet struct {
// contains filtered or unexported fields
}
Keeps track of all VoteSets from round 0 to round 'round'.
Also keeps track of up to one RoundVoteSet greater than 'round' from each peer, to facilitate catchup syncing of commits.
A commit is +2/3 precommits for a block at a round, but which round is not known in advance, so when a peer provides a precommit for a round greater than mtx.round, we create a new entry in roundVoteSets but also remember the peer to prevent abuse. We let each peer provide us with up to 2 unexpected "catchup" rounds. One for their LastCommit round, and another for the official commit round.
func NewHeightVoteSet ¶
func NewHeightVoteSet(chainID string, height int, valSet *types.ValidatorSet) *HeightVoteSet
func (*HeightVoteSet) AddVote ¶ added in v0.8.0
Duplicate votes return added=false, err=nil. By convention, peerKey is "" if origin is self.
func (*HeightVoteSet) Height ¶
func (hvs *HeightVoteSet) Height() int
func (*HeightVoteSet) POLInfo ¶ added in v0.8.0
func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID)
Last round and blockID that has +2/3 prevotes for a particular block or nil. Returns -1 if no such round exists.
func (*HeightVoteSet) Precommits ¶
func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet
func (*HeightVoteSet) Reset ¶
func (hvs *HeightVoteSet) Reset(height int, valSet *types.ValidatorSet)
func (*HeightVoteSet) Round ¶
func (hvs *HeightVoteSet) Round() int
func (*HeightVoteSet) SetPeerMaj23 ¶ added in v0.8.0
If a peer claims that it has 2/3 majority for given blockKey, call this. NOTE: if there are too many peers, or too much peer churn, this can cause memory issues. TODO: implement ability to remove peers too
func (*HeightVoteSet) SetRound ¶
func (hvs *HeightVoteSet) SetRound(round int)
Create more RoundVoteSets up to round.
func (*HeightVoteSet) String ¶
func (hvs *HeightVoteSet) String() string
func (*HeightVoteSet) StringIndented ¶
func (hvs *HeightVoteSet) StringIndented(indent string) string
type NewRoundStepMessage ¶
type NewRoundStepMessage struct { Height int Round int Step RoundStepType SecondsSinceStartTime int LastCommitRound int }
NewRoundStepMessage is sent for every step taken in the ConsensusState. For every height/round/step transition
func (*NewRoundStepMessage) String ¶
func (m *NewRoundStepMessage) String() string
String returns a string representation.
type PeerRoundState ¶
type PeerRoundState struct { Height int // Height peer is at Round int // Round peer is at, -1 if unknown. Step RoundStepType // Step peer is at StartTime time.Time // Estimated start of round 0 at this height Proposal bool // True if peer has proposal for this round ProposalBlockPartsHeader types.PartSetHeader // ProposalBlockParts *cmn.BitArray // ProposalPOLRound int // Proposal's POL round. -1 if none. ProposalPOL *cmn.BitArray // nil until ProposalPOLMessage received. Prevotes *cmn.BitArray // All votes peer has for this round Precommits *cmn.BitArray // All precommits peer has for this round LastCommitRound int // Round of commit for last height. -1 if none. LastCommit *cmn.BitArray // All commit precommits of commit for last height. CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none. CatchupCommit *cmn.BitArray // All commit precommits peer has for this height & CatchupCommitRound }
PeerRoundState contains the known state of a peer. NOTE: Read-only when returned by PeerState.GetRoundState().
func (PeerRoundState) String ¶ added in v0.8.0
func (prs PeerRoundState) String() string
String returns a string representation of the PeerRoundState
func (PeerRoundState) StringIndented ¶ added in v0.8.0
func (prs PeerRoundState) StringIndented(indent string) string
StringIndented returns a string representation of the PeerRoundState
type PeerState ¶
type PeerState struct { Peer *p2p.Peer PeerRoundState // contains filtered or unexported fields }
PeerState contains the known state of a peer, including its connection and threadsafe access to its PeerRoundState.
func NewPeerState ¶
NewPeerState returns a new PeerState for the given Peer
func (*PeerState) ApplyCommitStepMessage ¶
func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage)
ApplyCommitStepMessage updates the peer state for the new commit.
func (*PeerState) ApplyHasVoteMessage ¶
func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage)
ApplyHasVoteMessage updates the peer state for the new vote.
func (*PeerState) ApplyNewRoundStepMessage ¶
func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage)
ApplyNewRoundStepMessage updates the peer state for the new round.
func (*PeerState) ApplyProposalPOLMessage ¶
func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage)
ApplyProposalPOLMessage updates the peer state for the new proposal POL.
func (*PeerState) ApplyVoteSetBitsMessage ¶ added in v0.8.0
func (ps *PeerState) ApplyVoteSetBitsMessage(msg *VoteSetBitsMessage, ourVotes *cmn.BitArray)
ApplyVoteSetBitsMessage updates the peer state for the bit-array of votes it claims to have for the corresponding BlockID. `ourVotes` is a BitArray of votes we have for msg.BlockID NOTE: if ourVotes is nil (e.g. msg.Height < rs.Height), we conservatively overwrite ps's votes w/ msg.Votes.
func (*PeerState) EnsureVoteBitArrays ¶
EnsureVoteVitArrays ensures the bit-arrays have been allocated for tracking what votes this peer has received. NOTE: It's important to make sure that numValidators actually matches what the node sees as the number of validators for height.
func (*PeerState) GetHeight ¶
GetHeight returns an atomic snapshot of the PeerRoundState's height used by the mempool to ensure peers are caught up before broadcasting new txs
func (*PeerState) GetRoundState ¶
func (ps *PeerState) GetRoundState() *PeerRoundState
GetRoundState returns an atomic snapshot of the PeerRoundState. There's no point in mutating it since it won't change PeerState.
func (*PeerState) PickSendVote ¶
func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool
PickSendVote picks a vote and sends it to the peer. Returns true if vote was sent.
func (*PeerState) PickVoteToSend ¶
PickVoteToSend picks a vote to send to the peer. Returns true if a vote was picked. NOTE: `votes` must be the correct Size() for the Height().
func (*PeerState) SetHasProposal ¶
SetHasProposal sets the given proposal as known for the peer.
func (*PeerState) SetHasProposalBlockPart ¶
SetHasProposalBlockPart sets the given block part index as known for the peer.
func (*PeerState) SetHasVote ¶
SetHasVote sets the given vote as known by the peer
func (*PeerState) StringIndented ¶ added in v0.8.0
StringIndented returns a string representation of the PeerState
type PrivValidator ¶ added in v0.8.0
type ProposalMessage ¶
ProposalMessage is sent when a new block is proposed.
func (*ProposalMessage) String ¶
func (m *ProposalMessage) String() string
String returns a string representation.
type ProposalPOLMessage ¶
ProposalPOLMessage is sent when a previous proposal is re-proposed.
func (*ProposalPOLMessage) String ¶
func (m *ProposalPOLMessage) String() string
String returns a string representation.
type RoundState ¶
type RoundState struct { Height int // Height we are working on Round int Step RoundStepType StartTime time.Time CommitTime time.Time // Subjective time when +2/3 precommits for Block at Round were found Validators *types.ValidatorSet Proposal *types.Proposal ProposalBlock *types.Block ProposalBlockParts *types.PartSet LockedRound int LockedBlock *types.Block LockedBlockParts *types.PartSet Votes *HeightVoteSet CommitRound int // LastCommit *types.VoteSet // Last precommits at Height-1 LastValidators *types.ValidatorSet }
Immutable when returned from ConsensusState.GetRoundState() TODO: Actually, only the top pointer is copied, so access to field pointers is still racey
func (*RoundState) RoundStateEvent ¶
func (rs *RoundState) RoundStateEvent() types.EventDataRoundState
func (*RoundState) String ¶
func (rs *RoundState) String() string
func (*RoundState) StringIndented ¶
func (rs *RoundState) StringIndented(indent string) string
func (*RoundState) StringShort ¶
func (rs *RoundState) StringShort() string
type RoundStepType ¶
type RoundStepType uint8 // These must be numeric, ordered.
func (RoundStepType) String ¶
func (rs RoundStepType) String() string
type TimedWALMessage ¶ added in v0.8.0
type TimedWALMessage struct { Time time.Time `json:"time"` Msg WALMessage `json:"msg"` }
type TimeoutTicker ¶ added in v0.8.0
type TimeoutTicker interface { Start() (bool, error) Stop() bool Chan() <-chan timeoutInfo // on which to receive a timeout ScheduleTimeout(ti timeoutInfo) // reset the timer SetLogger(log.Logger) }
TimeoutTicker is a timer that schedules timeouts conditional on the height/round/step in the timeoutInfo. The timeoutInfo.Duration may be non-positive.
func NewTimeoutTicker ¶ added in v0.8.0
func NewTimeoutTicker() TimeoutTicker
type VoteMessage ¶
VoteMessage is sent when voting for a proposal (or lack thereof).
func (*VoteMessage) String ¶
func (m *VoteMessage) String() string
String returns a string representation.
type VoteSetBitsMessage ¶ added in v0.8.0
type VoteSetBitsMessage struct { Height int Round int Type byte BlockID types.BlockID Votes *cmn.BitArray }
VoteSetBitsMessage is sent to communicate the bit-array of votes seen for the BlockID.
func (*VoteSetBitsMessage) String ¶ added in v0.8.0
func (m *VoteSetBitsMessage) String() string
String returns a string representation.
type VoteSetMaj23Message ¶ added in v0.8.0
VoteSetMaj23Message is sent to indicate that a given BlockID has seen +2/3 votes.
func (*VoteSetMaj23Message) String ¶ added in v0.8.0
func (m *VoteSetMaj23Message) String() string
String returns a string representation.
type WAL ¶
type WAL struct { BaseService // contains filtered or unexported fields }
Write ahead logger writes msgs to disk before they are processed. Can be used for crash-recovery and deterministic replay TODO: currently the wal is overwritten during replay catchup
give it a mode so it's either reading or appending - must read to end to start appending again
func (*WAL) Save ¶
func (wal *WAL) Save(wmsg WALMessage)
called in newStep and for each pass in receiveRoutine
type WALMessage ¶ added in v0.8.0
type WALMessage interface{}