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 Aug 2, 2023
1 parent 7fe6d93 commit 8aa7abb
Show file tree
Hide file tree
Showing 22 changed files with 2,596 additions and 5 deletions.
91 changes: 91 additions & 0 deletions wire/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

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

const (
Expand Down Expand Up @@ -322,6 +323,38 @@ func readElement(r io.Reader, element interface{}) error {
}
return nil

// Mix identity
case *[33]byte:
_, err := io.ReadFull(r, e[:])
if err != nil {
return err
}
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 +410,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 +488,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 Expand Up @@ -765,3 +843,16 @@ func isStrictAscii(s string) bool {

return true
}

// mustHash returns the hash of the serialized message. If message
// serialization errors, it panics with a wrapped error.
func mustHash(msg Message, pver uint32) chainhash.Hash {
h := blake256.New()
err := msg.BtcEncode(h, pver)
if err != nil {
err := fmt.Errorf("hash of %T failed due to serialization "+
"error: %w", msg, err)
panic(err)
}
return *(*chainhash.Hash)(h.Sum(nil))
}
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
6 changes: 2 additions & 4 deletions wire/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ go 1.17
require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/chaincfg/chainhash v1.0.4
github.com/decred/dcrd/crypto/blake256 v1.0.1
lukechampine.com/blake3 v1.2.1
)

require (
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
)
require github.com/klauspost/cpuid/v2 v2.0.9 // indirect
2 changes: 2 additions & 0 deletions wire/invvect.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
InvTypeTx InvType = 1
InvTypeBlock InvType = 2
InvTypeFilteredBlock InvType = 3
InvTypeMix InvType = 4
)

// Map of service flags back to their constant names for pretty printing.
Expand All @@ -38,6 +39,7 @@ var ivStrings = map[InvType]string{
InvTypeTx: "MSG_TX",
InvTypeBlock: "MSG_BLOCK",
InvTypeFilteredBlock: "MSG_FILTERED_BLOCK",
InvTypeMix: "MSG_MIX",
}

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

// Message is an interface that describes a Decred message. A type that
Expand Down Expand Up @@ -168,6 +175,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
84 changes: 84 additions & 0 deletions wire/mixvec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 (
"bytes"
"fmt"
"strings"
)

// MixVec is a N-element vector of Msize []byte messages.
type MixVec struct {
N uint32
Msize uint32
Data []byte
}

// NewMixVec returns a zero vector for holding n messages of msize length.
func NewMixVec(n, msize uint32) *MixVec {
return &MixVec{
N: n,
Msize: msize,
Data: make([]byte, n*msize),
}
}

// IsDim returns whether the Vec has dimensions n-by-msize.
func (v *MixVec) IsDim(n, msize uint32) bool {
return v.N == n && v.Msize == msize && len(v.Data) == int(n*msize)
}

// Equals returns whether the two vectors have equal dimensions and data.
func (v *MixVec) Equals(other *MixVec) bool {
return other.IsDim(v.N, v.Msize) && bytes.Equal(other.Data, v.Data)
}

// M returns the i'th message of the vector.
func (v *MixVec) M(i int) []byte {
off := uint32(i) * v.Msize
return v.Data[off : off+v.Msize]
}

func (v *MixVec) String() string {
b := new(strings.Builder)
b.Grow(2 + int(v.N*(2*v.Msize+1)))
b.WriteString("[")
for i := 0; uint32(i) < v.N; i++ {
if i != 0 {
b.WriteString(" ")
}
fmt.Fprintf(b, "%x", v.M(i))
}
b.WriteString("]")
return b.String()
}

// Xor writes the xor of each vector element of src1 and src2 into v.
// Source and destination vectors are allowed to be equal.
// Panics if vectors do not share identical dimensions.
func (v *MixVec) Xor(src1, src2 *MixVec) {
switch {
case v.N != src1.N, v.Msize != src1.Msize, len(v.Data) != len(src1.Data):
fallthrough
case v.N != src2.N, v.Msize != src2.Msize, len(v.Data) != len(src2.Data):
panic("dcnet: vectors do not share identical dimensions")
}
for i := range v.Data {
v.Data[i] = src1.Data[i] ^ src2.Data[i]
}
}

// XorVectors calculates the xor of all vectors.
// Panics if vectors do not share identical dimensions.
func XorVectors(vs ...MixVec) MixVec {
n := uint32(len(vs))
msize := vs[0].Msize
res := NewMixVec(n, msize)
for i := range vs {
res.Xor(res, &vs[i])
}
return *res
}
4 changes: 4 additions & 0 deletions wire/msggetinitstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const (
// InitStateTSpends is the init state type used to request tpends for
// voting.
InitStateTSpends = "tspends"

// InitStateMixPRs is the init state type used to request mixing pair
// request messages.
InitStateMixPRs = "mixprs"
)

// MsgGetInitState implements the Message interface and represents a
Expand Down
1 change: 1 addition & 0 deletions wire/msginitstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type MsgInitState struct {
BlockHashes []chainhash.Hash
VoteHashes []chainhash.Hash
TSpendHashes []chainhash.Hash
MixPRHashes []chainhash.Hash // XXX serialize this depending on the protocol version
}

// AddBlockHash adds a new block hash to the message. Up to
Expand Down
Loading

0 comments on commit 8aa7abb

Please sign in to comment.