Skip to content

Commit

Permalink
wire: add p2p mixing messages
Browse files Browse the repository at this point in the history
These messages implement the stages of a cspp mix.  Messages will be
broadcast to all full nodes and all peers participating in mixing
through inventory messages.
  • Loading branch information
jrick committed Mar 8, 2023
1 parent 1510b02 commit a83f02a
Show file tree
Hide file tree
Showing 16 changed files with 2,273 additions and 0 deletions.
69 changes: 69 additions & 0 deletions wire/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,30 @@ func readElement(r io.Reader, element interface{}) error {
}
return nil

// Mix signature
case *[64]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil

// sntrup4591651 ciphertext
case *[1047]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil

// sntrup4591651 public key
case *[1218]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
return nil

case *ServiceFlag:
rv, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
Expand Down Expand Up @@ -377,6 +401,20 @@ func writeElement(w io.Writer, element interface{}) error {
// Attempt to write the element based on the concrete type via fast
// type assertions first.
switch e := element.(type) {
case uint8:
err := binarySerializer.PutUint8(w, e)
if err != nil {
return err
}
return nil

case uint16:
err := binarySerializer.PutUint16(w, littleEndian, e)
if err != nil {
return err
}
return nil

case int32:
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
if err != nil {
Expand Down Expand Up @@ -441,13 +479,44 @@ func writeElement(w io.Writer, element interface{}) error {
}
return nil

case *[32]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil

case *chainhash.Hash:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil

// Mix signature
case *[64]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil

// sntrup4591761 ciphertext
case *[1047]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil

// sntrup4591761 public key
case *[1218]byte:
_, err := w.Write(e[:])
if err != nil {
return err
}
return nil

case ServiceFlag:
err := binarySerializer.PutUint64(w, littleEndian, uint64(e))
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions wire/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ const (
// ErrTooManyTSpends is returned when the number of tspend hashes
// exceeds the maximum allowed.
ErrTooManyTSpends

// ErrMixPRScriptClassTooLong is returned when a mixing script class
// type string is longer than allowed by the protocol.
ErrMixPRScriptClassTooLong

// ErrTooManyMixPRUTXOs is returned when a MixPR message contains
// more UTXOs than allowed by the protocol.
ErrTooManyMixPRUTXOs

// ErrTooManyPrevMixMsgs is returned when too many previous messages of
// a mix run are referenced by a message.
ErrTooManyPrevMixMsgs
)

// Map of ErrorCode values back to their constant names for pretty printing.
Expand Down Expand Up @@ -168,6 +180,9 @@ var errorCodeStrings = map[ErrorCode]string{
ErrTooManyInitStateTypes: "ErrTooManyInitStateTypes",
ErrInitStateTypeTooLong: "ErrInitStateTypeTooLong",
ErrTooManyTSpends: "ErrTooManyTSpends",
ErrMixPRScriptClassTooLong: "ErrMixPRScriptClassTooLong",
ErrTooManyMixPRUTXOs: "ErrTooManyMixPRUTXOs",
ErrTooManyPrevMixMsgs: "ErrTooManyPrevMixMsgs",
}

// String returns the ErrorCode as a human-readable name.
Expand Down
24 changes: 24 additions & 0 deletions wire/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ const (
CmdCFilterV2 = "cfilterv2"
CmdGetInitState = "getinitstate"
CmdInitState = "initstate"
CmdMixPR = "mixpr"
CmdMixKE = "mixke"
CmdMixCT = "mixct"
CmdMixSR = "mixsr"
CmdMixDC = "mixdc"
CmdMixCM = "mixcm"
)

// Message is an interface that describes a Decred message. A type that
Expand Down Expand Up @@ -168,6 +174,24 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdInitState:
msg = &MsgInitState{}

case CmdMixPR:
msg = &MsgMixPR{}

case CmdMixKE:
msg = &MsgMixKE{}

case CmdMixCT:
msg = &MsgMixCT{}

case CmdMixSR:
msg = &MsgMixSR{}

case CmdMixDC:
msg = &MsgMixDC{}

case CmdMixCM:
msg = &MsgMixCM{}

default:
str := fmt.Sprintf("unhandled command [%s]", command)
return nil, messageError(op, ErrUnknownCmd, str)
Expand Down
212 changes: 212 additions & 0 deletions wire/msgmixcm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// Copyright (c) 2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
"fmt"
"io"

"github.com/decred/dcrd/chaincfg/chainhash"
)

// MsgMixCM contains a partially-signed mix transaction, with signatures
// contributed from the peer identity. When all CM messages are received,
// signatures can be merged and the transaction may be published, ending a
// successful mix session.
//
// It implements the Message interface.
type MsgMixCM struct {
Signature [64]byte
Identity [32]byte
SessionID [32]byte
Expiry int64
Run uint32
Mix *MsgTx
SeenDCs []chainhash.Hash // XXX may be unnecessary; depends on the blaming message
}

// BtcDecode decodes r using the Decred protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgMixCM) BtcDecode(r io.Reader, pver uint32) error {
const op = "MsgMixCM.BtcDecode"
if pver < MixVersion {
msg := fmt.Sprintf("%s message invalid for protocol version %d",
msg.Command(), pver)
return messageError(op, ErrMsgInvalidForPVer, msg)
}

err := readElements(r, &msg.Signature, &msg.Identity, &msg.SessionID,
&msg.Expiry, &msg.Run)
if err != nil {
return err
}

if msg.Mix == nil {
msg.Mix = NewMsgTx()
}
err = msg.Mix.BtcDecode(r, pver)
if err != nil {
return err
}

count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxPrevMixMsgs {
msg := fmt.Sprintf("too many previous referenced messages [%v]", count)
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.SeenDCs = seen

return nil
}

// BtcEncode encodes the receiver to w using the Decred protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgMixCM) BtcEncode(w io.Writer, pver uint32) error {
const op = "MsgMixCM.BtcEncode"
if pver < MixVersion {
msg := fmt.Sprintf("%s message invalid for protocol version %d",
msg.Command(), pver)
return messageError(op, ErrMsgInvalidForPVer, msg)
}

err := writeElement(w, &msg.Signature)
if err != nil {
return err
}

err = msg.writeMessageNoSignature(op, w, pver)
if err != nil {
return err
}

return nil
}

// writeMessageNoSignature serializes all elements of the message except for
// the signature. This allows code reuse between message serialization, and
// signing and verifying these message contents.
func (msg *MsgMixCM) writeMessageNoSignature(op string, w io.Writer, pver uint32) error {
err := writeElements(w, &msg.Identity, &msg.SessionID, msg.Expiry,
msg.Run)
if err != nil {
return err
}

err = msg.Mix.BtcEncode(w, pver)
if err != nil {
return err
}

count := len(msg.SeenDCs)
if count > MaxPrevMixMsgs {
msg := fmt.Sprintf("too many previous referenced messages [%v]", count)
return messageError(op, ErrTooManyPrevMixMsgs, msg)
}

err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for i := range msg.SeenDCs {
err = writeElement(w, &msg.SeenDCs[i])
if err != nil {
return err
}
}

return nil
}

// WriteSigned writes a tag identifying the message data, followed by all
// message fields excluding the signature. This is the data committed to when
// the message is signed.
func (msg *MsgMixCM) WriteSigned(w io.Writer) error {
const op = "MsgMixCM.WriteSigned"

err := WriteVarString(w, MixVersion, CmdMixCM+"-sig")
if err != nil {
return err
}

err = msg.writeMessageNoSignature(op, w, MixVersion)
if err != nil {
return err
}

return nil
}

// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgMixCM) Command() string {
return CmdMixCM
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgMixCM) MaxPayloadLength(pver uint32) uint32 {
return 0 // XXX 32 + 32 + MaxTxSize + 4 + 64
}

// Hash returns the hash of the serialized message.
func (msg *MsgMixCM) Hash() chainhash.Hash {
return mustHash(msg, MixVersion)
}

// GetIdentity returns the message sender's public key identity.
func (msg *MsgMixCM) GetIdentity() []byte {
return msg.Identity[:]
}

// GetSignature returns the message signature.
func (msg *MsgMixCM) GetSignature() []byte {
return msg.Signature[:]
}

// Expires returns the block height at which the message expires.
func (msg *MsgMixCM) Expires() int64 {
return msg.Expiry
}

// PrevMsgs returns the previous DC messages seen by the peer.
func (msg *MsgMixCM) PrevMsgs() []chainhash.Hash {
return msg.SeenDCs
}

// Sid returns the session ID.
func (msg *MsgMixCM) Sid() []byte {
return msg.SessionID[:]
}

// GetRun returns the run number.
func (msg *MsgMixCM) GetRun() uint32 {
return msg.Run
}

// NewMsgMixCM returns a new mixke message that conforms to the Message
// interface using the passed parameters and defaults for the remaining fields.
func NewMsgMixCM(identity [32]byte, sid [32]byte, expiry int64, run uint32,
mix *MsgTx, seenDCs []chainhash.Hash) *MsgMixCM {

return &MsgMixCM{
Identity: identity,
SessionID: sid,
Expiry: expiry,
Run: run,
Mix: mix,
SeenDCs: seenDCs,
}
}
Loading

0 comments on commit a83f02a

Please sign in to comment.