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 Nov 10, 2023
1 parent bcc0db1 commit 842398e
Show file tree
Hide file tree
Showing 26 changed files with 2,622 additions and 8 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
5 changes: 4 additions & 1 deletion wire/error_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2017 The btcsuite developers
// Copyright (c) 2015-2020 The Decred developers
// Copyright (c) 2015-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand Down Expand Up @@ -50,6 +50,9 @@ func TestMessageErrorCodeStringer(t *testing.T) {
{ErrTooManyInitStateTypes, "ErrTooManyInitStateTypes"},
{ErrInitStateTypeTooLong, "ErrInitStateTypeTooLong"},
{ErrTooManyTSpends, "ErrTooManyTSpends"},
{ErrMixPRScriptClassTooLong, "ErrMixPRScriptClassTooLong"},
{ErrTooManyMixPRUTXOs, "ErrTooManyMixPRUTXOs"},
{ErrTooManyPrevMixMsgs, "ErrTooManyPrevMixMsgs"},

{0xffff, "Unknown ErrorCode (65535)"},
}
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
3 changes: 2 additions & 1 deletion wire/invvect_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Copyright (c) 2015-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -23,6 +23,7 @@ func TestInvTypeStringer(t *testing.T) {
{InvTypeError, "ERROR"},
{InvTypeTx, "MSG_TX"},
{InvTypeBlock, "MSG_BLOCK"},
{InvTypeMix, "MSG_MIX"},
{0xffffffff, "Unknown InvType (4294967295)"},
}

Expand Down
28 changes: 28 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,27 @@ 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{}

case CmdMixRS:
msg = &MsgMixRS{}

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

Expand Down Expand Up @@ -80,6 +80,16 @@ func TestMessage(t *testing.T) {
msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
msgGetInitState := NewMsgGetInitState()
msgInitState := NewMsgInitState()
msgMixPR, err := NewMsgMixPR([33]byte{}, 1, 1, "", 1, 1, 1, 1, []MixPRUTXO{}, NewTxOut(0, []byte{}))
if err != nil {
t.Errorf("NewMsgMixPR: %v", err)
}
msgMixKE := NewMsgMixKE([33]byte{}, [32]byte{}, 1, 1, [33]byte{}, [1218]byte{}, [32]byte{}, []chainhash.Hash{})
msgMixCT := NewMsgMixCT([33]byte{}, [32]byte{}, 1, 1, [][1047]byte{}, []chainhash.Hash{})
msgMixSR := NewMsgMixSR([33]byte{}, [32]byte{}, 1, 1, [][][]byte{{{}}}, []chainhash.Hash{})
msgMixDC := NewMsgMixDC([33]byte{}, [32]byte{}, 1, 1, []MixVec{*NewMixVec(1, 1)}, []chainhash.Hash{})
msgMixCM := NewMsgMixCM([33]byte{}, [32]byte{}, 1, 1, NewMsgTx(), []chainhash.Hash{})
msgMixRS := NewMsgMixRS([33]byte{}, [32]byte{}, 1, 1, [32]byte{}, [][]byte{}, [][]byte{})

tests := []struct {
in Message // Value to encode
Expand Down Expand Up @@ -112,6 +122,13 @@ func TestMessage(t *testing.T) {
{msgCFTypes, msgCFTypes, pver, MainNet, 26},
{msgGetInitState, msgGetInitState, pver, MainNet, 25},
{msgInitState, msgInitState, pver, MainNet, 27},
{msgMixPR, msgMixPR, pver, MainNet, 169},
{msgMixKE, msgMixKE, pver, MainNet, 1449},
{msgMixCT, msgMixCT, pver, MainNet, 166},
{msgMixSR, msgMixSR, pver, MainNet, 169},
{msgMixDC, msgMixDC, pver, MainNet, 171},
{msgMixCM, msgMixCM, pver, MainNet, 181},
{msgMixRS, msgMixRS, pver, MainNet, 213},
}

t.Logf("Running %d tests", len(tests))
Expand Down
45 changes: 45 additions & 0 deletions wire/mixvec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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"
"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),
}
}

func (v *MixVec) String() string {
m := func(i int) []byte {
off := uint32(i) * v.Msize
return v.Data[off : off+v.Msize]
}

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", m(i))
}
b.WriteString("]")
return b.String()
}
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 842398e

Please sign in to comment.