Repository: decred/dcrd
Pull request #3244 (2024-05-03 20:33:56)

wire: Remove PR hashes from (get)initstate
This reverts all of the initstate changes made in
b9d8d49c901bb7cbb19ed36d636c3e3d86a1fe43.

The limit of 100 was too too low to be useful and cranking this value up was not a proper solution either.  It also only synced pair request messages, which would cause mid-run mixes to leave stale PRs around in the mixpool until they expired.

This is handled better in newer mixpool code by requesting unknown pair request messages when they are seen in a key exchange.  The mixpool records the KE as an orphan and will process it after all missing PRs have been requested and processed.


Patch:
diff --git a/blockchain/chaingen/generator.go b/blockchain/chaingen/generator.go
index b51b54f1..4631de7a 100644
--- a/blockchain/chaingen/generator.go
+++ b/blockchain/chaingen/generator.go
@@ -1371,7 +1371,7 @@ func (g *Generator) calcNextRequiredDiffASERT() uint32 {
 		g.params.WorkDiffV2HalfLifeSecs)
 }
 
-// calcNextRequiredDifficultyEMA returns the required proof-of-work difficulty
+// CalcNextRequiredDifficulty returns the required proof-of-work difficulty
 // for the block after the current tip block the generator is associated with
 // using the difficulty algorithm configured for the generator.
 func (g *Generator) CalcNextRequiredDifficulty() uint32 {
diff --git a/internal/mining/mining.go b/internal/mining/mining.go
index d2b2be20..ef51e961 100644
--- a/internal/mining/mining.go
+++ b/internal/mining/mining.go
@@ -897,7 +897,15 @@ func (g *BlkTmplGenerator) handleTooFewVoters(nextHeight int64,
 			return nil, err
 		}
 		header := &block.Header
-		header.MerkleRoot = calcBlockMerkleRoot(block.Transactions, block.STransactions, hdrCmtActive)
+		header.MerkleRoot = calcBlockMerkleRoot(block.Transactions,
+			block.STransactions, hdrCmtActive)
+
+		// Calculate the required difficulty for the block.
+		reqDifficulty, err := g.cfg.CalcNextRequiredDifficulty(prevHash, ts)
+		if err != nil {
+			return nil, makeError(ErrGettingDifficulty, err.Error())
+		}
+		header.Bits = reqDifficulty
 
 		// Calculate the stake root or commitment root depending on the result
 		// of the header commitments agenda vote.
diff --git a/rpcadaptors.go b/rpcadaptors.go
index 643f9f80..ea22f783 100644
--- a/rpcadaptors.go
+++ b/rpcadaptors.go
@@ -94,7 +94,7 @@ func (p *rpcPeer) LastPingNonce() uint64 {
 // This function is safe for concurrent access and is part of the rpcserver.Peer
 // interface implementation.
 func (p *rpcPeer) IsTxRelayDisabled() bool {
-	return (*serverPeer)(p).relayTxDisabled()
+	return (*serverPeer)(p).disableRelayTx.Load()
 }
 
 // BanScore returns the current integer value that represents how close the peer
diff --git a/server.go b/server.go
index 1c0ee6d2..a6692915 100644
--- a/server.go
+++ b/server.go
@@ -551,8 +551,7 @@ type serverPeer struct {
 	server         *server
 	persistent     bool
 	continueHash   atomic.Pointer[chainhash.Hash]
-	relayMtx       sync.Mutex
-	disableRelayTx bool
+	disableRelayTx atomic.Bool
 	isWhitelisted  bool
 	knownAddresses *apbf.Filter
 	banScore       connmgr.DynamicBanScore
@@ -575,8 +574,7 @@ type serverPeer struct {
 	blockProcessed chan struct{}
 
 	// peerNa is network address of the peer connected to.
-	peerNa    *wire.NetAddress
-	peerNaMtx sync.Mutex
+	peerNa atomic.Pointer[wire.NetAddress]
 
 	// announcedBlock tracks the most recent block announced to this peer and is
 	// used to filter duplicates.
@@ -824,25 +822,6 @@ func (sp *serverPeer) addressKnown(na *addrmgr.NetAddress) bool {
 	return sp.knownAddresses.Contains([]byte(na.Key()))
 }
 
-// setDisableRelayTx toggles relaying of transactions for the given peer.
-// It is safe for concurrent access.
-func (sp *serverPeer) setDisableRelayTx(disable bool) {
-	sp.relayMtx.Lock()
-	sp.disableRelayTx = disable
-	sp.relayMtx.Unlock()
-}
-
-// relayTxDisabled returns whether or not relaying of transactions for the given
-// peer is disabled.
-// It is safe for concurrent access.
-func (sp *serverPeer) relayTxDisabled() bool {
-	sp.relayMtx.Lock()
-	isDisabled := sp.disableRelayTx
-	sp.relayMtx.Unlock()
-
-	return isDisabled
-}
-
 // wireToAddrmgrNetAddress converts a wire NetAddress to an address manager
 // NetAddress.
 func wireToAddrmgrNetAddress(netAddr *wire.NetAddress) *addrmgr.NetAddress {
@@ -1022,12 +1001,10 @@ func (sp *serverPeer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) {
 		}
 	}
 
-	sp.peerNaMtx.Lock()
-	sp.peerNa = &msg.AddrYou
-	sp.peerNaMtx.Unlock()
+	sp.peerNa.Store(&msg.AddrYou)
 
 	// Choose whether or not to relay transactions.
-	sp.setDisableRelayTx(msg.DisableRelayTx)
+	sp.disableRelayTx.Store(msg.DisableRelayTx)
 
 	// Add the remote peer time as a sample for creating an offset against
 	// the local clock to keep the network time in sync.
@@ -1861,9 +1838,7 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool {
 		return false
 	}
 
-	sp.peerNaMtx.Lock()
-	na := sp.peerNa
-	sp.peerNaMtx.Unlock()
+	na := sp.peerNa.Load()
 
 	// Add the new peer and start it.
 	srvrLog.Debugf("New peer %s", sp)
@@ -2062,9 +2037,9 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
 		}
 
 		if iv.Type == wire.InvTypeTx {
-			// Don't relay the transaction to the peer when it has
-			// transaction relaying disabled.
-			if sp.relayTxDisabled() {
+			// Don't relay the transaction to the peer when it has transaction
+			// relaying disabled.
+			if sp.disableRelayTx.Load() {
 				return
 			}
 		}
diff --git a/upnp.go b/upnp.go
index b6879678..4f1c0118 100644
--- a/upnp.go
+++ b/upnp.go
@@ -188,7 +188,7 @@ func getChildDevice(d *device, deviceType string) *device {
 	return nil
 }
 
-// getChildDevice searches the service list of device for a service with the
+// getChildService searches the service list of device for a service with the
 // given type.
 func getChildService(d *device, serviceType string) *service {
 	for i := range d.ServiceList.Service {
@@ -281,7 +281,7 @@ type soapEnvelope struct {
 	Body    soapBody `xml:"Body"`
 }
 
-// soapRequests performs a soap request with the given parameters and returns
+// soapRequest performs a soap request with the given parameters and returns
 // the xml replied stripped of the soap headers. in the case that the request is
 // unsuccessful the an error is returned.
 func soapRequest(url, function, message string) (replyXML []byte, err error) {
diff --git a/wire/msggetinitstate.go b/wire/msggetinitstate.go
index c787c772..350b6fe4 100644
--- a/wire/msggetinitstate.go
+++ b/wire/msggetinitstate.go
@@ -29,10 +29,6 @@ const (
 	// InitStateTSpends is the init state type used to request tpends for
 	// voting.
 	InitStateTSpends = "tspends"
-
-	// InitStateMixPairReqs is the init state type used to request mixing pair
-	// request messages.
-	InitStateMixPairReqs = "mixpairreqs"
 )
 
 // MsgGetInitState implements the Message interface and represents a
diff --git a/wire/msginitstate.go b/wire/msginitstate.go
index 05a71fdf..1ec60ec3 100644
--- a/wire/msginitstate.go
+++ b/wire/msginitstate.go
@@ -22,10 +22,6 @@ const MaxISVotesAtHeadPerMsg = 40 // 8 * 5
 // message.
 const MaxISTSpendsAtHeadPerMsg = 7
 
-// MaxISMixPairReqsPerMsg is the maximum number of pair request mix messages
-// at head per message.
-const MaxISMixPairReqsPerMsg = 100
-
 // MsgInitState implements the Message interface and represents an initial
 // state message.  It is used to receive ephemeral startup information from a
 // remote peer, such as blocks that can be mined upon, votes for such blocks
@@ -34,10 +30,9 @@ const MaxISMixPairReqsPerMsg = 100
 // The content of such a message depends upon what the local peer requested
 // during a previous GetInitState msg.
 type MsgInitState struct {
-	BlockHashes      []chainhash.Hash
-	VoteHashes       []chainhash.Hash
-	TSpendHashes     []chainhash.Hash
-	MixPairReqHashes []chainhash.Hash
+	BlockHashes  []chainhash.Hash
+	VoteHashes   []chainhash.Hash
+	TSpendHashes []chainhash.Hash
 }
 
 // AddBlockHash adds a new block hash to the message. Up to
@@ -82,20 +77,6 @@ func (msg *MsgInitState) AddTSpendHash(hash *chainhash.Hash) error {
 	return nil
 }
 
-// AddMixPairReqHash adds a new mixing pair request message hash to the message.
-// Up to MaxISMixPRsPerMsg may be added before this function errors out.
-func (msg *MsgInitState) AddMixPairReqHash(hash *chainhash.Hash) error {
-	const op = "MsgInitState.AddMixPairReqHash"
-	if len(msg.MixPairReqHashes)+1 > MaxISMixPairReqsPerMsg {
-		msg := fmt.Sprintf("too many mixpairreq hashes for message [max %v]",
-			MaxISMixPairReqsPerMsg)
-		return messageError(op, ErrTooManyManyMixPairReqs, msg)
-	}
-
-	msg.MixPairReqHashes = append(msg.MixPairReqHashes, *hash)
-	return nil
-}
-
 // BtcDecode decodes r using the protocol encoding into the receiver.
 // This is part of the Message interface implementation.
 func (msg *MsgInitState) BtcDecode(r io.Reader, pver uint32) error {
@@ -163,29 +144,6 @@ func (msg *MsgInitState) BtcDecode(r io.Reader, pver uint32) error {
 		}
 	}
 
-	// Read num mixpairreq hashes (when enabled by protocol).
-	if pver < MixVersion {
-		return nil
-	}
-
-	count, err = ReadVarInt(r, pver)
-	if err != nil {
-		return err
-	}
-	if count > MaxISMixPairReqsPerMsg {
-		msg := fmt.Sprintf("too many mixpairreq hashes for message "+
-			"[count %v, max %v]", count, MaxISMixPairReqsPerMsg)
-		return messageError(op, ErrTooManyManyMixPairReqs, msg)
-	}
-
-	msg.MixPairReqHashes = make([]chainhash.Hash, count)
-	for i := uint64(0); i < count; i++ {
-		err := readElement(r, &msg.MixPairReqHashes[i])
-		if err != nil {
-			return err
-		}
-	}
-
 	return nil
 }
 
@@ -259,30 +217,6 @@ func (msg *MsgInitState) BtcEncode(w io.Writer, pver uint32) error {
 		}
 	}
 
-	// Write mixpairreq hashes when enabled by protocol.
-	if pver < MixVersion {
-		return nil
-	}
-
-	count = len(msg.MixPairReqHashes)
-	if count > MaxISMixPairReqsPerMsg {
-		msg := fmt.Sprintf("too many mixpairreq hashes for message "+
-			"[count %v, max %v]", count, MaxISMixPairReqsPerMsg)
-		return messageError(op, ErrTooManyManyMixPairReqs, msg)
-	}
-
-	err = WriteVarInt(w, pver, uint64(count))
-	if err != nil {
-		return err
-	}
-
-	for i := range msg.MixPairReqHashes {
-		err = writeElement(w, &msg.MixPairReqHashes[i])
-		if err != nil {
-			return err
-		}
-	}
-
 	return nil
 }
 
@@ -299,29 +233,21 @@ func (msg *MsgInitState) MaxPayloadLength(pver uint32) uint32 {
 		return 0
 	}
 
-	max := uint32(VarIntSerializeSize(MaxISBlocksAtHeadPerMsg)) +
+	return uint32(VarIntSerializeSize(MaxISBlocksAtHeadPerMsg)) +
 		(MaxISBlocksAtHeadPerMsg * chainhash.HashSize) +
 		uint32(VarIntSerializeSize(MaxISVotesAtHeadPerMsg)) +
 		(MaxISVotesAtHeadPerMsg * chainhash.HashSize) +
 		uint32(VarIntSerializeSize(MaxISTSpendsAtHeadPerMsg)) +
 		(MaxISTSpendsAtHeadPerMsg * chainhash.HashSize)
-
-	if pver >= MixVersion {
-		max += uint32(VarIntSerializeSize(MaxISMixPairReqsPerMsg)) +
-			(MaxISMixPairReqsPerMsg * chainhash.HashSize)
-	}
-
-	return max
 }
 
 // NewMsgInitState returns a new Decred initstate message that conforms to the
 // Message interface using the defaults for the fields.
 func NewMsgInitState() *MsgInitState {
 	return &MsgInitState{
-		BlockHashes:      make([]chainhash.Hash, 0, MaxISBlocksAtHeadPerMsg),
-		VoteHashes:       make([]chainhash.Hash, 0, MaxISVotesAtHeadPerMsg),
-		TSpendHashes:     make([]chainhash.Hash, 0, MaxISTSpendsAtHeadPerMsg),
-		MixPairReqHashes: make([]chainhash.Hash, 0, MaxISMixPairReqsPerMsg),
+		BlockHashes:  make([]chainhash.Hash, 0, MaxISBlocksAtHeadPerMsg),
+		VoteHashes:   make([]chainhash.Hash, 0, MaxISVotesAtHeadPerMsg),
+		TSpendHashes: make([]chainhash.Hash, 0, MaxISTSpendsAtHeadPerMsg),
 	}
 }
 
@@ -356,17 +282,9 @@ func NewMsgInitStateFilled(blockHashes []chainhash.Hash, voteHashes []chainhash.
 		return nil, messageError(op, ErrTooManyTSpends, msg)
 	}
 
-	// Not preallocated.  The purpose of this function is to create a
-	// MsgInitState from already allocated data.  Assume that if the
-	// caller also has mixpairreq hashes to include, they have also been
-	// preallocated, so there is no reason to allocate the memory here
-	// too.
-	var mixPairReqHashes []chainhash.Hash
-
 	return &MsgInitState{
-		BlockHashes:      blockHashes,
-		VoteHashes:       voteHashes,
-		TSpendHashes:     tspendHashes,
-		MixPairReqHashes: mixPairReqHashes,
+		BlockHashes:  blockHashes,
+		VoteHashes:   voteHashes,
+		TSpendHashes: tspendHashes,
 	}, nil
 }
diff --git a/wire/msgmixkeyexchange.go b/wire/msgmixkeyexchange.go
index e7bfa4ab..62c4553f 100644
--- a/wire/msgmixkeyexchange.go
+++ b/wire/msgmixkeyexchange.go
@@ -30,6 +30,7 @@ type MsgMixKeyExchange struct {
 	Signature  [64]byte
 	Identity   [33]byte
 	SessionID  [32]byte
+	Epoch      uint64
 	Run        uint32
 	ECDH       [33]byte   // Secp256k1 public key
 	PQPK       [1218]byte // Sntrup4591761 public key
@@ -53,7 +54,7 @@ func (msg *MsgMixKeyExchange) BtcDecode(r io.Reader, pver uint32) error {
 	}
 
 	err := readElements(r, &msg.Signature, &msg.Identity, &msg.SessionID,
-		&msg.Run, &msg.ECDH, &msg.PQPK, &msg.Commitment)
+		&msg.Epoch, &msg.Run, &msg.ECDH, &msg.PQPK, &msg.Commitment)
 	if err != nil {
 		return err
 	}
@@ -146,8 +147,8 @@ func (msg *MsgMixKeyExchange) writeMessageNoSignature(op string, w io.Writer, pv
 		return messageError(op, ErrTooManyPrevMixMsgs, msg)
 	}
 
-	err := writeElements(w, &msg.Identity, &msg.SessionID, msg.Run,
-		&msg.ECDH, &msg.PQPK, &msg.Commitment)
+	err := writeElements(w, &msg.Identity, &msg.SessionID, msg.Epoch,
+		msg.Run, &msg.ECDH, &msg.PQPK, &msg.Commitment)
 	if err != nil {
 		return err
 	}
@@ -188,7 +189,7 @@ func (msg *MsgMixKeyExchange) MaxPayloadLength(pver uint32) uint32 {
 	}
 
 	// See tests for this calculation.
-	return 17803
+	return 17811
 }
 
 // Pub returns the message sender's public key identity.
@@ -219,12 +220,14 @@ func (msg *MsgMixKeyExchange) GetRun() uint32 {
 // NewMsgMixKeyExchange returns a new mixkeyxchg message that conforms to the
 // Message interface using the passed parameters and defaults for the
 // remaining fields.
-func NewMsgMixKeyExchange(identity [33]byte, sid [32]byte, run uint32,
-	ecdh [33]byte, pqpk [1218]byte, commitment [32]byte, seenPRs []chainhash.Hash) *MsgMixKeyExchange {
+func NewMsgMixKeyExchange(identity [33]byte, sid [32]byte, epoch uint64,
+	run uint32, ecdh [33]byte, pqpk [1218]byte, commitment [32]byte,
+	seenPRs []chainhash.Hash) *MsgMixKeyExchange {
 
 	return &MsgMixKeyExchange{
 		Identity:   identity,
 		SessionID:  sid,
+		Epoch:      epoch,
 		Run:        run,
 		ECDH:       ecdh,
 		PQPK:       pqpk,


Test patch:
diff --git a/bech32/bech32_test.go b/bech32/bech32_test.go
index 4d65f3e4..11f61833 100644
--- a/bech32/bech32_test.go
+++ b/bech32/bech32_test.go
@@ -546,7 +546,7 @@ func BenchmarkConvertBitsDown(b *testing.B) {
 	}
 }
 
-// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior
+// BenchmarkConvertBitsUp benchmarks the speed and memory allocation behavior
 // of ConvertBits when converting from a lower base into a higher base (e.g. 5
 // => 8).
 //
diff --git a/wire/message_test.go b/wire/message_test.go
index fee36296..bcf43112 100644
--- a/wire/message_test.go
+++ b/wire/message_test.go
@@ -84,7 +84,7 @@ func TestMessage(t *testing.T) {
 	if err != nil {
 		t.Errorf("NewMsgMixPairReq: %v", err)
 	}
-	msgMixKE := NewMsgMixKeyExchange([33]byte{}, [32]byte{}, 1, [33]byte{}, [1218]byte{}, [32]byte{}, []chainhash.Hash{})
+	msgMixKE := NewMsgMixKeyExchange([33]byte{}, [32]byte{}, 1, 1, [33]byte{}, [1218]byte{}, [32]byte{}, []chainhash.Hash{})
 	msgMixCT := NewMsgMixCiphertexts([33]byte{}, [32]byte{}, 1, [][1047]byte{}, []chainhash.Hash{})
 	msgMixSR := NewMsgMixSlotReserve([33]byte{}, [32]byte{}, 1, [][][]byte{{{}}}, []chainhash.Hash{})
 	msgMixDC := NewMsgMixDCNet([33]byte{}, [32]byte{}, 1, []MixVect{make(MixVect, 1)}, []chainhash.Hash{})
@@ -121,9 +121,9 @@ func TestMessage(t *testing.T) {
 		{msgCFHeaders, msgCFHeaders, pver, MainNet, 58},
 		{msgCFTypes, msgCFTypes, pver, MainNet, 26},
 		{msgGetInitState, msgGetInitState, pver, MainNet, 25},
-		{msgInitState, msgInitState, pver, MainNet, 28},
+		{msgInitState, msgInitState, pver, MainNet, 27},
 		{msgMixPR, msgMixPR, pver, MainNet, 165},
-		{msgMixKE, msgMixKE, pver, MainNet, 1441},
+		{msgMixKE, msgMixKE, pver, MainNet, 1449},
 		{msgMixCT, msgMixCT, pver, MainNet, 158},
 		{msgMixSR, msgMixSR, pver, MainNet, 161},
 		{msgMixDC, msgMixDC, pver, MainNet, 181},
diff --git a/wire/msginitstate_test.go b/wire/msginitstate_test.go
index 73814cd3..520e7bd5 100644
--- a/wire/msginitstate_test.go
+++ b/wire/msginitstate_test.go
@@ -30,7 +30,7 @@ func TestInitState(t *testing.T) {
 	// Ensure max payload returns the expected value for latest protocol
 	// version. a var int and n * hashes for each of block, vote and tspend
 	// hashes.
-	wantPayload := uint32((1 + 32*8) + (1 + 32*40) + (1 + 32*7) + (1 + 32*100))
+	wantPayload := uint32((1 + 32*8) + (1 + 32*40) + (1 + 32*7))
 	maxPayload := msg.MaxPayloadLength(pver)
 	if maxPayload != wantPayload {
 		t.Errorf("MaxPayloadLength: wrong max payload length for "+
@@ -119,7 +119,6 @@ func TestInitStateWire(t *testing.T) {
 		0x00, // Varint for number of blocks
 		0x00, // Varint for number of votes
 		0x00, // Varint for number of tspends
-		0x00, // Varint for number of mixpairreqs
 	}
 
 	fakeBlock1, _ := chainhash.NewHashFromStr("4433221144332211443322114" +
@@ -138,12 +137,6 @@ func TestInitStateWire(t *testing.T) {
 		"999999999999999999991199999999999999919")
 	fakeTSpend3, _ := chainhash.NewHashFromStr("aaaaaaaaaaaa9200aaaaaa" +
 		"aaaaaaaaaaaaaaaaaaa9a9a9aaaaaaaaaaaaaaa")
-	fakeMixPairReq1, _ := chainhash.NewHashFromStr("bbbbbbbbbbbb9200bbbbbb" +
-		"bbbbbbbbbbbbbbbbbbb9b9b9bbbbbbbbbbbbbbb")
-	fakeMixPairReq2, _ := chainhash.NewHashFromStr("cccccccccccc9200cccccc" +
-		"ccccccccccccccccccc9c9c9ccccccccccccccc")
-	fakeMixPairReq3, _ := chainhash.NewHashFromStr("dddddddddddd9200dddddd" +
-		"ddddddddddddddddddd9d9d9ddddddddddddddd")
 
 	// MsgInitState message with multiple values for each hash.
 	multiData := NewMsgInitState()
@@ -155,9 +148,6 @@ func TestInitStateWire(t *testing.T) {
 	multiData.AddTSpendHash(fakeTSpend1)
 	multiData.AddTSpendHash(fakeTSpend2)
 	multiData.AddTSpendHash(fakeTSpend3)
-	multiData.AddMixPairReqHash(fakeMixPairReq1)
-	multiData.AddMixPairReqHash(fakeMixPairReq2)
-	multiData.AddMixPairReqHash(fakeMixPairReq3)
 
 	multiDataEncoded := []byte{
 		0x02,                                           // Varint for number of block hashes
@@ -195,19 +185,6 @@ func TestInitStateWire(t *testing.T) {
 		0x9a, 0x9a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
 		0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x20,
 		0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00,
-		0x03,                                           // Varint for number of mixpr hashes
-		0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x9b, // Fake mixpairreq 1
-		0x9b, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
-		0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x20,
-		0xb9, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x00,
-		0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x9c, // Fake mixpairreq 2
-		0x9c, 0x9c, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
-		0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c, 0x20,
-		0xc9, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c, 0x00,
-		0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x9d, // Fake mixpairreq 3
-		0x9d, 0x9d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-		0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x0d, 0x20,
-		0xd9, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x0d, 0x00,
 	}
 
 	tests := []struct {
@@ -278,12 +255,6 @@ func TestInitStateWireErrors(t *testing.T) {
 		"999999999999999999991199999999999999919")
 	fakeTSpend3, _ := chainhash.NewHashFromStr("aaaaaaaaaaaa9200aaaaaa" +
 		"aaaaaaaaaaaaaaaaaaa9a9a9aaaaaaaaaaaaaaa")
-	fakeMixPairReq1, _ := chainhash.NewHashFromStr("bbbbbbbbbbbb9200bbbbbb" +
-		"bbbbbbbbbbbbbbbbbbb9b9b9bbbbbbbbbbbbbbb")
-	fakeMixPairReq2, _ := chainhash.NewHashFromStr("cccccccccccc9200cccccc" +
-		"ccccccccccccccccccc9c9c9ccccccccccccccc")
-	fakeMixPairReq3, _ := chainhash.NewHashFromStr("dddddddddddd9200dddddd" +
-		"ddddddddddddddddddd9d9d9ddddddddddddddd")
 
 	// MsgInitState message with multiple values for each hash.
 	baseMsg := NewMsgInitState()
@@ -295,9 +266,6 @@ func TestInitStateWireErrors(t *testing.T) {
 	baseMsg.AddTSpendHash(fakeTSpend1)
 	baseMsg.AddTSpendHash(fakeTSpend2)
 	baseMsg.AddTSpendHash(fakeTSpend3)
-	baseMsg.AddMixPairReqHash(fakeMixPairReq1)
-	baseMsg.AddMixPairReqHash(fakeMixPairReq2)
-	baseMsg.AddMixPairReqHash(fakeMixPairReq3)
 
 	baseMsgEncoded := []byte{
 		0x02,                                           // Varint for number of block hashes
@@ -335,19 +303,6 @@ func TestInitStateWireErrors(t *testing.T) {
 		0x9a, 0x9a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
 		0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x20,
 		0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x00,
-		0x03,                                           // Varint for number of mixpairreq hashes
-		0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x9b, // Fake mixpairreq 1
-		0x9b, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
-		0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x20,
-		0xb9, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x0b, 0x00,
-		0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x9c, // Fake mixpairreq 2
-		0x9c, 0x9c, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
-		0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c, 0x20,
-		0xc9, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c, 0x00,
-		0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x9d, // Fake mixpairreq 3
-		0x9d, 0x9d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-		0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x0d, 0x20,
-		0xd9, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x0d, 0x00,
 	}
 
 	// Message that forces an error by having more than the max allowed
diff --git a/wire/msgmixkeyexchange_test.go b/wire/msgmixkeyexchange_test.go
index e5f83a32..94118c97 100644
--- a/wire/msgmixkeyexchange_test.go
+++ b/wire/msgmixkeyexchange_test.go
@@ -21,18 +21,19 @@ func newTestMixKeyExchange() *MsgMixKeyExchange {
 	id := *(*[33]byte)(repeat(0x81, 33))
 	sid := *(*[32]byte)(repeat(0x82, 32))
 
-	const run = uint32(0x83838383)
+	const epoch = uint64(0x8383838383838383)
+	const run = uint32(0x84848484)
 
-	ecdh := *(*[33]byte)(repeat(0x84, 33))
-	pqpk := *(*[1218]byte)(repeat(0x85, 1218))
-	commitment := *(*[32]byte)(repeat(0x86, 32))
+	ecdh := *(*[33]byte)(repeat(0x85, 33))
+	pqpk := *(*[1218]byte)(repeat(0x86, 1218))
+	commitment := *(*[32]byte)(repeat(0x87, 32))
 
 	seenPRs := make([]chainhash.Hash, 4)
-	for b := byte(0x87); b < 0x8B; b++ {
-		copy(seenPRs[b-0x87][:], repeat(b, 32))
+	for b := byte(0x88); b < 0x8C; b++ {
+		copy(seenPRs[b-0x88][:], repeat(b, 32))
 	}
 
-	ke := NewMsgMixKeyExchange(id, sid, run, ecdh, pqpk, commitment, seenPRs)
+	ke := NewMsgMixKeyExchange(id, sid, epoch, run, ecdh, pqpk, commitment, seenPRs)
 	ke.Signature = sig
 
 	return ke
@@ -53,16 +54,17 @@ func TestMsgMixKeyExchangeWire(t *testing.T) {
 	expected = append(expected, repeat(0x80, 64)...)   // Signature
 	expected = append(expected, repeat(0x81, 33)...)   // Identity
 	expected = append(expected, repeat(0x82, 32)...)   // Session ID
-	expected = append(expected, repeat(0x83, 4)...)    // Run
-	expected = append(expected, repeat(0x84, 33)...)   // ECDH public key
-	expected = append(expected, repeat(0x85, 1218)...) // PQ public key
-	expected = append(expected, repeat(0x86, 32)...)   // Secrets commitment
-	// Four seen PRs (repeating 32 bytes of 0x87, 0x88, 0x89, 0x8a)
+	expected = append(expected, repeat(0x83, 8)...)    // Epoch
+	expected = append(expected, repeat(0x84, 4)...)    // Run
+	expected = append(expected, repeat(0x85, 33)...)   // ECDH public key
+	expected = append(expected, repeat(0x86, 1218)...) // PQ public key
+	expected = append(expected, repeat(0x87, 32)...)   // Secrets commitment
+	// Four seen PRs (repeating 32 bytes of 0x88, 0x89, 0x8a, 0x8b)
 	expected = append(expected, 0x04)
-	expected = append(expected, repeat(0x87, 32)...)
 	expected = append(expected, repeat(0x88, 32)...)
 	expected = append(expected, repeat(0x89, 32)...)
 	expected = append(expected, repeat(0x8a, 32)...)
+	expected = append(expected, repeat(0x8b, 32)...)
 
 	expectedSerializationEqual(t, buf.Bytes(), expected)
 
@@ -155,6 +157,7 @@ func TestMsgMixKeyExchangeMaxPayloadLength(t *testing.T) {
 	var expectedLen uint32 = 64 + // Signature
 		33 + // Identity
 		32 + // Session ID
+		8 + // Epoch
 		4 + // Run
 		33 + // ECDH public key
 		1218 + // sntrup4591761 public key