diff --git a/wire/msgmixsecrets.go b/wire/msgmixsecrets.go index 53d0b06e2d..7532cc0bf5 100644 --- a/wire/msgmixsecrets.go +++ b/wire/msgmixsecrets.go @@ -25,6 +25,7 @@ type MsgMixSecrets struct { Seed [32]byte SlotReserveMsgs [][]byte DCNetMsgs MixVect + SeenSecrets []chainhash.Hash // hash records the hash of the message. It is a member of the // message for convenience and performance, but is never automatically @@ -43,6 +44,9 @@ func writeMixVect(op string, w io.Writer, pver uint32, vec MixVect) error { if err != nil { return err } + if len(vec) == 0 { + return nil + } err = WriteVarInt(w, pver, MixMsgSize) if err != nil { return err @@ -138,6 +142,25 @@ func (msg *MsgMixSecrets) BtcDecode(r io.Reader, pver uint32) error { } msg.DCNetMsgs = dcnetMsgs + count, err := ReadVarInt(r, pver) + if err != nil { + return err + } + if count > MaxMixPeers { + msg := fmt.Sprintf("too many previous referenced messages [count %v, max %v]", + count, MaxMixPeers) + return messageError(op, ErrTooManyPrevMixMsgs, msg) + } + + seen := make([]chainhash.Hash, count) + for i := range seen { + err := readElement(r, &seen[i]) + if err != nil { + return err + } + } + msg.SeenSecrets = seen + return nil } @@ -173,6 +196,17 @@ func (msg *MsgMixSecrets) Hash() chainhash.Hash { return msg.hash } +// Commitment returns a hash committing to the contents of the reveal secrets +// message without committing to any previous seen messages or the message +// signature. This is the hash that is referenced by peers' key exchange +// messages. +func (msg *MsgMixSecrets) Commitment(h hash.Hash) chainhash.Hash { + msgCopy := *msg + msgCopy.SeenSecrets = nil + msgCopy.WriteHash(h) + return msgCopy.hash +} + // WriteHash serializes the message to a hasher and records the sum in the // message's Hash field. // @@ -196,6 +230,14 @@ func (msg *MsgMixSecrets) WriteHash(h hash.Hash) { // // This method never errors for invalid message construction. func (msg *MsgMixSecrets) writeMessageNoSignature(op string, w io.Writer, pver uint32) error { + // Limit to max previous messages hashes. + count := len(msg.SeenSecrets) + if count > MaxMixPeers { + msg := fmt.Sprintf("too many previous referenced messages [count %v, max %v]", + count, MaxMixPeers) + return messageError(op, ErrTooManyPrevMixMsgs, msg) + } + err := writeElements(w, &msg.Identity, &msg.SessionID, msg.Run, &msg.Seed) if err != nil { return err @@ -217,6 +259,17 @@ func (msg *MsgMixSecrets) writeMessageNoSignature(op string, w io.Writer, pver u return err } + err = WriteVarInt(w, pver, uint64(count)) + if err != nil { + return err + } + for i := range msg.SeenSecrets { + err := writeElement(w, &msg.SeenSecrets[i]) + if err != nil { + return err + } + } + return nil } @@ -242,7 +295,7 @@ func (msg *MsgMixSecrets) MaxPayloadLength(pver uint32) uint32 { } // See tests for this calculation - return 54444 + return 70831 } // Pub returns the message sender's public key identity. @@ -255,13 +308,14 @@ func (msg *MsgMixSecrets) Sig() []byte { return msg.Signature[:] } -// PrevMsgs returns nil. Previous messages are not needed to perform blame -// assignment, because of the assumption that all previous messages must have -// been received for a blame stage to be necessary. Additionally, a -// commitment to the secrets message is included in the key exchange, and -// future message hashes are not available at that time. +// PrevMsgs returns previously revealed secrets messages by other peers. An +// honest peer who needs to report blame assignment does not need to reference +// any previous secrets messages, and a secrets message with other referenced +// secrets is necessary to begin blame assignment. Dishonest peers who +// initially reveal their secrets without blame assignment being necessary are +// themselves removed in future runs. func (msg *MsgMixSecrets) PrevMsgs() []chainhash.Hash { - return nil + return msg.SeenSecrets } // Sid returns the session ID. @@ -287,5 +341,6 @@ func NewMsgMixSecrets(identity [33]byte, sid [32]byte, run uint32, Seed: seed, SlotReserveMsgs: slotReserveMsgs, DCNetMsgs: dcNetMsgs, + SeenSecrets: make([]chainhash.Hash, 0), } } diff --git a/wire/msgmixsecrets_test.go b/wire/msgmixsecrets_test.go index 2905d94eb4..e0fc7935a4 100644 --- a/wire/msgmixsecrets_test.go +++ b/wire/msgmixsecrets_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/decred/dcrd/chaincfg/chainhash" ) func newTestMixSecrets() *MsgMixSecrets { @@ -34,7 +35,13 @@ func newTestMixSecrets() *MsgMixSecrets { copy(m[b-0x89][:], repeat(b, 20)) } + seenRSs := make([]chainhash.Hash, 4) + for b := byte(0x8D); b < 0x91; b++ { + copy(seenRSs[b-0x8D][:], repeat(b, 32)) + } + rs := NewMsgMixSecrets(id, sid, run, seed, sr, m) + rs.SeenSecrets = seenRSs rs.Signature = sig return rs @@ -67,12 +74,18 @@ func TestMsgMixSecretsWire(t *testing.T) { expected = append(expected, repeat(0x87, 32)...) expected = append(expected, 0x20) expected = append(expected, repeat(0x88, 32)...) - // Four slot reservation mixed messages (repeating 20 bytes of 0x89, 0x8a, 0x8b, 0x8c) + // Four xor dc-net mixed messages (repeating 20 bytes of 0x89, 0x8a, 0x8b, 0x8c) expected = append(expected, 0x04, 0x14) expected = append(expected, repeat(0x89, 20)...) expected = append(expected, repeat(0x8a, 20)...) expected = append(expected, repeat(0x8b, 20)...) expected = append(expected, repeat(0x8c, 20)...) + // Four seen RSs (repeating 32 bytes of 0x8d, 0x8e, 0x8f, 0x90) + expected = append(expected, 0x04) + expected = append(expected, repeat(0x8d, 32)...) + expected = append(expected, repeat(0x8e, 32)...) + expected = append(expected, repeat(0x8f, 32)...) + expected = append(expected, repeat(0x90, 32)...) expectedSerializationEqual(t, buf.Bytes(), expected) @@ -168,7 +181,9 @@ func TestMsgMixSecretsMaxPayloadLength(t *testing.T) { MaxMixMcount*varBytesLen(MaxMixFieldValLen) + // Unpadded SR values uint32(VarIntSerializeSize(MaxMixMcount)) + // DC-net message count uint32(VarIntSerializeSize(MixMsgSize)) + // DC-net message size - MaxMixMcount*MixMsgSize // DC-net messages + MaxMixMcount*MixMsgSize + // DC-net messages + uint32(VarIntSerializeSize(MaxMixPeers)) + // RS count + 32*MaxMixPeers // RS hashes tests := []struct { name string