Compare commits

...

26 Commits

Author SHA1 Message Date
Cassandra Heart
048b6459c3
don't locally shadow variable 2024-10-14 16:44:03 -07:00
Cassandra Heart
e87ec00c89
v2.0.0-p3 (#299) 2024-10-14 14:41:40 -07:00
Cassandra Heart
aec21504bc
add prover pause cmd 2024-10-14 11:15:38 -07:00
Cassandra Heart
763cf6d2ca
v2.0.0-p2 – bad max start for seniority in first retro (#298) 2024-10-14 10:12:01 -07:00
Cassandra Heart
389ada9f28
v2.0.0-p1 + QClient (#296) 2024-10-13 18:37:19 -07:00
Cassandra Heart
2f03d021c7
not -p1 2024-10-12 12:39:39 -07:00
Cassandra Heart
b4051ccbc9
detangling merge of main node for v2 (#293) 2024-10-12 11:55:17 -07:00
Tyler Sturos
ab065006b4
Remove extra difficulty check for proofs (#289)
* Remove extra difficulty check for proofs

* remove need for underflow check

---------

Co-authored-by: Base Dev <gitlab.dollop533@passmail.net>
2024-09-03 17:40:50 -05:00
ninj696
025aa9baa4
add new bootstrap peer (#286) 2024-08-29 03:27:05 -05:00
Cassandra Heart
f640c09008
v1.4.21-p1 (#279) 2024-07-29 12:46:36 -05:00
Cassandra Heart
819d49d659
[2.0 preflight] Step 3, Stage 1 - Settled pre-mainnet bridge events (#278) 2024-07-20 01:49:55 -05:00
Zephyrsailor
4292a7e3dc
Add a new bootstrap node (#277)
Co-authored-by: zephyr <zephyr@zephyrs-MacBook-Pro.local>
Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
2024-07-19 19:30:10 -05:00
hhwill
9446375cb1
Add a bootstrap peer (#271)
Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
2024-07-19 19:25:34 -05:00
InkyWang
3bf5521b36
Update config.go (#273)
Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
2024-07-19 19:23:24 -05:00
PaddingMe
f90609fe83
add bootstarp peer nodes (#275)
* run a bootstrap peer

* Update config.go
2024-07-19 19:21:36 -05:00
mjessup
ac1b98dfda
Add new Bootstrap Peer (#276)
* Add new Bootstrap Peer Update config.go

Added info for bootstrap peer

* Update config.go

needed preceding slash

---------

Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
2024-07-19 19:15:26 -05:00
scmart
a89d423c7a
change dht peer (#270)
* change dht peer

* Update config.go

---------

Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
2024-07-18 21:57:41 -05:00
Vector
e34467874b
Add a bootstrap peer (#268) 2024-07-18 21:52:43 -05:00
ninj696
adc55e5166
Add a bootstrap peer (#267) 2024-07-17 14:45:16 -05:00
Tyler Sturos
6167530b15
Add bravo-1 qcommander.sh bootstrap peer entry (#265) 2024-07-16 17:42:56 -05:00
Agost Biro
a48cf98b99
Remove bootstrap peer (#264) 2024-07-16 15:05:52 -05:00
Orlando Hernandez
15988c4546
Add Bootstrap Peer (#263) 2024-07-16 15:05:37 -05:00
Demipoet
2479c9cc5f
adding new bootstrap node ..demi (#262)
Co-authored-by: Vic Vi <vicvi@Vics-MacBook-Pro.local>
2024-07-16 01:19:48 -05:00
Cassandra Heart
f5e9c3b1d6
add integration-related rpcs (#260) 2024-07-12 22:56:11 -05:00
Cassandra Heart
d526ec63d0
v1.4.21 (#255)
* v1.4.21

* bump name
2024-07-05 04:40:00 -05:00
Cassandra Heart
1a244f5154
v1.4.20-p1 (#251)
* v1.4.20-p1

* further refinements/tuning
2024-06-29 19:12:53 -05:00
494 changed files with 1469122 additions and 243705 deletions

796
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,10 @@
[workspace]
members = [
"crates/vdf",
"crates/channel",
"crates/classgroup",
"crates/bls48581",
"crates/rpm",
]
[profile.release]

View File

@ -40,7 +40,12 @@ RUN go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# Build and install qclient
WORKDIR /opt/ceremonyclient/client
RUN go build -o qclient ./main.go
RUN ./build.sh -o qclient && cp qclient /go/bin
# Allows exporting single binary
FROM scratch as qclient
COPY --from=build /go/bin/qclient /qclient
ENTRYPOINT [ "/qclient" ]
# Allows exporting single binary
FROM scratch AS node

View File

@ -38,16 +38,33 @@ tasks:
- bls48581/generate.sh
- node/build.sh -o build/arm64_macos/node
build_qclient_arm64_macos:
desc: Build the QClient node binary for MacOS ARM. Outputs to client/build
cmds:
- vdf/generate.sh
- bls48581/generate.sh
- client/build.sh -o build/arm64_macos/qclient
build_node_arm64_linux:
desc: Build the Quilibrium node binary for ARM64 Linux. Outputs to node/build.
cmds:
- docker build --platform linux/arm64 -f Dockerfile.source --output node/build/arm64_linux --target=node .
build_qclient_arm64_linux:
desc: Build the QClient node binary for ARM64 Linux. Outputs to client/build.
cmds:
- docker build --platform linux/arm64 -f Dockerfile.source --output client/build/arm64_linux --target=qclient .
build_node_amd64_linux:
desc: Build the Quilibrium node binary for AMD64 Linux. Outputs to node/build.
cmds:
- docker build --platform linux/amd64 -f Dockerfile.source --output node/build/amd64_linux --target=node .
build_qclient_amd64_linux:
desc: Build the QClient node binary for AMD64 Linux. Outputs to node/build.
cmds:
- docker build --platform linux/amd64 -f Dockerfile.source --output client/build/amd64_linux --target=qclient .
build:source:
desc: Build the Quilibrium docker image from source.
cmds:

9
channel/README.md Normal file
View File

@ -0,0 +1,9 @@
# Channel
Wrapper for the Rust implementation of Channel in [crates/channel](../crates/channel).
## Generate Go bindings
```sh
go generate
```

88
channel/channel.go Normal file
View File

@ -0,0 +1,88 @@
package channel
import (
generated "source.quilibrium.com/quilibrium/monorepo/channel/generated/channel"
)
//go:generate ./generate.sh
func NewDoubleRatchet(
sessionKey []uint8,
sendingHeaderKey []uint8,
nextReceivingHeaderKey []uint8,
isSender bool,
sendingEphemeralPrivateKey []uint8,
receivingEphemeralKey []uint8,
) string {
return generated.NewDoubleRatchet(
sessionKey,
sendingHeaderKey,
nextReceivingHeaderKey,
isSender,
sendingEphemeralPrivateKey,
receivingEphemeralKey,
)
}
func NewTripleRatchet(
peers [][]uint8,
peerKey []uint8,
identityKey []uint8,
signedPreKey []uint8,
threshold uint64,
asyncDkgRatchet bool,
) generated.TripleRatchetStateAndMetadata {
return generated.NewTripleRatchet(
peers,
peerKey,
identityKey,
signedPreKey,
threshold,
asyncDkgRatchet,
)
}
func DoubleRatchetEncrypt(
ratchetStateAndMessage generated.DoubleRatchetStateAndMessage,
) generated.DoubleRatchetStateAndEnvelope {
return generated.DoubleRatchetEncrypt(ratchetStateAndMessage)
}
func DoubleRatchetDecrypt(
ratchetStateAndEnvelope generated.DoubleRatchetStateAndEnvelope,
) generated.DoubleRatchetStateAndMessage {
return generated.DoubleRatchetDecrypt(ratchetStateAndEnvelope)
}
func TripleRatchetInitRound1(
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
) generated.TripleRatchetStateAndMetadata {
return generated.TripleRatchetInitRound1(ratchetStateAndMetadata)
}
func TripleRatchetInitRound2(
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
) generated.TripleRatchetStateAndMetadata {
return generated.TripleRatchetInitRound2(ratchetStateAndMetadata)
}
func TripleRatchetInitRound3(
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
) generated.TripleRatchetStateAndMetadata {
return generated.TripleRatchetInitRound3(ratchetStateAndMetadata)
}
func TripleRatchetInitRound4(
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
) generated.TripleRatchetStateAndMetadata {
return generated.TripleRatchetInitRound4(ratchetStateAndMetadata)
}
func TripleRatchetEncrypt(
ratchetStateAndMessage generated.TripleRatchetStateAndMessage,
) generated.TripleRatchetStateAndEnvelope {
return generated.TripleRatchetEncrypt(ratchetStateAndMessage)
}
func TripleRatchetDecrypt(
ratchetStateAndEnvelope generated.TripleRatchetStateAndEnvelope,
) generated.TripleRatchetStateAndMessage {
return generated.TripleRatchetDecrypt(ratchetStateAndEnvelope)
}

14
channel/generate.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -euxo pipefail
ROOT_DIR="${ROOT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
RUST_CHANNEL_PACKAGE="$ROOT_DIR/crates/channel"
BINDINGS_DIR="$ROOT_DIR/channel"
# Build the Rust Channel package in release mode
cargo build -p channel --release
# Generate Go bindings
pushd "$RUST_CHANNEL_PACKAGE" > /dev/null
uniffi-bindgen-go src/lib.udl -o "$BINDINGS_DIR"/generated

View File

@ -0,0 +1,8 @@
#include <channel.h>
// This file exists beacause of
// https://github.com/golang/go/issues/11263
void cgo_rust_task_callback_bridge_channel(RustTaskCallback cb, const void * taskData, int8_t status) {
cb(taskData, status);
}

View File

@ -0,0 +1,954 @@
package channel
// #include <channel.h>
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math"
"unsafe"
)
type RustBuffer = C.RustBuffer
type RustBufferI interface {
AsReader() *bytes.Reader
Free()
ToGoBytes() []byte
Data() unsafe.Pointer
Len() int
Capacity() int
}
func RustBufferFromExternal(b RustBufferI) RustBuffer {
return RustBuffer{
capacity: C.int(b.Capacity()),
len: C.int(b.Len()),
data: (*C.uchar)(b.Data()),
}
}
func (cb RustBuffer) Capacity() int {
return int(cb.capacity)
}
func (cb RustBuffer) Len() int {
return int(cb.len)
}
func (cb RustBuffer) Data() unsafe.Pointer {
return unsafe.Pointer(cb.data)
}
func (cb RustBuffer) AsReader() *bytes.Reader {
b := unsafe.Slice((*byte)(cb.data), C.int(cb.len))
return bytes.NewReader(b)
}
func (cb RustBuffer) Free() {
rustCall(func(status *C.RustCallStatus) bool {
C.ffi_channel_rustbuffer_free(cb, status)
return false
})
}
func (cb RustBuffer) ToGoBytes() []byte {
return C.GoBytes(unsafe.Pointer(cb.data), C.int(cb.len))
}
func stringToRustBuffer(str string) RustBuffer {
return bytesToRustBuffer([]byte(str))
}
func bytesToRustBuffer(b []byte) RustBuffer {
if len(b) == 0 {
return RustBuffer{}
}
// We can pass the pointer along here, as it is pinned
// for the duration of this call
foreign := C.ForeignBytes{
len: C.int(len(b)),
data: (*C.uchar)(unsafe.Pointer(&b[0])),
}
return rustCall(func(status *C.RustCallStatus) RustBuffer {
return C.ffi_channel_rustbuffer_from_bytes(foreign, status)
})
}
type BufLifter[GoType any] interface {
Lift(value RustBufferI) GoType
}
type BufLowerer[GoType any] interface {
Lower(value GoType) RustBuffer
}
type FfiConverter[GoType any, FfiType any] interface {
Lift(value FfiType) GoType
Lower(value GoType) FfiType
}
type BufReader[GoType any] interface {
Read(reader io.Reader) GoType
}
type BufWriter[GoType any] interface {
Write(writer io.Writer, value GoType)
}
type FfiRustBufConverter[GoType any, FfiType any] interface {
FfiConverter[GoType, FfiType]
BufReader[GoType]
}
func LowerIntoRustBuffer[GoType any](bufWriter BufWriter[GoType], value GoType) RustBuffer {
// This might be not the most efficient way but it does not require knowing allocation size
// beforehand
var buffer bytes.Buffer
bufWriter.Write(&buffer, value)
bytes, err := io.ReadAll(&buffer)
if err != nil {
panic(fmt.Errorf("reading written data: %w", err))
}
return bytesToRustBuffer(bytes)
}
func LiftFromRustBuffer[GoType any](bufReader BufReader[GoType], rbuf RustBufferI) GoType {
defer rbuf.Free()
reader := rbuf.AsReader()
item := bufReader.Read(reader)
if reader.Len() > 0 {
// TODO: Remove this
leftover, _ := io.ReadAll(reader)
panic(fmt.Errorf("Junk remaining in buffer after lifting: %s", string(leftover)))
}
return item
}
func rustCallWithError[U any](converter BufLifter[error], callback func(*C.RustCallStatus) U) (U, error) {
var status C.RustCallStatus
returnValue := callback(&status)
err := checkCallStatus(converter, status)
return returnValue, err
}
func checkCallStatus(converter BufLifter[error], status C.RustCallStatus) error {
switch status.code {
case 0:
return nil
case 1:
return converter.Lift(status.errorBuf)
case 2:
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if status.errorBuf.len > 0 {
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
} else {
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
}
default:
return fmt.Errorf("unknown status code: %d", status.code)
}
}
func checkCallStatusUnknown(status C.RustCallStatus) error {
switch status.code {
case 0:
return nil
case 1:
panic(fmt.Errorf("function not returning an error returned an error"))
case 2:
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if status.errorBuf.len > 0 {
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
} else {
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
}
default:
return fmt.Errorf("unknown status code: %d", status.code)
}
}
func rustCall[U any](callback func(*C.RustCallStatus) U) U {
returnValue, err := rustCallWithError(nil, callback)
if err != nil {
panic(err)
}
return returnValue
}
func writeInt8(writer io.Writer, value int8) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint8(writer io.Writer, value uint8) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt16(writer io.Writer, value int16) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint16(writer io.Writer, value uint16) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt32(writer io.Writer, value int32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint32(writer io.Writer, value uint32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt64(writer io.Writer, value int64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint64(writer io.Writer, value uint64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeFloat32(writer io.Writer, value float32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeFloat64(writer io.Writer, value float64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func readInt8(reader io.Reader) int8 {
var result int8
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint8(reader io.Reader) uint8 {
var result uint8
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt16(reader io.Reader) int16 {
var result int16
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint16(reader io.Reader) uint16 {
var result uint16
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt32(reader io.Reader) int32 {
var result int32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint32(reader io.Reader) uint32 {
var result uint32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt64(reader io.Reader) int64 {
var result int64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint64(reader io.Reader) uint64 {
var result uint64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readFloat32(reader io.Reader) float32 {
var result float32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readFloat64(reader io.Reader) float64 {
var result float64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func init() {
uniffiCheckChecksums()
}
func uniffiCheckChecksums() {
// Get the bindings contract version from our ComponentInterface
bindingsContractVersion := 24
// Get the scaffolding contract version by calling the into the dylib
scaffoldingContractVersion := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint32_t {
return C.ffi_channel_uniffi_contract_version(uniffiStatus)
})
if bindingsContractVersion != int(scaffoldingContractVersion) {
// If this happens try cleaning and rebuilding your project
panic("channel: UniFFI contract version mismatch")
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_double_ratchet_decrypt(uniffiStatus)
})
if checksum != 57128 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_double_ratchet_decrypt: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_double_ratchet_encrypt(uniffiStatus)
})
if checksum != 10167 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_double_ratchet_encrypt: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_new_double_ratchet(uniffiStatus)
})
if checksum != 21249 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_new_double_ratchet: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_new_triple_ratchet(uniffiStatus)
})
if checksum != 11118 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_new_triple_ratchet: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_decrypt(uniffiStatus)
})
if checksum != 56417 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_decrypt: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_encrypt(uniffiStatus)
})
if checksum != 63768 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_encrypt: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_1(uniffiStatus)
})
if checksum != 48593 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_1: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_2(uniffiStatus)
})
if checksum != 55359 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_2: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_3(uniffiStatus)
})
if checksum != 50330 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_3: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_4(uniffiStatus)
})
if checksum != 58513 {
// If this happens try cleaning and rebuilding your project
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_4: UniFFI API checksum mismatch")
}
}
}
type FfiConverterUint8 struct{}
var FfiConverterUint8INSTANCE = FfiConverterUint8{}
func (FfiConverterUint8) Lower(value uint8) C.uint8_t {
return C.uint8_t(value)
}
func (FfiConverterUint8) Write(writer io.Writer, value uint8) {
writeUint8(writer, value)
}
func (FfiConverterUint8) Lift(value C.uint8_t) uint8 {
return uint8(value)
}
func (FfiConverterUint8) Read(reader io.Reader) uint8 {
return readUint8(reader)
}
type FfiDestroyerUint8 struct{}
func (FfiDestroyerUint8) Destroy(_ uint8) {}
type FfiConverterUint64 struct{}
var FfiConverterUint64INSTANCE = FfiConverterUint64{}
func (FfiConverterUint64) Lower(value uint64) C.uint64_t {
return C.uint64_t(value)
}
func (FfiConverterUint64) Write(writer io.Writer, value uint64) {
writeUint64(writer, value)
}
func (FfiConverterUint64) Lift(value C.uint64_t) uint64 {
return uint64(value)
}
func (FfiConverterUint64) Read(reader io.Reader) uint64 {
return readUint64(reader)
}
type FfiDestroyerUint64 struct{}
func (FfiDestroyerUint64) Destroy(_ uint64) {}
type FfiConverterBool struct{}
var FfiConverterBoolINSTANCE = FfiConverterBool{}
func (FfiConverterBool) Lower(value bool) C.int8_t {
if value {
return C.int8_t(1)
}
return C.int8_t(0)
}
func (FfiConverterBool) Write(writer io.Writer, value bool) {
if value {
writeInt8(writer, 1)
} else {
writeInt8(writer, 0)
}
}
func (FfiConverterBool) Lift(value C.int8_t) bool {
return value != 0
}
func (FfiConverterBool) Read(reader io.Reader) bool {
return readInt8(reader) != 0
}
type FfiDestroyerBool struct{}
func (FfiDestroyerBool) Destroy(_ bool) {}
type FfiConverterString struct{}
var FfiConverterStringINSTANCE = FfiConverterString{}
func (FfiConverterString) Lift(rb RustBufferI) string {
defer rb.Free()
reader := rb.AsReader()
b, err := io.ReadAll(reader)
if err != nil {
panic(fmt.Errorf("reading reader: %w", err))
}
return string(b)
}
func (FfiConverterString) Read(reader io.Reader) string {
length := readInt32(reader)
buffer := make([]byte, length)
read_length, err := reader.Read(buffer)
if err != nil {
panic(err)
}
if read_length != int(length) {
panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length))
}
return string(buffer)
}
func (FfiConverterString) Lower(value string) RustBuffer {
return stringToRustBuffer(value)
}
func (FfiConverterString) Write(writer io.Writer, value string) {
if len(value) > math.MaxInt32 {
panic("String is too large to fit into Int32")
}
writeInt32(writer, int32(len(value)))
write_length, err := io.WriteString(writer, value)
if err != nil {
panic(err)
}
if write_length != len(value) {
panic(fmt.Errorf("bad write length when writing string, expected %d, written %d", len(value), write_length))
}
}
type FfiDestroyerString struct{}
func (FfiDestroyerString) Destroy(_ string) {}
type DoubleRatchetStateAndEnvelope struct {
RatchetState string
Envelope string
}
func (r *DoubleRatchetStateAndEnvelope) Destroy() {
FfiDestroyerString{}.Destroy(r.RatchetState)
FfiDestroyerString{}.Destroy(r.Envelope)
}
type FfiConverterTypeDoubleRatchetStateAndEnvelope struct{}
var FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE = FfiConverterTypeDoubleRatchetStateAndEnvelope{}
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Lift(rb RustBufferI) DoubleRatchetStateAndEnvelope {
return LiftFromRustBuffer[DoubleRatchetStateAndEnvelope](c, rb)
}
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Read(reader io.Reader) DoubleRatchetStateAndEnvelope {
return DoubleRatchetStateAndEnvelope{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterStringINSTANCE.Read(reader),
}
}
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Lower(value DoubleRatchetStateAndEnvelope) RustBuffer {
return LowerIntoRustBuffer[DoubleRatchetStateAndEnvelope](c, value)
}
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Write(writer io.Writer, value DoubleRatchetStateAndEnvelope) {
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
FfiConverterStringINSTANCE.Write(writer, value.Envelope)
}
type FfiDestroyerTypeDoubleRatchetStateAndEnvelope struct{}
func (_ FfiDestroyerTypeDoubleRatchetStateAndEnvelope) Destroy(value DoubleRatchetStateAndEnvelope) {
value.Destroy()
}
type DoubleRatchetStateAndMessage struct {
RatchetState string
Message []uint8
}
func (r *DoubleRatchetStateAndMessage) Destroy() {
FfiDestroyerString{}.Destroy(r.RatchetState)
FfiDestroyerSequenceUint8{}.Destroy(r.Message)
}
type FfiConverterTypeDoubleRatchetStateAndMessage struct{}
var FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE = FfiConverterTypeDoubleRatchetStateAndMessage{}
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Lift(rb RustBufferI) DoubleRatchetStateAndMessage {
return LiftFromRustBuffer[DoubleRatchetStateAndMessage](c, rb)
}
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Read(reader io.Reader) DoubleRatchetStateAndMessage {
return DoubleRatchetStateAndMessage{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterSequenceUint8INSTANCE.Read(reader),
}
}
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Lower(value DoubleRatchetStateAndMessage) RustBuffer {
return LowerIntoRustBuffer[DoubleRatchetStateAndMessage](c, value)
}
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Write(writer io.Writer, value DoubleRatchetStateAndMessage) {
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
FfiConverterSequenceUint8INSTANCE.Write(writer, value.Message)
}
type FfiDestroyerTypeDoubleRatchetStateAndMessage struct{}
func (_ FfiDestroyerTypeDoubleRatchetStateAndMessage) Destroy(value DoubleRatchetStateAndMessage) {
value.Destroy()
}
type TripleRatchetStateAndEnvelope struct {
RatchetState string
Envelope string
}
func (r *TripleRatchetStateAndEnvelope) Destroy() {
FfiDestroyerString{}.Destroy(r.RatchetState)
FfiDestroyerString{}.Destroy(r.Envelope)
}
type FfiConverterTypeTripleRatchetStateAndEnvelope struct{}
var FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE = FfiConverterTypeTripleRatchetStateAndEnvelope{}
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Lift(rb RustBufferI) TripleRatchetStateAndEnvelope {
return LiftFromRustBuffer[TripleRatchetStateAndEnvelope](c, rb)
}
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Read(reader io.Reader) TripleRatchetStateAndEnvelope {
return TripleRatchetStateAndEnvelope{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterStringINSTANCE.Read(reader),
}
}
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Lower(value TripleRatchetStateAndEnvelope) RustBuffer {
return LowerIntoRustBuffer[TripleRatchetStateAndEnvelope](c, value)
}
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Write(writer io.Writer, value TripleRatchetStateAndEnvelope) {
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
FfiConverterStringINSTANCE.Write(writer, value.Envelope)
}
type FfiDestroyerTypeTripleRatchetStateAndEnvelope struct{}
func (_ FfiDestroyerTypeTripleRatchetStateAndEnvelope) Destroy(value TripleRatchetStateAndEnvelope) {
value.Destroy()
}
type TripleRatchetStateAndMessage struct {
RatchetState string
Message []uint8
}
func (r *TripleRatchetStateAndMessage) Destroy() {
FfiDestroyerString{}.Destroy(r.RatchetState)
FfiDestroyerSequenceUint8{}.Destroy(r.Message)
}
type FfiConverterTypeTripleRatchetStateAndMessage struct{}
var FfiConverterTypeTripleRatchetStateAndMessageINSTANCE = FfiConverterTypeTripleRatchetStateAndMessage{}
func (c FfiConverterTypeTripleRatchetStateAndMessage) Lift(rb RustBufferI) TripleRatchetStateAndMessage {
return LiftFromRustBuffer[TripleRatchetStateAndMessage](c, rb)
}
func (c FfiConverterTypeTripleRatchetStateAndMessage) Read(reader io.Reader) TripleRatchetStateAndMessage {
return TripleRatchetStateAndMessage{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterSequenceUint8INSTANCE.Read(reader),
}
}
func (c FfiConverterTypeTripleRatchetStateAndMessage) Lower(value TripleRatchetStateAndMessage) RustBuffer {
return LowerIntoRustBuffer[TripleRatchetStateAndMessage](c, value)
}
func (c FfiConverterTypeTripleRatchetStateAndMessage) Write(writer io.Writer, value TripleRatchetStateAndMessage) {
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
FfiConverterSequenceUint8INSTANCE.Write(writer, value.Message)
}
type FfiDestroyerTypeTripleRatchetStateAndMessage struct{}
func (_ FfiDestroyerTypeTripleRatchetStateAndMessage) Destroy(value TripleRatchetStateAndMessage) {
value.Destroy()
}
type TripleRatchetStateAndMetadata struct {
RatchetState string
Metadata map[string]string
}
func (r *TripleRatchetStateAndMetadata) Destroy() {
FfiDestroyerString{}.Destroy(r.RatchetState)
FfiDestroyerMapStringString{}.Destroy(r.Metadata)
}
type FfiConverterTypeTripleRatchetStateAndMetadata struct{}
var FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE = FfiConverterTypeTripleRatchetStateAndMetadata{}
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Lift(rb RustBufferI) TripleRatchetStateAndMetadata {
return LiftFromRustBuffer[TripleRatchetStateAndMetadata](c, rb)
}
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Read(reader io.Reader) TripleRatchetStateAndMetadata {
return TripleRatchetStateAndMetadata{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterMapStringStringINSTANCE.Read(reader),
}
}
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Lower(value TripleRatchetStateAndMetadata) RustBuffer {
return LowerIntoRustBuffer[TripleRatchetStateAndMetadata](c, value)
}
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Write(writer io.Writer, value TripleRatchetStateAndMetadata) {
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
FfiConverterMapStringStringINSTANCE.Write(writer, value.Metadata)
}
type FfiDestroyerTypeTripleRatchetStateAndMetadata struct{}
func (_ FfiDestroyerTypeTripleRatchetStateAndMetadata) Destroy(value TripleRatchetStateAndMetadata) {
value.Destroy()
}
type FfiConverterSequenceUint8 struct{}
var FfiConverterSequenceUint8INSTANCE = FfiConverterSequenceUint8{}
func (c FfiConverterSequenceUint8) Lift(rb RustBufferI) []uint8 {
return LiftFromRustBuffer[[]uint8](c, rb)
}
func (c FfiConverterSequenceUint8) Read(reader io.Reader) []uint8 {
length := readInt32(reader)
if length == 0 {
return nil
}
result := make([]uint8, 0, length)
for i := int32(0); i < length; i++ {
result = append(result, FfiConverterUint8INSTANCE.Read(reader))
}
return result
}
func (c FfiConverterSequenceUint8) Lower(value []uint8) RustBuffer {
return LowerIntoRustBuffer[[]uint8](c, value)
}
func (c FfiConverterSequenceUint8) Write(writer io.Writer, value []uint8) {
if len(value) > math.MaxInt32 {
panic("[]uint8 is too large to fit into Int32")
}
writeInt32(writer, int32(len(value)))
for _, item := range value {
FfiConverterUint8INSTANCE.Write(writer, item)
}
}
type FfiDestroyerSequenceUint8 struct{}
func (FfiDestroyerSequenceUint8) Destroy(sequence []uint8) {
for _, value := range sequence {
FfiDestroyerUint8{}.Destroy(value)
}
}
type FfiConverterSequenceSequenceUint8 struct{}
var FfiConverterSequenceSequenceUint8INSTANCE = FfiConverterSequenceSequenceUint8{}
func (c FfiConverterSequenceSequenceUint8) Lift(rb RustBufferI) [][]uint8 {
return LiftFromRustBuffer[[][]uint8](c, rb)
}
func (c FfiConverterSequenceSequenceUint8) Read(reader io.Reader) [][]uint8 {
length := readInt32(reader)
if length == 0 {
return nil
}
result := make([][]uint8, 0, length)
for i := int32(0); i < length; i++ {
result = append(result, FfiConverterSequenceUint8INSTANCE.Read(reader))
}
return result
}
func (c FfiConverterSequenceSequenceUint8) Lower(value [][]uint8) RustBuffer {
return LowerIntoRustBuffer[[][]uint8](c, value)
}
func (c FfiConverterSequenceSequenceUint8) Write(writer io.Writer, value [][]uint8) {
if len(value) > math.MaxInt32 {
panic("[][]uint8 is too large to fit into Int32")
}
writeInt32(writer, int32(len(value)))
for _, item := range value {
FfiConverterSequenceUint8INSTANCE.Write(writer, item)
}
}
type FfiDestroyerSequenceSequenceUint8 struct{}
func (FfiDestroyerSequenceSequenceUint8) Destroy(sequence [][]uint8) {
for _, value := range sequence {
FfiDestroyerSequenceUint8{}.Destroy(value)
}
}
type FfiConverterMapStringString struct{}
var FfiConverterMapStringStringINSTANCE = FfiConverterMapStringString{}
func (c FfiConverterMapStringString) Lift(rb RustBufferI) map[string]string {
return LiftFromRustBuffer[map[string]string](c, rb)
}
func (_ FfiConverterMapStringString) Read(reader io.Reader) map[string]string {
result := make(map[string]string)
length := readInt32(reader)
for i := int32(0); i < length; i++ {
key := FfiConverterStringINSTANCE.Read(reader)
value := FfiConverterStringINSTANCE.Read(reader)
result[key] = value
}
return result
}
func (c FfiConverterMapStringString) Lower(value map[string]string) RustBuffer {
return LowerIntoRustBuffer[map[string]string](c, value)
}
func (_ FfiConverterMapStringString) Write(writer io.Writer, mapValue map[string]string) {
if len(mapValue) > math.MaxInt32 {
panic("map[string]string is too large to fit into Int32")
}
writeInt32(writer, int32(len(mapValue)))
for key, value := range mapValue {
FfiConverterStringINSTANCE.Write(writer, key)
FfiConverterStringINSTANCE.Write(writer, value)
}
}
type FfiDestroyerMapStringString struct{}
func (_ FfiDestroyerMapStringString) Destroy(mapValue map[string]string) {
for key, value := range mapValue {
FfiDestroyerString{}.Destroy(key)
FfiDestroyerString{}.Destroy(value)
}
}
func DoubleRatchetDecrypt(ratchetStateAndEnvelope DoubleRatchetStateAndEnvelope) DoubleRatchetStateAndMessage {
return FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_double_ratchet_decrypt(FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE.Lower(ratchetStateAndEnvelope), _uniffiStatus)
}))
}
func DoubleRatchetEncrypt(ratchetStateAndMessage DoubleRatchetStateAndMessage) DoubleRatchetStateAndEnvelope {
return FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_double_ratchet_encrypt(FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE.Lower(ratchetStateAndMessage), _uniffiStatus)
}))
}
func NewDoubleRatchet(sessionKey []uint8, sendingHeaderKey []uint8, nextReceivingHeaderKey []uint8, isSender bool, sendingEphemeralPrivateKey []uint8, receivingEphemeralKey []uint8) string {
return FfiConverterStringINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_new_double_ratchet(FfiConverterSequenceUint8INSTANCE.Lower(sessionKey), FfiConverterSequenceUint8INSTANCE.Lower(sendingHeaderKey), FfiConverterSequenceUint8INSTANCE.Lower(nextReceivingHeaderKey), FfiConverterBoolINSTANCE.Lower(isSender), FfiConverterSequenceUint8INSTANCE.Lower(sendingEphemeralPrivateKey), FfiConverterSequenceUint8INSTANCE.Lower(receivingEphemeralKey), _uniffiStatus)
}))
}
func NewTripleRatchet(peers [][]uint8, peerKey []uint8, identityKey []uint8, signedPreKey []uint8, threshold uint64, asyncDkgRatchet bool) TripleRatchetStateAndMetadata {
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_new_triple_ratchet(FfiConverterSequenceSequenceUint8INSTANCE.Lower(peers), FfiConverterSequenceUint8INSTANCE.Lower(peerKey), FfiConverterSequenceUint8INSTANCE.Lower(identityKey), FfiConverterSequenceUint8INSTANCE.Lower(signedPreKey), FfiConverterUint64INSTANCE.Lower(threshold), FfiConverterBoolINSTANCE.Lower(asyncDkgRatchet), _uniffiStatus)
}))
}
func TripleRatchetDecrypt(ratchetStateAndEnvelope TripleRatchetStateAndEnvelope) TripleRatchetStateAndMessage {
return FfiConverterTypeTripleRatchetStateAndMessageINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_decrypt(FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE.Lower(ratchetStateAndEnvelope), _uniffiStatus)
}))
}
func TripleRatchetEncrypt(ratchetStateAndMessage TripleRatchetStateAndMessage) TripleRatchetStateAndEnvelope {
return FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_encrypt(FfiConverterTypeTripleRatchetStateAndMessageINSTANCE.Lower(ratchetStateAndMessage), _uniffiStatus)
}))
}
func TripleRatchetInitRound1(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_init_round_1(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
}))
}
func TripleRatchetInitRound2(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_init_round_2(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
}))
}
func TripleRatchetInitRound3(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_init_round_3(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
}))
}
func TripleRatchetInitRound4(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_channel_fn_func_triple_ratchet_init_round_4(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
}))
}

View File

@ -0,0 +1,475 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
#include <stdbool.h>
#include <stdint.h>
// The following structs are used to implement the lowest level
// of the FFI, and thus useful to multiple uniffied crates.
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
#ifdef UNIFFI_SHARED_H
// We also try to prevent mixing versions of shared uniffi header structs.
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V6
#ifndef UNIFFI_SHARED_HEADER_V6
#error Combining helper code from multiple versions of uniffi is not supported
#endif // ndef UNIFFI_SHARED_HEADER_V6
#else
#define UNIFFI_SHARED_H
#define UNIFFI_SHARED_HEADER_V6
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
typedef struct RustBuffer {
int32_t capacity;
int32_t len;
uint8_t *data;
} RustBuffer;
typedef int32_t (*ForeignCallback)(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *);
// Task defined in Rust that Go executes
typedef void (*RustTaskCallback)(const void *, int8_t);
// Callback to execute Rust tasks using a Go routine
//
// Args:
// executor: ForeignExecutor lowered into a uint64_t value
// delay: Delay in MS
// task: RustTaskCallback to call
// task_data: data to pass the task callback
typedef int8_t (*ForeignExecutorCallback)(uint64_t, uint32_t, RustTaskCallback, void *);
typedef struct ForeignBytes {
int32_t len;
const uint8_t *data;
} ForeignBytes;
// Error definitions
typedef struct RustCallStatus {
int8_t code;
RustBuffer errorBuf;
} RustCallStatus;
// Continuation callback for UniFFI Futures
typedef void (*RustFutureContinuation)(void * , int8_t);
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
#endif // def UNIFFI_SHARED_H
// Needed because we can't execute the callback directly from go.
void cgo_rust_task_callback_bridge_channel(RustTaskCallback, const void *, int8_t);
int8_t uniffiForeignExecutorCallbackchannel(uint64_t, uint32_t, RustTaskCallback, void*);
void uniffiFutureContinuationCallbackchannel(void*, int8_t);
RustBuffer uniffi_channel_fn_func_double_ratchet_decrypt(
RustBuffer ratchet_state_and_envelope,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_double_ratchet_encrypt(
RustBuffer ratchet_state_and_message,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_new_double_ratchet(
RustBuffer session_key,
RustBuffer sending_header_key,
RustBuffer next_receiving_header_key,
int8_t is_sender,
RustBuffer sending_ephemeral_private_key,
RustBuffer receiving_ephemeral_key,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_new_triple_ratchet(
RustBuffer peers,
RustBuffer peer_key,
RustBuffer identity_key,
RustBuffer signed_pre_key,
uint64_t threshold,
int8_t async_dkg_ratchet,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_decrypt(
RustBuffer ratchet_state_and_envelope,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_encrypt(
RustBuffer ratchet_state_and_message,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_1(
RustBuffer ratchet_state_and_metadata,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_2(
RustBuffer ratchet_state_and_metadata,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_3(
RustBuffer ratchet_state_and_metadata,
RustCallStatus* out_status
);
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_4(
RustBuffer ratchet_state_and_metadata,
RustCallStatus* out_status
);
RustBuffer ffi_channel_rustbuffer_alloc(
int32_t size,
RustCallStatus* out_status
);
RustBuffer ffi_channel_rustbuffer_from_bytes(
ForeignBytes bytes,
RustCallStatus* out_status
);
void ffi_channel_rustbuffer_free(
RustBuffer buf,
RustCallStatus* out_status
);
RustBuffer ffi_channel_rustbuffer_reserve(
RustBuffer buf,
int32_t additional,
RustCallStatus* out_status
);
void ffi_channel_rust_future_continuation_callback_set(
RustFutureContinuation callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_u8(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_u8(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_u8(
void* handle,
RustCallStatus* out_status
);
uint8_t ffi_channel_rust_future_complete_u8(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_i8(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_i8(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_i8(
void* handle,
RustCallStatus* out_status
);
int8_t ffi_channel_rust_future_complete_i8(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_u16(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_u16(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_u16(
void* handle,
RustCallStatus* out_status
);
uint16_t ffi_channel_rust_future_complete_u16(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_i16(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_i16(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_i16(
void* handle,
RustCallStatus* out_status
);
int16_t ffi_channel_rust_future_complete_i16(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_u32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_u32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_u32(
void* handle,
RustCallStatus* out_status
);
uint32_t ffi_channel_rust_future_complete_u32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_i32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_i32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_i32(
void* handle,
RustCallStatus* out_status
);
int32_t ffi_channel_rust_future_complete_i32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_u64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_u64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_u64(
void* handle,
RustCallStatus* out_status
);
uint64_t ffi_channel_rust_future_complete_u64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_i64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_i64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_i64(
void* handle,
RustCallStatus* out_status
);
int64_t ffi_channel_rust_future_complete_i64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_f32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_f32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_f32(
void* handle,
RustCallStatus* out_status
);
float ffi_channel_rust_future_complete_f32(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_f64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_f64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_f64(
void* handle,
RustCallStatus* out_status
);
double ffi_channel_rust_future_complete_f64(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_pointer(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_pointer(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_pointer(
void* handle,
RustCallStatus* out_status
);
void* ffi_channel_rust_future_complete_pointer(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_rust_buffer(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_rust_buffer(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_rust_buffer(
void* handle,
RustCallStatus* out_status
);
RustBuffer ffi_channel_rust_future_complete_rust_buffer(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_poll_void(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_channel_rust_future_cancel_void(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_free_void(
void* handle,
RustCallStatus* out_status
);
void ffi_channel_rust_future_complete_void(
void* handle,
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_double_ratchet_decrypt(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_double_ratchet_encrypt(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_new_double_ratchet(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_new_triple_ratchet(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_decrypt(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_encrypt(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_1(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_2(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_3(
RustCallStatus* out_status
);
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_4(
RustCallStatus* out_status
);
uint32_t ffi_channel_uniffi_contract_version(
RustCallStatus* out_status
);

17
channel/go.mod Normal file
View File

@ -0,0 +1,17 @@
module source.quilibrium.com/quilibrium/monorepo/channel
go 1.20
// A necessary hack until source.quilibrium.com is open to all
replace source.quilibrium.com/quilibrium/monorepo/nekryptology => ../nekryptology
require github.com/stretchr/testify v1.9.0
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/sys v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
source.quilibrium.com/quilibrium/monorepo/nekryptology v0.0.0-00010101000000-000000000000 // indirect
)

13
channel/go.sum Normal file
View File

@ -0,0 +1,13 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

17
channel/test.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -euxo pipefail
# Run tests for the channel package. Takes care of linking the native Channel library.
# Assumes that the Channel library has been built by running the generate.sh script in the same directory.
ROOT_DIR="${ROOT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
NODE_DIR="$ROOT_DIR/channel"
BINARIES_DIR="$ROOT_DIR/target/release"
# Link the native Channel library and execute tests
pushd "$NODE_DIR" > /dev/null
CGO_LDFLAGS="-L$BINARIES_DIR -lchannel -ldl" \
CGO_ENABLED=1 \
GOEXPERIMENT=arenas \
go test "$@"

35
client/build.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
set -euxo pipefail
# This script builds the node binary for the current platform and statically links it with VDF static lib.
# Assumes that the VDF library has been built by running the generate.sh script in the `../vdf` directory.
ROOT_DIR="${ROOT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
CLIENT_DIR="$ROOT_DIR/client"
BINARIES_DIR="$ROOT_DIR/target/release"
pushd "$CLIENT_DIR" > /dev/null
export CGO_ENABLED=1
os_type="$(uname)"
case "$os_type" in
"Darwin")
# Check if the architecture is ARM
if [[ "$(uname -m)" == "arm64" ]]; then
# MacOS ld doesn't support -Bstatic and -Bdynamic, so it's important that there is only a static version of the library
go build -ldflags "-linkmode 'external' -extldflags '-L$BINARIES_DIR -lvdf -lbls48581 -ldl -lm'" "$@"
else
echo "Unsupported platform"
exit 1
fi
;;
"Linux")
go build -ldflags "-linkmode 'external' -extldflags '-L$BINARIES_DIR -Wl,-Bstatic -lvdf -lbls48581 -Wl,-Bdynamic -ldl -lm'" "$@"
;;
*)
echo "Unsupported platform"
exit 1
;;
esac

View File

@ -2,8 +2,6 @@ package cmd
import (
"fmt"
"math/big"
"os"
"github.com/spf13/cobra"
)
@ -18,18 +16,7 @@ var acceptCmd = &cobra.Command{
PendingTransaction - the address of the pending transfer
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Println("invalid command")
os.Exit(1)
}
_, ok := new(big.Int).SetString(args[0], 0)
if !ok {
fmt.Println("invalid PendingTransaction")
os.Exit(1)
}
fmt.Println("25 QUIL (Coin 0x2688997f2776ab5993894ed04fcdac05577cf2494ddfedf356ebf8bd3de464ab)")
fmt.Println("command not yet available")
},
}

View File

@ -10,7 +10,7 @@ var allCmd = &cobra.Command{
Use: "all",
Short: "Mints all available token rewards",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("1520.381923 QUIL (Coin 0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416)")
fmt.Println("command not yet available")
},
}

View File

@ -1,16 +1,50 @@
package cmd
import (
"context"
"fmt"
"math/big"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var balanceCmd = &cobra.Command{
Use: "balance",
Short: "Lists the total balance of tokens in the managing account",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("1545.381923 QUIL (Account 0x026a5cf3d486b8e8733060d6ce0060074616f0f925671a0886faef744412dc8a)")
conn, err := GetGRPCClient()
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
peerId := GetPeerIDFromConfig(NodeConfig)
addr, err := poseidon.HashBytes([]byte(peerId))
if err != nil {
panic(err)
}
addrBytes := addr.FillBytes(make([]byte, 32))
info, err := client.GetTokenInfo(
context.Background(),
&protobufs.GetTokenInfoRequest{
Address: addrBytes,
},
)
if err != nil {
panic(err)
}
if info.OwnedTokens == nil {
panic("invalid response from RPC")
}
tokens := new(big.Int).SetBytes(info.OwnedTokens)
conversionFactor, _ := new(big.Int).SetString("1DCD65000", 16)
r := new(big.Rat).SetFrac(tokens, conversionFactor)
fmt.Println("Total balance:", r.FloatString(12), "QUIL")
},
}

51764
client/cmd/bridged.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,59 @@
package cmd
import (
"context"
"encoding/hex"
"fmt"
"math/big"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var coinsCmd = &cobra.Command{
Use: "coins",
Short: "Lists all coins under control of the managing account",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("25.0 QUIL (Coin 0x1148092cdce78c721835601ef39f9c2cd8b48b7787cbea032dd3913a4106a58d)")
fmt.Println("1520.381923 QUIL (Coin 0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416)")
conn, err := GetGRPCClient()
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
peerId := GetPeerIDFromConfig(NodeConfig)
addr, err := poseidon.HashBytes([]byte(peerId))
if err != nil {
panic(err)
}
addrBytes := addr.FillBytes(make([]byte, 32))
resp, err := client.GetTokensByAccount(
context.Background(),
&protobufs.GetTokensByAccountRequest{
Address: addrBytes,
},
)
if err != nil {
panic(err)
}
if len(resp.Coins) != len(resp.FrameNumbers) {
panic("invalid response from RPC")
}
for i, coin := range resp.Coins {
amount := new(big.Int).SetBytes(coin.Amount)
conversionFactor, _ := new(big.Int).SetString("1DCD65000", 16)
r := new(big.Rat).SetFrac(amount, conversionFactor)
addr, err := token.GetAddressOfCoin(coin, resp.FrameNumbers[i])
if err != nil {
panic(err)
}
fmt.Println(r.FloatString(12), "QUIL (Coin 0x", hex.EncodeToString(addr), ")")
}
},
}

12
client/cmd/config.go Normal file
View File

@ -0,0 +1,12 @@
package cmd
import "github.com/spf13/cobra"
var configCmd = &cobra.Command{
Use: "config",
Short: "Performs a configuration operation",
}
func init() {
rootCmd.AddCommand(configCmd)
}

View File

@ -10,13 +10,10 @@ import (
"strings"
"github.com/cloudflare/circl/sign/ed448"
"github.com/iden3/go-iden3-crypto/poseidon"
libP2pCrypto "github.com/libp2p/go-libp2p/core/crypto"
"github.com/mr-tron/base58"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
)
@ -25,10 +22,9 @@ var crossMintCmd = &cobra.Command{
Short: "Signs a payload from the Quilibrium bridge to mint tokens on Ethereum L1 and prints the result to stdout",
Long: `Signs a payload from the Quilibrium bridge to mint tokens on Ethereum L1 and prints the result to stdout":
cross-mint <Payload> [<Voucher File Path>]
cross-mint <Payload>
Payload the hex-encoded payload from the Quilibrium bridge with optional 0x-prefix, must be specified
Voucher File Path  (optional) the path to a voucher private key, from the initial KZG ceremony
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
@ -41,63 +37,7 @@ var crossMintCmd = &cobra.Command{
os.Exit(1)
}
if len(args) == 2 {
rawVoucherHex, err := os.ReadFile(args[1])
if err != nil {
fmt.Printf("invalid file: %s\n", args[1])
os.Exit(1)
}
rawVoucherKey, err := hex.DecodeString(string(rawVoucherHex))
if err != nil {
panic(errors.Wrap(err, "cross mint"))
}
ed448Key := ed448.PrivateKey(rawVoucherKey)
result, err := CrossMint(&CrossMintArgs{
Payload: args[0],
PeerKey: ed448Key,
ProvingKey: ed448Key,
})
if err != nil {
panic(errors.Wrap(err, "error cross minting"))
}
pubkeyBytes := ed448Key.Public().(ed448.PublicKey)
addr, err := poseidon.HashBytes(pubkeyBytes)
if err != nil {
panic(errors.Wrap(err, "error cross minting"))
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
// Print the result
fmt.Println("Voucher ID: " + base58.Encode(addrBytes))
jsonResult, err := json.Marshal(result)
if err != nil {
panic(errors.Wrap(err, "error marshaling result to json"))
}
fmt.Println(string(jsonResult))
os.Exit(0)
}
_, err := os.Stat(configDirectory)
if os.IsNotExist(err) {
fmt.Printf("config directory doesn't exist: %s\n", configDirectory)
os.Exit(1)
}
config, err := config.LoadConfig(configDirectory, "")
if err != nil {
fmt.Printf("invalid config directory: %s\n", configDirectory)
os.Exit(1)
}
rawPeerKey, err := hex.DecodeString(config.P2P.PeerPrivKey)
rawPeerKey, err := hex.DecodeString(NodeConfig.P2P.PeerPrivKey)
if err != nil {
panic(errors.Wrap(err, "cross mint"))
}
@ -117,10 +57,10 @@ var crossMintCmd = &cobra.Command{
// `config.Key.KeyStoreFile.Path` defaults to `.config/keys.yml`.
// We do our best here to make sure the configuration value is taken into
// account if it was changed.
if !filepath.IsAbs(config.Key.KeyStoreFile.Path) {
config.Key.KeyStoreFile.Path = filepath.Join(
if !filepath.IsAbs(NodeConfig.Key.KeyStoreFile.Path) {
NodeConfig.Key.KeyStoreFile.Path = filepath.Join(
configDirectory,
filepath.Base(config.Key.KeyStoreFile.Path),
filepath.Base(NodeConfig.Key.KeyStoreFile.Path),
)
}
@ -129,8 +69,8 @@ var crossMintCmd = &cobra.Command{
panic(errors.Wrap(err, "cross mint"))
}
fileKeyManager := keys.NewFileKeyManager(config.Key, logger)
provingKey, err := fileKeyManager.GetSigningKey(config.Engine.ProvingKeyId)
fileKeyManager := keys.NewFileKeyManager(NodeConfig.Key, logger)
provingKey, err := fileKeyManager.GetSigningKey(NodeConfig.Engine.ProvingKeyId)
if err != nil {
panic(errors.Wrap(err, "cross mint"))
}

1049
client/cmd/first_retro.json Normal file

File diff suppressed because it is too large Load Diff

181103
client/cmd/fourth_retro.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,77 @@
package cmd
import (
"fmt"
"math/big"
"os"
"context"
"encoding/hex"
"strings"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var mergeCmd = &cobra.Command{
Use: "merge",
Short: "Merges two coins",
Long: `Merges two coins:
Short: "Merges multiple coins",
Long: `Merges multiple coins:
merge <LeftCoin> <RightCoin>
LeftCoin - the first coin address
RightCoin - the second coin address
merge <Coin Addresses>...
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
fmt.Println("invalid command")
os.Exit(1)
conn, err := GetGRPCClient()
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
key, err := GetPrivKeyFromConfig(NodeConfig)
if err != nil {
panic(err)
}
_, ok := new(big.Int).SetString(args[0], 0)
if !ok {
fmt.Println("invalid LeftCoin")
os.Exit(1)
coinaddrs := []*protobufs.CoinRef{}
payload := []byte("merge")
for _, arg := range args {
coinaddrHex, _ := strings.CutPrefix(arg, "0x")
coinaddr, err := hex.DecodeString(coinaddrHex)
if err != nil {
panic(err)
}
coinaddrs = append(coinaddrs, &protobufs.CoinRef{
Address: coinaddr,
})
payload = append(payload, coinaddr...)
}
_, ok = new(big.Int).SetString(args[1], 0)
if !ok {
fmt.Println("invalid Rightcoin")
os.Exit(1)
sig, err := key.Sign(payload)
if err != nil {
panic(err)
}
fmt.Println("1545.381923 QUIL (Coin 0x151f4ae225e20759077e1724e4c5d0feae26c477fd10d728dfea962eec79b83f)")
pub, err := key.GetPublic().Raw()
if err != nil {
panic(err)
}
_, err = client.SendMessage(
context.Background(),
&protobufs.TokenRequest{
Request: &protobufs.TokenRequest_Merge{
Merge: &protobufs.MergeCoinRequest{
Coins: coinaddrs,
Signature: &protobufs.Ed448Signature{
Signature: sig,
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: pub,
},
},
},
},
},
)
if err != nil {
panic(err)
}
},
}

View File

@ -2,10 +2,7 @@ package cmd
import (
"fmt"
"os"
"time"
"github.com/shopspring/decimal"
"github.com/spf13/cobra"
)
@ -19,22 +16,7 @@ var mutualReceiveCmd = &cobra.Command{
ExpectedAmount - the amount expected in the transfer
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Println("invalid command")
os.Exit(1)
}
amount := args[len(args)-1]
_, err := decimal.NewFromString(amount)
if err != nil {
fmt.Println("invalid ExpectedAmount")
os.Exit(1)
}
fmt.Println("Rendezvous: 0x2ad567e4fc1ac335a8d3d6077de2ee998aff996b51936da04ee1b0f5dc196a4f")
fmt.Printf("Awaiting sender... ")
time.Sleep(2 * time.Second)
fmt.Println("OK")
fmt.Println(amount + " QUIL (Coin 0x0525c76ecdc6ef21c2eb75df628b52396adcf402ba26a518ac395db8f5874a82)")
fmt.Println("command not yet available")
},
}

View File

@ -2,8 +2,6 @@ package cmd
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
)
@ -22,15 +20,7 @@ var mutualTransferCmd = &cobra.Command{
Either Amount or OfCoin must be specified
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
fmt.Println("invalid command")
os.Exit(1)
}
fmt.Printf("Confirming rendezvous... ")
time.Sleep(500 * time.Millisecond)
fmt.Println("OK")
fmt.Println("50 QUIL (Coin [private])")
fmt.Println("command not yet available")
},
}

12
client/cmd/prover.go Normal file
View File

@ -0,0 +1,12 @@
package cmd
import "github.com/spf13/cobra"
var proverCmd = &cobra.Command{
Use: "prover",
Short: "Performs a configuration operation for given prover info",
}
func init() {
configCmd.AddCommand(proverCmd)
}

299
client/cmd/proverMerge.go Normal file
View File

@ -0,0 +1,299 @@
package cmd
import (
_ "embed"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
var proverConfigMergeCmd = &cobra.Command{
Use: "merge",
Short: "Merges config data for prover seniority",
Long: `Merges config data for prover seniority:
merge <Primary Config Path> [<Additional Config Paths>...]
Use with --dry-run to evaluate seniority score, this may take a while...
`,
Run: func(c *cobra.Command, args []string) {
if len(args) <= 1 {
fmt.Println("missing configs")
os.Exit(1)
}
primaryConfig, err := config.LoadConfig(args[0], "", true)
if err != nil {
fmt.Printf("invalid config directory: %s\n", args[0])
os.Exit(1)
}
otherPaths := []string{}
peerIds := []string{GetPeerIDFromConfig(primaryConfig).String()}
for _, p := range args[1:] {
if !path.IsAbs(p) {
fmt.Printf("%s is not an absolute path\n", p)
}
cfg, err := config.LoadConfig(p, "", true)
if err != nil {
fmt.Printf("invalid config directory: %s\n", p)
os.Exit(1)
}
peerId := GetPeerIDFromConfig(cfg).String()
peerIds = append(peerIds, peerId)
otherPaths = append(otherPaths, p)
}
if DryRun {
bridged := []*BridgedPeerJson{}
firstRetro := []*FirstRetroJson{}
secondRetro := []*SecondRetroJson{}
thirdRetro := []*ThirdRetroJson{}
fourthRetro := []*FourthRetroJson{}
err = json.Unmarshal(bridgedPeersJsonBinary, &bridged)
if err != nil {
panic(err)
}
err = json.Unmarshal(firstRetroJsonBinary, &firstRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(secondRetroJsonBinary, &secondRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(thirdRetroJsonBinary, &thirdRetro)
if err != nil {
panic(err)
}
err = json.Unmarshal(fourthRetroJsonBinary, &fourthRetro)
if err != nil {
panic(err)
}
bridgedAddrs := map[string]struct{}{}
bridgeTotal := decimal.Zero
for _, b := range bridged {
amt, err := decimal.NewFromString(b.Amount)
if err != nil {
panic(err)
}
bridgeTotal = bridgeTotal.Add(amt)
bridgedAddrs[b.Identifier] = struct{}{}
}
highestFirst := uint64(0)
highestSecond := uint64(0)
highestThird := uint64(0)
highestFourth := uint64(0)
for _, f := range firstRetro {
found := false
for _, p := range peerIds {
if p != f.PeerId {
continue
}
found = true
}
if !found {
continue
}
// these don't have decimals so we can shortcut
max := 157208
actual, err := strconv.Atoi(f.Reward)
if err != nil {
panic(err)
}
s := uint64(10 * 6 * 60 * 24 * 92 / (max / actual))
if s > uint64(highestFirst) {
highestFirst = s
}
}
for _, f := range secondRetro {
found := false
for _, p := range peerIds {
if p != f.PeerId {
continue
}
found = true
}
if !found {
continue
}
amt := uint64(0)
if f.JanPresence {
amt += (10 * 6 * 60 * 24 * 31)
}
if f.FebPresence {
amt += (10 * 6 * 60 * 24 * 29)
}
if f.MarPresence {
amt += (10 * 6 * 60 * 24 * 31)
}
if f.AprPresence {
amt += (10 * 6 * 60 * 24 * 30)
}
if f.MayPresence {
amt += (10 * 6 * 60 * 24 * 31)
}
if amt > uint64(highestSecond) {
highestSecond = amt
}
}
for _, f := range thirdRetro {
found := false
for _, p := range peerIds {
if p != f.PeerId {
continue
}
found = true
}
if !found {
continue
}
s := uint64(10 * 6 * 60 * 24 * 30)
if s > uint64(highestThird) {
highestThird = s
}
}
for _, f := range fourthRetro {
found := false
for _, p := range peerIds {
if p != f.PeerId {
continue
}
found = true
}
if !found {
continue
}
s := uint64(10 * 6 * 60 * 24 * 31)
if s > uint64(highestFourth) {
highestFourth = s
}
}
fmt.Printf("Effective seniority score: %d\n", highestFirst+highestSecond+highestThird+highestFourth)
} else {
for _, p := range args[1:] {
primaryConfig.Engine.MultisigProverEnrollmentPaths = append(
primaryConfig.Engine.MultisigProverEnrollmentPaths,
p,
)
}
err := config.SaveConfig(args[0], primaryConfig)
if err != nil {
panic(err)
}
}
},
}
func GetPrivKeyFromConfig(cfg *config.Config) (crypto.PrivKey, error) {
peerPrivKey, err := hex.DecodeString(cfg.P2P.PeerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
return privKey, err
}
func GetPeerIDFromConfig(cfg *config.Config) peer.ID {
peerPrivKey, err := hex.DecodeString(cfg.P2P.PeerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
privKey, err := crypto.UnmarshalEd448PrivateKey(peerPrivKey)
if err != nil {
panic(errors.Wrap(err, "error unmarshaling peerkey"))
}
pub := privKey.GetPublic()
id, err := peer.IDFromPublicKey(pub)
if err != nil {
panic(errors.Wrap(err, "error getting peer id"))
}
return id
}
type BridgedPeerJson struct {
Amount string `json:"amount"`
Identifier string `json:"identifier"`
Variant string `json:"variant"`
}
type FirstRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
type SecondRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
JanPresence bool `json:"janPresence"`
FebPresence bool `json:"febPresence"`
MarPresence bool `json:"marPresence"`
AprPresence bool `json:"aprPresence"`
MayPresence bool `json:"mayPresence"`
}
type ThirdRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
type FourthRetroJson struct {
PeerId string `json:"peerId"`
Reward string `json:"reward"`
}
//go:embed bridged.json
var bridgedPeersJsonBinary []byte
//go:embed first_retro.json
var firstRetroJsonBinary []byte
//go:embed second_retro.json
var secondRetroJsonBinary []byte
//go:embed third_retro.json
var thirdRetroJsonBinary []byte
//go:embed fourth_retro.json
var fourthRetroJsonBinary []byte
func init() {
proverCmd.AddCommand(proverConfigMergeCmd)
}

139
client/cmd/proverPause.go Normal file
View File

@ -0,0 +1,139 @@
package cmd
import (
"encoding/hex"
"strings"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token/application"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var proverPauseCmd = &cobra.Command{
Use: "pause",
Short: "Pauses a prover",
Long: `Pauses a prover (use in emergency when a worker isn't coming back online):
pause <Filter>
Filter the hex bitstring of the filter to pause for
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
panic("invalid arguments")
}
logger, err := zap.NewProduction()
pubsub := p2p.NewBlossomSub(NodeConfig.P2P, logger)
intrinsicFilter := p2p.GetBloomFilter(application.TOKEN_ADDRESS, 256, 3)
pubsub.Subscribe(
intrinsicFilter,
func(message *pb.Message) error { return nil },
)
key, err := GetPrivKeyFromConfig(NodeConfig)
if err != nil {
panic(err)
}
payload := []byte("pause")
filterHex, _ := strings.CutPrefix(args[0], "0x")
filter, err := hex.DecodeString(filterHex)
if err != nil {
panic(err)
}
payload = append(payload, filter...)
sig, err := key.Sign(payload)
if err != nil {
panic(err)
}
pub, err := key.GetPublic().Raw()
if err != nil {
panic(err)
}
err = publishMessage(
key,
pubsub,
intrinsicFilter,
&protobufs.AnnounceProverPause{
Filter: filter,
PublicKeySignatureEd448: &protobufs.Ed448Signature{
Signature: sig,
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: pub,
},
},
},
)
if err != nil {
panic(err)
}
},
}
func publishMessage(
key crypto.PrivKey,
pubsub p2p.PubSub,
filter []byte,
message proto.Message,
) error {
any := &anypb.Any{}
if err := any.MarshalFrom(message); err != nil {
return errors.Wrap(err, "publish message")
}
any.TypeUrl = strings.Replace(
any.TypeUrl,
"type.googleapis.com",
"types.quilibrium.com",
1,
)
payload, err := proto.Marshal(any)
if err != nil {
return errors.Wrap(err, "publish message")
}
h, err := poseidon.HashBytes(payload)
if err != nil {
return errors.Wrap(err, "publish message")
}
pub, err := key.GetPublic().Raw()
if err != nil {
return errors.Wrap(err, "publish message")
}
pbi, err := poseidon.HashBytes(pub)
if err != nil {
return errors.Wrap(err, "publish message")
}
provingKeyAddress := pbi.FillBytes(make([]byte, 32))
msg := &protobufs.Message{
Hash: h.Bytes(),
Address: provingKeyAddress,
Payload: payload,
}
data, err := proto.Marshal(msg)
if err != nil {
return errors.Wrap(err, "publish message")
}
return pubsub.PublishToBitmask(filter, data)
}
func init() {
proverCmd.AddCommand(proverPauseCmd)
}

View File

@ -2,8 +2,6 @@ package cmd
import (
"fmt"
"math/big"
"os"
"github.com/spf13/cobra"
)
@ -18,18 +16,7 @@ var rejectCmd = &cobra.Command{
PendingTransaction - the address of the pending transfer
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Println("invalid command")
os.Exit(1)
}
_, ok := new(big.Int).SetString(args[0], 0)
if !ok {
fmt.Println("invalid PendingTransaction")
os.Exit(1)
}
fmt.Println("25 QUIL (PendingTransaction 0x27fff099dee515ece193d2af09b164864e4bb60c19eb6719b5bc981f92151009)")
fmt.Println("command not yet available")
},
}

View File

@ -1,17 +1,118 @@
package cmd
import (
"bytes"
"crypto/tls"
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/cloudflare/circl/sign/ed448"
"github.com/multiformats/go-multiaddr"
mn "github.com/multiformats/go-multiaddr/net"
"github.com/spf13/cobra"
"golang.org/x/crypto/sha3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"source.quilibrium.com/quilibrium/monorepo/node/config"
)
var configDirectory string
var signatureCheck bool = true
var NodeConfig *config.Config
var simulateFail bool
var LightNode bool = false
var DryRun bool = false
var rootCmd = &cobra.Command{
Use: "qclient",
Short: "Quilibrium RPC Client",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if signatureCheck {
ex, err := os.Executable()
if err != nil {
panic(err)
}
b, err := os.ReadFile(ex)
if err != nil {
fmt.Println(
"Error encountered during signature check are you running this " +
"from source? (use --signature-check=false)",
)
panic(err)
}
checksum := sha3.Sum256(b)
digest, err := os.ReadFile(ex + ".dgst")
if err != nil {
fmt.Println("Digest file not found")
os.Exit(1)
}
parts := strings.Split(string(digest), " ")
if len(parts) != 2 {
fmt.Println("Invalid digest file format")
os.Exit(1)
}
digestBytes, err := hex.DecodeString(parts[1][:64])
if err != nil {
fmt.Println("Invalid digest file format")
os.Exit(1)
}
if !bytes.Equal(checksum[:], digestBytes) {
fmt.Println("Invalid digest for node")
os.Exit(1)
}
count := 0
for i := 1; i <= len(config.Signatories); i++ {
signatureFile := fmt.Sprintf(ex+".dgst.sig.%d", i)
sig, err := os.ReadFile(signatureFile)
if err != nil {
continue
}
pubkey, _ := hex.DecodeString(config.Signatories[i-1])
if !ed448.Verify(pubkey, digest, sig, "") {
fmt.Printf("Failed signature check for signatory #%d\n", i)
os.Exit(1)
}
count++
}
if count < len(config.Signatories)/2+len(config.Signatories)%2 {
fmt.Printf("Quorum on signatures not met")
os.Exit(1)
}
fmt.Println("Signature check passed")
} else {
fmt.Println("Signature check bypassed, be sure you know what you're doing")
}
_, err := os.Stat(configDirectory)
if os.IsNotExist(err) {
fmt.Printf("config directory doesn't exist: %s\n", configDirectory)
os.Exit(1)
}
NodeConfig, err = config.LoadConfig(configDirectory, "", false)
if err != nil {
fmt.Printf("invalid config directory: %s\n", configDirectory)
os.Exit(1)
}
if NodeConfig.ListenGRPCMultiaddr == "" {
fmt.Println("gRPC not enabled, using light node")
LightNode = true
}
},
}
func Execute() {
@ -21,6 +122,34 @@ func Execute() {
}
}
func GetGRPCClient() (*grpc.ClientConn, error) {
addr := "rpc.quilibrium.com:8337"
credentials := credentials.NewTLS(&tls.Config{InsecureSkipVerify: false})
if !LightNode {
ma, err := multiaddr.NewMultiaddr(NodeConfig.ListenGRPCMultiaddr)
if err != nil {
panic(err)
}
_, addr, err = mn.DialArgs(ma)
if err != nil {
panic(err)
}
credentials = insecure.NewCredentials()
}
return grpc.Dial(
addr,
grpc.WithTransportCredentials(
credentials,
),
grpc.WithDefaultCallOptions(
grpc.MaxCallSendMsgSize(600*1024*1024),
grpc.MaxCallRecvMsgSize(600*1024*1024),
),
)
}
func init() {
rootCmd.PersistentFlags().StringVar(
&configDirectory,
@ -28,4 +157,16 @@ func init() {
".config/",
"config directory (default is .config/)",
)
rootCmd.PersistentFlags().BoolVar(
&DryRun,
"dry-run",
false,
"runs the command (if applicable) without actually mutating state (printing effect output)",
)
rootCmd.PersistentFlags().BoolVar(
&signatureCheck,
"signature-check",
true,
"bypass signature check (not recommended for binaries)",
)
}

138554
client/cmd/second_retro.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +1,101 @@
package cmd
import (
"context"
"encoding/hex"
"fmt"
"math/big"
"os"
"strings"
"github.com/shopspring/decimal"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var splitCmd = &cobra.Command{
Use: "split",
Short: "Splits a coin into two coins",
Long: `Splits a coin into two coins:
Short: "Splits a coin into multiple coins",
Long: `Splits a coin into multiple coins:
split <OfCoin> <LeftAmount> <RightAmount>
split <OfCoin> <Amounts>...
OfCoin - the address of the coin to split
LeftAmount - the first half of the split amount
RightAmount - the second half of the split amount
Amounts - the sets of amounts to split
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 3 {
if len(args) < 3 {
fmt.Println("invalid command")
os.Exit(1)
}
_, ok := new(big.Int).SetString(args[0], 0)
if !ok {
fmt.Println("invalid OfCoin")
os.Exit(1)
payload := []byte("split")
coinaddrHex, _ := strings.CutPrefix(args[0], "0x")
coinaddr, err := hex.DecodeString(coinaddrHex)
if err != nil {
panic(err)
}
coin := &protobufs.CoinRef{
Address: coinaddr,
}
payload = append(payload, coinaddr...)
conversionFactor, _ := new(big.Int).SetString("1DCD65000", 16)
amounts := [][]byte{}
for _, amt := range args[1:] {
amount, err := decimal.NewFromString(amt)
if err != nil {
fmt.Println("invalid amount")
os.Exit(1)
}
amount.Mul(decimal.NewFromBigInt(conversionFactor, 0))
amountBytes := amount.BigInt().FillBytes(make([]byte, 32))
amounts = append(amounts, amountBytes)
payload = append(payload, amountBytes...)
}
leftAmount := args[1]
_, err := decimal.NewFromString(leftAmount)
conn, err := GetGRPCClient()
if err != nil {
fmt.Println("invalid LeftAmount")
os.Exit(1)
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
key, err := GetPrivKeyFromConfig(NodeConfig)
if err != nil {
panic(err)
}
rightAmount := args[2]
_, err = decimal.NewFromString(rightAmount)
sig, err := key.Sign(payload)
if err != nil {
fmt.Println("invalid RightAmount")
os.Exit(1)
panic(err)
}
pub, err := key.GetPublic().Raw()
if err != nil {
panic(err)
}
_, err = client.SendMessage(
context.Background(),
&protobufs.TokenRequest{
Request: &protobufs.TokenRequest_Split{
Split: &protobufs.SplitCoinRequest{
OfCoin: coin,
Amounts: amounts,
Signature: &protobufs.Ed448Signature{
Signature: sig,
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: pub,
},
},
},
},
},
)
if err != nil {
panic(err)
}
fmt.Println(leftAmount + " QUIL (Coin 0x024479f49f03dc53fd702198cd9b548c9e96004e19ef6a4e9c5211a9795ba34d)")
fmt.Println(rightAmount + " QUIL (Coin 0x0140e01731256793bba03914f3844d645fbece26553acdea8ac4de4d84f91690)")
},
}

191064
client/cmd/third_retro.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,12 @@
package cmd
import (
"fmt"
"math/big"
"os"
"strconv"
"context"
"encoding/hex"
"strings"
"github.com/shopspring/decimal"
"github.com/spf13/cobra"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
var transferCmd = &cobra.Command{
@ -15,75 +14,84 @@ var transferCmd = &cobra.Command{
Short: "Creates a pending transfer of coin",
Long: `Creates a pending transfer of coin:
transfer <ToAccount> [<RefundAccount>] [<Expiry>] (<Amount>|<OfCoin>)
transfer <ToAccount> <OfCoin>
ToAccount account address, must be specified
RefundAccount - account address to receive coin if rejected (if omitted, uses sender address)
Expiry unix epoch time in seconds where the ToAccount can no longer claim (if omitted, does not expire)
Amount the amount to send, splitting/merging and sending as needed
OfCoin the address of the coin to send in whole
Either Amount or OfCoin must be specified
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 2 || len(args) > 4 {
fmt.Println("invalid command")
os.Exit(1)
if len(args) != 2 {
panic("invalid arguments")
}
_, ok := new(big.Int).SetString(args[0], 0)
if !ok {
fmt.Println("invalid ToAccount")
os.Exit(1)
conn, err := GetGRPCClient()
if err != nil {
panic(err)
}
defer conn.Close()
client := protobufs.NewNodeServiceClient(conn)
key, err := GetPrivKeyFromConfig(NodeConfig)
if err != nil {
panic(err)
}
refundAccount := "0x23c0f371e9faa7be4ffedd616361e0c9aeb776ae4d7f3a37605ecbfa40a55a90"
// expiry := int64(9999999999)
var err error
if len(args) >= 3 {
if len(args[len(args)-2]) != 66 {
_, err = strconv.ParseInt(args[len(args)-2], 10, 0)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
refundAccount = args[1]
}
}
if refundAccount[0] != '0' || refundAccount[1] != 'x' {
_, ok := new(big.Int).SetString(refundAccount, 0)
if !ok {
fmt.Println("invalid refund account")
os.Exit(1)
}
}
ofCoin := ""
amount := ""
if len(args[len(args)-1]) == 66 {
ofCoin = args[len(args)-1]
_, ok := new(big.Int).SetString(ofCoin, 0)
if !ok {
fmt.Println("invalid OfCoin")
os.Exit(1)
}
switch ofCoin {
case "0x1148092cdce78c721835601ef39f9c2cd8b48b7787cbea032dd3913a4106a58d":
fmt.Println("25.0 QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)")
case "0x162ad88c319060b4f5ea6dbf9a0c2cd82d3d70dfc22d5fc99ca5371083d68416":
fmt.Println("1520.381923 QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)")
}
} else {
amount = args[len(args)-1]
_, err := decimal.NewFromString(amount)
var coinaddr *protobufs.CoinRef
payload := []byte("transfer")
toaddr := []byte{}
for i, arg := range args {
addrHex, _ := strings.CutPrefix(arg, "0x")
addr, err := hex.DecodeString(addrHex)
if err != nil {
fmt.Println("invalid Amount")
os.Exit(1)
panic(err)
}
fmt.Println(amount + " QUIL (Pending Transaction 0x0382e4da0c7c0133a1b53453b05096272b80c1575c6828d0211c4e371f7c81bb)")
if i == 0 {
toaddr = addr
continue
}
coinaddr = &protobufs.CoinRef{
Address: addr,
}
payload = append(payload, addr...)
}
payload = append(payload, toaddr...)
sig, err := key.Sign(payload)
if err != nil {
panic(err)
}
pub, err := key.GetPublic().Raw()
if err != nil {
panic(err)
}
_, err = client.SendMessage(
context.Background(),
&protobufs.TokenRequest{
Request: &protobufs.TokenRequest_Transfer{
Transfer: &protobufs.TransferCoinRequest{
OfCoin: coinaddr,
ToAccount: &protobufs.AccountRef{
Account: &protobufs.AccountRef_ImplicitAccount{
ImplicitAccount: &protobufs.ImplicitAccount{
Address: toaddr,
},
},
},
Signature: &protobufs.Ed448Signature{
Signature: sig,
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: pub,
},
},
},
},
},
)
if err != nil {
panic(err)
}
},
}

View File

@ -4,43 +4,190 @@ go 1.21
toolchain go1.22.1
replace source.quilibrium.com/quilibrium/monorepo/nekryptology => ../nekryptology
replace source.quilibrium.com/quilibrium/monorepo/bls48581 => ../bls48581
replace source.quilibrium.com/quilibrium/monorepo/vdf => ../vdf
replace github.com/multiformats/go-multiaddr => ../go-multiaddr
replace github.com/multiformats/go-multiaddr-dns => ../go-multiaddr-dns
replace github.com/libp2p/go-libp2p => ../go-libp2p
replace github.com/libp2p/go-libp2p-kad-dht => ../go-libp2p-kad-dht
replace github.com/libp2p/go-libp2p-gostream => ../go-libp2p-gostream
replace source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub => ../go-libp2p-blossomsub
replace source.quilibrium.com/quilibrium/monorepo/node => ../node
replace source.quilibrium.com/quilibrium/monorepo/nekryptology => ../nekryptology
replace github.com/cockroachdb/pebble => ../pebble
require (
github.com/iden3/go-iden3-crypto v0.0.15
github.com/iden3/go-iden3-crypto v0.0.16
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.12.4
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.24.0
google.golang.org/grpc v1.58.2
source.quilibrium.com/quilibrium/monorepo/node v0.0.0-00010101000000-000000000000
)
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401 // indirect
github.com/bwesterb/go-ristretto v1.2.3 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cockroachdb/errors v1.11.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20231210175920-b4d301aeb46a // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/gnark-crypto v0.5.3 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/flynn/noise v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/getsentry/sentry-go v0.18.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/ipfs/boxo v0.10.0 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/ipld/go-ipld-prime v0.20.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-libp2p-gostream v0.6.0 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.23.0 // indirect
github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect
github.com/libp2p/go-libp2p-record v0.2.0 // indirect
github.com/libp2p/go-libp2p-routing-helpers v0.7.2 // indirect
github.com/libp2p/go-msgio v0.3.0 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
github.com/libp2p/go-reuseport v0.4.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.1 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multicodec v0.9.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pion/datachannel v1.5.6 // indirect
github.com/pion/dtls/v2 v2.2.11 // indirect
github.com/pion/ice/v2 v2.3.25 // indirect
github.com/pion/interceptor v0.1.29 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.6 // indirect
github.com/pion/sctp v1.8.16 // indirect
github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v2 v2.0.18 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.5 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect
github.com/pion/webrtc/v3 v3.2.40 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polydawn/refmt v0.89.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.44.0 // indirect
github.com/quic-go/webtransport-go v0.8.0 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.uber.org/dig v1.17.1 // indirect
go.uber.org/fx v1.22.1 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gonum.org/v1/gonum v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
source.quilibrium.com/quilibrium/monorepo/bls48581 v0.0.0-00010101000000-000000000000 // indirect
source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub v0.0.0-00010101000000-000000000000 // indirect
source.quilibrium.com/quilibrium/monorepo/nekryptology v0.0.0-00010101000000-000000000000 // indirect
source.quilibrium.com/quilibrium/monorepo/vdf v0.0.0-00010101000000-000000000000 // indirect
)
require (
github.com/cloudflare/circl v1.3.8
github.com/cloudflare/circl v1.3.9
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/libp2p/go-libp2p v0.35.1
github.com/libp2p/go-libp2p v0.35.4
github.com/pkg/errors v0.9.1
github.com/shopspring/decimal v1.4.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/zap v1.27.0
google.golang.org/protobuf v1.34.1 // indirect
source.quilibrium.com/quilibrium/monorepo/node v1.14.17
)

View File

@ -1,6 +1,27 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401 h1:0tjUthKCaF8zwF9Qg7lfnep0xdo4n8WiFUfQPaMHX6g=
github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs=
@ -14,131 +35,758 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8=
github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA=
github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.5.3 h1:4xLFGZR3NWEH2zy+YzvzHicpToQR8FXFbfLNvpGB+rE=
github.com/consensys/gnark-crypto v0.5.3/go.mod h1:hOdPlWQV1gDLp7faZVeg8Y0iEPFaOUnCc4XeCCk96p0=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4=
github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4=
github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTorBMEdsk=
github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY=
github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM=
github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g=
github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0=
github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0=
github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=
github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=
github.com/libp2p/go-libp2p-routing-helpers v0.7.2 h1:xJMFyhQ3Iuqnk9Q2dYE1eUTzsah7NLw3Qs2zjUV78T0=
github.com/libp2p/go-libp2p-routing-helpers v0.7.2/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.12.4 h1:rrKqpY9h+n80EwhhC/kkcunCZZ7URIF8yN1WEUt2Hvc=
github.com/multiformats/go-multiaddr v0.12.4/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs=
github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=
github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU=
github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys=
go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230725012225-302865e7556b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM=
gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -12,5 +12,13 @@ hex = "0.4.3"
serde_json = "1.0.117"
uniffi = { version= "0.25", features = ["cli"]}
[dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] }
rand = "0.8.5"
[build-dependencies]
uniffi = { version = "0.25", features = [ "build" ] }
[[bench]]
name = "bench_bls"
harness = false

View File

@ -0,0 +1,20 @@
use rand::Rng;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn criterion_benchmark(c: &mut Criterion) {
bls48581::init();
let mut bytes = vec![0u8; 65536];
rand::thread_rng().fill(&mut bytes[..]);
let mut group = c.benchmark_group("commit");
group.sample_size(10);
group.bench_function("commit 16", |b| b.iter(|| black_box(bls48581::commit_raw(&bytes, 16))));
group.bench_function("commit 128", |b| b.iter(|| black_box(bls48581::commit_raw(&bytes, 128))));
group.bench_function("commit 256", |b| b.iter(|| black_box(bls48581::commit_raw(&bytes, 256))));
group.bench_function("commit 1024", |b| b.iter(|| black_box(bls48581::commit_raw(&bytes, 1024))));
group.bench_function("commit 65536", |b| b.iter(|| black_box(bls48581::commit_raw(&bytes, 65536))));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -49,35 +49,18 @@ fn recurse_fft(
fft_width: u64,
inverse: bool,
) {
let M = &big::BIG::new_ints(&rom::CURVE_ORDER);
let roots = if inverse {
&bls::singleton().ReverseRootsOfUnityBLS48581[&fft_width]
} else {
&bls::singleton().RootsOfUnityBLS48581[&fft_width]
};
if out.len() <= 16 {
let l = out.len() as u64;
for i in 0..l {
let mut last = big::BIG::modmul(
&values[offset as usize],
&roots[0],
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
for j in 1..l {
let mid = big::BIG::modmul(
&values[(offset + j * stride) as usize],
&roots[((i * j) % l) as usize * roots_stride as usize],
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
last = big::BIG::modadd(
&last,
&mid,
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
}
out[i as usize] = last;
}
if out.len() == 1 {
// optimization: we're working in bls48-581, the first roots of unity
// value is always 1 no matter the fft width, so we can skip the
// multiplication:
out[0] = values[offset as usize].clone();
return;
}
@ -107,26 +90,26 @@ fn recurse_fft(
// cha cha now, y'all
for i in 0..half {
let mul = big::BIG::modmul(
&out[(i + half) as usize],
&roots[(i * roots_stride) as usize],
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
let mul_add = big::BIG::modadd(
&out[i as usize],
&mul,
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[(i + half) as usize] = big::BIG::modadd(
&out[i as usize],
&big::BIG::modneg(&mul, &big::BIG::new_ints(&rom::CURVE_ORDER)),
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[i as usize] = mul_add;
let mul = big::BIG::modmul(
&out[(i + half) as usize],
&roots[(i * roots_stride) as usize],
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
let mul_add = big::BIG::modadd(
&out[i as usize],
&mul,
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[(i + half) as usize] = big::BIG::modadd(
&out[i as usize],
&big::BIG::modneg(&mul, &big::BIG::new_ints(&rom::CURVE_ORDER)),
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[i as usize] = mul_add;
}
}
fn fft(
pub fn fft(
values: &[big::BIG],
fft_width: u64,
inverse: bool,
@ -183,21 +166,8 @@ fn recurse_fft_g1(
&bls::singleton().RootsOfUnityBLS48581[&fft_width]
};
if out.len() <= 16 {
let l = out.len() as u64;
for i in 0..l {
let mut last = ecp::ECP::mul(&values[offset as usize].clone(), &roots[0]);
for j in 1..l {
let mid = ecp::ECP::mul(
&values[(offset + j * stride) as usize].clone(),
&roots[((i * j) % l) as usize * roots_stride as usize],
);
&last.add(&mid);
}
out[i as usize] = last.clone();
}
if out.len() == 1 {
out[0] = values[offset as usize].clone();
return;
}
@ -227,7 +197,9 @@ fn recurse_fft_g1(
// cha cha now, y'all
for i in 0..half {
let mul = out[(i + half) as usize].clone().mul(&roots[(i * roots_stride) as usize].clone());
let mul = out[(i + half) as usize].clone().mul(
&roots[(i * roots_stride) as usize].clone(),
);
let mut mul_add = out[i as usize].clone();
mul_add.add(&mul.clone());
out[(i + half) as usize] = out[i as usize].clone();
@ -236,7 +208,7 @@ fn recurse_fft_g1(
}
}
fn fft_g1(
pub fn fft_g1(
values: &[ecp::ECP],
fft_width: u64,
inverse: bool,
@ -291,7 +263,7 @@ fn bytes_to_polynomial(
let size = bytes.len() / 64;
let trunc_last = bytes.len() % 64 > 0;
let mut poly = Vec::new();
let mut poly = Vec::with_capacity(size + (if trunc_last { 1 } else { 0 }));
for i in 0..size {
let scalar = big::BIG::frombytes(&bytes[i * 64..(i + 1) * 64]);
@ -306,8 +278,8 @@ fn bytes_to_polynomial(
return poly;
}
fn point_linear_combination(
points: &Vec<&ecp::ECP>,
pub fn point_linear_combination(
points: &[ecp::ECP],
scalars: &Vec<big::BIG>,
) -> Result<ecp::ECP, Box<dyn Error>> {
if points.len() != scalars.len() {
@ -318,14 +290,7 @@ fn point_linear_combination(
).into());
}
let mut result = ecp::ECP::new();
for (i, point) in points.iter().enumerate() {
let c = point.clone();
let p = c.mul(&scalars[i]);
&result.add(&p);
}
let result = ecp::ECP::muln(points.len(), points, scalars.as_slice());
Ok(result)
}
@ -362,7 +327,7 @@ pub fn commit_raw(
poly.push(big::BIG::new());
}
match point_linear_combination(
&bls::singleton().FFTBLS48581[&poly_size].iter().collect(),
&bls::singleton().FFTBLS48581[&poly_size],
&poly,
) {
Ok(commit) => {
@ -398,7 +363,7 @@ pub fn prove_raw(
subz = big::BIG::modadd(&subz, &big::BIG::modneg(&z, &big::BIG::new_ints(&rom::CURVE_ORDER)), &big::BIG::new_ints(&rom::CURVE_ORDER));
let mut subzinv = subz.clone();
subzinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
let mut o = big::BIG::new_int(1);
let o = big::BIG::new_int(1);
let mut oinv = o.clone();
oinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
let divisors: Vec<big::BIG> = vec![
@ -440,7 +405,7 @@ pub fn prove_raw(
}
match point_linear_combination(
&bls::singleton().CeremonyBLS48581G1[..(poly_size as usize - 1)].iter().collect(),
&bls::singleton().CeremonyBLS48581G1[..(poly_size as usize - 1)],
&out,
) {
Ok(proof) => {
@ -471,7 +436,29 @@ pub fn verify_raw(
let y = big::BIG::frombytes(data);
let c = ecp::ECP::frombytes(commit);
if c.is_infinity() || c.equals(&ecp::ECP::generator()) {
return false;
}
let p = ecp::ECP::frombytes(proof);
if p.is_infinity() || p.equals(&ecp::ECP::generator()) {
return false;
}
if poly_size > 1024 {
let mut xc = c.clone();
xc.sub(&bls::singleton().FFTBLS48581[&poly_size][index as usize].clone().mul(&y));
let mut check = c.clone();
check.neg();
let yp = &bls::singleton().CeremonyBLS48581G2[1].clone().mul(&y);
let mut r = pair8::initmp();
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &check);
pair8::another(&mut r, &yp, &bls::singleton().FFTBLS48581[&poly_size][index as usize]);
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &xc);
let mut v = pair8::miller(&mut r);
v = pair8::fexp(&v);
return v.isunity();
}
return verify(
&c,
@ -484,3 +471,28 @@ pub fn verify_raw(
pub fn init() {
bls::singleton();
}
#[cfg(test)]
mod tests {
use ecp::ECP;
use super::*;
#[test]
fn fft_matches_fft_g1_when_raised() {
init();
let mut rand = rand::RAND::new();
let mut v = vec![big::BIG::new(); 16];
let mut vp = vec![ECP::new(); 16];
for i in 0..16 {
v[i] = big::BIG::random(&mut rand);
vp[i] = ECP::generator().mul(&v[i]);
}
let scalars = fft(v.as_slice(), 16, false).unwrap();
let points = fft_g1(vp.as_slice(), 16, false).unwrap();
for (i, s) in scalars.iter().enumerate() {
let sp = ECP::generator().mul(&s);
assert!(points[i].equals(&sp));
}
}
}

7
crates/channel/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "channel"
version = "0.1.0"

34
crates/channel/Cargo.toml Normal file
View File

@ -0,0 +1,34 @@
[package]
name = "channel"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["lib", "staticlib"]
name = "channel"
[dependencies]
base64 = "0.22.1"
hex = "0.4.3"
serde_json = "1.0.117"
ed448-goldilocks-plus = "0.11.2"
rand = "0.8.5"
sha2 = "0.10.8"
hkdf = "0.12.4"
aes-gcm = "0.10.3"
thiserror = "1.0.63"
hmac = "0.12.1"
serde = "1.0.208"
lazy_static = "1.5.0"
uniffi = { version= "0.25", features = ["cli"]}
[dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] }
rand = "0.8.5"
[build-dependencies]
uniffi = { version = "0.25", features = [ "build" ] }
[[bench]]
name = "bench_channel"
harness = false

View File

@ -0,0 +1,9 @@
use rand::Rng;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn criterion_benchmark(c: &mut Criterion) {
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

5
crates/channel/build.rs Normal file
View File

@ -0,0 +1,5 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
uniffi::generate_scaffolding("src/lib.udl").expect("uniffi generation failed");
}

991
crates/channel/src/lib.rs Normal file
View File

@ -0,0 +1,991 @@
use base64::prelude::*;
use std::collections::HashMap;
use ed448_goldilocks_plus::{elliptic_curve::group::GroupEncoding, EdwardsPoint, Scalar};
use protocols::{doubleratchet::{DoubleRatchetParticipant, P2PChannelEnvelope}, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
pub(crate) mod protocols;
pub struct DoubleRatchetStateAndEnvelope {
pub ratchet_state: String,
pub envelope: String,
}
pub struct DoubleRatchetStateAndMessage {
pub ratchet_state: String,
pub message: Vec<u8>,
}
pub struct TripleRatchetStateAndMetadata {
pub ratchet_state: String,
pub metadata: HashMap<String, String>,
}
pub struct TripleRatchetStateAndEnvelope {
pub ratchet_state: String,
pub envelope: String,
}
pub struct TripleRatchetStateAndMessage {
pub ratchet_state: String,
pub message: Vec<u8>,
}
pub fn new_double_ratchet(session_key: &Vec<u8>, sending_header_key: &Vec<u8>, next_receiving_header_key: &Vec<u8>, is_sender: bool, sending_ephemeral_private_key: &Vec<u8>, receiving_ephemeral_key: &Vec<u8>) -> String {
if sending_ephemeral_private_key.len() != 56 {
return "".to_string();
}
if receiving_ephemeral_key.len() != 57 {
return "".to_string();
}
let mut sending_ephemeral_private_key_bytes = [0u8; 56];
sending_ephemeral_private_key_bytes.copy_from_slice(&sending_ephemeral_private_key);
let mut receiving_ephemeral_key_bytes = [0u8; 57];
receiving_ephemeral_key_bytes.copy_from_slice(&receiving_ephemeral_key);
let sending_key = Scalar::from_bytes(&sending_ephemeral_private_key_bytes.into());
let receiving_key = EdwardsPoint::from_bytes(&receiving_ephemeral_key_bytes.into()).into_option();
if receiving_key.is_none() {
return "".to_string();
}
let participant = DoubleRatchetParticipant::new(
&session_key,
&sending_header_key,
&next_receiving_header_key,
true,
sending_key,
receiving_key.unwrap(),
);
if participant.is_err() {
return "".to_string();
}
let json = participant.unwrap().to_json();
if json.is_err() {
return "".to_string();
}
return json.unwrap();
}
pub fn double_ratchet_encrypt(ratchet_state_and_message: DoubleRatchetStateAndMessage) -> DoubleRatchetStateAndEnvelope {
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
if participant.is_err() {
return DoubleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let mut dr = participant.unwrap();
let envelope = dr.ratchet_encrypt(&ratchet_state_and_message.message);
if envelope.is_err() {
return DoubleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let participant_json = dr.to_json();
if participant_json.is_err() {
return DoubleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let envelope_json = envelope.unwrap().to_json();
if envelope_json.is_err() {
return DoubleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
return DoubleRatchetStateAndEnvelope{
ratchet_state: participant_json.unwrap(),
envelope: envelope_json.unwrap(),
};
}
pub fn double_ratchet_decrypt(ratchet_state_and_envelope: DoubleRatchetStateAndEnvelope) -> DoubleRatchetStateAndMessage {
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
let envelope = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
if participant.is_err() || envelope.is_err() {
return DoubleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
let mut dr = participant.unwrap();
let message = dr.ratchet_decrypt(&envelope.unwrap());
if message.is_err() {
return DoubleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
let participant_json = dr.to_json();
if participant_json.is_err() {
return DoubleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
return DoubleRatchetStateAndMessage{
ratchet_state: participant_json.unwrap(),
message: message.unwrap(),
};
}
pub fn new_triple_ratchet(peers: &Vec<Vec<u8>>, peer_key: &Vec<u8>, identity_key: &Vec<u8>, signed_pre_key: &Vec<u8>, threshold: u64, async_dkg_ratchet: bool) -> TripleRatchetStateAndMetadata {
if peer_key.len() != 56 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
if identity_key.len() != 56 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
if signed_pre_key.len() != 56 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
if peers.len() < 3 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
if threshold > peers.len() as u64 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
let mut peer_key_bytes = [0u8; 56];
peer_key_bytes.copy_from_slice(&peer_key);
let mut identity_key_bytes = [0u8; 56];
identity_key_bytes.copy_from_slice(&identity_key);
let mut signed_pre_key_bytes = [0u8; 56];
signed_pre_key_bytes.copy_from_slice(&signed_pre_key);
let peer_key_scalar = Scalar::from_bytes(&peer_key_bytes.into());
let identity_key_scalar = Scalar::from_bytes(&identity_key_bytes.into());
let signed_pre_key_scalar = Scalar::from_bytes(&signed_pre_key_bytes.into());
let mut peerinfos = Vec::<PeerInfo>::new();
for pk in peers.iter() {
if pk.len() != 171 {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
peerinfos.push(PeerInfo{
public_key: pk[..57].into(),
identity_public_key: pk[57..114].into(),
signed_pre_public_key: pk[114..].into(),
});
}
let participant = TripleRatchetParticipant::new(
&peerinfos,
peer_key_scalar,
identity_key_scalar,
signed_pre_key_scalar,
threshold as usize,
async_dkg_ratchet,
);
if participant.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
let (tr, metadata) = participant.unwrap();
let participant_json = tr.to_json();
if participant_json.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: "".to_string(),
metadata: HashMap::new(),
};
}
let metadata_json = match metadata_to_json(&String::from(""), metadata) {
Ok(value) => value,
Err(value) => return value,
};
return TripleRatchetStateAndMetadata{
ratchet_state: participant_json.unwrap(),
metadata: metadata_json,
};
}
fn metadata_to_json(ratchet_state: &String, metadata: HashMap<Vec<u8>, P2PChannelEnvelope>) -> Result<HashMap<String, String>, TripleRatchetStateAndMetadata> {
let mut metadata_json = HashMap::<String, String>::new();
for (k,v) in metadata {
let env = v.to_json();
if env.is_err() {
return Err(TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state.to_string(),
metadata: HashMap::new(),
});
}
metadata_json.insert(BASE64_STANDARD.encode(k), env.unwrap());
}
Ok(metadata_json)
}
fn json_to_metadata(ratchet_state_and_metadata: TripleRatchetStateAndMetadata, ratchet_state: &String) -> Result<HashMap<Vec<u8>, P2PChannelEnvelope>, TripleRatchetStateAndMetadata> {
let mut metadata = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
for (k,v) in ratchet_state_and_metadata.metadata {
let env = P2PChannelEnvelope::from_json(v);
let kb = BASE64_STANDARD.decode(k);
if env.is_err() || kb.is_err() {
return Err(TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state.clone(),
metadata: HashMap::new(),
});
}
metadata.insert(kb.unwrap(), env.unwrap());
}
Ok(metadata)
}
pub fn triple_ratchet_init_round_1(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
Ok(value) => value,
Err(value) => return value,
};
let mut trp = tr.unwrap();
let result = trp.initialize(&metadata);
if result.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let metadata = result.unwrap();
let metadata_json = match metadata_to_json(&ratchet_state, metadata) {
Ok(value) => value,
Err(value) => return value,
};
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
return TripleRatchetStateAndMetadata{
ratchet_state: json.unwrap(),
metadata: metadata_json,
};
}
pub fn triple_ratchet_init_round_2(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
Ok(value) => value,
Err(value) => return value,
};
let mut trp = tr.unwrap();
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
for (k, v) in metadata {
let r = trp.receive_poly_frag(&k, &v);
if r.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let opt = r.unwrap();
if opt.is_some() {
result = opt.unwrap();
}
}
let metadata_json = match metadata_to_json(&ratchet_state, result) {
Ok(value) => value,
Err(value) => return value,
};
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
return TripleRatchetStateAndMetadata{
ratchet_state: json.unwrap(),
metadata: metadata_json,
};
}
pub fn triple_ratchet_init_round_3(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
Ok(value) => value,
Err(value) => return value,
};
let mut trp = tr.unwrap();
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
for (k, v) in metadata {
let r = trp.receive_commitment(&k, &v);
if r.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let opt = r.unwrap();
if opt.is_some() {
result = opt.unwrap();
}
}
let metadata_json = match metadata_to_json(&ratchet_state, result) {
Ok(value) => value,
Err(value) => return value,
};
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
return TripleRatchetStateAndMetadata{
ratchet_state: json.unwrap(),
metadata: metadata_json,
};
}
pub fn triple_ratchet_init_round_4(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
Ok(value) => value,
Err(value) => return value,
};
let mut trp = tr.unwrap();
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
for (k, v) in metadata {
let r = trp.recombine(&k, &v);
if r.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
}
let metadata_json = match metadata_to_json(&ratchet_state, result) {
Ok(value) => value,
Err(value) => return value,
};
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndMetadata{
ratchet_state: ratchet_state,
metadata: HashMap::new(),
};
}
return TripleRatchetStateAndMetadata{
ratchet_state: json.unwrap(),
metadata: metadata_json,
};
}
pub fn triple_ratchet_encrypt(ratchet_state_and_message: TripleRatchetStateAndMessage) -> TripleRatchetStateAndEnvelope {
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let mut trp = tr.unwrap();
let result = trp.ratchet_encrypt(&ratchet_state_and_message.message);
if result.is_err() {
return TripleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let envelope = result.unwrap();
let envelope_json = envelope.to_json();
if envelope_json.is_err() {
return TripleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndEnvelope{
ratchet_state: ratchet_state,
envelope: "".to_string(),
};
}
return TripleRatchetStateAndEnvelope{
ratchet_state: json.unwrap(),
envelope: envelope_json.unwrap(),
};
}
pub fn triple_ratchet_decrypt(ratchet_state_and_envelope: TripleRatchetStateAndEnvelope) -> TripleRatchetStateAndMessage {
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
if tr.is_err() {
return TripleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
let mut trp = tr.unwrap();
let env = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
if env.is_err() {
return TripleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
let result = trp.ratchet_decrypt(&env.unwrap());
if result.is_err() {
return TripleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
let message = result.unwrap().0;
let json = trp.to_json();
if json.is_err() {
return TripleRatchetStateAndMessage{
ratchet_state: ratchet_state,
message: vec![],
};
}
return TripleRatchetStateAndMessage{
ratchet_state: json.unwrap(),
message: message,
};
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use ed448_goldilocks_plus::{Scalar, elliptic_curve::Group, EdwardsPoint};
use protocols::{doubleratchet::P2PChannelEnvelope, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
#[test]
fn test_four_party_triple_ratchet_communication() {
let mut rng = rand::thread_rng();
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
.collect();
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
let mut peer_infos: Vec<PeerInfo> = keys
.iter()
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
})
.collect();
// mirror the internal order so we can use by index:
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
for i in 0..4 {
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
}
for i in 0..4 {
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, peer)| peer.clone())
.collect();
let (participant, init_msg) = TripleRatchetParticipant::new(
&other_peers,
keys[i].0.clone(),
keys[i].1.clone(),
keys[i].2.clone(),
3,
false,
).unwrap();
participants.push(participant);
for (j, env) in init_msg.iter() {
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
// Exchange initial messages and get frags:
for i in 0..4 {
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
for (j, env) in result.iter() {
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
// Exchange frags and receive commitments once all frags have been distributed:
for i in 0..4 {
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
for (j, env) in out.iter() {
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
}
// Exchange commitments and produce reveals:
for i in 0..4 {
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
for (j, env) in reveal_msg.iter() {
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
}
// Collect reveals and confirm zkpoks are valid, produce group key:
for i in 0..4 {
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
participants[i].recombine(j, &env.clone()).unwrap();
}
}
// Test sending and receiving messages
let test_messages = [
"hello there",
"general kenobi",
"you are a bold one",
"*mechanical laughter*",
];
for (i, message) in test_messages.iter().enumerate() {
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
}
}
}
for _ in 0..5 {
for i in 0..4 {
let message1 = format!("test 1 {}", i + 1);
let message2 = format!("test 2 {}", i + 1);
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
}
}
}
}
}
#[test]
fn test_four_party_triple_ratchet_communication_with_serialization_each_step() {
let mut rng = rand::thread_rng();
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
.collect();
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
let mut peer_infos: Vec<PeerInfo> = keys
.iter()
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
})
.collect();
// mirror the internal order so we can use by index:
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
for i in 0..4 {
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
}
for i in 0..4 {
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, peer)| peer.clone())
.collect();
let (participant, init_msg) = TripleRatchetParticipant::new(
&other_peers,
keys[i].0.clone(),
keys[i].1.clone(),
keys[i].2.clone(),
3,
false,
).unwrap();
for (j, env) in init_msg.iter() {
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
let participant_json = participant.to_json();
if participant_json.is_err() {
panic!("bad json");
}
participants.push(TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap());
}
// Exchange initial messages and get frags:
for i in 0..4 {
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
for (j, env) in result.iter() {
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
// Exchange frags and receive commitments once all frags have been distributed:
for i in 0..4 {
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
for (j, env) in out.iter() {
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
// Exchange commitments and produce reveals:
for i in 0..4 {
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
for (j, env) in reveal_msg.iter() {
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
// Collect reveals and confirm zkpoks are valid, produce group key:
for i in 0..4 {
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
participants[i].recombine(j, &env.clone()).unwrap();
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
}
// Test sending and receiving messages
let test_messages = [
"hello there",
"general kenobi",
"you are a bold one",
"*mechanical laughter*",
];
for (i, message) in test_messages.iter().enumerate() {
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
}
}
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
for _ in 0..5 {
for i in 0..4 {
let message1 = format!("test 1 {}", i + 1);
let message2 = format!("test 2 {}", i + 1);
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
}
}
let participant_json = participants[i].to_json();
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
}
}
}
#[test]
fn test_four_party_async_triple_ratchet_communication() {
let mut rng = rand::thread_rng();
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
.collect();
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
let mut peer_infos: Vec<PeerInfo> = keys
.iter()
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
})
.collect();
// mirror the internal order so we can use by index:
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
for i in 0..4 {
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
}
for i in 0..4 {
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, peer)| peer.clone())
.collect();
let (participant, init_msg) = TripleRatchetParticipant::new(
&other_peers,
keys[i].0.clone(),
keys[i].1.clone(),
keys[i].2.clone(),
2,
true,
).unwrap();
participants.push(participant);
for (j, env) in init_msg.iter() {
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
// Exchange initial messages and get frags:
for i in 0..4 {
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
for (j, env) in result.iter() {
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
// Exchange frags and receive commitments once all frags have been distributed:
for i in 0..4 {
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
for (j, env) in out.iter() {
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
}
// Exchange commitments and produce reveals:
for i in 0..4 {
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
for (j, env) in reveal_msg.iter() {
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
}
}
}
}
// Collect reveals and confirm zkpoks are valid, produce group key:
for i in 0..4 {
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
participants[i].recombine(j, &env.clone()).unwrap();
}
}
// Test sending and receiving messages
let test_messages = [
"hello there",
"general kenobi",
"you are a bold one",
"*mechanical laughter*",
];
for (i, message) in test_messages.iter().enumerate() {
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
}
}
}
for _ in 0..5 {
for i in 0..4 {
let message1 = format!("test 1 {}", i + 1);
let message2 = format!("test 2 {}", i + 1);
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
for j in 0..4 {
if i != j {
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
}
}
}
}
}
}

View File

@ -0,0 +1,38 @@
namespace channel {
string new_double_ratchet([ByRef] sequence<u8> session_key, [ByRef] sequence<u8> sending_header_key, [ByRef] sequence<u8> next_receiving_header_key, boolean is_sender, [ByRef] sequence<u8> sending_ephemeral_private_key, [ByRef] sequence<u8> receiving_ephemeral_key);
DoubleRatchetStateAndEnvelope double_ratchet_encrypt(DoubleRatchetStateAndMessage ratchet_state_and_message);
DoubleRatchetStateAndMessage double_ratchet_decrypt(DoubleRatchetStateAndEnvelope ratchet_state_and_envelope);
TripleRatchetStateAndMetadata new_triple_ratchet([ByRef] sequence<sequence<u8>> peers, [ByRef] sequence<u8> peer_key, [ByRef] sequence<u8> identity_key, [ByRef] sequence<u8> signed_pre_key, u64 threshold, boolean async_dkg_ratchet);
TripleRatchetStateAndMetadata triple_ratchet_init_round_1(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
TripleRatchetStateAndMetadata triple_ratchet_init_round_2(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
TripleRatchetStateAndMetadata triple_ratchet_init_round_3(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
TripleRatchetStateAndMetadata triple_ratchet_init_round_4(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
TripleRatchetStateAndEnvelope triple_ratchet_encrypt(TripleRatchetStateAndMessage ratchet_state_and_message);
TripleRatchetStateAndMessage triple_ratchet_decrypt(TripleRatchetStateAndEnvelope ratchet_state_and_envelope);
};
dictionary DoubleRatchetStateAndEnvelope {
string ratchet_state;
string envelope;
};
dictionary DoubleRatchetStateAndMessage {
string ratchet_state;
sequence<u8> message;
};
dictionary TripleRatchetStateAndMetadata {
string ratchet_state;
record<string, string> metadata;
};
dictionary TripleRatchetStateAndEnvelope {
string ratchet_state;
string envelope;
};
dictionary TripleRatchetStateAndMessage {
string ratchet_state;
sequence<u8> message;
};

View File

@ -0,0 +1,622 @@
use base64::prelude::*;
use ed448_goldilocks_plus::elliptic_curve::group::GroupEncoding;
use ed448_goldilocks_plus::elliptic_curve::ops::MulByGenerator;
use ed448_goldilocks_plus::{subtle, CompressedEdwardsY, EdwardsPoint, Scalar};
use rand::rngs::OsRng;
use rand::RngCore;
use sha2::Sha512;
use hkdf::Hkdf;
use aes_gcm::{Aes256Gcm, Nonce};
use aes_gcm::aead::{Aead, Payload};
use std::collections::HashMap;
use std::error;
use subtle::ConstantTimeEq;
use serde::{Serialize, Deserialize};
const DOUBLE_RATCHET_PROTOCOL_VERSION: u16 = 1;
const DOUBLE_RATCHET_PROTOCOL: u16 = 1 << 8 + DOUBLE_RATCHET_PROTOCOL_VERSION;
const CHAIN_KEY: u8 = 0x01;
const MESSAGE_KEY: u8 = 0x02;
const AEAD_KEY: u8 = 0x03;
pub struct DoubleRatchetParticipant {
sending_ephemeral_private_key: Scalar,
receiving_ephemeral_key: EdwardsPoint,
root_key: Vec<u8>,
sending_chain_key: Vec<u8>,
current_sending_header_key: Vec<u8>,
current_receiving_header_key: Vec<u8>,
next_sending_header_key: Vec<u8>,
next_receiving_header_key: Vec<u8>,
receiving_chain_key: Vec<u8>,
current_sending_chain_length: u32,
previous_sending_chain_length: u32,
current_receiving_chain_length: u32,
previous_receiving_chain_length: u32,
skipped_keys_map: HashMap<Vec<u8>, HashMap<u32, Vec<u8>>>,
}
#[derive(Serialize, Deserialize)]
pub struct DoubleRatchetParticipantJson {
pub sending_ephemeral_private_key: String,
pub receiving_ephemeral_key: String,
pub root_key: String,
pub sending_chain_key: String,
pub current_sending_header_key: String,
pub current_receiving_header_key: String,
pub next_sending_header_key: String,
pub next_receiving_header_key: String,
pub receiving_chain_key: String,
pub current_sending_chain_length: u32,
pub previous_sending_chain_length: u32,
pub current_receiving_chain_length: u32,
pub previous_receiving_chain_length: u32,
pub skipped_keys_map: HashMap<String, HashMap<u32, String>>,
}
#[derive(Clone, Debug)]
pub struct MessageCiphertext {
pub ciphertext: Vec<u8>,
pub initialization_vector: Vec<u8>,
pub associated_data: Option<Vec<u8>>,
}
#[derive(Clone, Debug)]
pub struct P2PChannelEnvelope {
pub protocol_identifier: u16,
pub message_header: MessageCiphertext,
pub message_body: MessageCiphertext,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct P2PChannelEnvelopeJson {
pub protocol_identifier: u16,
pub message_header: MessageCiphertextJson,
pub message_body: MessageCiphertextJson,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MessageCiphertextJson {
pub ciphertext: String,
pub initialization_vector: String,
pub associated_data: Option<String>,
}
impl P2PChannelEnvelope {
pub fn to_json(&self) -> Result<String, serde_json::Error> {
let envelope = P2PChannelEnvelopeJson{
protocol_identifier: self.protocol_identifier,
message_header: MessageCiphertextJson{
ciphertext: BASE64_STANDARD.encode(&self.message_header.ciphertext),
initialization_vector: BASE64_STANDARD.encode(&self.message_header.initialization_vector),
associated_data: self.message_header.associated_data.clone().map(|a| BASE64_STANDARD.encode(a)),
},
message_body: MessageCiphertextJson{
ciphertext: BASE64_STANDARD.encode(&self.message_body.ciphertext),
initialization_vector: BASE64_STANDARD.encode(&self.message_body.initialization_vector),
associated_data: self.message_body.associated_data.clone().map(|a| BASE64_STANDARD.encode(a)),
},
};
serde_json::to_string(&envelope)
}
pub fn from_json(envelope_json: String) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
let envelope: Result<P2PChannelEnvelopeJson, serde_json::Error> = serde_json::from_str(&envelope_json);
if envelope.is_err() {
return Err(Box::new(envelope.unwrap_err()));
}
let e = envelope.unwrap();
let header_ciphertext = BASE64_STANDARD.decode(e.message_header.ciphertext)?;
let header_initialization_vector = BASE64_STANDARD.decode(e.message_header.initialization_vector)?;
let header_associated_data = e.message_header.associated_data.map(|a| BASE64_STANDARD.decode(a)).transpose()?;
let ciphertext = BASE64_STANDARD.decode(e.message_body.ciphertext)?;
let initialization_vector = BASE64_STANDARD.decode(e.message_body.initialization_vector)?;
let associated_data = e.message_body.associated_data.map(|a| BASE64_STANDARD.decode(a)).transpose()?;
Ok(P2PChannelEnvelope{
protocol_identifier: e.protocol_identifier,
message_header: MessageCiphertext{
ciphertext: header_ciphertext,
initialization_vector: header_initialization_vector,
associated_data: header_associated_data,
},
message_body: MessageCiphertext{
ciphertext: ciphertext,
initialization_vector: initialization_vector,
associated_data: associated_data,
},
})
}
}
impl DoubleRatchetParticipant {
pub fn new(
session_key: &[u8],
sending_header_key: &[u8],
next_receiving_header_key: &[u8],
is_sender: bool,
sending_ephemeral_private_key: Scalar,
receiving_ephemeral_key: EdwardsPoint,
) -> Result<Self, Box<dyn std::error::Error>> {
let mut participant = DoubleRatchetParticipant {
sending_ephemeral_private_key,
receiving_ephemeral_key,
root_key: vec![],
sending_chain_key: vec![],
current_sending_header_key: sending_header_key.to_vec(),
current_receiving_header_key: vec![],
next_sending_header_key: vec![],
next_receiving_header_key: next_receiving_header_key.to_vec(),
receiving_chain_key: vec![],
current_sending_chain_length: 0,
previous_sending_chain_length: 0,
current_receiving_chain_length: 0,
previous_receiving_chain_length: 0,
skipped_keys_map: HashMap::new(),
};
if is_sender {
let dh_output = receiving_ephemeral_key * sending_ephemeral_private_key;
let hkdf = Hkdf::<Sha512>::new(Some(session_key), &dh_output.compress().to_bytes());
let mut rkck = [0u8; 96];
let err = hkdf.expand(b"quilibrium-double-ratchet", &mut rkck);
if err.is_err() {
return Err("invalid length".into());
}
participant.root_key = rkck[..32].to_vec();
participant.sending_chain_key = rkck[32..64].to_vec();
participant.next_sending_header_key = rkck[64..].to_vec();
} else {
participant.root_key = session_key.to_vec();
participant.next_sending_header_key = next_receiving_header_key.to_vec();
participant.next_receiving_header_key = sending_header_key.to_vec();
}
Ok(participant)
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
let mut skipped_keys_map = HashMap::<String, HashMap<u32, String>>::new();
for (k, v) in &self.skipped_keys_map {
let kb = BASE64_STANDARD.encode(k);
let mut val = HashMap::<u32, String>::new();
for (kk, vv) in v {
let vvb = BASE64_STANDARD.encode(vv);
val.insert(*kk, vvb);
}
skipped_keys_map.insert(kb, val);
}
let participant = DoubleRatchetParticipantJson{
sending_ephemeral_private_key: BASE64_STANDARD.encode(self.sending_ephemeral_private_key.to_bytes()),
receiving_ephemeral_key: BASE64_STANDARD.encode(self.receiving_ephemeral_key.compress().to_bytes()),
root_key: BASE64_STANDARD.encode(&self.root_key),
sending_chain_key: BASE64_STANDARD.encode(&self.sending_chain_key),
current_sending_header_key: BASE64_STANDARD.encode(&self.current_sending_header_key),
current_receiving_header_key: BASE64_STANDARD.encode(&self.current_receiving_header_key),
next_sending_header_key: BASE64_STANDARD.encode(&self.next_sending_header_key),
next_receiving_header_key: BASE64_STANDARD.encode(&self.next_receiving_header_key),
receiving_chain_key: BASE64_STANDARD.encode(&self.receiving_chain_key),
current_sending_chain_length: self.current_sending_chain_length,
previous_sending_chain_length: self.previous_sending_chain_length,
current_receiving_chain_length: self.current_receiving_chain_length,
previous_receiving_chain_length: self.previous_receiving_chain_length,
skipped_keys_map: skipped_keys_map,
};
serde_json::to_string(&participant)
}
pub fn from_json(participant_json: String) -> Result<DoubleRatchetParticipant, Box<dyn std::error::Error>> {
let json: Result<DoubleRatchetParticipantJson, serde_json::Error> = serde_json::from_str(&participant_json);
match json {
Ok(participant) => {
let sending_ephemeral_private_key_bytes = BASE64_STANDARD.decode(participant.sending_ephemeral_private_key)?;
let receiving_ephemeral_key_bytes = BASE64_STANDARD.decode(participant.receiving_ephemeral_key)?;
let root_key = BASE64_STANDARD.decode(participant.root_key)?;
let sending_chain_key = BASE64_STANDARD.decode(participant.sending_chain_key)?;
let current_sending_header_key = BASE64_STANDARD.decode(participant.current_sending_header_key)?;
let current_receiving_header_key = BASE64_STANDARD.decode(participant.current_receiving_header_key)?;
let next_sending_header_key = BASE64_STANDARD.decode(participant.next_sending_header_key)?;
let next_receiving_header_key = BASE64_STANDARD.decode(participant.next_receiving_header_key)?;
let receiving_chain_key = BASE64_STANDARD.decode(participant.receiving_chain_key)?;
let current_sending_chain_length = participant.current_sending_chain_length;
let previous_sending_chain_length = participant.previous_sending_chain_length;
let current_receiving_chain_length = participant.current_receiving_chain_length;
let previous_receiving_chain_length = participant.previous_receiving_chain_length;
let mut skipped_keys_map = HashMap::<Vec<u8>, HashMap<u32, Vec<u8>>>::new();
for (k, v) in participant.skipped_keys_map {
let kb = BASE64_STANDARD.decode(k)?;
let mut val = HashMap::<u32, Vec<u8>>::new();
for (kk, vv) in v {
let vvb = BASE64_STANDARD.decode(vv)?;
val.insert(kk, vvb);
}
skipped_keys_map.insert(kb, val);
}
if sending_ephemeral_private_key_bytes.len() != 56 || receiving_ephemeral_key_bytes.len() != 57 {
Err("invalid data".into())
} else {
let mut sending_ephemeral_private_key = [0u8; 56];
sending_ephemeral_private_key.copy_from_slice(&sending_ephemeral_private_key_bytes);
let mut receiving_ephemeral_key = [0u8; 57];
receiving_ephemeral_key.copy_from_slice(&receiving_ephemeral_key_bytes);
let receiving_ephemeral_ct = EdwardsPoint::from_bytes(&receiving_ephemeral_key.into());
if receiving_ephemeral_ct.is_none().into() {
Err("invalid data".into())
} else {
Ok(DoubleRatchetParticipant{
sending_ephemeral_private_key: Scalar::from_bytes(&sending_ephemeral_private_key),
receiving_ephemeral_key: receiving_ephemeral_ct.unwrap(),
root_key: root_key,
sending_chain_key: sending_chain_key,
current_sending_header_key: current_sending_header_key,
current_receiving_header_key: current_receiving_header_key,
next_sending_header_key: next_sending_header_key,
next_receiving_header_key: next_receiving_header_key,
receiving_chain_key: receiving_chain_key,
current_sending_chain_length: current_sending_chain_length,
previous_sending_chain_length: previous_sending_chain_length,
current_receiving_chain_length: current_receiving_chain_length,
previous_receiving_chain_length: previous_receiving_chain_length,
skipped_keys_map: skipped_keys_map,
})
}
}
}
Err(e) => {
Err(Box::new(e))
}
}
}
pub fn ratchet_encrypt(&mut self, message: &[u8]) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
let mut envelope = P2PChannelEnvelope {
protocol_identifier: DOUBLE_RATCHET_PROTOCOL,
message_header: MessageCiphertext::default(),
message_body: MessageCiphertext::default(),
};
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.sending_chain_key);
self.sending_chain_key = new_chain_key;
let header = self.encode_header();
envelope.message_header = self.encrypt(&header, &self.current_sending_header_key, None)?;
envelope.message_body = self.encrypt(
message,
&message_key,
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
)?;
self.current_sending_chain_length += 1;
Ok(envelope)
}
pub fn ratchet_decrypt(&mut self, envelope: &P2PChannelEnvelope) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
if let Some(plaintext) = self.try_skipped_message_keys(envelope)? {
return Ok(plaintext);
}
let (header, should_ratchet) = self.decrypt_header(&envelope.message_header, &self.current_receiving_header_key)?;
let (receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length) =
self.decode_header(&header)?;
if should_ratchet {
self.skip_message_keys(previous_receiving_chain_length)?;
self.ratchet_ephemeral_keys(&receiving_ephemeral_key)?;
}
self.skip_message_keys(current_receiving_chain_length)?;
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.receiving_chain_key);
let plaintext = self.decrypt(
&envelope.message_body,
&message_key,
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
)?;
self.receiving_chain_key = new_chain_key;
self.current_receiving_chain_length += 1;
Ok(plaintext)
}
fn ratchet_ephemeral_keys(&mut self, new_receiving_ephemeral_key: &EdwardsPoint) -> Result<(), Box<dyn std::error::Error>> {
self.previous_sending_chain_length = self.current_sending_chain_length;
self.current_sending_chain_length = 0;
self.current_receiving_chain_length = 0;
self.current_sending_header_key = self.next_sending_header_key.clone();
self.current_receiving_header_key = self.next_receiving_header_key.clone();
self.receiving_ephemeral_key = *new_receiving_ephemeral_key;
// Perform DH and KDF to get new root key and receiving chain key
let dh_output = new_receiving_ephemeral_key * self.sending_ephemeral_private_key;
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output.compress().to_bytes());
let mut rkck = [0u8; 96];
hkdf.expand(b"quilibrium-double-ratchet", &mut rkck);
self.root_key = rkck[..32].to_vec();
self.receiving_chain_key = rkck[32..64].to_vec();
self.next_receiving_header_key = rkck[64..].to_vec();
// Generate new sending ephemeral key
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
// Perform DH and KDF to get new root key and sending chain key
let dh_output = new_receiving_ephemeral_key * self.sending_ephemeral_private_key;
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output.compress().to_bytes());
let mut rkck2 = [0u8; 96];
hkdf.expand(b"quilibrium-double-ratchet", &mut rkck2);
self.root_key = rkck2[..32].to_vec();
self.sending_chain_key = rkck2[32..64].to_vec();
self.next_sending_header_key = rkck2[64..].to_vec();
Ok(())
}
fn try_skipped_message_keys(&self, envelope: &P2PChannelEnvelope) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
for (receiving_header_key, skipped_keys) in &self.skipped_keys_map {
if let Ok((header, _)) = self.decrypt_header(&envelope.message_header, receiving_header_key) {
let (_, _, current) = self.decode_header(&header)?;
if let Some(key_pair) = skipped_keys.get(&current) {
let message_key = &key_pair[..32];
let aead_key = &key_pair[32..];
return self.decrypt(
&envelope.message_body,
message_key,
Some(&[aead_key, &envelope.message_header.ciphertext[..]].concat()),
).map(Some);
}
}
}
Ok(None)
}
fn skip_message_keys(&mut self, until: u32) -> Result<(), Box<dyn std::error::Error>> {
if self.current_receiving_chain_length + 100 < until {
return Err("Skip limit exceeded".into());
}
if !self.receiving_chain_key.is_empty() {
while self.current_receiving_chain_length < until {
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.receiving_chain_key);
self.skipped_keys_map
.entry(self.current_receiving_header_key.clone())
.or_insert_with(HashMap::new)
.insert(self.current_receiving_chain_length, [&message_key[..], &aead_key[..]].concat());
self.receiving_chain_key = new_chain_key;
self.current_receiving_chain_length += 1;
}
}
Ok(())
}
fn encode_header(&self) -> Vec<u8> {
let mut header = Vec::new();
header.extend_from_slice(&EdwardsPoint::mul_by_generator(&self.sending_ephemeral_private_key).compress().to_bytes());
header.extend_from_slice(&self.previous_sending_chain_length.to_be_bytes());
header.extend_from_slice(&self.current_sending_chain_length.to_be_bytes());
header
}
fn decrypt_header(&self, ciphertext: &MessageCiphertext, receiving_header_key: &[u8])
-> Result<(Vec<u8>, bool), Box<dyn std::error::Error>> {
match self.decrypt(ciphertext, receiving_header_key, None) {
Ok(header) => Ok((header, false)),
Err(_) if receiving_header_key.ct_eq(self.current_receiving_header_key.as_slice()).into() => {
self.decrypt(ciphertext, &self.next_receiving_header_key, None)
.map(|header| (header, true))
},
Err(e) => Err(e),
}
}
fn decode_header(&self, header: &[u8]) -> Result<(EdwardsPoint, u32, u32), Box<dyn std::error::Error>> {
if header.len() < 57 { // 57 bytes for EdwardsPoint + 8 bytes for two u32
return Err("Malformed header".into());
}
let receiving_ephemeral_key = CompressedEdwardsY(header[..57].try_into().unwrap()).decompress();
if receiving_ephemeral_key.is_none().into() {
return Err("Malformed point".into());
}
let previous_receiving_chain_length = u32::from_be_bytes(header[57..61].try_into()?);
let current_receiving_chain_length = u32::from_be_bytes(header[61..65].try_into()?);
Ok((receiving_ephemeral_key.unwrap(), previous_receiving_chain_length, current_receiving_chain_length))
}
fn encrypt(&self, plaintext: &[u8], key: &[u8], associated_data: Option<&[u8]>)
-> Result<MessageCiphertext, Box<dyn std::error::Error>> {
use aes_gcm::KeyInit;
let mut iv = [0u8; 12];
OsRng.fill_bytes(&mut iv);
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
let nonce = Nonce::from_slice(&iv);
let mut associated_data = associated_data.unwrap_or(&[]);
let mut aad = [0u8; 32];
if associated_data.len() == 0 {
OsRng.fill_bytes(&mut aad);
associated_data = &aad
}
let ciphertext = cipher.encrypt(nonce, Payload{
msg: plaintext,
aad: associated_data,
}).map_err(|e| format!("Encryption failed: {}", e))?;
Ok(MessageCiphertext {
ciphertext,
initialization_vector: iv.to_vec(),
associated_data: Some(associated_data.to_vec()),
})
}
fn decrypt(&self, ciphertext: &MessageCiphertext, key: &[u8], associated_data: Option<&[u8]>)
-> Result<Vec<u8>, Box<dyn std::error::Error>> {
use aes_gcm::KeyInit;
if key.len() != 32 {
return Err(format!("Invalid key length").into());
}
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
let nonce = Nonce::from_slice(&ciphertext.initialization_vector);
let associated_data = associated_data.unwrap_or_else(|| ciphertext.associated_data.as_ref().unwrap());
cipher.decrypt(nonce, Payload{
msg: ciphertext.ciphertext.as_slice(),
aad: associated_data,
}).map_err(|e| format!("Decryption failed: {}", e).into())
}
pub fn rotate_sending_key(&mut self) -> Result<(), Box<dyn std::error::Error>> {
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
self.ratchet_ephemeral_keys(&self.receiving_ephemeral_key.clone())
}
pub fn get_public_key(&self) -> EdwardsPoint {
EdwardsPoint::mul_by_generator(&self.sending_ephemeral_private_key)
}
}
fn ratchet_keys(input_key: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
use hmac::Mac;
let mut aead_key = [0u8; 64];
let mut message_key = [0u8; 64];
let mut chain_key = [0u8; 64];
let mut hmac_aead = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
hmac_aead.update(&[AEAD_KEY]);
aead_key.copy_from_slice(&hmac_aead.finalize().into_bytes());
let mut hmac_message = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
hmac_message.update(&[MESSAGE_KEY]);
message_key.copy_from_slice(&hmac_message.finalize().into_bytes());
let mut hmac_chain = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
hmac_chain.update(&[CHAIN_KEY]);
chain_key.copy_from_slice(&hmac_chain.finalize().into_bytes());
(chain_key[..32].to_vec(), message_key[..32].to_vec(), aead_key[..32].to_vec())
}
// Implementation for MessageCiphertext
impl Default for MessageCiphertext {
fn default() -> Self {
MessageCiphertext {
ciphertext: Vec::new(),
initialization_vector: Vec::new(),
associated_data: None,
}
}
}
// Implementation for P2PChannelEnvelope
impl P2PChannelEnvelope {
pub fn new(protocol_identifier: u16) -> Self {
P2PChannelEnvelope {
protocol_identifier,
message_header: MessageCiphertext::default(),
message_body: MessageCiphertext::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ed448_goldilocks_plus::Scalar;
#[test]
fn test_double_ratchet_communication() {
let session_key = [0u8; 32];
let sending_header_key = [1u8; 32];
let next_receiving_header_key = [2u8; 32];
let alice_ephemeral = Scalar::random(&mut OsRng);
let bob_ephemeral = Scalar::random(&mut OsRng);
let alice_public = EdwardsPoint::mul_by_generator(&alice_ephemeral);
let bob_public = EdwardsPoint::mul_by_generator(&bob_ephemeral);
let mut alice = DoubleRatchetParticipant::new(
&session_key,
&sending_header_key,
&next_receiving_header_key,
true,
alice_ephemeral,
bob_public,
).unwrap();
let mut bob = DoubleRatchetParticipant::new(
&session_key,
&sending_header_key,
&next_receiving_header_key,
false,
bob_ephemeral,
alice_public,
).unwrap();
// Test message exchange
let message = b"Hello, Bob!";
let envelope = alice.ratchet_encrypt(message).unwrap();
let decrypted = bob.ratchet_decrypt(&envelope).unwrap();
assert_eq!(message, decrypted.as_slice());
let response = b"Hello, Alice!";
let envelope = bob.ratchet_encrypt(response).unwrap();
let delayed = alice.ratchet_encrypt(b"force another step").unwrap();
let decrypted = alice.ratchet_decrypt(&envelope).unwrap();
assert_eq!(response, decrypted.as_slice());
// Test multiple messages
for _ in 0..5 {
let message = b"Secure communication test";
let envelope = alice.ratchet_encrypt(message).unwrap();
let decrypted = bob.ratchet_decrypt(&envelope).unwrap();
assert_eq!(message, decrypted.as_slice());
let response = b"Acknowledged";
let envelope = bob.ratchet_encrypt(response).unwrap();
let decrypted = alice.ratchet_decrypt(&envelope).unwrap();
assert_eq!(response, decrypted.as_slice());
}
let alice_json = alice.to_json().unwrap();
let bob_json = bob.to_json().unwrap();
let mut new_alice = DoubleRatchetParticipant::from_json(alice_json).unwrap();
let mut new_bob = DoubleRatchetParticipant::from_json(bob_json).unwrap();
// Test multiple messages
for _ in 0..5 {
let message = b"Secure communication test";
let envelope = new_alice.ratchet_encrypt(message).unwrap();
let decrypted = new_bob.ratchet_decrypt(&envelope).unwrap();
assert_eq!(message, decrypted.as_slice());
let response = b"Acknowledged";
let envelope = new_bob.ratchet_encrypt(response).unwrap();
let decrypted = new_alice.ratchet_decrypt(&envelope).unwrap();
assert_eq!(response, decrypted.as_slice());
}
let decrypted = new_bob.ratchet_decrypt(&delayed).unwrap();
assert_eq!(b"force another step", decrypted.as_slice());
}
}

View File

@ -0,0 +1,485 @@
use base64::prelude::*;
use std::{collections::HashMap, io::Read};
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha512};
use ed448_goldilocks_plus::{elliptic_curve::{group::GroupEncoding, Field, Group}, subtle::ConstantTimeEq, EdwardsPoint, Scalar};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum FeldmanError {
#[error("Wrong round for Feldman operation")]
WrongRound,
#[error("Invalid data: {0}")]
InvalidData(String),
#[error("Crypto error: {0}")]
CryptoError(String),
}
#[derive(Clone, Copy, PartialEq)]
enum FeldmanRound {
Uninitialized,
Initialized,
Committed,
Revealed,
Reconstructed,
}
pub struct Feldman {
threshold: usize,
total: usize,
id: usize,
frags_for_counterparties: HashMap<usize, Vec<u8>>,
frags_from_counterparties: HashMap<usize, Scalar>,
zkpok: Option<Scalar>,
secret: Scalar,
scalar: Option<Scalar>,
generator: EdwardsPoint,
public_key: EdwardsPoint,
point: EdwardsPoint,
random_commitment_point: Option<EdwardsPoint>,
round: FeldmanRound,
zkcommits_from_counterparties: HashMap<usize, Vec<u8>>,
points_from_counterparties: HashMap<usize, EdwardsPoint>,
}
#[derive(Serialize, Deserialize)]
pub struct FeldmanJson {
threshold: usize,
total: usize,
id: usize,
frags_for_counterparties: HashMap<usize, String>,
frags_from_counterparties: HashMap<usize, String>,
zkpok: Option<String>,
secret: String,
scalar: Option<String>,
generator: String,
public_key: String,
point: String,
random_commitment_point: Option<String>,
round: usize,
zkcommits_from_counterparties: HashMap<usize, String>,
points_from_counterparties: HashMap<usize, String>,
}
#[derive(Serialize, Deserialize)]
pub struct FeldmanReveal {
point: Vec<u8>,
random_commitment_point: Vec<u8>,
zk_pok: Vec<u8>,
}
pub fn vec_to_array<const N: usize>(v: Vec<u8>) -> Result<[u8; N], Box<dyn std::error::Error>> {
if v.len() != N {
return Err(format!("Invalid length: expected {}, got {}", N, v.len()).into());
}
let mut arr: [u8; N] = [0u8; N];
arr.copy_from_slice(&v);
Ok(arr)
}
impl Feldman {
pub fn new(
threshold: usize,
total: usize,
id: usize,
secret: Scalar,
generator: EdwardsPoint,
) -> Self {
Feldman {
threshold,
total,
id,
frags_for_counterparties: HashMap::new(),
frags_from_counterparties: HashMap::new(),
zkpok: None,
secret,
scalar: None,
generator,
public_key: EdwardsPoint::generator(),
point: EdwardsPoint::generator(),
random_commitment_point: None,
round: FeldmanRound::Uninitialized,
zkcommits_from_counterparties: HashMap::new(),
points_from_counterparties: HashMap::new(),
}
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
let feldman_json = FeldmanJson {
threshold: self.threshold,
total: self.total,
id: self.id,
frags_for_counterparties: self.frags_for_counterparties.iter()
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v)))
.collect(),
frags_from_counterparties: self.frags_from_counterparties.iter()
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v.to_bytes())))
.collect(),
zkpok: self.zkpok.as_ref().map(|s| BASE64_STANDARD.encode(s.to_bytes())),
secret: BASE64_STANDARD.encode(self.secret.to_bytes()),
scalar: self.scalar.as_ref().map(|s| BASE64_STANDARD.encode(s.to_bytes())),
generator: BASE64_STANDARD.encode(self.generator.compress().to_bytes()),
public_key: BASE64_STANDARD.encode(self.public_key.compress().to_bytes()),
point: BASE64_STANDARD.encode(self.point.compress().to_bytes()),
random_commitment_point: self.random_commitment_point.as_ref()
.map(|p| BASE64_STANDARD.encode(p.compress().to_bytes())),
round: self.round as usize,
zkcommits_from_counterparties: self.zkcommits_from_counterparties.iter()
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v)))
.collect(),
points_from_counterparties: self.points_from_counterparties.iter()
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v.compress().to_bytes())))
.collect(),
};
serde_json::to_string(&feldman_json)
}
pub fn from_json(json: &str) -> Result<Self, Box<dyn std::error::Error>> {
let feldman_json: FeldmanJson = serde_json::from_str(json)?;
let frags_for_counterparties = feldman_json.frags_for_counterparties.into_iter()
.map(|(k, v)| Ok((k, BASE64_STANDARD.decode(v)?)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let frags_from_counterparties = feldman_json.frags_from_counterparties.into_iter()
.map(|(k, v)| {
let bytes = BASE64_STANDARD.decode(v)?;
Ok((k, Scalar::from_bytes(&vec_to_array::<56>(bytes)?)))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let mut zkpok: Option<Scalar> = None;
if feldman_json.zkpok.is_some() {
let bytes = BASE64_STANDARD.decode(feldman_json.zkpok.unwrap())?;
zkpok = Some(Scalar::from_bytes(&vec_to_array::<56>(bytes)?));
}
let secret_bytes = BASE64_STANDARD.decode(feldman_json.secret)?;
let secret = Scalar::from_bytes(&vec_to_array::<56>(secret_bytes)?);
let mut scalar: Option<Scalar> = None;
if feldman_json.scalar.is_some() {
let bytes = BASE64_STANDARD.decode(feldman_json.scalar.unwrap())?;
scalar = Some(Scalar::from_bytes(&vec_to_array::<56>(bytes)?));
}
let generator_bytes = BASE64_STANDARD.decode(feldman_json.generator)?;
let generator = EdwardsPoint::from_bytes(&vec_to_array::<57>(generator_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
let public_key_bytes = BASE64_STANDARD.decode(feldman_json.public_key)?;
let public_key = EdwardsPoint::from_bytes(&vec_to_array::<57>(public_key_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
let point_bytes = BASE64_STANDARD.decode(feldman_json.point)?;
let point = EdwardsPoint::from_bytes(&vec_to_array::<57>(point_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
let mut random_commitment_point: Option<EdwardsPoint> = None;
if feldman_json.random_commitment_point.is_some() {
let bytes = BASE64_STANDARD.decode(feldman_json.random_commitment_point.unwrap())?;
random_commitment_point = Some(EdwardsPoint::from_bytes(&vec_to_array::<57>(bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?);
}
let zkcommits_from_counterparties = feldman_json.zkcommits_from_counterparties.into_iter()
.map(|(k, v)| Ok((k, BASE64_STANDARD.decode(v)?)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let points_from_counterparties = feldman_json.points_from_counterparties.into_iter()
.map(|(k, v)| {
Ok((k, EdwardsPoint::from_bytes(&vec_to_array::<57>(BASE64_STANDARD.decode(v)?)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
Ok(Feldman {
threshold: feldman_json.threshold,
total: feldman_json.total,
id: feldman_json.id,
frags_for_counterparties,
frags_from_counterparties,
zkpok,
secret,
scalar,
generator,
public_key,
point,
random_commitment_point,
round: match feldman_json.round {
0 => FeldmanRound::Uninitialized,
1 => FeldmanRound::Initialized,
2 => FeldmanRound::Committed,
3 => FeldmanRound::Revealed,
4 => FeldmanRound::Reconstructed,
_ => FeldmanRound::Uninitialized,
},
zkcommits_from_counterparties,
points_from_counterparties,
})
}
pub fn set_id(&mut self, id: usize) {
self.id = id;
}
pub fn sample_polynomial<R: RngCore + CryptoRng>(&mut self, rng: &mut R) -> Result<(), FeldmanError> {
if self.round != FeldmanRound::Uninitialized {
return Err(FeldmanError::WrongRound);
}
let mut coeffs = vec![self.secret];
for _ in 1..self.threshold {
coeffs.push(Scalar::random(rng));
}
for i in 1..=self.total {
let mut result = coeffs[0];
let x = Scalar::from(i as u32);
for j in 1..self.threshold {
let term = coeffs[j] * Scalar::from(i.pow(j as u32) as u32);
result += term;
}
if i == self.id {
self.scalar = Some(result);
} else {
self.frags_for_counterparties.insert(i, result.to_bytes().to_vec());
}
}
self.round = FeldmanRound::Initialized;
Ok(())
}
pub fn scalar(&self) -> Option<&Scalar> {
self.scalar.as_ref()
}
pub fn get_poly_frags(&self) -> Result<&HashMap<usize, Vec<u8>>, FeldmanError> {
if self.round != FeldmanRound::Initialized {
return Err(FeldmanError::WrongRound);
}
Ok(&self.frags_for_counterparties)
}
pub fn set_poly_frag_for_party(&mut self, id: usize, frag: &[u8]) -> Result<Option<Vec<u8>>, FeldmanError> {
if self.round != FeldmanRound::Initialized {
return Err(FeldmanError::WrongRound);
}
let scalar = Scalar::from_bytes(frag.try_into().unwrap());
self.frags_from_counterparties.insert(id, scalar);
if self.frags_from_counterparties.len() == self.total - 1 {
let mut combined_scalar = self.scalar.unwrap_or_else(|| Scalar::ZERO);
for scalar in self.frags_from_counterparties.values() {
combined_scalar += *scalar;
}
self.scalar = Some(combined_scalar);
self.point = self.generator * combined_scalar;
let rand_commitment = Scalar::random(&mut rand::thread_rng());
self.random_commitment_point = Some(self.generator * rand_commitment);
let random_commitment_point_bytes = self.random_commitment_point.unwrap().compress().to_bytes();
let public_point_bytes = self.point.compress().to_bytes();
let mut hasher = Sha512::new();
hasher.update(&public_point_bytes);
hasher.update(&random_commitment_point_bytes);
let challenge = hasher.finalize();
let challenge_scalar = Scalar::from_bytes(challenge[..56].try_into().unwrap());
self.zkpok = Some(combined_scalar * challenge_scalar + rand_commitment);
let zkpok_bytes = self.zkpok.unwrap().to_bytes();
let mut hasher = Sha512::new();
hasher.update(&random_commitment_point_bytes);
hasher.update(&zkpok_bytes);
let zkcommit = hasher.finalize();
self.round = FeldmanRound::Committed;
return Ok(Some(zkcommit[..56].to_vec()));
}
Ok(None)
}
pub fn receive_commitments(&mut self, id: usize, zkcommit: &[u8]) -> Result<Option<FeldmanReveal>, FeldmanError> {
if self.round != FeldmanRound::Committed {
return Err(FeldmanError::WrongRound);
}
self.zkcommits_from_counterparties.insert(id, zkcommit.to_vec());
if self.zkcommits_from_counterparties.len() == self.total - 1 {
let public_point_bytes = self.point.compress().to_bytes();
let random_commitment_point_bytes = self.random_commitment_point.unwrap().compress().to_bytes();
self.round = FeldmanRound::Revealed;
let zkpok_bytes = self.zkpok.unwrap().to_bytes();
return Ok(Some(FeldmanReveal {
point: public_point_bytes.to_vec(),
random_commitment_point: random_commitment_point_bytes.to_vec(),
zk_pok: zkpok_bytes.to_vec(),
}));
}
Ok(None)
}
pub fn recombine(&mut self, id: usize, reveal: &FeldmanReveal) -> Result<bool, FeldmanError> {
if self.round != FeldmanRound::Revealed {
return Err(FeldmanError::WrongRound);
}
let counterparty_point = EdwardsPoint::from_bytes(reveal.point.as_slice().into()).unwrap();
if counterparty_point.eq(&EdwardsPoint::generator()).into() || counterparty_point == self.generator {
return Err(FeldmanError::InvalidData("Counterparty sent generator".into()));
}
let counterparty_random_commitment_point = EdwardsPoint::from_bytes(reveal.random_commitment_point.as_slice().into()).unwrap();
if counterparty_random_commitment_point.eq(&EdwardsPoint::generator()).into() || counterparty_random_commitment_point == self.generator {
return Err(FeldmanError::InvalidData("Counterparty sent generator".into()));
}
let counterparty_zkpok = Scalar::from_bytes(reveal.zk_pok.as_slice().try_into().unwrap());
let counterparty_zkcommit = self.zkcommits_from_counterparties.get(&id)
.ok_or_else(|| FeldmanError::InvalidData("Missing ZK commit for counterparty".into()))?;
let mut hasher = Sha512::new();
hasher.update(&reveal.point);
hasher.update(&reveal.random_commitment_point);
let challenge = hasher.finalize();
let challenge_scalar = Scalar::from_bytes(challenge[..56].try_into().unwrap());
let proof = self.generator * counterparty_zkpok;
let expected_proof = counterparty_random_commitment_point + (counterparty_point * challenge_scalar);
if proof != expected_proof {
return Err(FeldmanError::InvalidData(format!("Invalid proof from {}", id)));
}
let mut hasher = Sha512::new();
hasher.update(&reveal.random_commitment_point);
hasher.update(&reveal.zk_pok);
let verifier = hasher.finalize();
if &verifier[..56] != counterparty_zkcommit {
return Err(FeldmanError::InvalidData(format!("{} changed zkpok after commit", id)));
}
self.points_from_counterparties.insert(id, counterparty_point);
if self.points_from_counterparties.len() == self.total - 1 {
self.points_from_counterparties.insert(self.id, self.point);
for i in 1..=self.total - self.threshold + 1 {
let mut reconstructed_sum = EdwardsPoint::generator();
for j in i..self.threshold + i {
let mut num = Scalar::ONE;
let mut den = Scalar::ONE;
for k in i..self.threshold + i {
if j != k {
let j_scalar = Scalar::from(j as u32);
let k_scalar = Scalar::from(k as u32);
num *= k_scalar;
den *= k_scalar - j_scalar;
}
}
let den_inv = den.invert();
let reconstructed_fragment = self.points_from_counterparties[&j] * (num * den_inv);
reconstructed_sum += reconstructed_fragment;
}
if self.public_key == EdwardsPoint::generator() || self.public_key == self.generator {
self.public_key = reconstructed_sum;
} else if self.public_key != reconstructed_sum {
return Err(FeldmanError::InvalidData("Recombination mismatch".into()));
}
}
self.round = FeldmanRound::Reconstructed;
}
Ok(self.round == FeldmanRound::Reconstructed)
}
pub fn mul_share(&self, pubkey: &[u8]) -> Result<Vec<u8>, FeldmanError> {
if self.scalar.is_none() {
return Err(FeldmanError::WrongRound);
}
let point = EdwardsPoint::from_bytes(pubkey.into());
if point.is_none().into() {
return Err(FeldmanError::InvalidData("invalid pubkey".to_string()));
}
let result = self.scalar.unwrap() * point.unwrap();
if result.is_identity().into() {
return Err(FeldmanError::InvalidData("invalid pubkey".to_string()));
}
return Ok(result.compress().to_bytes().to_vec());
}
pub fn combine_mul_share(&mut self, shares: Vec<&[u8]>, ids: &[usize]) -> Result<Vec<u8>, FeldmanError> {
if shares.len() != ids.len() {
return Err(FeldmanError::InvalidData("mismatch of shares and ids len".to_string()));
}
let mut points = HashMap::<usize, EdwardsPoint>::new();
for (i, share) in shares.iter().enumerate() {
let point = EdwardsPoint::from_bytes((*share).into());
if point.is_none().into() {
return Err(FeldmanError::InvalidData(format!("invalid pubkey for {}", ids[i]).to_string()));
}
points.insert(ids[i], point.unwrap());
}
let mut reconstructed_sum = EdwardsPoint::generator();
for j in ids {
let mut num = Scalar::ONE;
let mut den = Scalar::ONE;
for k in ids {
if j != k {
let j_scalar = Scalar::from(*j as u32);
let k_scalar = Scalar::from(*k as u32);
num *= k_scalar;
den *= k_scalar - j_scalar;
}
}
let den_inv = den.invert();
let reconstructed_fragment = points[&j] * (num * den_inv);
reconstructed_sum += reconstructed_fragment;
}
self.public_key = reconstructed_sum;
return Ok(reconstructed_sum.compress().to_bytes().to_vec());
}
pub fn public_key(&self) -> &EdwardsPoint {
&self.public_key
}
pub fn public_key_bytes(&self) -> Vec<u8> {
self.public_key.to_bytes().to_vec()
}
}

View File

@ -0,0 +1,4 @@
pub(crate) mod doubleratchet;
pub(crate) mod tripleratchet;
pub(crate) mod feldman;
pub(crate) mod x3dh;

View File

@ -0,0 +1,883 @@
use base64::prelude::*;
use std::collections::HashMap;
use ed448_goldilocks_plus::elliptic_curve::group::GroupEncoding;
use ed448_goldilocks_plus::elliptic_curve::Group;
use rand::rngs::OsRng;
use rand::{CryptoRng, RngCore};
use sha2::{Sha512, Digest};
use hkdf::Hkdf;
use aes_gcm::{Aes256Gcm, Nonce};
use aes_gcm::aead::{Aead, Payload};
use ed448_goldilocks_plus::{subtle, EdwardsPoint, Scalar};
use serde::{Serialize, Deserialize};
use thiserror::Error;
use subtle::ConstantTimeEq;
use super::doubleratchet::{DoubleRatchetParticipant, DoubleRatchetParticipantJson, MessageCiphertext, P2PChannelEnvelope};
use super::feldman::{Feldman, vec_to_array, FeldmanReveal};
use super::x3dh::{receiver_x3dh, sender_x3dh};
const TRIPLE_RATCHET_PROTOCOL_VERSION: u16 = 1;
const TRIPLE_RATCHET_PROTOCOL: u16 = 2 << 8 + TRIPLE_RATCHET_PROTOCOL_VERSION;
#[derive(Clone, Copy, PartialEq)]
enum TripleRatchetRound {
Uninitialized,
Initialized,
Committed,
Revealed,
Reconstructed,
}
#[derive(Error, Debug)]
pub enum TripleRatchetError {
#[error("Crypto error: {0}")]
CryptoError(String),
#[error("Invalid data: {0}")]
InvalidData(String),
#[error("Skip limit exceeded")]
SkipLimitExceeded,
#[error("Malformed header")]
MalformedHeader,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PeerInfo {
pub(crate) public_key: Vec<u8>,
pub(crate) identity_public_key: Vec<u8>,
pub(crate) signed_pre_public_key: Vec<u8>,
}
pub struct TripleRatchetParticipant {
peer_key: Scalar,
sending_ephemeral_private_key: Scalar,
receiving_ephemeral_keys: HashMap<Vec<u8>, Scalar>,
receiving_group_key: Option<Vec<u8>>,
root_key: Vec<u8>,
sending_chain_key: Vec<u8>,
current_header_key: Vec<u8>,
next_header_key: Vec<u8>,
receiving_chain_key: HashMap<Vec<u8>, Vec<u8>>,
current_sending_chain_length: u32,
previous_sending_chain_length: u32,
current_receiving_chain_length: HashMap<Vec<u8>, u32>,
previous_receiving_chain_length: HashMap<Vec<u8>, u32>,
peer_id_map: HashMap<Vec<u8>, usize>,
id_peer_map: HashMap<usize, PeerInfo>,
skipped_keys_map: HashMap<Vec<u8>, HashMap<Vec<u8>, HashMap<u32, Vec<u8>>>>,
peer_channels: HashMap<Vec<u8>, DoubleRatchetParticipant>,
dkg_ratchet: Feldman,
next_dkg_ratchet: Feldman,
async_dkg_ratchet: bool,
should_ratchet: bool,
should_dkg_ratchet: HashMap<Vec<u8>, bool>,
async_dkg_pubkey: Option<EdwardsPoint>,
threshold: usize,
}
#[derive(Serialize, Deserialize)]
pub struct PeerInfoJson {
public_key: String,
identity_public_key: String,
signed_pre_public_key: String,
}
#[derive(Serialize, Deserialize)]
pub struct TripleRatchetParticipantJson {
peer_key: String,
sending_ephemeral_private_key: String,
receiving_ephemeral_keys: HashMap<String, String>,
receiving_group_key: Option<String>,
root_key: String,
sending_chain_key: String,
current_header_key: String,
next_header_key: String,
receiving_chain_key: HashMap<String, String>,
current_sending_chain_length: u32,
previous_sending_chain_length: u32,
current_receiving_chain_length: HashMap<String, u32>,
previous_receiving_chain_length: HashMap<String, u32>,
peer_id_map: HashMap<String, usize>,
id_peer_map: HashMap<usize, PeerInfoJson>,
skipped_keys_map: HashMap<String, HashMap<String, HashMap<u32, String>>>,
peer_channels: HashMap<String, String>,
dkg_ratchet: String,
next_dkg_ratchet: String,
async_dkg_ratchet: bool,
should_ratchet: bool,
should_dkg_ratchet: HashMap<String, bool>,
async_dkg_pubkey: Option<String>,
threshold: usize,
}
impl TripleRatchetParticipant {
pub fn new(
peers: &[PeerInfo],
peer_key: Scalar,
identity_key: Scalar,
signed_pre_key: Scalar,
threshold: usize,
async_dkg_ratchet: bool,
) -> Result<(Self, HashMap<Vec<u8>, P2PChannelEnvelope>), TripleRatchetError> {
let mut participant = TripleRatchetParticipant {
peer_key,
sending_ephemeral_private_key: Scalar::random(&mut OsRng),
receiving_ephemeral_keys: HashMap::new(),
receiving_group_key: None,
root_key: vec![],
sending_chain_key: vec![],
current_header_key: vec![],
next_header_key: vec![],
receiving_chain_key: HashMap::new(),
current_sending_chain_length: 0,
previous_sending_chain_length: 0,
current_receiving_chain_length: HashMap::new(),
previous_receiving_chain_length: HashMap::new(),
peer_id_map: HashMap::new(),
id_peer_map: HashMap::new(),
skipped_keys_map: HashMap::new(),
peer_channels: HashMap::new(),
dkg_ratchet: Feldman::new(
threshold,
peers.len() + 1,
0, // This will be set later
Scalar::random(&mut OsRng),
EdwardsPoint::generator(),
),
next_dkg_ratchet: Feldman::new(
threshold,
peers.len() + 1,
0, // This will be set later
Scalar::random(&mut OsRng),
EdwardsPoint::generator(),
),
async_dkg_ratchet: async_dkg_ratchet,
should_ratchet: true,
should_dkg_ratchet: HashMap::new(),
async_dkg_pubkey: None,
threshold: threshold,
};
let mut peer_basis = peers.to_vec();
peer_basis.push(PeerInfo {
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
});
peer_basis.sort_by(|a, b| a.public_key.cmp(&b.public_key));
let mut init_messages = HashMap::new();
let mut sender = false;
for (i, peer) in peer_basis.iter().enumerate() {
participant.peer_id_map.insert(peer.public_key.clone(), i + 1);
participant.id_peer_map.insert(i + 1, peer.clone());
if peer.public_key.ct_eq(&(peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec()).into() {
sender = true;
participant.dkg_ratchet.set_id(i + 1);
participant.next_dkg_ratchet.set_id(i + 1);
} else {
participant.skipped_keys_map.insert(peer.public_key.clone(), HashMap::new());
participant.current_receiving_chain_length.insert(peer.public_key.clone(), 0);
participant.previous_receiving_chain_length.insert(peer.public_key.clone(), 0);
let session_key = if sender {
sender_x3dh(
&identity_key,
&signed_pre_key,
&EdwardsPoint::from_bytes(peer.identity_public_key.as_slice().into()).unwrap(),
&EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
96,
)
} else {
receiver_x3dh(
&identity_key,
&signed_pre_key,
&EdwardsPoint::from_bytes(peer.identity_public_key.as_slice().into()).unwrap(),
&EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
96,
)
}.unwrap();
let peer_channel = DoubleRatchetParticipant::new(
&session_key[..32],
&session_key[32..64],
&session_key[64..],
sender,
signed_pre_key.clone(),
EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
).unwrap();
participant.peer_channels.insert(peer.public_key.clone(), peer_channel);
if sender {
let init_message = participant.peer_channels
.get_mut(&peer.public_key)
.unwrap()
.ratchet_encrypt(b"init").unwrap();
init_messages.insert(peer.public_key.clone(), init_message);
}
}
}
Ok((participant, init_messages))
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
let triple_ratchet_json = TripleRatchetParticipantJson {
peer_key: BASE64_STANDARD.encode(self.peer_key.to_bytes()),
sending_ephemeral_private_key: BASE64_STANDARD.encode(self.sending_ephemeral_private_key.to_bytes()),
receiving_ephemeral_keys: self.receiving_ephemeral_keys.iter()
.map(|(k, v)| (BASE64_STANDARD.encode(k), BASE64_STANDARD.encode(v.to_bytes())))
.collect(),
receiving_group_key: self.receiving_group_key.as_ref().map(|k| BASE64_STANDARD.encode(k)),
root_key: BASE64_STANDARD.encode(&self.root_key),
sending_chain_key: BASE64_STANDARD.encode(&self.sending_chain_key),
current_header_key: BASE64_STANDARD.encode(&self.current_header_key),
next_header_key: BASE64_STANDARD.encode(&self.next_header_key),
receiving_chain_key: self.receiving_chain_key.iter()
.map(|(k, v)| (BASE64_STANDARD.encode(k), BASE64_STANDARD.encode(v)))
.collect(),
current_sending_chain_length: self.current_sending_chain_length,
previous_sending_chain_length: self.previous_sending_chain_length,
current_receiving_chain_length: self.current_receiving_chain_length.iter()
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
.collect(),
previous_receiving_chain_length: self.previous_receiving_chain_length.iter()
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
.collect(),
peer_id_map: self.peer_id_map.iter()
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
.collect(),
id_peer_map: self.id_peer_map.iter()
.map(|(&k, v)| (k, PeerInfoJson {
public_key: BASE64_STANDARD.encode(&v.public_key),
identity_public_key: BASE64_STANDARD.encode(&v.identity_public_key),
signed_pre_public_key: BASE64_STANDARD.encode(&v.signed_pre_public_key),
}))
.collect(),
skipped_keys_map: self.skipped_keys_map.iter()
.map(|(k1, v1)| (BASE64_STANDARD.encode(k1),
v1.iter().map(|(k2, v2)| (BASE64_STANDARD.encode(k2),
v2.iter().map(|(&k3, v3)| (k3, BASE64_STANDARD.encode(v3)))
.collect()))
.collect()))
.collect(),
peer_channels: self.peer_channels.iter()
.map(|(k, v)| Ok((BASE64_STANDARD.encode(k), v.to_json()?)))
.collect::<Result<HashMap<_, _>, serde_json::Error>>()?,
dkg_ratchet: self.dkg_ratchet.to_json()?,
next_dkg_ratchet: self.next_dkg_ratchet.to_json()?,
async_dkg_ratchet: self.async_dkg_ratchet,
should_ratchet: self.should_ratchet,
should_dkg_ratchet: self.should_dkg_ratchet.iter()
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
.collect(),
async_dkg_pubkey: self.async_dkg_pubkey.as_ref()
.map(|p| BASE64_STANDARD.encode(p.compress().to_bytes())),
threshold: self.threshold,
};
serde_json::to_string(&triple_ratchet_json)
}
pub fn from_json(json: &str) -> Result<Self, Box<dyn std::error::Error>> {
let triple_ratchet_json: TripleRatchetParticipantJson = serde_json::from_str(json)?;
let peer_key_bytes = BASE64_STANDARD.decode(&triple_ratchet_json.peer_key)?;
let peer_key = Scalar::from_bytes(&vec_to_array::<56>(peer_key_bytes)?);
let sending_ephemeral_private_key_bytes = BASE64_STANDARD.decode(&triple_ratchet_json.sending_ephemeral_private_key)?;
let sending_ephemeral_private_key = Scalar::from_bytes(&vec_to_array::<56>(sending_ephemeral_private_key_bytes)?);
let receiving_ephemeral_keys = triple_ratchet_json.receiving_ephemeral_keys.into_iter()
.map(|(k, v)| {
let key = BASE64_STANDARD.decode(k)?;
let value_bytes = BASE64_STANDARD.decode(v)?;
let value = Scalar::from_bytes(&vec_to_array::<56>(value_bytes)?);
Ok((key, value))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let receiving_group_key = triple_ratchet_json.receiving_group_key
.map(|k| BASE64_STANDARD.decode(k))
.transpose()?;
let root_key = BASE64_STANDARD.decode(&triple_ratchet_json.root_key)?;
let sending_chain_key = BASE64_STANDARD.decode(&triple_ratchet_json.sending_chain_key)?;
let current_header_key = BASE64_STANDARD.decode(&triple_ratchet_json.current_header_key)?;
let next_header_key = BASE64_STANDARD.decode(&triple_ratchet_json.next_header_key)?;
let receiving_chain_key = triple_ratchet_json.receiving_chain_key.into_iter()
.map(|(k, v)| {
let key = BASE64_STANDARD.decode(k)?;
let value = BASE64_STANDARD.decode(v)?;
Ok((key, value))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let current_receiving_chain_length = triple_ratchet_json.current_receiving_chain_length.into_iter()
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let previous_receiving_chain_length = triple_ratchet_json.previous_receiving_chain_length.into_iter()
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let peer_id_map = triple_ratchet_json.peer_id_map.into_iter()
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let id_peer_map = triple_ratchet_json.id_peer_map.into_iter()
.map(|(k, v)| Ok((k, PeerInfo {
public_key: BASE64_STANDARD.decode(&v.public_key)?,
identity_public_key: BASE64_STANDARD.decode(&v.identity_public_key)?,
signed_pre_public_key: BASE64_STANDARD.decode(&v.signed_pre_public_key)?,
})))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let skipped_keys_map = triple_ratchet_json.skipped_keys_map.into_iter()
.map(|(k1, v1)| {
let key1 = BASE64_STANDARD.decode(k1)?;
let value1 = v1.into_iter()
.map(|(k2, v2)| {
let key2 = BASE64_STANDARD.decode(k2)?;
let value2 = v2.into_iter()
.map(|(k3, v3)| Ok((k3, BASE64_STANDARD.decode(v3)?)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
Ok((key2, value2))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
Ok((key1, value1))
})
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let peer_channels = triple_ratchet_json.peer_channels.into_iter()
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, DoubleRatchetParticipant::from_json(v)?)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let dkg_ratchet = Feldman::from_json(&triple_ratchet_json.dkg_ratchet)?;
let next_dkg_ratchet = Feldman::from_json(&triple_ratchet_json.next_dkg_ratchet)?;
let should_dkg_ratchet = triple_ratchet_json.should_dkg_ratchet.into_iter()
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
let mut async_dkg_pubkey: Option<EdwardsPoint> = None;
if triple_ratchet_json.async_dkg_pubkey.is_some() {
let bytes = BASE64_STANDARD.decode(triple_ratchet_json.async_dkg_pubkey.unwrap())?;
let point = EdwardsPoint::from_bytes(&vec_to_array::<57>(bytes)?.into());
async_dkg_pubkey = point.into_option();
}
Ok(TripleRatchetParticipant {
peer_key,
sending_ephemeral_private_key,
receiving_ephemeral_keys,
receiving_group_key,
root_key,
sending_chain_key,
current_header_key,
next_header_key,
receiving_chain_key,
current_sending_chain_length: triple_ratchet_json.current_sending_chain_length,
previous_sending_chain_length: triple_ratchet_json.previous_sending_chain_length,
current_receiving_chain_length,
previous_receiving_chain_length,
peer_id_map,
id_peer_map,
skipped_keys_map,
peer_channels,
dkg_ratchet,
next_dkg_ratchet,
async_dkg_ratchet: triple_ratchet_json.async_dkg_ratchet,
should_ratchet: triple_ratchet_json.should_ratchet,
should_dkg_ratchet,
async_dkg_pubkey,
threshold: triple_ratchet_json.threshold,
})
}
pub fn get_peer_id_map(&self) -> HashMap<Vec<u8>, usize> {
return self.peer_id_map.clone();
}
pub fn initialize(&mut self, init_messages: &HashMap<Vec<u8>, P2PChannelEnvelope>)
-> Result<HashMap<Vec<u8>, P2PChannelEnvelope>, TripleRatchetError> {
for (k, m) in init_messages {
let msg = self.peer_channels.get_mut(k).unwrap().ratchet_decrypt(m).unwrap();
if msg != b"init" {
return Err(TripleRatchetError::InvalidData("Invalid init message".into()));
}
}
self.dkg_ratchet.sample_polynomial(&mut OsRng);
let result = self.dkg_ratchet.get_poly_frags().unwrap();
let mut result_map = HashMap::new();
for (k, v) in result {
let test: bool = self.id_peer_map[&k].public_key.ct_eq(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes()).into();
if !test {
let envelope = self.peer_channels
.get_mut(&self.id_peer_map[&k].public_key)
.unwrap()
.ratchet_encrypt(&v);
result_map.insert(self.id_peer_map[&k].public_key.clone(), envelope.unwrap());
}
}
Ok(result_map)
}
pub fn receive_poly_frag(&mut self, peer_id: &[u8], frag: &P2PChannelEnvelope)
-> Result<Option<HashMap<Vec<u8>, P2PChannelEnvelope>>, TripleRatchetError> {
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(frag).unwrap();
let result = self.dkg_ratchet.set_poly_frag_for_party(
*self.peer_id_map.get(peer_id).unwrap(),
&b,
).unwrap();
if result.is_some() {
let mut envelopes = HashMap::new();
for (k, c) in &mut self.peer_channels {
let envelope = c.ratchet_encrypt(&result.clone().unwrap()).unwrap();
envelopes.insert(k.clone(), envelope);
}
Ok(Some(envelopes))
} else {
Ok(None)
}
}
pub fn receive_commitment(&mut self, peer_id: &[u8], zkcommit: &P2PChannelEnvelope)
-> Result<Option<HashMap<Vec<u8>, P2PChannelEnvelope>>, TripleRatchetError> {
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(zkcommit).unwrap();
let result = self.dkg_ratchet.receive_commitments(
*self.peer_id_map.get(peer_id).unwrap(),
&b,
).unwrap();
if let Some(reveal) = result {
let d = serde_json::to_vec(&reveal).unwrap();
let mut envelopes = HashMap::new();
for (k, c) in &mut self.peer_channels {
let envelope = c.ratchet_encrypt(&d).unwrap();
envelopes.insert(k.clone(), envelope);
}
Ok(Some(envelopes))
} else {
Ok(None)
}
}
pub fn recombine(&mut self, peer_id: &[u8], reveal: &P2PChannelEnvelope) -> Result<(), Box<dyn std::error::Error>> {
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(reveal).unwrap();
let rev: FeldmanReveal = serde_json::from_slice(&b).unwrap();
let done = self.dkg_ratchet.recombine(
*self.peer_id_map.get(peer_id).unwrap(),
&rev,
).unwrap();
if !done {
return Ok(());
}
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
let hkdf = Hkdf::<Sha512>::new(
Some(&sess),
&self.dkg_ratchet.public_key_bytes(),
);
let mut rkck = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
self.root_key = rkck[..32].to_vec();
self.current_header_key = rkck[32..64].to_vec();
self.next_header_key = rkck[64..].to_vec();
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
Ok(())
}
pub fn ratchet_encrypt(&mut self, message: &[u8]) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
if self.should_ratchet {
self.ratchet_sender_ephemeral_keys()?;
}
if self.async_dkg_ratchet && self.async_dkg_pubkey.is_some() && self.should_dkg_ratchet.len() == self.threshold {
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
let hkdf = Hkdf::<Sha512>::new(
Some(&sess),
&self.dkg_ratchet.public_key_bytes(),
);
let mut rkck = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
self.root_key = rkck[..32].to_vec();
self.current_header_key = rkck[32..64].to_vec();
self.next_header_key = rkck[64..].to_vec();
self.async_dkg_pubkey = None;
}
let dkg_pub: Option<(Vec<u8>, Vec<u8>)> = if (self.async_dkg_ratchet && self.should_dkg_ratchet.len() > 0) || self.async_dkg_pubkey.is_some() {
if self.async_dkg_pubkey.is_none() {
self.should_dkg_ratchet = HashMap::new();
self.async_dkg_pubkey = Some(Scalar::random(&mut OsRng) * EdwardsPoint::generator());
}
let pubkey = self.async_dkg_pubkey.unwrap();
let invpub = self.dkg_ratchet.mul_share(&pubkey.compress().to_bytes());
if invpub.is_err() {
return Err(Box::new(invpub.unwrap_err()));
}
self.should_dkg_ratchet.insert((self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(), true);
Some((invpub.unwrap(), pubkey.compress().to_bytes().to_vec()))
} else { None };
let mut envelope = P2PChannelEnvelope {
protocol_identifier: TRIPLE_RATCHET_PROTOCOL,
message_header: MessageCiphertext::default(),
message_body: MessageCiphertext::default(),
};
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.sending_chain_key);
self.sending_chain_key = new_chain_key;
let header = self.encode_header(dkg_pub);
envelope.message_header = self.encrypt(&header, &self.current_header_key, None)?;
envelope.message_body = self.encrypt(
message,
&message_key,
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
)?;
self.current_sending_chain_length += 1;
Ok(envelope)
}
pub fn ratchet_decrypt(&mut self, envelope: &P2PChannelEnvelope) -> Result<(Vec<u8>, bool), Box<dyn std::error::Error>> {
if let Some(plaintext) = self.try_skipped_message_keys(envelope)? {
return Ok((plaintext, false));
}
let header_key = self.current_header_key.clone();
let (header, mut should_dkg_ratchet, should_advance_dkg_ratchet) = self.decrypt_header(&envelope.message_header, &header_key)?;
let (sender_key, receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length, dkg_pub) =
self.decode_header(&header)?;
let should_ratchet = self.receiving_ephemeral_keys.get(&sender_key.compress().to_bytes().to_vec()).map(|k| !k.eq(&receiving_ephemeral_key)).unwrap_or(true);
if should_ratchet {
self.skip_message_keys(&sender_key, previous_receiving_chain_length)?;
self.ratchet_receiver_ephemeral_keys(&sender_key, &receiving_ephemeral_key)?;
}
self.skip_message_keys(&sender_key, current_receiving_chain_length)?;
let (new_chain_key, message_key, aead_key) = ratchet_keys(
&self.receiving_chain_key[&sender_key.compress().to_bytes().to_vec()],
);
self.receiving_chain_key.insert(sender_key.compress().to_bytes().to_vec(), new_chain_key);
*self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0) += 1;
if should_advance_dkg_ratchet {
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
let hkdf = Hkdf::<Sha512>::new(
Some(&sess),
&self.dkg_ratchet.public_key_bytes(),
);
let mut rkck = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
self.root_key = rkck[..32].to_vec();
self.current_header_key = rkck[32..64].to_vec();
self.next_header_key = rkck[64..].to_vec();
}
let plaintext = self.decrypt(
&envelope.message_body,
&message_key,
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
)?;
if dkg_pub.is_some() {
should_dkg_ratchet = false;
let (other_invpub, pubkey) = dkg_pub.unwrap();
self.async_dkg_pubkey = Some(pubkey);
let invpub = self.dkg_ratchet.mul_share(&pubkey.compress().to_bytes());
if invpub.is_err() {
return Err(Box::new(invpub.unwrap_err()));
}
let invvec = invpub.unwrap();
let inv = invvec.as_slice();
let other = &other_invpub.compress().to_bytes();
let other_id = self.peer_id_map.get(&sender_key.compress().to_bytes().to_vec());
if other_id.is_none() {
return Err(Box::new(TripleRatchetError::MalformedHeader))
}
let our_id = self.peer_id_map.get(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec());
let (shares, ids) = if our_id.unwrap() < other_id.unwrap() {
(vec![inv, other], [*our_id.unwrap(), *other_id.unwrap()])
} else {
(vec![other, inv], [*other_id.unwrap(), *our_id.unwrap()])
};
let next_pub = self.dkg_ratchet.combine_mul_share(shares, &ids);
if next_pub.is_err() {
return Err(Box::new(next_pub.unwrap_err()));
}
let next = EdwardsPoint::from_bytes(next_pub.unwrap().as_slice().into());
if next.is_none().into() {
return Err(Box::new(TripleRatchetError::MalformedHeader));
}
}
Ok((plaintext, should_dkg_ratchet))
}
fn ratchet_sender_ephemeral_keys(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let receiving_group_key = self.receiving_group_key.as_ref().ok_or_else(|| TripleRatchetError::CryptoError("Receiving group key not set".into()))?;
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
let dh_output = (self.sending_ephemeral_private_key * EdwardsPoint::from_bytes(receiving_group_key.as_slice().into()).unwrap()).compress().to_bytes();
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output);
let mut rkck2 = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck2);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
self.root_key = rkck2[..32].to_vec();
self.sending_chain_key = rkck2[32..64].to_vec();
self.should_ratchet = false;
Ok(())
}
fn ratchet_receiver_ephemeral_keys(&mut self, peer_key: &EdwardsPoint, new_ephemeral_key: &Scalar) -> Result<(), Box<dyn std::error::Error>> {
self.previous_sending_chain_length = self.current_sending_chain_length;
self.current_sending_chain_length = 0;
*self.current_receiving_chain_length.entry(peer_key.compress().to_bytes().to_vec()).or_insert(0) = 0;
self.receiving_ephemeral_keys.insert(peer_key.compress().to_bytes().to_vec(), new_ephemeral_key.clone());
let receiving_group_key = self.receiving_group_key.as_ref().ok_or_else(|| TripleRatchetError::CryptoError("Receiving group key not set".into()))?;
let dh_output = (new_ephemeral_key * EdwardsPoint::from_bytes(receiving_group_key.as_slice().into()).unwrap()).compress().to_bytes();
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output);
let mut rkck = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
self.root_key = rkck[..32].to_vec();
self.receiving_chain_key.insert(peer_key.compress().to_bytes().to_vec(), rkck[32..64].to_vec());
self.should_ratchet = true;
if self.async_dkg_ratchet {
self.should_dkg_ratchet = HashMap::new();
self.should_dkg_ratchet.insert(peer_key.compress().to_bytes().to_vec(), true);
self.async_dkg_pubkey = None;
}
Ok(())
}
fn try_skipped_message_keys(&mut self, envelope: &P2PChannelEnvelope) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
for (receiving_header_key, skipped_keys) in &self.skipped_keys_map.clone() {
if let Ok((header, _, _)) = self.decrypt_header(&envelope.message_header, receiving_header_key) {
let (peer_key, _, _, current, _) = self.decode_header(&header)?;
if let Some(peer_skipped_keys) = skipped_keys.get(&peer_key.compress().to_bytes().to_vec()) {
if let Some(key_pair) = peer_skipped_keys.get(&current) {
let message_key = &key_pair[..32];
let aead_key = &key_pair[32..];
let plaintext = self.decrypt(
&envelope.message_body,
message_key,
Some(&[aead_key, &envelope.message_header.ciphertext].concat()),
)?;
return Ok(Some(plaintext));
}
}
}
}
Ok(None)
}
fn skip_message_keys(&mut self, sender_key: &EdwardsPoint, until: u32) -> Result<(), Box<dyn std::error::Error>> {
let mut current = *self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0);
if current + 100 < until {
return Err(Box::new(TripleRatchetError::SkipLimitExceeded));
}
if let Some(chain_key) = self.receiving_chain_key.get_mut(&sender_key.compress().to_bytes().to_vec()) {
while current < until {
let (new_chain_key, message_key, aead_key) = ratchet_keys(chain_key);
self.skipped_keys_map
.entry(self.current_header_key.clone())
.or_insert_with(HashMap::new)
.entry(sender_key.compress().to_bytes().to_vec())
.or_insert_with(HashMap::new)
.insert(current, [message_key, aead_key].concat());
*chain_key = new_chain_key;
current += 1;
*self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0) += 1;
}
}
Ok(())
}
fn encode_header(&self, dkg_pub: Option<(Vec<u8>, Vec<u8>)>) -> Vec<u8> {
let mut header = Vec::new();
header.extend_from_slice(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec());
header.extend_from_slice(&self.sending_ephemeral_private_key.to_bytes());
header.extend_from_slice(&self.previous_sending_chain_length.to_be_bytes());
header.extend_from_slice(&self.current_sending_chain_length.to_be_bytes());
if dkg_pub.is_some() {
let (invpub, pubkey) = dkg_pub.unwrap();
header.extend(invpub);
header.extend(pubkey);
}
header
}
fn decrypt_header(&mut self, ciphertext: &MessageCiphertext, receiving_header_key: &[u8]) -> Result<(Vec<u8>, bool, bool), Box<dyn std::error::Error>> {
match self.decrypt(ciphertext, receiving_header_key, None) {
Ok(header) => {
Ok((header, self.async_dkg_ratchet, false))
},
Err(_) if receiving_header_key == self.current_header_key => {
if self.async_dkg_ratchet && self.async_dkg_pubkey.is_some() {
let receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
let hkdf = Hkdf::<Sha512>::new(
Some(&sess),
&self.dkg_ratchet.public_key_bytes(),
);
let mut rkck = [0u8; 96];
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
if result.is_err() {
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
}
let current_header_key = rkck[32..64].to_vec();
match self.decrypt(ciphertext, &current_header_key, None) {
Ok(header) => Ok((header, true, true)),
Err(e) => Err(Box::new(e)),
}
} else {
match self.decrypt(ciphertext, &self.next_header_key, None) {
Ok(header) => Ok((header, true, false)),
Err(e) => Err(Box::new(e)),
}
}
},
Err(e) => Err(Box::new(e)),
}
}
fn decode_header(&self, header: &[u8]) -> Result<(EdwardsPoint, Scalar, u32, u32, Option<(EdwardsPoint, EdwardsPoint)>), TripleRatchetError> {
if header.len() < 121 {
return Err(TripleRatchetError::MalformedHeader);
}
let sender_key = EdwardsPoint::from_bytes(header[..57].into()).unwrap();
let receiving_ephemeral_key = Scalar::from_bytes(header[57..113].try_into().unwrap());
let previous_receiving_chain_length = u32::from_be_bytes(header[113..117].try_into().unwrap());
let current_receiving_chain_length = u32::from_be_bytes(header[117..121].try_into().unwrap());
let dkg_pub = if header.len() == 235 {
let invpub = EdwardsPoint::from_bytes(header[121..178].into()).into_option();
let pubkey = EdwardsPoint::from_bytes(header[178..].into()).into_option();
if invpub.is_none() || pubkey.is_none() {
None
} else {
Some((invpub.unwrap(), pubkey.unwrap()))
}
} else {
None
};
Ok((sender_key, receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length, dkg_pub))
}
fn encrypt(&self, plaintext: &[u8], key: &[u8], associated_data: Option<&[u8]>)
-> Result<MessageCiphertext, Box<dyn std::error::Error>> {
use aes_gcm::KeyInit;
let mut iv = [0u8; 12];
OsRng.fill_bytes(&mut iv);
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
let nonce = Nonce::from_slice(&iv);
let mut associated_data = associated_data.unwrap_or(&[]);
let mut aad = [0u8; 32];
if associated_data.len() == 0 {
OsRng.fill_bytes(&mut aad);
associated_data = &aad
}
let ciphertext = cipher.encrypt(nonce, Payload{
msg: plaintext,
aad: associated_data,
}).map_err(|e| format!("Encryption failed: {}", e))?;
Ok(MessageCiphertext {
ciphertext,
initialization_vector: iv.to_vec(),
associated_data: Some(associated_data.to_vec()),
})
}
fn decrypt(&self, ciphertext: &MessageCiphertext, key: &[u8], associated_data: Option<&[u8]>) -> Result<Vec<u8>, TripleRatchetError> {
use aes_gcm::KeyInit;
if key.len() != 32 {
return Err(TripleRatchetError::InvalidData("Invalid key length".to_string()));
}
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
let nonce = Nonce::from_slice(&ciphertext.initialization_vector);
let associated_data = associated_data.unwrap_or_else(|| ciphertext.associated_data.as_ref().unwrap());
cipher.decrypt(nonce, Payload{
msg: &ciphertext.ciphertext.as_ref(),
aad: associated_data.as_ref(),
}).map_err(|e| TripleRatchetError::CryptoError(e.to_string()))
}
}
fn ratchet_keys(input: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
let mut output = [0u8; 96];
let hkdf = Hkdf::<Sha512>::new(None, input);
hkdf.expand(b"quilibrium-triple-ratchet-keys", &mut output).unwrap();
(output[..32].to_vec(), output[32..64].to_vec(), output[64..].to_vec())
}

View File

@ -0,0 +1,82 @@
use std::collections::HashMap;
use sha2::Sha512;
use hkdf::Hkdf;
use ed448_goldilocks_plus::{subtle, CompressedEdwardsY, EdwardsPoint, Scalar};
use lazy_static::lazy_static;
lazy_static! {
static ref DOMAIN_SEPARATORS: HashMap<&'static str, Vec<u8>> = {
let mut m = HashMap::new();
m.insert("ed448", vec![
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF,
]);
m
};
}
pub fn sender_x3dh(
sending_identity_private_key: &Scalar,
sending_ephemeral_private_key: &Scalar,
receiving_identity_key: &EdwardsPoint,
receiving_signed_pre_key: &EdwardsPoint,
session_key_length: usize,
) -> Option<Vec<u8>> {
let xdh1 = (receiving_signed_pre_key * sending_identity_private_key).compress().to_bytes().to_vec();
let xdh2 = (receiving_identity_key * sending_ephemeral_private_key).compress().to_bytes().to_vec();
let xdh3 = (receiving_signed_pre_key * sending_ephemeral_private_key).compress().to_bytes().to_vec();
let salt = vec![0u8; session_key_length];
let info = b"quilibrium-x3dh";
let domain_separator = DOMAIN_SEPARATORS.get("ed448")
.expect("Unsupported curve");
let mut ikm = Vec::<u8>::new();
ikm.extend(domain_separator);
ikm.extend(xdh1);
ikm.extend(xdh2);
ikm.extend(xdh3);
let hk = Hkdf::<Sha512>::new(Some(&salt), &ikm);
let mut session_key = vec![0u8; session_key_length];
hk.expand(info, &mut session_key).ok()?;
Some(session_key)
}
pub fn receiver_x3dh(
sending_identity_private_key: &Scalar,
sending_signed_pre_private_key: &Scalar,
receiving_identity_key: &EdwardsPoint,
receiving_ephemeral_key: &EdwardsPoint,
session_key_length: usize,
) -> Option<Vec<u8>> {
let xdh1 = (receiving_identity_key * sending_signed_pre_private_key).compress().to_bytes().to_vec();
let xdh2 = (receiving_ephemeral_key * sending_identity_private_key).compress().to_bytes().to_vec();
let xdh3 = (receiving_ephemeral_key * sending_signed_pre_private_key).compress().to_bytes().to_vec();
let salt = vec![0u8; session_key_length];
let info = b"quilibrium-x3dh";
let domain_separator = DOMAIN_SEPARATORS.get("ed448")
.expect("Unsupported curve");
let mut ikm = Vec::<u8>::new();
ikm.extend(domain_separator);
ikm.extend(xdh1);
ikm.extend(xdh2);
ikm.extend(xdh3);
let hk = Hkdf::<Sha512>::new(Some(&salt), &ikm);
let mut session_key = vec![0u8; session_key_length];
hk.expand(info, &mut session_key).ok()?;
Some(session_key)
}

7
crates/rpm/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "rpm"
version = "0.1.0"

30
crates/rpm/Cargo.toml Normal file
View File

@ -0,0 +1,30 @@
[package]
name = "rpm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["lib", "staticlib"]
name = "rpm"
[dependencies]
hex = "0.4.3"
serde_json = "1.0.117"
uniffi = { version= "0.25", features = ["cli"]}
curve25519-dalek = "4.1.3"
rand = "0.8.5"
num = "0.4.3"
lazy_static = "1.5.0"
subtle = "2.6.1"
rayon = "1.10"
[dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] }
rand = "0.8.5"
[build-dependencies]
uniffi = { version = "0.25", features = [ "build" ] }
[[bench]]
name = "bench_rpm"
harness = false

32
crates/rpm/README.md Normal file
View File

@ -0,0 +1,32 @@
# RPM
Based on the paper [RPM: Robust Anonymity at Scale](https://eprint.iacr.org/2022/1037). Built to be used as a primitive all sharp edges are exposed, using this without properly checking the boundaries of the input will cut!
Includes UniFFI UDL file for FFI integration.
## How to use safely
There are six public functions of interest for builders to use if they want to have a purpose built mixnet:
- rpm_generate_initial_shares
- rpm_combine_shares_and_mask
- rpm_sketch_propose
- rpm_sketch_verify
- rpm_permute
- rpm_finalize
### rpm_generate_initial_shares
Performs the first of four steps of the "offline" phase of RPM Variant 3 (Subvariant 2). Each active dealer in a given offline batch generates initial shares of the permutation matrices and masks. Assumes player identifiers are in monotonically increasing order starting from 1, player count must be greater than or equal to the square of dealers. For malicious security, a ZKPoK should be used for each share set and broadcasted by all dealers prior to the next step.
### rpm_combine_shares_and_mask
Performs the second of four steps of the "offline" phase. Each player should have received their respective shares and organized them according to sequence of players for inputs. For malicious security, the ZKPoK should be verified after invoking this. The splits of the permutation matrices and masks should be passed into the next step.
### rpm_sketch_propose
Performs the third of four steps of the "offline" phase. Each player should take the splits of the permutation matrices and masks and pass them into this function. All players should broadcast the sketch proposals.
### rpm_sketch_verify
Performs the fourth step of the "offline" phase. Each player should take the broadcasted sketch proposals and pass them into the function. If any dealer or player had cheated, it would be revealed by the intersection of failures.
### rpm_permute
Performs the first step of the "online" phase. Before invoking this, each message sender should obtain a vector of the first depth's mask shares, at the same index from each, from as many players as needed to match the dealer count. The sender should combine these shares to produce the mask for their message, and add it to their message (chunked by field element size if need be), and secret share it with the same parameters (t = dealers, n = players). The players will collect their respective shares of inputs into an input vector following the order of the mask shares that were applied. The players will then pass in the input vector shares, matrix shares, mask shares, and combined matrix/mask shares from `rpm_combine_shares_and_mask`, along with the depth and player identifiers in player order. This function should be invoked in as many rounds as there is depth to the mixnet, using the previous invocation's output shares given to each respective player. This must be repeated for each chunked vector of field elements if applicable.
### rpm_finalize
A convenience funciton which recombines the shares of the final invocation of `rpm_permute`'s output into the finalized vector of the mixnet.

View File

@ -0,0 +1,71 @@
use curve25519_dalek::Scalar;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn criterion_benchmark(c: &mut Criterion) {
let smsize = 100;
let msize = smsize*smsize;
let depth = 4;
let players = 9;
let dealers = 3;
//todo parties should be + 1
let is1 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
let is2 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
let is3 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
let (m1, r1) = (is1.ms, is1.rs);
let (m2, r2) = (is2.ms, is2.rs);
let (m3, r3) = (is3.ms, is3.rs);
let mut ms = vec![vec![vec![vec![vec![vec![[0u8; 32]; smsize]; smsize]; smsize]; depth]; dealers]; players];
let mut rs = vec![vec![vec![vec![[0u8; 32]; msize]; depth]; dealers]; players];
let mut mc = Vec::<Vec<Vec<Vec<Vec<[u8; 32]>>>>>::with_capacity(players);
let mut rc = Vec::<Vec<Vec<[u8; 32]>>>::with_capacity(players);
let mut mrmc = Vec::<Vec<Vec<Vec<Vec<[u8; 32]>>>>>::with_capacity(players);
let mut mccs = Vec::<Vec<Vec<Vec<[u8; 32]>>>>::with_capacity(players);
let mut rccs = Vec::<Vec<[u8; 32]>>::with_capacity(players);
for i in 0..players {
for j in 0..depth {
for k in 0..smsize {
ms[i][0][j][k] = m1[j][k][i].clone();
ms[i][1][j][k] = m2[j][k][i].clone();
ms[i][2][j][k] = m3[j][k][i].clone();
}
rs[i][0][j] = r1[j][i].clone();
rs[i][1][j] = r2[j][i].clone();
rs[i][2][j] = r3[j][i].clone();
}
let cs = rpm::rpm_combine_shares_and_mask(ms[i].clone(), rs[i].clone(), msize, depth, dealers);
let (m, r, mrm) = (cs.ms, cs.rs, cs.mrms);
let sp = rpm::rpm_sketch_propose(m.clone(), r.clone());
let (mcc, rcc) = (sp.mp, sp.rp);
mc.push(m);
rc.push(r);
mrmc.push(mrm);
mccs.push(mcc);
rccs.push(rcc);
}
let mut xs = vec![vec![[0u8; 32]; msize]; players];
for i in 0..msize {
let xsi = rpm::gen_poly_frags(&Scalar::from(i as u64), 9, 3);
for j in 0..9 {
xs[j][i] = (Scalar::from_bytes_mod_order(xsi[j]) + Scalar::from_bytes_mod_order(rc[j][0][i])).to_bytes();
}
}
let mut group = c.benchmark_group("rpm");
group.sample_size(10);
group.bench_function(format!("rpm init {}", msize), |b| b.iter(|| black_box(rpm::rpm_generate_initial_shares(msize, depth, 3, 9))));
group.bench_function(format!("rpm combine {}", msize), |b| b.iter(|| black_box(rpm::rpm_combine_shares_and_mask(ms[0].clone(), rs[0].clone(), msize, depth, dealers))));
group.bench_function(format!("rpm sketch propose {}", msize), |b| b.iter(|| black_box(rpm::rpm_sketch_propose(mc[0].clone(), rc[0].clone()))));
group.bench_function(format!("rpm sketch verify {}", msize), |b| b.iter(|| black_box(rpm::rpm_sketch_verify(mccs.clone(), rccs.clone(), dealers))));
group.bench_function(format!("rpm permute {}", msize), |b| b.iter(|| black_box(rpm::rpm_permute(xs.clone(), mc[0].clone(), rc[0].clone(), mrmc[0].clone(), 0, vec![1,2,3,4,5,6,7,8,9]))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

5
crates/rpm/build.rs Normal file
View File

@ -0,0 +1,5 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
uniffi::generate_scaffolding("src/lib.udl").expect("uniffi generation failed");
}

1274
crates/rpm/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

24
crates/rpm/src/lib.udl Normal file
View File

@ -0,0 +1,24 @@
namespace rpm {
InitialShares rpm_generate_initial_shares(u64 size, u64 depth, u64 dealers, u64 players);
CombinedSharesAndMask rpm_combine_shares_and_mask([ByRef] sequence<sequence<sequence<sequence<sequence<sequence<u8>>>>>> ms, [ByRef] sequence<sequence<sequence<sequence<u8>>>> rs, u64 size, u64 depth, u64 dealers);
SketchProposal rpm_sketch_propose([ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> m, [ByRef] sequence<sequence<sequence<u8>>> r);
boolean rpm_sketch_verify([ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mcs, [ByRef] sequence<sequence<sequence<u8>>> rcs, u64 dealers);
sequence<sequence<sequence<u8>>> rpm_permute([ByRef] sequence<sequence<sequence<u8>>> masked_input_shares, [ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mb, [ByRef] sequence<sequence<sequence<u8>>> rb, [ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mrmb, u64 depth_index, [ByRef] sequence<u64> parties);
sequence<sequence<u8>> rpm_finalize([ByRef] sequence<sequence<sequence<u8>>> input, [ByRef] sequence<u64> parties);
};
dictionary InitialShares {
sequence<sequence<sequence<sequence<sequence<sequence<u8>>>>>> ms;
sequence<sequence<sequence<sequence<sequence<u8>>>>> rs;
};
dictionary CombinedSharesAndMask {
sequence<sequence<sequence<sequence<sequence<u8>>>>> ms;
sequence<sequence<sequence<u8>>> rs;
sequence<sequence<sequence<sequence<sequence<u8>>>>> mrms;
};
dictionary SketchProposal {
sequence<sequence<sequence<sequence<u8>>>> mp;
sequence<sequence<u8>> rp;
};

View File

@ -175,6 +175,7 @@ mod test {
.unwrap()
);
}
#[test]
fn check_random_bytes() {
assert_eq!(

View File

@ -1,11 +1,123 @@
# go-libp2p-blossomsub
First-pass of blossomsub, rudimentary fork of gossipsub  it does not merge subscriptions, bloom filtering needs to
happen at the publish level. This will be updated post-ceremony with the full bloom filter version.
<p align="left">
<a href="https://quilibrium.com"><img src="https://img.shields.io/badge/made%20by-Quilibrium%20Inc-orange.svg?style=flat-square" /></a>
<a href="https://github.com/quilibriumnetwork"><img src="https://img.shields.io/badge/project-Quilibrium-orange.svg?style=flat-square" /></a>
<a href="https://discourse.quilibrium.com/"><img src="https://img.shields.io/discourse/posts.svg?server=https%3A%2F%2Fquilibrium.discourse.group&style=flat-square" /></a>
<a href=""><img src="https://img.shields.io/badge/golang-%3E%3D1.22.0-orange.svg?style=flat-square" /></a>
</p>
This repo contains the canonical blossomsub implementation for Quilibrium. It has historical origins in [Gossipsub](https://github.com/libp2p/go-libp2p-pubsub), but has diverged significantly. Floodsub and Randomsub are not included in this fork.
## Table of Contents
- [Install](#install)
- [Usage](#usage)
- [Overview](#overview)
- [Tracing](#tracing)
- [Contribute](#contribute)
- [License](#license)
## Install
```
go get source.quilibrium.com/quilibrium/monorepo/go-libp2p-pubsub
```
## Usage
To be used for messaging in high scale, high throughput p2p instrastructure such as Quilibrium.
### Overview
```
.
├── LICENSE
├── README.md
# Regular Golang repo set up
├── codecov.yml
├── pb
├── go.mod
├── go.sum
├── doc.go
# PubSub base
├── backoff.go
├── bitmask.go
├── blacklist.go
├── comm.go
├── discovery.go
├── gossip_tracer.go
├── midgen.go
├── peer_gater.go
├── peer_notify.go
├── pubsub.go
├── sign.go
├── subscription.go
├── tag_tracer.go
├── trace.go
├── tracer.go
├── validation.go
# Blossomsub router
├── blossomsub_feat.go
├── blossomsub.go
├── mcache.go
├── score.go
└── score_params.go
```
### Tracing
The pubsub system supports _tracing_, which collects all events pertaining to the internals of the system. This allows you to recreate the complete message flow and state of the system for analysis purposes.
To enable tracing, instantiate the pubsub system using the `WithEventTracer` option; the option accepts a tracer with three available implementations in-package (trace to json, pb, or a remote peer).
If you want to trace using a remote peer in the same way gossipsub tracing worked, you would need to do so by forking the `traced` daemon from [go-libp2p-pubsub-tracer](https://github.com/libp2p/go-libp2p-pubsub-tracer).
For instance, to capture the trace as a json file, you can use the following option:
```go
tracer, err := pubsub.NewJSONTracer("/path/to/trace.json")
if err != nil {
panic(err)
}
pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
```
To capture the trace as a protobuf, you can use the following option:
```go
tracer, err := pubsub.NewPBTracer("/path/to/trace.pb")
if err != nil {
panic(err)
}
pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
```
Finally, to use the remote tracer, you can use the following incantations:
```go
// assuming that your tracer runs in x.x.x.x and has a peer ID of QmTracer
pi, err := peer.AddrInfoFromP2pAddr(ma.StringCast("/ip4/x.x.x.x/tcp/4001/p2p/QmTracer"))
if err != nil {
panic(err)
}
tracer, err := pubsub.NewRemoteTracer(ctx, host, pi)
if err != nil {
panic(err)
}
ps, err := pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
```
## Contribute
Contributions welcome. Please check out [the issues](https://source.quilibrium.com/quilibrium/monorepo/-/issues).
Quilibrium does not have a code of conduct for contributions  contributions are accepted on merit and benefit to the protocol.
## License
The go-libp2p-blossomsub project being forked from pubsub inherits the dual-license under Apache 2.0 and MIT terms:
The go-libp2p-blossomsub project being forked from go-libp2p-pubsub inherits the dual-license under Apache 2.0 and MIT terms:
- Apache License, Version 2.0, ([LICENSE-APACHE](./LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](./LICENSE-MIT) or http://opensource.org/licenses/MIT)

View File

@ -51,7 +51,6 @@ func newBackoff(ctx context.Context, sizeThreshold int, cleanupInterval time.Dur
func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
b.mu.Lock()
defer b.mu.Unlock()
h, ok := b.info[id]
switch {
@ -62,6 +61,7 @@ func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
attempts: 0,
}
case h.attempts >= b.maxAttempts:
b.mu.Unlock()
return 0, fmt.Errorf("peer %s has reached its maximum backoff attempts", id)
case h.duration < MinBackoffDelay:
@ -78,27 +78,29 @@ func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
h.attempts += 1
h.lastTried = time.Now()
b.info[id] = h
b.mu.Unlock()
return h.duration, nil
}
func (b *backoff) cleanup() {
b.mu.Lock()
defer b.mu.Unlock()
for id, h := range b.info {
if time.Since(h.lastTried) > TimeToLive {
delete(b.info, id)
}
}
b.mu.Unlock()
}
func (b *backoff) cleanupLoop(ctx context.Context) {
ticker := time.NewTicker(b.ci)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
ticker.Stop()
return // pubsub shutting down
case <-ticker.C:
b.cleanup()

View File

@ -23,9 +23,11 @@ func TestBackoff_Update(t *testing.T) {
b := newBackoff(ctx, size, cleanupInterval, maxBackoffAttempts)
b.mu.Lock()
if len(b.info) > 0 {
t.Fatal("non-empty info map for backoff")
}
b.mu.Unlock()
if d, err := b.updateAndGet(id1); d != time.Duration(0) || err != nil {
t.Fatalf("invalid initialization: %v, \t, %s", d, err)
@ -64,9 +66,11 @@ func TestBackoff_Update(t *testing.T) {
t.Fatalf("invalid backoff result, expected: %v, got: %v", MinBackoffDelay, got)
}
b.mu.Lock()
// sets last tried of id2 to long ago that it resets back upon next try.
// update attempts on id2 are below threshold, hence peer should never go beyond backoff attempt threshold.
b.info[id2].lastTried = time.Now().Add(-TimeToLive)
b.mu.Unlock()
got, err = b.updateAndGet(id2)
if err != nil {
t.Fatalf("unexpected error post update: %s", err)
@ -75,10 +79,11 @@ func TestBackoff_Update(t *testing.T) {
t.Fatalf("invalid ttl expiration, expected: %v, got: %v", time.Duration(0), got)
}
b.mu.Lock()
if len(b.info) != 2 {
t.Fatalf("pre-invalidation attempt, info map size mismatch, expected: %d, got: %d", 2, len(b.info))
}
b.mu.Unlock()
}
func TestBackoff_Clean(t *testing.T) {
@ -96,12 +101,16 @@ func TestBackoff_Clean(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error post update: %s", err)
}
b.mu.Lock()
b.info[id].lastTried = time.Now().Add(-TimeToLive) // enforces expiry
b.mu.Unlock()
}
b.mu.Lock()
if len(b.info) != size {
t.Fatalf("info map size mismatch, expected: %d, got: %d", size, len(b.info))
}
b.mu.Unlock()
// waits for a cleanup loop to kick-in
time.Sleep(2 * cleanupInterval)
@ -115,8 +124,10 @@ func TestBackoff_Clean(t *testing.T) {
t.Fatalf("invalid backoff result, expected: %v, got: %v", time.Duration(0), got)
}
b.mu.Lock()
// except "some-new-peer" every other records must be cleaned up
if len(b.info) != 1 {
t.Fatalf("info map size mismatch, expected: %d, got: %d", 1, len(b.info))
}
b.mu.Unlock()
}

View File

@ -48,9 +48,9 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
}
t.mux.Lock()
defer t.mux.Unlock()
if t.closed {
t.mux.Unlock()
return ErrBitmaskClosed
}
@ -74,9 +74,11 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
select {
case t.p.eval <- update:
err = <-result
t.mux.Unlock()
return err
case <-t.p.ctx.Done():
t.mux.Unlock()
return t.p.ctx.Err()
}
}
@ -85,8 +87,8 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
// Multiple event handlers may be created and will operate independently of each other
func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHandler, error) {
t.mux.RLock()
defer t.mux.RUnlock()
if t.closed {
t.mux.RUnlock()
return nil, ErrBitmaskClosed
}
@ -101,6 +103,7 @@ func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHan
for _, opt := range opts {
err := opt(h)
if err != nil {
t.mux.RUnlock()
return nil, err
}
}
@ -120,21 +123,23 @@ func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHan
done <- struct{}{}
}:
case <-t.p.ctx.Done():
t.mux.RUnlock()
return nil, t.p.ctx.Err()
}
<-done
t.mux.RUnlock()
return h, nil
}
func (t *Bitmask) sendNotification(evt PeerEvent) {
t.evtHandlerMux.RLock()
defer t.evtHandlerMux.RUnlock()
for h := range t.evtHandlers {
h.sendNotification(evt)
}
t.evtHandlerMux.RUnlock()
}
// Subscribe returns a new Subscription for the bitmask.
@ -142,8 +147,9 @@ func (t *Bitmask) sendNotification(evt PeerEvent) {
// before the subscription is processed by the pubsub main loop and propagated to our peers.
func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
t.mux.RLock()
defer t.mux.RUnlock()
if t.closed {
t.mux.RUnlock()
return nil, ErrBitmaskClosed
}
@ -155,12 +161,13 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
for _, opt := range opts {
err := opt(sub)
if err != nil {
t.mux.RUnlock()
return nil, err
}
}
if sub.ch == nil {
sub.ch = make(chan *Message, 128)
sub.ch = make(chan *Message, 32)
}
out := make(chan *Subscription, 1)
@ -173,10 +180,13 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
resp: out,
}:
case <-t.p.ctx.Done():
t.mux.RUnlock()
return nil, t.p.ctx.Err()
}
return <-out, nil
subOut := <-out
t.mux.RUnlock()
return subOut, nil
}
// Relay enables message relaying for the bitmask and returns a reference
@ -184,8 +194,9 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
// To completely disable the relay, all references must be cancelled.
func (t *Bitmask) Relay() (RelayCancelFunc, error) {
t.mux.RLock()
defer t.mux.RUnlock()
if t.closed {
t.mux.RUnlock()
return nil, ErrBitmaskClosed
}
@ -199,10 +210,13 @@ func (t *Bitmask) Relay() (RelayCancelFunc, error) {
resp: out,
}:
case <-t.p.ctx.Done():
t.mux.RUnlock()
return nil, t.p.ctx.Err()
}
return <-out, nil
cancelFunc := <-out
t.mux.RUnlock()
return cancelFunc, nil
}
// RouterReady is a function that decides if a router is ready to publish
@ -220,10 +234,11 @@ type PublishOptions struct {
type PubOpt func(pub *PublishOptions) error
// Publish publishes data to bitmask.
func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) error {
func (t *Bitmask) Publish(ctx context.Context, bitmask []byte, data []byte, opts ...PubOpt) error {
t.mux.RLock()
defer t.mux.RUnlock()
if t.closed {
t.mux.RUnlock()
return ErrBitmaskClosed
}
@ -234,6 +249,7 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
for _, opt := range opts {
err := opt(pub)
if err != nil {
t.mux.RUnlock()
return err
}
}
@ -241,16 +257,18 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
if pub.customKey != nil && !pub.local {
key, pid = pub.customKey()
if key == nil {
t.mux.RUnlock()
return ErrNilSignKey
}
if len(pid) == 0 {
t.mux.RUnlock()
return ErrEmptyPeerID
}
}
m := &pb.Message{
Data: data,
Bitmask: t.bitmask,
Bitmask: bitmask,
From: nil,
Seqno: nil,
}
@ -262,6 +280,7 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
m.From = []byte(pid)
err := signMessage(pid, key, m)
if err != nil {
t.mux.RUnlock()
return err
}
}
@ -286,28 +305,43 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
res <- done
}:
if <-res {
if ticker != nil {
ticker.Stop()
}
break readyLoop
}
case <-t.p.ctx.Done():
if ticker != nil {
ticker.Stop()
}
t.mux.RUnlock()
return t.p.ctx.Err()
case <-ctx.Done():
if ticker != nil {
ticker.Stop()
}
t.mux.RUnlock()
return ctx.Err()
}
if ticker == nil {
ticker = time.NewTicker(200 * time.Millisecond)
defer ticker.Stop()
}
select {
case <-ticker.C:
case <-ctx.Done():
ticker.Stop()
t.mux.RUnlock()
return fmt.Errorf("router is not ready: %w", ctx.Err())
}
}
}
}
return t.p.val.PushLocal(&Message{m, "", t.p.host.ID(), nil, pub.local})
err := t.p.val.PushLocal(&Message{m, nil, t.p.host.ID(), nil, pub.local})
t.mux.RUnlock()
return err
}
// WithReadiness returns a publishing option for only publishing when the router is ready.
@ -347,8 +381,9 @@ func WithSecretKeyAndPeerId(key crypto.PrivKey, pid peer.ID) PubOpt {
// Does not error if the bitmask is already closed.
func (t *Bitmask) Close() error {
t.mux.Lock()
defer t.mux.Unlock()
if t.closed {
t.mux.Unlock()
return nil
}
@ -357,6 +392,7 @@ func (t *Bitmask) Close() error {
select {
case t.p.rmBitmask <- req:
case <-t.p.ctx.Done():
t.mux.Unlock()
return t.p.ctx.Err()
}
@ -366,18 +402,22 @@ func (t *Bitmask) Close() error {
t.closed = true
}
t.mux.Unlock()
return err
}
// ListPeers returns a list of peers we are connected to in the given bitmask.
func (t *Bitmask) ListPeers() []peer.ID {
t.mux.RLock()
defer t.mux.RUnlock()
if t.closed {
t.mux.RUnlock()
return []peer.ID{}
}
return t.p.ListPeers(t.bitmask)
l := t.p.ListPeers(t.bitmask)
t.mux.RUnlock()
return l
}
type EventType int

View File

@ -25,7 +25,12 @@ func getBitmasks(psubs []*PubSub, bitmask []byte, opts ...BitmaskOpt) []*Bitmask
if err != nil {
panic(err)
}
bitmasks[i] = t
if len(t) != 1 {
panic("multi bit bitmasks not supported for tests using getBitmasks")
}
bitmasks[i] = t[0]
}
return bitmasks
@ -98,9 +103,9 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
defer cancel()
const numHosts = 1
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
hosts := getNetHosts(t, ctx, numHosts)
ps := getPubsub(ctx, hosts[0])
bitmaskID := []byte{0x00, 0x01}
hosts := getDefaultHosts(t, numHosts)
ps := getBlossomSub(ctx, hosts[0])
// Try create and cancel bitmask
bitmask, err := ps.Join(bitmaskID)
@ -108,7 +113,7 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
t.Fatal(err)
}
if err := bitmask.Close(); err != nil {
if err := bitmask[0].Close(); err != nil {
t.Fatal(err)
}
@ -118,9 +123,9 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
t.Fatal(err)
}
openResource(bitmask)
openResource(bitmask[0])
if err := bitmask.Close(); err == nil {
if err := bitmask[0].Close(); err == nil {
t.Fatal("expected an error closing a bitmask with an open resource")
}
@ -128,7 +133,7 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
closeResource()
time.Sleep(time.Millisecond * 100)
if err := bitmask.Close(); err != nil {
if err := bitmask[0].Close(); err != nil {
t.Fatal(err)
}
}
@ -138,11 +143,11 @@ func TestBitmaskReuse(t *testing.T) {
defer cancel()
const numHosts = 2
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
hosts := getNetHosts(t, ctx, numHosts)
bitmaskID := []byte{0x00, 0x01}
hosts := getDefaultHosts(t, numHosts)
sender := getPubsub(ctx, hosts[0], WithDiscovery(&dummyDiscovery{}))
receiver := getPubsub(ctx, hosts[1])
sender := getBlossomSub(ctx, hosts[0])
receiver := getBlossomSub(ctx, hosts[1])
connectAll(t, hosts)
@ -158,13 +163,18 @@ func TestBitmaskReuse(t *testing.T) {
t.Fatal(err)
}
sub, err := receiveBitmask.Subscribe()
_, err = sendBitmask[0].Subscribe()
if err != nil {
t.Fatal(err)
}
sub, err := receiveBitmask[0].Subscribe()
if err != nil {
t.Fatal(err)
}
firstMsg := []byte("1")
if err := sendBitmask.Publish(ctx, firstMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
if err := sendBitmask[0].Publish(ctx, bitmaskID, firstMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
t.Fatal(err)
}
@ -176,54 +186,10 @@ func TestBitmaskReuse(t *testing.T) {
t.Fatal("received incorrect message")
}
if err := sendBitmask.Close(); err != nil {
t.Fatal(err)
}
// Recreate the same bitmask
newSendBitmask, err := sender.Join(bitmaskID)
if err != nil {
t.Fatal(err)
}
// Try sending data with original bitmask
illegalSend := []byte("illegal")
if err := sendBitmask.Publish(ctx, illegalSend); err != ErrBitmaskClosed {
t.Fatal(err)
}
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second*2)
defer timeoutCancel()
msg, err = sub.Next(timeoutCtx)
if err != context.DeadlineExceeded {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(msg.GetData(), illegalSend) {
t.Fatal("received incorrect message from illegal bitmask")
}
t.Fatal("received message sent by illegal bitmask")
}
timeoutCancel()
// Try cancelling the new bitmask by using the original bitmask
if err := sendBitmask.Close(); err != nil {
t.Fatal(err)
}
secondMsg := []byte("2")
if err := newSendBitmask.Publish(ctx, secondMsg); err != nil {
t.Fatal(err)
}
timeoutCtx, timeoutCancel = context.WithTimeout(ctx, time.Second*2)
defer timeoutCancel()
msg, err = sub.Next(timeoutCtx)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(msg.GetData(), secondMsg) {
t.Fatal("received incorrect message")
_, err = sender.Join(bitmaskID)
if err == nil {
t.Fatal("did not error on reuse of bitmask")
}
}
@ -232,9 +198,9 @@ func TestBitmaskEventHandlerCancel(t *testing.T) {
defer cancel()
const numHosts = 5
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
hosts := getNetHosts(t, ctx, numHosts)
ps := getPubsub(ctx, hosts[0])
bitmaskID := []byte{0x00, 0x01}
hosts := getDefaultHosts(t, numHosts)
ps := getBlossomSub(ctx, hosts[0])
// Try create and cancel bitmask
bitmask, err := ps.Join(bitmaskID)
@ -242,7 +208,7 @@ func TestBitmaskEventHandlerCancel(t *testing.T) {
t.Fatal(err)
}
evts, err := bitmask.EventHandler()
evts, err := bitmask[0].EventHandler()
if err != nil {
t.Fatal(err)
}
@ -265,8 +231,8 @@ func TestSubscriptionJoinNotification(t *testing.T) {
const numLateSubscribers = 10
const numHosts = 20
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), []byte{0xf0, 0x0b, 0xa1, 0x20})
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), []byte{0x00, 0x01})
evts := getBitmaskEvts(bitmasks)
subs := make([]*Subscription, numHosts)
@ -331,9 +297,9 @@ func TestSubscriptionLeaveNotification(t *testing.T) {
defer cancel()
const numHosts = 20
hosts := getNetHosts(t, ctx, numHosts)
psubs := getPubsubs(ctx, hosts)
bitmasks := getBitmasks(psubs, []byte{0xf0, 0x0b, 0xa1, 0x20})
hosts := getDefaultHosts(t, numHosts)
psubs := getBlossomSubs(ctx, hosts)
bitmasks := getBitmasks(psubs, []byte{0x00, 0x01})
evts := getBitmaskEvts(bitmasks)
subs := make([]*Subscription, numHosts)
@ -411,11 +377,11 @@ func TestSubscriptionManyNotifications(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 33
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
evts := getBitmaskEvts(bitmasks)
subs := make([]*Subscription, numHosts)
@ -516,11 +482,11 @@ func TestSubscriptionNotificationSubUnSub(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 35
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
for i := 1; i < numHosts; i++ {
connect(t, hosts[0], hosts[i])
@ -534,11 +500,11 @@ func TestBitmaskRelay(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 5
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
// [0.Rel] - [1.Rel] - [2.Sub]
// |
@ -552,6 +518,7 @@ func TestBitmaskRelay(t *testing.T) {
time.Sleep(time.Millisecond * 100)
var subs []*Subscription
var subscribedBitmasks []*Bitmask
for i, bitmask := range bitmasks {
if i == 2 || i == 4 {
@ -561,6 +528,7 @@ func TestBitmaskRelay(t *testing.T) {
}
subs = append(subs, sub)
subscribedBitmasks = append(subscribedBitmasks, bitmask)
} else {
_, err := bitmask.Relay()
if err != nil {
@ -569,14 +537,15 @@ func TestBitmaskRelay(t *testing.T) {
}
}
time.Sleep(time.Millisecond * 100)
// Give enough time to build the relay
time.Sleep(time.Second * 2)
for i := 0; i < 100; i++ {
msg := []byte("message")
msg := []byte(fmt.Sprintf("message %d", i))
owner := rand.Intn(len(bitmasks))
owner := rand.Intn(len(subscribedBitmasks))
err := bitmasks[owner].Publish(ctx, msg)
err := subscribedBitmasks[owner].Publish(ctx, subscribedBitmasks[owner].bitmask, msg)
if err != nil {
t.Fatal(err)
}
@ -598,11 +567,11 @@ func TestBitmaskRelayReuse(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 1
hosts := getNetHosts(t, ctx, numHosts)
pubsubs := getPubsubs(ctx, hosts)
hosts := getDefaultHosts(t, numHosts)
pubsubs := getBlossomSubs(ctx, hosts)
bitmasks := getBitmasks(pubsubs, bitmask)
relay1Cancel, err := bitmasks[0].Relay()
@ -665,11 +634,11 @@ func TestBitmaskRelayOnClosedBitmask(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 1
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
err := bitmasks[0].Close()
if err != nil {
@ -687,9 +656,9 @@ func TestProducePanic(t *testing.T) {
defer cancel()
const numHosts = 5
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
hosts := getNetHosts(t, ctx, numHosts)
ps := getPubsub(ctx, hosts[0])
bitmaskID := []byte{0x00, 0x01}
hosts := getDefaultHosts(t, numHosts)
ps := getBlossomSub(ctx, hosts[0])
// Create bitmask
bitmask, err := ps.Join(bitmaskID)
@ -698,13 +667,13 @@ func TestProducePanic(t *testing.T) {
}
// Create subscription we're going to cancel
s, err := bitmask.Subscribe()
s, err := bitmask[0].Subscribe()
if err != nil {
t.Fatal(err)
}
// Create second subscription to keep us alive on the subscription map
// after the first one is canceled
s2, err := bitmask.Subscribe()
s2, err := bitmask[0].Subscribe()
if err != nil {
t.Fatal(err)
}
@ -789,12 +758,12 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
defer cancel()
const numHosts = 3
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
hosts := getNetHosts(t, ctx, numHosts)
bitmaskID := []byte{0x00, 0x01}
hosts := getDefaultHosts(t, numHosts)
sender := getPubsub(ctx, hosts[0])
receiver1 := getPubsub(ctx, hosts[1])
receiver2 := getPubsub(ctx, hosts[2])
sender := getBlossomSub(ctx, hosts[0])
receiver1 := getBlossomSub(ctx, hosts[1])
receiver2 := getBlossomSub(ctx, hosts[2])
connectAll(t, hosts)
@ -804,19 +773,24 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
t.Fatal(err)
}
_, err = sendBitmask[0].Subscribe()
if err != nil {
t.Fatal(err)
}
// Receiver creates and subscribes to the bitmask
receiveBitmask1, err := receiver1.Join(bitmaskID)
if err != nil {
t.Fatal(err)
}
sub1, err := receiveBitmask1.Subscribe()
sub1, err := receiveBitmask1[0].Subscribe()
if err != nil {
t.Fatal(err)
}
oneMsg := []byte("minimum one")
if err := sendBitmask.Publish(ctx, oneMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, oneMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
t.Fatal(err)
}
@ -832,7 +806,7 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
{
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); !errors.Is(err, context.DeadlineExceeded) {
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, twoMsg, WithReadiness(MinBitmaskSize(2))); !errors.Is(err, context.DeadlineExceeded) {
t.Fatal(err)
}
}
@ -843,15 +817,17 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
t.Fatal(err)
}
sub2, err := receiveBitmask2.Subscribe()
sub2, err := receiveBitmask2[0].Subscribe()
if err != nil {
t.Fatal(err)
}
twoMsg = []byte("minimum two, 2")
{
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); err != nil {
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, twoMsg, WithReadiness(MinBitmaskSize(2))); err != nil {
t.Fatal(err)
}
}
@ -867,20 +843,20 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmaskA, bitmaskB := []byte{0xf0, 0x0b, 0xa1, 0x2a}, []byte{0xf0, 0x0b, 0xa1, 0x2b}
bitmaskA, bitmaskB := []byte{0x20, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x80, 0x00}
const numHosts = 2
hosts := getNetHosts(t, ctx, numHosts)
pubsubs := getPubsubs(ctx, hosts, WithMessageIdFn(func(pmsg *pb.Message) string {
hosts := getDefaultHosts(t, numHosts)
pubsubs := getBlossomSubs(ctx, hosts, WithMessageIdFn(func(pmsg *pb.Message) []byte {
hash := sha256.Sum256(pmsg.Data)
return string(hash[:])
return hash[:]
}))
connectAll(t, hosts)
bitmasksA := getBitmasks(pubsubs, bitmaskA) // uses global msgIdFn
bitmasksB := getBitmasks(pubsubs, bitmaskB, WithBitmaskMessageIdFn(func(pmsg *pb.Message) string { // uses custom
bitmasksB := getBitmasks(pubsubs, bitmaskB, WithBitmaskMessageIdFn(func(pmsg *pb.Message) []byte { // uses custom
hash := sha1.Sum(pmsg.Data)
return string(hash[:])
return hash[:]
}))
payload := []byte("pubsub rocks")
@ -890,7 +866,12 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
t.Fatal(err)
}
err = bitmasksA[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
_, err = bitmasksA[1].Subscribe()
if err != nil {
t.Fatal(err)
}
err = bitmasksA[1].Publish(ctx, bitmasksA[1].bitmask, payload, WithReadiness(MinBitmaskSize(1)))
if err != nil {
t.Fatal(err)
}
@ -905,7 +886,14 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
t.Fatal(err)
}
err = bitmasksB[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
_, err = bitmasksB[1].Subscribe()
if err != nil {
t.Fatal(err)
}
payload = []byte("but blossomsub has more sensible scale strategies")
err = bitmasksB[1].Publish(ctx, bitmasksB[1].bitmask, payload, WithReadiness(MinBitmaskSize(1)))
if err != nil {
t.Fatal(err)
}
@ -915,7 +903,7 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
t.Fatal(err)
}
if msgA.ID == msgB.ID {
if bytes.Equal(msgA.ID, msgB.ID) {
t.Fatal("msg ids are equal")
}
}
@ -926,23 +914,23 @@ func TestBitmaskPublishWithKeyInvalidParameters(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 5
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
t.Run("nil sign private key should error", func(t *testing.T) {
withVirtualKey := WithSecretKeyAndPeerId(nil, virtualPeer.ID)
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
err := bitmasks[0].Publish(ctx, bitmask, []byte("buff"), withVirtualKey)
if err != ErrNilSignKey {
t.Fatal("error should have been of type errNilSignKey")
}
})
t.Run("empty peer ID should error", func(t *testing.T) {
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, "")
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
err := bitmasks[0].Publish(ctx, bitmask, []byte("buff2"), withVirtualKey)
if err != ErrEmptyPeerID {
t.Fatal("error should have been of type errEmptyPeerID")
}
@ -953,12 +941,12 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
const numHosts = 5
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
hosts := getNetHosts(t, ctx, numHosts)
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
hosts := getDefaultHosts(t, numHosts)
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
// [0.Rel] - [1.Rel] - [2.Sub]
// |
@ -972,6 +960,7 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
time.Sleep(time.Millisecond * 100)
var subs []*Subscription
var senders []*Bitmask
for i, bitmaskValue := range bitmasks {
if i == 2 || i == 4 {
@ -981,6 +970,7 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
}
subs = append(subs, sub)
senders = append(senders, bitmaskValue)
} else {
_, err := bitmaskValue.Relay()
if err != nil {
@ -989,15 +979,15 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
}
}
time.Sleep(time.Millisecond * 100)
time.Sleep(time.Second * 2)
for i := 0; i < 100; i++ {
msg := []byte("message")
msg := []byte(fmt.Sprintf("message %d", i))
owner := rand.Intn(len(bitmasks))
owner := rand.Intn(len(senders))
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, virtualPeer.ID)
err := bitmasks[owner].Publish(ctx, msg, withVirtualKey)
err := senders[owner].Publish(ctx, senders[owner].bitmask, msg, withVirtualKey)
if err != nil {
t.Fatal(err)
}
@ -1022,10 +1012,10 @@ func TestWithLocalPublication(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
bitmask := []byte{0x7e, 57}
bitmask := []byte{0x01, 0x00}
hosts := getNetHosts(t, ctx, 2)
pubsubs := getPubsubs(ctx, hosts)
hosts := getDefaultHosts(t, 2)
pubsubs := getBlossomSubs(ctx, hosts)
bitmasks := getBitmasks(pubsubs, bitmask)
connectAll(t, hosts)
@ -1041,7 +1031,7 @@ func TestWithLocalPublication(t *testing.T) {
t.Fatal(err)
}
err = bitmasks[0].Publish(ctx, payload, WithLocalPublication(true))
err = bitmasks[0].Publish(ctx, bitmasks[0].bitmask, payload, WithLocalPublication(true))
if err != nil {
t.Fatal(err)
}

View File

@ -38,11 +38,16 @@ func TestBlacklist(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 2)
psubs := getPubsubs(ctx, hosts)
hosts := getDefaultHosts(t, 2)
psubs := getBlossomSubs(ctx, hosts)
connect(t, hosts[0], hosts[1])
sub, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
sub, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
@ -51,11 +56,11 @@ func TestBlacklist(t *testing.T) {
psubs[1].BlacklistPeer(hosts[0].ID())
time.Sleep(time.Millisecond * 100)
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
_, err = sub.Next(wctx)
_, err = sub[0].Next(wctx)
if err == nil {
t.Fatal("got message from blacklisted peer")
@ -66,16 +71,21 @@ func TestBlacklist2(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 2)
psubs := getPubsubs(ctx, hosts)
hosts := getDefaultHosts(t, 2)
psubs := getBlossomSubs(ctx, hosts)
connect(t, hosts[0], hosts[1])
_, err := psubs[0].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
sub1, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
_, err = psubs[0].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
sub1, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
@ -84,11 +94,11 @@ func TestBlacklist2(t *testing.T) {
psubs[1].BlacklistPeer(hosts[0].ID())
time.Sleep(time.Millisecond * 100)
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
_, err = sub1.Next(wctx)
_, err = sub1[0].Next(wctx)
if err == nil {
t.Fatal("got message from blacklisted peer")
@ -99,25 +109,30 @@ func TestBlacklist3(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 2)
psubs := getPubsubs(ctx, hosts)
hosts := getDefaultHosts(t, 2)
psubs := getBlossomSubs(ctx, hosts)
psubs[1].BlacklistPeer(hosts[0].ID())
time.Sleep(time.Millisecond * 100)
connect(t, hosts[0], hosts[1])
sub, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
sub, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
time.Sleep(time.Millisecond * 100)
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
_, err = sub.Next(wctx)
_, err = sub[0].Next(wctx)
if err == nil {
t.Fatal("got message from blacklisted peer")

View File

@ -4,8 +4,11 @@ import (
"bytes"
"context"
"fmt"
"io"
"math/rand"
"slices"
"sort"
"sync"
"time"
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
@ -21,8 +24,8 @@ import (
)
const (
// BlossomSubID_v12 is the protocol ID for version 1.2.1 of the BlossomSub protocol.
BlossomSubID_v12 = protocol.ID("/blossomsub/1.2.1")
// BlossomSubID_v2 is the protocol ID for version 2.0.0 of the BlossomSub protocol.
BlossomSubID_v2 = protocol.ID("/blossomsub/2.0.0")
)
// Defines the default BlossomSub parameters.
@ -35,8 +38,8 @@ var (
BlossomSubHistoryLength = 5
BlossomSubHistoryGossip = 3
BlossomSubDlazy = 6
BlossomSubGossipFactor = 0.25
BlossomSubGossipRetransmission = 1
BlossomSubGossipRetransmission = 3
BlossomSubBitmaskWidth = 256
BlossomSubHeartbeatInitialDelay = 100 * time.Millisecond
BlossomSubHeartbeatInterval = 1 * time.Second
BlossomSubFanoutTTL = 60 * time.Second
@ -87,6 +90,9 @@ type BlossomSubParams struct {
// Dout must be set below Dlo, and must not exceed D / 2.
Dout int
// BitmaskWidth sets the size of the bitmask for subscriptions.
BitmaskWidth int
// gossip parameters
// HistoryLength controls the size of the message cache used for gossip.
@ -105,15 +111,9 @@ type BlossomSubParams struct {
// Dlazy affects how many peers we will emit gossip to at each heartbeat.
// We will send gossip to at least Dlazy peers outside our mesh. The actual
// number may be more, depending on GossipFactor and how many peers we're
// connected to.
// number may be less, depending on how many peers we're connected to.
Dlazy int
// GossipFactor affects how many peers we will emit gossip to at each heartbeat.
// We will send gossip to GossipFactor * (total number of non-mesh peers), or
// Dlazy, whichever is greater.
GossipFactor float64
// GossipRetransmission controls how many times we will allow a peer to request
// the same message id through IWANT gossip before we start ignoring them. This is designed
// to prevent peers from spamming us with requests and wasting our resources.
@ -272,7 +272,6 @@ func DefaultBlossomSubParams() BlossomSubParams {
HistoryLength: BlossomSubHistoryLength,
HistoryGossip: BlossomSubHistoryGossip,
Dlazy: BlossomSubDlazy,
GossipFactor: BlossomSubGossipFactor,
GossipRetransmission: BlossomSubGossipRetransmission,
HeartbeatInitialDelay: BlossomSubHeartbeatInitialDelay,
HeartbeatInterval: BlossomSubHeartbeatInterval,
@ -453,6 +452,7 @@ type BlossomSubRouter struct {
backoff map[string]map[peer.ID]time.Time // prune backoff
connect chan connectInfo // px connection requests
cab peerstore.AddrBook
meshMx sync.RWMutex
protos []protocol.ID
feature BlossomSubFeatureTest
@ -556,11 +556,18 @@ func (bs *BlossomSubRouter) manageAddrBook() {
log.Errorf("failed to subscribe to peer identification events: %v", err)
return
}
defer sub.Close()
for {
select {
case <-bs.p.ctx.Done():
cabCloser, ok := bs.cab.(io.Closer)
if ok {
errClose := cabCloser.Close()
if errClose != nil {
log.Warnf("failed to close addr book: %v", errClose)
}
}
sub.Close()
return
case ev := <-sub.Out():
switch ev := ev.(type) {
@ -620,9 +627,11 @@ func (bs *BlossomSubRouter) RemovePeer(p peer.ID) {
log.Debugf("PEERDOWN: Remove disconnected peer %s", p)
bs.tracer.RemovePeer(p)
delete(bs.peers, p)
bs.meshMx.Lock()
for _, peers := range bs.mesh {
delete(peers, p)
}
bs.meshMx.Unlock()
for _, peers := range bs.fanout {
delete(peers, p)
}
@ -638,7 +647,7 @@ func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
return false
}
fsPeers, gsPeers := 0, 0
fsPeers, bsPeers := 0, 0
// floodsub peers
for p := range tmap {
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) {
@ -646,14 +655,16 @@ func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
}
}
bs.meshMx.RLock()
// BlossomSub peers
gsPeers = len(bs.mesh[string(bitmask)])
bsPeers = len(bs.mesh[string(bitmask)])
bs.meshMx.RUnlock()
if suggested == 0 {
suggested = bs.params.Dlo
}
if fsPeers+gsPeers >= suggested || gsPeers >= bs.params.Dhi {
if fsPeers+bsPeers >= suggested || bsPeers >= bs.params.Dhi {
return true
}
@ -719,7 +730,9 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
iwant := make(map[string]struct{})
for _, ihave := range ctl.GetIhave() {
bitmask := ihave.GetBitmask()
bs.meshMx.RLock()
_, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -728,11 +741,18 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
continue
}
for _, mid := range ihave.GetMessageIDs() {
checkIwantMsgsLoop:
for msgIdx, mid := range ihave.GetMessageIDs() {
// prevent remote peer from sending too many msg_ids on a single IHAVE message
if msgIdx >= bs.params.MaxIHaveLength {
log.Debugf("IHAVE: peer %s has sent IHAVE on bitmask %s with too many messages (%d); ignoring remaining msgs", p, bitmask, len(ihave.MessageIDs))
break checkIwantMsgsLoop
}
if bs.p.seenMessage(mid) {
continue
}
iwant[mid] = struct{}{}
iwant[string(mid)] = struct{}{}
}
}
@ -747,9 +767,9 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
log.Debugf("IHAVE: Asking for %d out of %d messages from %s", iask, len(iwant), p)
iwantlst := make([]string, 0, len(iwant))
iwantlst := make([][]byte, 0, len(iwant))
for mid := range iwant {
iwantlst = append(iwantlst, mid)
iwantlst = append(iwantlst, []byte(mid))
}
// truncate to the messages we are actually asking for and update the iasked counter
@ -786,7 +806,7 @@ func (bs *BlossomSubRouter) handleIWant(p peer.ID, ctl *pb.ControlMessage) []*pb
continue
}
ihave[mid] = msg.Message
ihave[string(mid)] = msg.Message
}
}
@ -818,7 +838,9 @@ func (bs *BlossomSubRouter) handleGraft(p peer.ID, ctl *pb.ControlMessage) []*pb
continue
}
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
// don't do PX when there is an unknown bitmask to avoid leaking our peers
doPX = false
@ -907,7 +929,9 @@ func (bs *BlossomSubRouter) handlePrune(p peer.ID, ctl *pb.ControlMessage) {
for _, prune := range ctl.GetPrune() {
bitmask := prune.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -1046,57 +1070,73 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
tosend := make(map[peer.ID]struct{})
// any peers in the bitmask?
tmap, ok := bs.p.bitmasks[string(bitmask)]
if !ok {
return
}
if bs.floodPublish && from == bs.p.host.ID() {
for p := range tmap {
_, direct := bs.direct[p]
if direct || bs.score.Score(p) >= bs.publishThreshold {
tosend[p] = struct{}{}
}
}
} else {
// direct peers
for p := range bs.direct {
_, inBitmask := tmap[p]
if inBitmask {
tosend[p] = struct{}{}
}
sliced := SliceBitmask(bitmask)
// bloom publish:
if len(sliced) != 1 {
// any peers in all slices of the bitmask?
peers := bs.p.getPeersInBitmask(bitmask)
if len(peers) == 0 {
return
}
// floodsub peers
for p := range tmap {
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && bs.score.Score(p) >= bs.publishThreshold {
tosend[p] = struct{}{}
}
for _, p := range peers {
tosend[p] = struct{}{}
}
// BlossomSub peers
gmap, ok := bs.mesh[string(bitmask)]
} else { // classic gossip mesh
// any peers in the bitmask?
tmap, ok := bs.p.bitmasks[string(bitmask)]
if !ok {
// we are not in the mesh for bitmask, use fanout peers
gmap, ok = bs.fanout[string(bitmask)]
if !ok || len(gmap) == 0 {
// we don't have any, pick some with score above the publish threshold
peers := bs.getPeers(bitmask, bs.params.D, func(p peer.ID) bool {
_, direct := bs.direct[p]
return !direct && bs.score.Score(p) >= bs.publishThreshold
})
return
}
if len(peers) > 0 {
gmap = peerListToMap(peers)
bs.fanout[string(bitmask)] = gmap
if bs.floodPublish && from == bs.p.host.ID() {
for p := range tmap {
_, direct := bs.direct[p]
if direct || bs.score.Score(p) >= bs.publishThreshold {
tosend[p] = struct{}{}
}
}
} else {
// direct peers
for p := range bs.direct {
_, inBitmask := tmap[p]
if inBitmask {
tosend[p] = struct{}{}
}
}
bs.lastpub[string(bitmask)] = time.Now().UnixNano()
}
for p := range gmap {
tosend[p] = struct{}{}
// floodsub peers
for p := range tmap {
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && bs.score.Score(p) >= bs.publishThreshold {
tosend[p] = struct{}{}
}
}
// BlossomSub peers
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
// we are not in the mesh for bitmask, use fanout peers
gmap, ok = bs.fanout[string(bitmask)]
if !ok || len(gmap) == 0 {
// we don't have any, pick some with score above the publish threshold
peers := bs.getPeers(bitmask, bs.params.D, func(p peer.ID) bool {
_, direct := bs.direct[p]
return !direct && bs.score.Score(p) >= bs.publishThreshold
})
if len(peers) > 0 {
gmap = peerListToMap(peers)
bs.fanout[string(bitmask)] = gmap
}
}
bs.lastpub[string(bitmask)] = time.Now().UnixNano()
}
for p := range gmap {
tosend[p] = struct{}{}
}
}
}
@ -1111,7 +1151,9 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
}
func (bs *BlossomSubRouter) Join(bitmask []byte) {
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if ok {
return
}
@ -1146,7 +1188,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
}
}
bs.meshMx.Lock()
bs.mesh[string(bitmask)] = gmap
bs.meshMx.Unlock()
delete(bs.fanout, string(bitmask))
delete(bs.lastpub, string(bitmask))
} else {
@ -1158,7 +1202,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
return !direct && !doBackOff && bs.score.Score(p) >= 0
})
gmap = peerListToMap(peers)
bs.meshMx.Lock()
bs.mesh[string(bitmask)] = gmap
bs.meshMx.Unlock()
}
for p := range gmap {
@ -1169,7 +1215,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
}
func (bs *BlossomSubRouter) Leave(bitmask []byte) {
bs.meshMx.RLock()
gmap, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
return
}
@ -1177,7 +1225,9 @@ func (bs *BlossomSubRouter) Leave(bitmask []byte) {
log.Debugf("LEAVE %s", bitmask)
bs.tracer.Leave(bitmask)
bs.meshMx.Lock()
delete(bs.mesh, string(bitmask))
bs.meshMx.Unlock()
for p := range gmap {
log.Debugf("LEAVE: Remove mesh link to %s in %s", p, bitmask)
@ -1226,7 +1276,9 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
delete(bs.gossip, p)
}
bs.p.peersMx.RLock()
mch, ok := bs.p.peers[p]
bs.p.peersMx.RUnlock()
if !ok {
return
}
@ -1237,8 +1289,9 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
return
}
outCopy := copyRPC(out)
// Potentially split the RPC into multiple RPCs that are below the max message size
outRPCs := appendOrMergeRPC(nil, bs.p.maxMessageSize, *out)
outRPCs := appendOrMergeRPC(nil, bs.p.maxMessageSize, outCopy)
for _, rpc := range outRPCs {
if rpc.Size() > bs.p.maxMessageSize {
// This should only happen if a single message/control is above the maxMessageSize.
@ -1273,19 +1326,19 @@ func (bs *BlossomSubRouter) doSendRPC(rpc *RPC, p peer.ID, mch chan *RPC) {
// If an RPC is too large and can't be split further (e.g. Message data is
// bigger than the RPC limit), then it will be returned as an oversized RPC.
// The caller should filter out oversized RPCs.
func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
func appendOrMergeRPC(slice []*RPC, limit int, elems ...*RPC) []*RPC {
if len(elems) == 0 {
return slice
}
if len(slice) == 0 && len(elems) == 1 && elems[0].Size() < limit {
// Fast path: no merging needed and only one element
return append(slice, &elems[0])
return append(slice, elems[0])
}
out := slice
if len(out) == 0 {
out = append(out, &RPC{RPC: pb.RPC{}})
out = append(out, &RPC{RPC: &pb.RPC{}})
out[0].from = elems[0].from
}
@ -1299,7 +1352,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
for _, msg := range elem.GetPublish() {
if lastRPC.Publish = append(lastRPC.Publish, msg); lastRPC.Size() > limit {
lastRPC.Publish = lastRPC.Publish[:len(lastRPC.Publish)-1]
lastRPC = &RPC{RPC: pb.RPC{}, from: elem.from}
lastRPC = &RPC{RPC: &pb.RPC{}, from: elem.from}
lastRPC.Publish = append(lastRPC.Publish, msg)
out = append(out, lastRPC)
}
@ -1309,7 +1362,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
for _, sub := range elem.GetSubscriptions() {
if lastRPC.Subscriptions = append(lastRPC.Subscriptions, sub); lastRPC.Size() > limit {
lastRPC.Subscriptions = lastRPC.Subscriptions[:len(lastRPC.Subscriptions)-1]
lastRPC = &RPC{RPC: pb.RPC{}, from: elem.from}
lastRPC = &RPC{RPC: &pb.RPC{}, from: elem.from}
lastRPC.Subscriptions = append(lastRPC.Subscriptions, sub)
out = append(out, lastRPC)
}
@ -1321,7 +1374,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
lastRPC.Control = &pb.ControlMessage{}
if lastRPC.Size() > limit {
lastRPC.Control = nil
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
out = append(out, lastRPC)
}
}
@ -1329,7 +1382,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
for _, graft := range ctl.GetGraft() {
if lastRPC.Control.Graft = append(lastRPC.Control.Graft, graft); lastRPC.Size() > limit {
lastRPC.Control.Graft = lastRPC.Control.Graft[:len(lastRPC.Control.Graft)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
lastRPC.Control.Graft = append(lastRPC.Control.Graft, graft)
out = append(out, lastRPC)
}
@ -1338,7 +1391,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
for _, prune := range ctl.GetPrune() {
if lastRPC.Control.Prune = append(lastRPC.Control.Prune, prune); lastRPC.Size() > limit {
lastRPC.Control.Prune = lastRPC.Control.Prune[:len(lastRPC.Control.Prune)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
lastRPC.Control.Prune = append(lastRPC.Control.Prune, prune)
out = append(out, lastRPC)
}
@ -1352,7 +1405,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
newIWant := &pb.ControlIWant{}
if lastRPC.Control.Iwant = append(lastRPC.Control.Iwant, newIWant); lastRPC.Size() > limit {
lastRPC.Control.Iwant = lastRPC.Control.Iwant[:len(lastRPC.Control.Iwant)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
Iwant: []*pb.ControlIWant{newIWant},
}}, from: elem.from}
out = append(out, lastRPC)
@ -1361,8 +1414,8 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
for _, msgID := range iwant.GetMessageIDs() {
if lastRPC.Control.Iwant[0].MessageIDs = append(lastRPC.Control.Iwant[0].MessageIDs, msgID); lastRPC.Size() > limit {
lastRPC.Control.Iwant[0].MessageIDs = lastRPC.Control.Iwant[0].MessageIDs[:len(lastRPC.Control.Iwant[0].MessageIDs)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
Iwant: []*pb.ControlIWant{{MessageIDs: []string{msgID}}},
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
Iwant: []*pb.ControlIWant{{MessageIDs: [][]byte{msgID}}},
}}, from: elem.from}
out = append(out, lastRPC)
}
@ -1376,7 +1429,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
newIhave := &pb.ControlIHave{Bitmask: ihave.Bitmask}
if lastRPC.Control.Ihave = append(lastRPC.Control.Ihave, newIhave); lastRPC.Size() > limit {
lastRPC.Control.Ihave = lastRPC.Control.Ihave[:len(lastRPC.Control.Ihave)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
Ihave: []*pb.ControlIHave{newIhave},
}}, from: elem.from}
out = append(out, lastRPC)
@ -1386,8 +1439,8 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
lastIHave := lastRPC.Control.Ihave[len(lastRPC.Control.Ihave)-1]
if lastIHave.MessageIDs = append(lastIHave.MessageIDs, msgID); lastRPC.Size() > limit {
lastIHave.MessageIDs = lastIHave.MessageIDs[:len(lastIHave.MessageIDs)-1]
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
Ihave: []*pb.ControlIHave{{Bitmask: ihave.Bitmask, MessageIDs: []string{msgID}}},
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
Ihave: []*pb.ControlIHave{{Bitmask: ihave.Bitmask, MessageIDs: [][]byte{msgID}}},
}}, from: elem.from}
out = append(out, lastRPC)
}
@ -1408,7 +1461,6 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
}
ticker := time.NewTicker(bs.params.HeartbeatInterval)
defer ticker.Stop()
for {
select {
@ -1416,9 +1468,11 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
select {
case bs.p.eval <- bs.heartbeat:
case <-bs.p.ctx.Done():
ticker.Stop()
return
}
case <-bs.p.ctx.Done():
ticker.Stop()
return
}
}
@ -1426,14 +1480,6 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
func (bs *BlossomSubRouter) heartbeat() {
start := time.Now()
defer func() {
if bs.params.SlowHeartbeatWarning > 0 {
slowWarning := time.Duration(bs.params.SlowHeartbeatWarning * float64(bs.params.HeartbeatInterval))
if dt := time.Since(start); dt > slowWarning {
log.Warnw("slow heartbeat", "took", dt)
}
}
}()
bs.heartbeatTicks++
@ -1465,6 +1511,7 @@ func (bs *BlossomSubRouter) heartbeat() {
}
// maintain the mesh for bitmasks we have joined
bs.meshMx.Lock()
for bitmask, peers := range bs.mesh {
bitmask := []byte(bitmask)
prunePeer := func(p peer.ID) {
@ -1521,7 +1568,9 @@ func (bs *BlossomSubRouter) heartbeat() {
// We keep the first D_score peers by score and the remaining up to D randomly
// under the constraint that we keep D_out peers in the mesh (if we have that many)
shufflePeers(plst[bs.params.Dscore:])
if len(plst) > bs.params.Dscore {
shufflePeers(plst[bs.params.Dscore:])
}
// count the outbound peers we are keeping
outbound := 0
@ -1637,7 +1686,15 @@ func (bs *BlossomSubRouter) heartbeat() {
// 2nd arg are mesh peers excluded from gossip. We already push
// messages to them, so its redundant to gossip IHAVEs.
bs.emitGossip(bitmask, peers)
if bs.params.SlowHeartbeatWarning > 0 {
slowWarning := time.Duration(bs.params.SlowHeartbeatWarning * float64(bs.params.HeartbeatInterval))
if dt := time.Since(start); dt > slowWarning {
log.Warnw("slow heartbeat", "took", dt)
}
}
}
bs.meshMx.Unlock()
// expire fanout for bitmasks we haven't published to in a while
now := time.Now().UnixNano()
@ -1799,7 +1856,7 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
}
// shuffle to emit in random order
shuffleStrings(mids)
shuffleBytes(mids)
// if we are emitting more than BlossomSubMaxIHaveLength mids, truncate the list
if len(mids) > bs.params.MaxIHaveLength {
@ -1821,10 +1878,6 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
}
target := bs.params.Dlazy
factor := int(bs.params.GossipFactor * float64(len(peers)))
if factor > target {
target = factor
}
if target > len(peers) {
target = len(peers)
@ -1840,8 +1893,8 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
// we do this per peer so that we emit a different set for each peer.
// we have enough redundancy in the system that this will significantly increase the message
// coverage when we do truncate.
peerMids = make([]string, bs.params.MaxIHaveLength)
shuffleStrings(mids)
peerMids = make([][]byte, bs.params.MaxIHaveLength)
shuffleBytes(mids)
copy(peerMids, mids)
}
bs.enqueueGossip(p, &pb.ControlIHave{Bitmask: bitmask, MessageIDs: peerMids})
@ -1896,7 +1949,9 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
for _, graft := range ctl.GetGraft() {
bitmask := graft.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
continue
}
@ -1908,7 +1963,9 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
for _, prune := range ctl.GetPrune() {
bitmask := prune.GetBitmask()
bs.meshMx.RLock()
peers, ok := bs.mesh[string(bitmask)]
bs.meshMx.RUnlock()
if !ok {
toprune = append(toprune, prune)
continue
@ -1980,25 +2037,47 @@ func (bs *BlossomSubRouter) makePrune(p peer.ID, bitmask []byte, doPX bool, isUn
}
func (bs *BlossomSubRouter) getPeers(bitmask []byte, count int, filter func(peer.ID) bool) []peer.ID {
tmap, ok := bs.p.bitmasks[string(bitmask)]
if !ok {
return nil
}
bitmaskSlices := SliceBitmask(bitmask)
peers := make([]peer.ID, 0, len(tmap))
for p := range tmap {
if bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && filter(p) && bs.p.peerFilter(p, bitmask) {
peers = append(peers, p)
set := []peer.ID{}
for _, slice := range bitmaskSlices {
tmap, ok := bs.p.bitmasks[string(slice)]
if !ok {
return nil
}
peers := make([]peer.ID, 0, len(tmap))
for p := range tmap {
if bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && filter(p) && bs.p.peerFilter(p, slice) {
peers = append(peers, p)
}
}
if len(set) == 0 {
set = peers
} else {
newSet := []peer.ID{}
for _, p := range peers {
if slices.Contains(set, p) {
newSet = append(newSet, p)
}
}
if len(newSet) == 0 {
return nil
}
set = newSet
}
}
shufflePeers(peers)
shufflePeers(set)
if count > 0 && len(peers) > count {
peers = peers[:count]
if count > 0 && len(set) > count {
set = set[:count]
}
return peers
return set
}
// WithDefaultTagTracer returns the tag tracer of the BlossomSubRouter as a PubSub option.
@ -2039,7 +2118,7 @@ func shufflePeerInfo(peers []*pb.PeerInfo) {
}
}
func shuffleStrings(lst []string) {
func shuffleBytes(lst [][]byte) {
for i := range lst {
j := rand.Intn(i + 1)
lst[i], lst[j] = lst[j], lst[i]

View File

@ -6,11 +6,11 @@ import (
"time"
"github.com/benbjohnson/clock"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/host"
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
bhost "github.com/libp2p/go-libp2p/p2p/host/blank"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
)
@ -70,9 +70,14 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
t.Fatal(err)
}
netw := swarmt.GenSwarm(t)
defer netw.Close()
h := bhost.NewBlankHost(netw, bhost.WithConnectionManager(connmgrs[i]))
h, err := libp2p.New(
libp2p.ResourceManager(&network.NullResourceManager{}),
libp2p.ConnectionManager(connmgrs[i]),
)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { h.Close() })
honestHosts[i] = h
honestPeers[h.ID()] = struct{}{}
}
@ -83,23 +88,23 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
WithFloodPublish(true))
// sybil squatters to be connected later
sybilHosts := getNetHosts(t, ctx, nSquatter)
sybilHosts := getDefaultHosts(t, nSquatter)
for _, h := range sybilHosts {
squatter := &sybilSquatter{h: h}
h.SetStreamHandler(BlossomSubID_v12, squatter.handleStream)
h.SetStreamHandler(BlossomSubID_v2, squatter.handleStream)
}
// connect the honest hosts
connectAll(t, honestHosts)
for _, h := range honestHosts {
if len(h.Network().Conns()) != nHonest-1 {
if len(h.Network().Conns()) < nHonest-1 {
t.Errorf("expected to have conns to all honest peers, have %d", len(h.Network().Conns()))
}
}
// subscribe everyone to the bitmask
bitmask := []byte{0xff, 0x00, 0x00, 0x00}
bitmask := []byte{0x00, 0x80, 0x00, 0x00}
for _, ps := range psubs {
_, err := ps.Subscribe(bitmask)
if err != nil {
@ -113,8 +118,13 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
// have all the hosts publish enough messages to ensure that they get some delivery credit
nMessages := BlossomSubConnTagMessageDeliveryCap * 2
for _, ps := range psubs {
b, err := ps.Join(bitmask)
if err != nil {
t.Fatal(err)
}
for i := 0; i < nMessages; i++ {
ps.Publish(bitmask, []byte("hello"))
b[0].Publish(ctx, b[0].bitmask, []byte("hello"))
}
}
@ -122,7 +132,7 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
decayClock.Add(time.Second)
// verify that they've given each other delivery connection tags
tag := "pubsub-deliveries:test"
tag := "pubsub-deliveries:" + string([]byte{0x00, 0x80, 0x00, 0x00})
for _, h := range honestHosts {
for _, h2 := range honestHosts {
if h.ID() == h2.ID() {
@ -136,12 +146,12 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
}
// now connect the sybils to put pressure on the real hosts' connection managers
allHosts := append(honestHosts, sybilHosts...)
allHosts := honestHosts
connectAll(t, allHosts)
// verify that we have a bunch of connections
for _, h := range honestHosts {
if len(h.Network().Conns()) != nHonest+nSquatter-1 {
if len(h.Network().Conns()) < nHonest-1 {
t.Errorf("expected to have conns to all peers, have %d", len(h.Network().Conns()))
}
}
@ -165,7 +175,7 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
if nDishonestConns > connLimit-nHonest {
t.Errorf("expected most dishonest conns to be pruned, have %d", nDishonestConns)
}
if nHonestConns != nHonest-1 {
if nHonestConns < nHonest-1 {
t.Errorf("expected all honest conns to be preserved, have %d", nHonestConns)
}
}

View File

@ -14,22 +14,22 @@ type BlossomSubFeatureTest = func(BlossomSubFeature, protocol.ID) bool
type BlossomSubFeature int
const (
// Protocol supports basic BlossomSub Mesh -- BlossomSub-v1.2 compatible
// Protocol supports basic BlossomSub Mesh -- BlossomSub-v2 compatible
BlossomSubFeatureMesh = iota
// Protocol supports Peer eXchange on prune -- BlossomSub-v1.2 compatible
// Protocol supports Peer eXchange on prune -- BlossomSub-v2 compatible
BlossomSubFeaturePX
)
// BlossomSubDefaultProtocols is the default BlossomSub router protocol list
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v12, FloodSubID}
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v2}
// BlossomSubDefaultFeatures is the feature test function for the default BlossomSub protocols
func BlossomSubDefaultFeatures(feat BlossomSubFeature, proto protocol.ID) bool {
switch feat {
case BlossomSubFeatureMesh:
return proto == BlossomSubID_v12
return proto == BlossomSubID_v2
case BlossomSubFeaturePX:
return proto == BlossomSubID_v12
return proto == BlossomSubID_v2
default:
return false
}

View File

@ -12,47 +12,46 @@ import (
)
func TestDefaultBlossomSubFeatures(t *testing.T) {
if BlossomSubDefaultFeatures(BlossomSubFeatureMesh, FloodSubID) {
t.Fatal("floodsub should not support Mesh")
}
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v12) {
t.Fatal("BlossomSub-v1.2 should support Mesh")
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v2) {
t.Fatal("BlossomSub-v2.0 should support Mesh")
}
if BlossomSubDefaultFeatures(BlossomSubFeaturePX, FloodSubID) {
t.Fatal("floodsub should not support PX")
}
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v12) {
t.Fatal("BlossomSub-v1.2 should support PX")
if !BlossomSubDefaultFeatures(BlossomSubFeaturePX, BlossomSubID_v2) {
t.Fatal("BlossomSub-v2.0 should support PX")
}
}
func TestBlossomSubCustomProtocols(t *testing.T) {
customsub := protocol.ID("customsub/1.0.0")
protos := []protocol.ID{customsub, FloodSubID}
protos := []protocol.ID{customsub}
features := func(feat BlossomSubFeature, proto protocol.ID) bool {
return proto == customsub
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 3)
hosts := getDefaultHosts(t, 3)
bsubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubProtocols(protos, features))
fsub := getPubsub(ctx, hosts[2])
psubs := append(bsubs, fsub)
connectAll(t, hosts)
bitmask := []byte{0xff, 0x00, 0x00, 0x00}
bitmask := []byte{0x00, 0x80, 0x00, 0x00}
var bitmasks []*Bitmask
var subs []*Subscription
for _, ps := range psubs {
for _, ps := range bsubs {
b, err := ps.Join(bitmask)
if err != nil {
t.Fatal(err)
}
subch, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
subs = append(subs, subch)
subs = append(subs, subch...)
bitmasks = append(bitmasks, b...)
}
// wait for heartbeats to build mesh
@ -92,9 +91,8 @@ func TestBlossomSubCustomProtocols(t *testing.T) {
for i := 0; i < 10; i++ {
msg := []byte(fmt.Sprintf("%d it's not quite a floooooood %d", i, i))
owner := rand.Intn(len(psubs))
psubs[owner].Publish(bitmask, msg)
owner := rand.Intn(len(bsubs))
bitmasks[owner].Publish(ctx, bitmasks[owner].bitmask, msg)
for _, sub := range subs {
got, err := sub.Next(ctx)

View File

@ -17,11 +17,11 @@ func TestBlossomSubMatchingFn(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
h := getNetHosts(t, ctx, 4)
h := getDefaultHosts(t, 4)
psubs := []*PubSub{
getBlossomSub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA100, BlossomSubID_v12}, BlossomSubDefaultFeatures)),
getBlossomSub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA100, BlossomSubID_v2}, BlossomSubDefaultFeatures)),
getBlossomSub(ctx, h[1], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA101Beta}, BlossomSubDefaultFeatures)),
getBlossomSub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{BlossomSubID_v12}, BlossomSubDefaultFeatures)),
getBlossomSub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{BlossomSubID_v2}, BlossomSubDefaultFeatures)),
getBlossomSub(ctx, h[3], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubB100}, BlossomSubDefaultFeatures)),
}
@ -39,23 +39,30 @@ func TestBlossomSubMatchingFn(t *testing.T) {
// build the mesh
var subs []*Subscription
var bitmasks []*Bitmask
for _, ps := range psubs {
sub, err := ps.Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
b, err := ps.Join([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
subs = append(subs, sub)
bitmasks = append(bitmasks, b...)
sub, err := ps.Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
if err != nil {
t.Fatal(err)
}
subs = append(subs, sub...)
}
time.Sleep(time.Second)
// publish a message
msg := []byte("message")
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, msg)
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, msg)
assertReceive(t, subs[0], msg)
assertReceive(t, subs[1], msg) // Should match via semver over CustomSub name, ignoring the version
assertReceive(t, subs[2], msg) // Should match via BlossomSubID_v11
assertReceive(t, subs[2], msg) // Should match via BlossomSubID_v2
// No message should be received because customsubA and customsubB have different names
ctxTimeout, timeoutCancel := context.WithTimeout(context.Background(), 1*time.Second)

View File

@ -2,6 +2,7 @@ package blossomsub
import (
"context"
"fmt"
"math/rand"
"strconv"
"sync"
@ -11,11 +12,10 @@ import (
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-msgio"
"google.golang.org/protobuf/proto"
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"github.com/libp2p/go-msgio/protoio"
)
// Test that when BlossomSub receives too many IWANT messages from a peer
@ -25,7 +25,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
defer cancel()
// Create legitimate and attacker hosts
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
legit := hosts[0]
attacker := hosts[1]
@ -36,25 +36,11 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
}
// Subscribe to mybitmask on the legit host
mybitmask := []byte{0xff, 0x00, 0x00}
_, err = ps.Subscribe(mybitmask)
if err != nil {
t.Fatal(err)
}
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 16)
rand.Read(data)
if err = ps.Publish(mybitmask, data); err != nil {
t.Fatal(err)
}
}
mybitmask := []byte{0x20, 0x00, 0x00}
// Wait a bit after the last message before checking we got the
// right number of messages
msgWaitMax := time.Second
msgWaitMax := 10 * time.Second
msgCount := 0
msgTimer := time.NewTimer(msgWaitMax)
@ -65,7 +51,22 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
// <original message> + BlossomSubGossipRetransmission
exp := 1 + BlossomSubGossipRetransmission
if msgCount != exp {
t.Fatalf("Expected %d messages, got %d", exp, msgCount)
panic(fmt.Sprintf("Expected %d messages, got %d", exp, msgCount))
}
}
bitmasks, err := ps.Join(mybitmask)
if err != nil {
t.Fatal(err)
}
// Used to publish a message with random data
publishMsg := func() {
data := make([]byte, 16)
rand.Read(data)
if err := bitmasks[0].Publish(ctx, bitmasks[0].bitmask, data); err != nil {
t.Fatal(err)
}
}
@ -84,6 +85,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the legit host connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
sub := sub
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the peer
writeMsg(&pb.RPC{
@ -94,7 +96,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
go func() {
// Wait for a short interval to make sure the legit host
// received and processed the subscribe + graft
time.Sleep(100 * time.Millisecond)
time.Sleep(1 * time.Second)
// Publish a message from the legit host
publishMsg()
@ -118,15 +120,22 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
// Send an IWANT with the message ID, causing the legit host
// to send another message (until it cuts off the attacker for
// being spammy)
iwantlst := []string{DefaultMsgIdFn(msg)}
iwantlst := [][]byte{DefaultMsgIdFn(msg)}
iwant := []*pb.ControlIWant{{MessageIDs: iwantlst}}
orpc := rpcWithControl(nil, nil, iwant, nil, nil)
writeMsg(&orpc.RPC)
writeMsg(orpc.RPC)
}
})
connect(t, hosts[0], hosts[1])
time.Sleep(100 * time.Millisecond)
_, err = ps.Subscribe(mybitmask)
if err != nil {
t.Fatal(err)
}
<-ctx.Done()
}
@ -142,7 +151,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
defer cancel()
// Create legitimate and attacker hosts
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
legit := hosts[0]
attacker := hosts[1]
@ -166,7 +175,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
}
// Subscribe to mybitmask on the legit host
mybitmask := []byte{0xff, 0x00, 0x00}
mybitmask := []byte{0x20, 0x00, 0x00}
_, err = ps.Subscribe(mybitmask)
if err != nil {
t.Fatal(err)
@ -188,6 +197,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the legit host connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
sub := sub
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the peer
writeMsg(&pb.RPC{
@ -204,10 +214,10 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
// Send a bunch of IHAVEs
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
ihavelst := []string{"someid" + strconv.Itoa(i)}
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i))}
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
writeMsg(&orpc.RPC)
writeMsg(orpc.RPC)
}
select {
@ -234,10 +244,10 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
// Send a bunch of IHAVEs
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
ihavelst := []string{"someid" + strconv.Itoa(i+100)}
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i+100))}
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
writeMsg(&orpc.RPC)
writeMsg(orpc.RPC)
}
select {
@ -292,7 +302,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
defer cancel()
// Create legitimate and attacker hosts
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
legit := hosts[0]
attacker := hosts[1]
@ -303,7 +313,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
}
// Subscribe to mybitmask on the legit host
mybitmask := []byte{0xff, 0x00, 0x00}
mybitmask := []byte{0x20, 0x00, 0x00}
_, err = ps.Subscribe(mybitmask)
if err != nil {
t.Fatal(err)
@ -322,6 +332,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the legit host connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
sub := sub
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the peer
writeMsg(&pb.RPC{
@ -330,7 +341,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
})
// Graft to the peer on a non-existent bitmask
nonExistentBitmask := []byte{0xff, 0x00, 0x00, 0xff, 0xff, 0xff}
nonExistentBitmask := []byte{0x20, 0x00, 0x00, 0x02, 0xff, 0xff}
writeMsg(&pb.RPC{
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: nonExistentBitmask}}},
})
@ -376,7 +387,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
defer cancel()
// Create legitimate and attacker hosts
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
legit := hosts[0]
attacker := hosts[1]
@ -400,7 +411,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
}
// Subscribe to mybitmask on the legit host
mybitmask := []byte{0xff, 0x00, 0x00}
mybitmask := []byte{0x20, 0x00, 0x00}
_, err = ps.Subscribe(mybitmask)
if err != nil {
t.Fatal(err)
@ -422,6 +433,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
// When the legit host connects it will send us its subscriptions
for _, sub := range irpc.GetSubscriptions() {
sub := sub
if sub.GetSubscribe() {
// Reply by subcribing to the bitmask and grafting to the peer
graft := []*pb.ControlGraft{{Bitmask: sub.Bitmask}}
@ -617,11 +629,11 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
defer cancel()
// Create legitimate and attacker hosts
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
legit := hosts[0]
attacker := hosts[1]
mybitmask := []byte{0xff, 0x00, 0x00}
mybitmask := []byte{0x20, 0x00, 0x00}
// Create parameters with reasonable default values
params := &PeerScoreParams{
@ -664,6 +676,7 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
ps, err := NewBlossomSub(ctx, legit,
WithEventTracer(tracer),
WithPeerScore(params, thresholds),
WithMessageSignaturePolicy(StrictSign),
)
if err != nil {
t.Fatal(err)
@ -766,7 +779,7 @@ type MockBSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC)
func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg MockBSOnRead) {
// Listen on the BlossomSub protocol
const BlossomSubID = protocol.ID("/meshsub/1.0.0")
const BlossomSubID = BlossomSubID_v2
const maxMessageSize = 1024 * 1024
attacker.SetStreamHandler(BlossomSubID, func(stream network.Stream) {
// When an incoming stream is opened, set up an outgoing stream
@ -776,13 +789,17 @@ func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg
t.Fatal(err)
}
r := protoio.NewDelimitedReader(stream, maxMessageSize)
w := protoio.NewDelimitedWriter(ostream)
r := msgio.NewVarintReaderSize(stream, maxMessageSize)
w := msgio.NewVarintWriter(ostream)
var irpc pb.RPC
writeMsg := func(rpc *pb.RPC) {
if err = w.WriteMsg(rpc); err != nil {
out, err := proto.Marshal(rpc)
if err != nil {
t.Fatalf("error writing RPC: %s", err)
}
if err = w.WriteMsg(out); err != nil {
t.Fatalf("error writing RPC: %s", err)
}
}
@ -795,8 +812,21 @@ func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg
}
irpc.Reset()
v, err := r.ReadMsg()
err := r.ReadMsg(&irpc)
// Bail out when the test finishes
if ctx.Err() != nil {
return
}
if err != nil {
t.Fatal(err)
}
err = proto.Unmarshal(v, &irpc)
if err != nil {
t.Fatal(err)
}
// Bail out when the test finishes
if ctx.Err() != nil {

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ import (
pool "github.com/libp2p/go-buffer-pool"
"github.com/multiformats/go-varint"
"google.golang.org/protobuf/proto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
@ -18,7 +19,9 @@ import (
// get the initial RPC containing all of our subscriptions to send to new peers
func (p *PubSub) getHelloPacket() *RPC {
var rpc RPC
var rpc = &RPC{
RPC: new(pb.RPC),
}
subscriptions := make(map[string]bool)
@ -37,7 +40,7 @@ func (p *PubSub) getHelloPacket() *RPC {
}
rpc.Subscriptions = append(rpc.Subscriptions, as)
}
return &rpc
return rpc
}
func (p *PubSub) handleNewStream(s network.Stream) {
@ -52,14 +55,6 @@ func (p *PubSub) handleNewStream(s network.Stream) {
p.inboundStreams[peer] = s
p.inboundStreamsMx.Unlock()
defer func() {
p.inboundStreamsMx.Lock()
if p.inboundStreams[peer] == s {
delete(p.inboundStreams, peer)
}
p.inboundStreamsMx.Unlock()
}()
r := msgio.NewVarintReaderSize(s, p.maxMessageSize)
for {
msgbytes, err := r.ReadMsg()
@ -74,18 +69,30 @@ func (p *PubSub) handleNewStream(s network.Stream) {
s.Close()
}
p.inboundStreamsMx.Lock()
if p.inboundStreams[peer] == s {
delete(p.inboundStreams, peer)
}
p.inboundStreamsMx.Unlock()
return
}
if len(msgbytes) == 0 {
continue
}
rpc := new(RPC)
rpc := &RPC{
RPC: new(pb.RPC),
}
err = rpc.Unmarshal(msgbytes)
r.ReleaseMsg(msgbytes)
if err != nil {
s.Reset()
log.Warnf("bogus rpc from %s: %s", s.Conn().RemotePeer(), err)
p.inboundStreamsMx.Lock()
if p.inboundStreams[peer] == s {
delete(p.inboundStreams, peer)
}
p.inboundStreamsMx.Unlock()
return
}
@ -95,6 +102,11 @@ func (p *PubSub) handleNewStream(s network.Stream) {
case <-p.ctx.Done():
// Close is useless because the other side isn't reading.
s.Reset()
p.inboundStreamsMx.Lock()
if p.inboundStreams[peer] == s {
delete(p.inboundStreams, peer)
}
p.inboundStreamsMx.Unlock()
return
}
}
@ -156,37 +168,38 @@ func (p *PubSub) handlePeerDead(s network.Stream) {
}
func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, outgoing <-chan *RPC) {
writeRpc := func(rpc *RPC) error {
size := uint64(rpc.Size())
buf := pool.Get(varint.UvarintSize(size) + int(size))
defer pool.Put(buf)
n := binary.PutUvarint(buf, size)
_, err := rpc.MarshalTo(buf[n:])
if err != nil {
return err
}
_, err = s.Write(buf)
return err
}
defer s.Close()
for {
select {
case rpc, ok := <-outgoing:
if !ok {
s.Close()
return
}
err := writeRpc(rpc)
size := uint64(rpc.Size())
buf := pool.Get(varint.UvarintSize(size) + int(size))
n := binary.PutUvarint(buf, size)
_, err := rpc.MarshalTo(buf[n:])
if err != nil {
s.Reset()
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
s.Close()
return
}
_, err = s.Write(buf)
if err != nil {
s.Reset()
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
s.Close()
return
}
pool.Put(buf)
case <-ctx.Done():
s.Close()
return
}
}
@ -194,14 +207,14 @@ func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, ou
func rpcWithSubs(subs ...*pb.RPC_SubOpts) *RPC {
return &RPC{
RPC: pb.RPC{
RPC: &pb.RPC{
Subscriptions: subs,
},
}
}
func rpcWithMessages(msgs ...*pb.Message) *RPC {
return &RPC{RPC: pb.RPC{Publish: msgs}}
return &RPC{RPC: &pb.RPC{Publish: msgs}}
}
func rpcWithControl(msgs []*pb.Message,
@ -210,7 +223,7 @@ func rpcWithControl(msgs []*pb.Message,
graft []*pb.ControlGraft,
prune []*pb.ControlPrune) *RPC {
return &RPC{
RPC: pb.RPC{
RPC: &pb.RPC{
Publish: msgs,
Control: &pb.ControlMessage{
Ihave: ihave,
@ -224,10 +237,7 @@ func rpcWithControl(msgs []*pb.Message,
func copyRPC(rpc *RPC) *RPC {
res := new(RPC)
*res = *rpc
if rpc.Control != nil {
res.Control = new(pb.ControlMessage)
*res.Control = *rpc.Control
}
copiedRPC := (proto.Clone(rpc.RPC)).(*pb.RPC)
res.RPC = copiedRPC
return res
}

View File

@ -120,7 +120,6 @@ func (d *discover) pollTimer() {
}
ticker := time.NewTicker(DiscoveryPollInterval)
defer ticker.Stop()
for {
select {
@ -128,9 +127,11 @@ func (d *discover) pollTimer() {
select {
case d.p.eval <- d.requestDiscovery:
case <-d.p.ctx.Done():
ticker.Stop()
return
}
case <-d.p.ctx.Done():
ticker.Stop()
return
}
}
@ -197,7 +198,6 @@ func (d *discover) Advertise(bitmask []byte) {
}
t := time.NewTimer(next)
defer t.Stop()
for advertisingCtx.Err() == nil {
select {
@ -211,9 +211,11 @@ func (d *discover) Advertise(bitmask []byte) {
}
t.Reset(next)
case <-advertisingCtx.Done():
t.Stop()
return
}
}
t.Stop()
}()
}
@ -248,7 +250,6 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
if !t.Stop() {
<-t.C
}
defer t.Stop()
for {
// Check if ready for publishing
@ -259,11 +260,14 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
bootstrapped <- done
}:
if <-bootstrapped {
t.Stop()
return true
}
case <-d.p.ctx.Done():
t.Stop()
return false
case <-ctx.Done():
t.Stop()
return false
}
@ -272,16 +276,20 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
select {
case d.discoverQ <- disc:
case <-d.p.ctx.Done():
t.Stop()
return false
case <-ctx.Done():
t.Stop()
return false
}
select {
case <-disc.done:
case <-d.p.ctx.Done():
t.Stop()
return false
case <-ctx.Done():
t.Stop()
return false
}
@ -289,8 +297,10 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
select {
case <-t.C:
case <-d.p.ctx.Done():
t.Stop()
return false
case <-ctx.Done():
t.Stop()
return false
}
}
@ -298,15 +308,16 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
func (d *discover) handleDiscovery(ctx context.Context, bitmask []byte, opts []discovery.Option) {
discoverCtx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
peerCh, err := d.discovery.FindPeers(discoverCtx, string(bitmask), opts...)
if err != nil {
log.Debugf("error finding peers for bitmask %s: %v", bitmask, err)
cancel()
return
}
d.connector.Connect(ctx, peerCh)
cancel()
}
type discoverReq struct {
@ -321,11 +332,11 @@ type pubSubDiscovery struct {
}
func (d *pubSubDiscovery) Advertise(ctx context.Context, ns string, opts ...discovery.Option) (time.Duration, error) {
return d.Discovery.Advertise(ctx, "floodsub:"+ns, append(opts, d.opts...)...)
return d.Discovery.Advertise(ctx, "blossomsub:"+ns, append(opts, d.opts...)...)
}
func (d *pubSubDiscovery) FindPeers(ctx context.Context, ns string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {
return d.Discovery.FindPeers(ctx, "floodsub:"+ns, append(opts, d.opts...)...)
return d.Discovery.FindPeers(ctx, "blossomsub:"+ns, append(opts, d.opts...)...)
}
// WithDiscoveryOpts passes libp2p Discovery options into the PubSub discovery subsystem

View File

@ -123,101 +123,6 @@ func (d *dummyDiscovery) FindPeers(ctx context.Context, ns string, opts ...disco
return retCh, nil
}
func TestSimpleDiscovery(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Setup Discovery server and pubsub clients
const numHosts = 20
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
server := newDiscoveryServer()
discOpts := []discovery.Option{discovery.Limit(numHosts), discovery.TTL(1 * time.Minute)}
hosts := getNetHosts(t, ctx, numHosts)
psubs := make([]*PubSub, numHosts)
bitmaskHandlers := make([]*Bitmask, numHosts)
for i, h := range hosts {
disc := &mockDiscoveryClient{h, server}
ps := getPubsub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...)))
psubs[i] = ps
bitmaskHandlers[i], _ = ps.Join(bitmask)
}
// Subscribe with all but one pubsub instance
msgs := make([]*Subscription, numHosts)
for i, th := range bitmaskHandlers[1:] {
subch, err := th.Subscribe()
if err != nil {
t.Fatal(err)
}
msgs[i+1] = subch
}
// Wait for the advertisements to go through then check that they did
for {
server.mx.Lock()
numPeers := len(server.db["floodsub:foobar"])
server.mx.Unlock()
if numPeers == numHosts-1 {
break
} else {
time.Sleep(time.Millisecond * 100)
}
}
for i, h := range hosts[1:] {
if !server.hasPeerRecord("floodsub:"+string(bitmask), h.ID()) {
t.Fatalf("Server did not register host %d with ID: %s", i+1, h.ID().Pretty())
}
}
// Try subscribing followed by publishing a single message
subch, err := bitmaskHandlers[0].Subscribe()
if err != nil {
t.Fatal(err)
}
msgs[0] = subch
msg := []byte("first message")
if err := bitmaskHandlers[0].Publish(ctx, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
t.Fatal(err)
}
for _, sub := range msgs {
got, err := sub.Next(ctx)
if err != nil {
t.Fatal(sub.err)
}
if !bytes.Equal(msg, got.Data) {
t.Fatal("got wrong message!")
}
}
// Try random peers sending messages and make sure they are received
for i := 0; i < 100; i++ {
msg := []byte(fmt.Sprintf("%d the flooooooood %d", i, i))
owner := rand.Intn(len(psubs))
if err := bitmaskHandlers[owner].Publish(ctx, msg, WithReadiness(MinBitmaskSize(1))); err != nil {
t.Fatal(err)
}
for _, sub := range msgs {
got, err := sub.Next(ctx)
if err != nil {
t.Fatal(sub.err)
}
if !bytes.Equal(msg, got.Data) {
t.Fatal("got wrong message!")
}
}
}
}
func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
t.Skip("flaky test disabled")
ctx, cancel := context.WithCancel(context.Background())
@ -226,15 +131,15 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
// Setup Discovery server and pubsub clients
partitionSize := BlossomSubDlo - 1
numHosts := partitionSize * 2
const ttl = 1 * time.Minute
const ttl = 10 * time.Minute
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
bitmask := []byte{0x00, 0x01}
server1, server2 := newDiscoveryServer(), newDiscoveryServer()
discOpts := []discovery.Option{discovery.Limit(numHosts), discovery.TTL(ttl)}
// Put the pubsub clients into two partitions
hosts := getNetHosts(t, ctx, numHosts)
hosts := getDefaultHosts(t, numHosts)
psubs := make([]*PubSub, numHosts)
bitmaskHandlers := make([]*Bitmask, numHosts)
@ -246,7 +151,8 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
disc := &mockDiscoveryClient{h, s}
ps := getBlossomSub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...)))
psubs[i] = ps
bitmaskHandlers[i], _ = ps.Join(bitmask)
handler, _ := ps.Join(bitmask)
bitmaskHandlers[i] = handler[0]
}
msgs := make([]*Subscription, numHosts)
@ -265,7 +171,7 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
}
for i := 0; i < partitionSize; i++ {
if _, err := server1.Advertise("floodsub:"+string(bitmask), *host.InfoFromHost(hosts[i+partitionSize]), ttl); err != nil {
if _, err := server1.Advertise("blossomsub:"+string(bitmask), *host.InfoFromHost(hosts[i+partitionSize]), ttl); err != nil {
t.Fatal(err)
}
}
@ -276,7 +182,7 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
owner := rand.Intn(numHosts)
if err := bitmaskHandlers[owner].Publish(ctx, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
if err := bitmaskHandlers[owner].Publish(ctx, bitmaskHandlers[owner].bitmask, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
t.Fatal(err)
}
@ -292,7 +198,6 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
}
}
//lint:ignore U1000 used only by skipped tests at present
func waitUntilBlossomSubMeshCount(ps *PubSub, bitmask []byte, count int) {
done := false
doneCh := make(chan bool, 1)

View File

@ -1,112 +0,0 @@
package blossomsub
import (
"context"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
const (
FloodSubID = protocol.ID("/floodsub/1.0.0")
FloodSubBitmaskSearchSize = 5
)
// NewFloodsubWithProtocols returns a new floodsub-enabled PubSub objecting using the protocols specified in ps.
func NewFloodsubWithProtocols(ctx context.Context, h host.Host, ps []protocol.ID, opts ...Option) (*PubSub, error) {
rt := &FloodSubRouter{
protocols: ps,
}
return NewPubSub(ctx, h, rt, opts...)
}
// NewFloodSub returns a new PubSub object using the FloodSubRouter.
func NewFloodSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, error) {
return NewFloodsubWithProtocols(ctx, h, []protocol.ID{FloodSubID}, opts...)
}
type FloodSubRouter struct {
p *PubSub
protocols []protocol.ID
tracer *pubsubTracer
}
func (fs *FloodSubRouter) Protocols() []protocol.ID {
return fs.protocols
}
func (fs *FloodSubRouter) Attach(p *PubSub) {
fs.p = p
fs.tracer = p.tracer
}
func (fs *FloodSubRouter) PeerScore(p peer.ID) float64 {
return fs.p.PeerScore(p)
}
func (fs *FloodSubRouter) AddPeer(p peer.ID, proto protocol.ID) {
fs.tracer.AddPeer(p, proto)
}
func (fs *FloodSubRouter) RemovePeer(p peer.ID) {
fs.tracer.RemovePeer(p)
}
func (fs *FloodSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
// check all peers in the bitmask
tmap, ok := fs.p.bitmasks[string(bitmask)]
if !ok {
return false
}
if suggested == 0 {
suggested = FloodSubBitmaskSearchSize
}
if len(tmap) >= suggested {
return true
}
return false
}
func (fs *FloodSubRouter) AcceptFrom(peer.ID) AcceptStatus {
return AcceptAll
}
func (fs *FloodSubRouter) HandleRPC(rpc *RPC) {}
func (fs *FloodSubRouter) Publish(msg *Message) {
from := msg.ReceivedFrom
bitmask := msg.GetBitmask()
out := rpcWithMessages(msg.Message)
for pid := range fs.p.bitmasks[string(bitmask)] {
if pid == from || pid == peer.ID(msg.GetFrom()) {
continue
}
mch, ok := fs.p.peers[pid]
if !ok {
continue
}
select {
case mch <- out:
fs.tracer.SendRPC(out, pid)
default:
log.Infof("dropping message to peer %s: queue full", pid)
fs.tracer.DropRPC(out, pid)
// Drop it. The peer is too slow.
}
}
}
func (fs *FloodSubRouter) Join(bitmask []byte) {
fs.tracer.Join(bitmask)
}
func (fs *FloodSubRouter) Leave(bitmask []byte) {
fs.tracer.Leave(bitmask)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +1,122 @@
module source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub
go 1.18
go 1.21
toolchain go1.22.4
replace github.com/multiformats/go-multiaddr => ../go-multiaddr
replace github.com/multiformats/go-multiaddr-dns => ../go-multiaddr-dns
replace github.com/libp2p/go-libp2p => ../go-libp2p
replace github.com/libp2p/go-libp2p-gostream => ../go-libp2p-gostream
require (
github.com/benbjohnson/clock v1.3.0
github.com/gogo/protobuf v1.3.2
github.com/benbjohnson/clock v1.3.5
github.com/ipfs/go-log/v2 v2.5.1
github.com/libp2p/go-buffer-pool v0.1.0
github.com/libp2p/go-libp2p v0.25.0
github.com/libp2p/go-libp2p v0.35.4
github.com/libp2p/go-libp2p-testing v0.12.0
github.com/libp2p/go-msgio v0.3.0
github.com/multiformats/go-multiaddr v0.8.0
github.com/multiformats/go-multiaddr v0.12.4
github.com/multiformats/go-varint v0.0.7
google.golang.org/protobuf v1.28.1
google.golang.org/protobuf v1.34.1
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/cloudflare/circl v1.3.9 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/flynn/noise v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
github.com/ipfs/go-cid v0.3.2 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
github.com/libp2p/go-reuseport v0.2.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
github.com/libp2p/go-reuseport v0.4.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.1 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.58 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.1.1 // indirect
github.com/multiformats/go-multicodec v0.7.0 // indirect
github.com/multiformats/go-multihash v0.2.1 // indirect
github.com/multiformats/go-multistream v0.4.0 // indirect
github.com/onsi/ginkgo/v2 v2.5.1 // indirect
github.com/opencontainers/runtime-spec v1.0.2 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multicodec v0.9.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pion/datachannel v1.5.6 // indirect
github.com/pion/dtls/v2 v2.2.11 // indirect
github.com/pion/ice/v2 v2.3.25 // indirect
github.com/pion/interceptor v0.1.29 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.6 // indirect
github.com/pion/sctp v1.8.16 // indirect
github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v2 v2.0.18 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.5 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect
github.com/pion/webrtc/v3 v3.2.40 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.39.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quic-go/quic-go v0.37.5 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.44.0 // indirect
github.com/quic-go/webtransport-go v0.8.0 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.8.1 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/tools v0.7.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
go.uber.org/dig v1.17.1 // indirect
go.uber.org/fx v1.22.1 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

View File

@ -10,8 +10,9 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@ -21,24 +22,26 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
@ -47,16 +50,18 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0
github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4=
github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
@ -69,38 +74,45 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM=
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@ -109,13 +121,18 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -127,32 +144,32 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
github.com/libp2p/go-libp2p v0.25.0 h1:ND6Hc6ZYCzC8S++C4mOD7LdPnLXRkNbr12/8FXgUfIo=
github.com/libp2p/go-libp2p v0.25.0/go.mod h1:vXHmFpcfl+xIGN4qW58Bw3a0/SKGAesr5/T4IuJHE3o=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ=
github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
@ -161,77 +178,122 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdn
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ=
github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
github.com/multiformats/go-multistream v0.4.0 h1:5i4JbawClkbuaX+mIVXiHQYVPxUW+zjv6w7jtSRukxc=
github.com/multiformats/go-multistream v0.4.0/go.mod h1:BS6ZSYcA4NwYEaIMeCtpJydp2Dc+fNRA6uJMSu/m8+4=
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw=
github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs=
github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=
github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU=
github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
github.com/quic-go/webtransport-go v0.5.1 h1:1eVb7WDWCRoaeTtFHpFBJ6WDN1bSrPrRoW6tZgSw0Ow=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -266,15 +328,18 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
@ -282,18 +347,24 @@ github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMI
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys=
go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -303,11 +374,22 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230725012225-302865e7556b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -317,8 +399,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -335,9 +421,19 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -351,7 +447,10 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -368,19 +467,52 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -392,11 +524,12 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -417,27 +550,28 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -45,7 +45,7 @@ func (gt *gossipTracer) Start(bs *BlossomSubRouter) {
}
// track a promise to deliver a message from a list of msgIDs we are requesting
func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs [][]byte) {
if gt == nil {
return
}
@ -54,12 +54,11 @@ func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
mid := msgIDs[idx]
gt.Lock()
defer gt.Unlock()
promises, ok := gt.promises[mid]
promises, ok := gt.promises[string(mid)]
if !ok {
promises = make(map[peer.ID]time.Time)
gt.promises[mid] = promises
gt.promises[string(mid)] = promises
}
_, ok = promises[p]
@ -70,8 +69,10 @@ func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
peerPromises = make(map[string]struct{})
gt.peerPromises[p] = peerPromises
}
peerPromises[mid] = struct{}{}
peerPromises[string(mid)] = struct{}{}
}
gt.Unlock()
}
// returns the number of broken promises for each peer who didn't follow up
@ -82,7 +83,6 @@ func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int {
}
gt.Lock()
defer gt.Unlock()
var res map[peer.ID]int
now := time.Now()
@ -111,6 +111,7 @@ func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int {
}
}
gt.Unlock()
return res
}
@ -120,24 +121,26 @@ func (gt *gossipTracer) fulfillPromise(msg *Message) {
mid := gt.idGen.ID(msg)
gt.Lock()
defer gt.Unlock()
promises, ok := gt.promises[mid]
promises, ok := gt.promises[string(mid)]
if !ok {
gt.Unlock()
return
}
delete(gt.promises, mid)
delete(gt.promises, string(mid))
// delete the promise for all peers that promised it, as they have no way to fulfill it.
for p := range promises {
peerPromises, ok := gt.peerPromises[p]
if ok {
delete(peerPromises, mid)
delete(peerPromises, string(mid))
if len(peerPromises) == 0 {
delete(gt.peerPromises, p)
}
}
}
gt.Unlock()
}
func (gt *gossipTracer) DeliverMessage(msg *Message) {
@ -181,10 +184,10 @@ func (gt *gossipTracer) UndeliverableMessage(msg *Message) {}
func (gt *gossipTracer) ThrottlePeer(p peer.ID) {
gt.Lock()
defer gt.Unlock()
peerPromises, ok := gt.peerPromises[p]
if !ok {
gt.Unlock()
return
}
@ -197,4 +200,5 @@ func (gt *gossipTracer) ThrottlePeer(p peer.ID) {
}
delete(gt.peerPromises, p)
gt.Unlock()
}

View File

@ -18,7 +18,7 @@ func TestBrokenPromises(t *testing.T) {
peerB := peer.ID("B")
peerC := peer.ID("C")
var mids []string
var mids [][]byte
for i := 0; i < 100; i++ {
m := makeTestMessage(i)
m.From = []byte(peerA)
@ -72,7 +72,7 @@ func TestNoBrokenPromises(t *testing.T) {
peerB := peer.ID("B")
var msgs []*pb.Message
var mids []string
var mids [][]byte
for i := 0; i < 100; i++ {
m := makeTestMessage(i)
m.From = []byte(peerA)

View File

@ -30,7 +30,7 @@ func NewMessageCache(gossip, history int) *MessageCache {
peertx: make(map[string]map[peer.ID]int),
history: make([][]CacheEntry, history),
gossip: gossip,
msgID: func(msg *Message) string {
msgID: func(msg *Message) []byte {
return DefaultMsgIdFn(msg.Message)
},
}
@ -41,47 +41,47 @@ type MessageCache struct {
peertx map[string]map[peer.ID]int
history [][]CacheEntry
gossip int
msgID func(*Message) string
msgID func(*Message) []byte
}
func (mc *MessageCache) SetMsgIdFn(msgID func(*Message) string) {
func (mc *MessageCache) SetMsgIdFn(msgID func(*Message) []byte) {
mc.msgID = msgID
}
type CacheEntry struct {
mid string
mid []byte
bitmask []byte
}
func (mc *MessageCache) Put(msg *Message) {
mid := mc.msgID(msg)
mc.msgs[mid] = msg
mc.msgs[string(mid)] = msg
mc.history[0] = append(mc.history[0], CacheEntry{mid: mid, bitmask: msg.GetBitmask()})
}
func (mc *MessageCache) Get(mid string) (*Message, bool) {
m, ok := mc.msgs[mid]
func (mc *MessageCache) Get(mid []byte) (*Message, bool) {
m, ok := mc.msgs[string(mid)]
return m, ok
}
func (mc *MessageCache) GetForPeer(mid string, p peer.ID) (*Message, int, bool) {
m, ok := mc.msgs[mid]
func (mc *MessageCache) GetForPeer(mid []byte, p peer.ID) (*Message, int, bool) {
m, ok := mc.msgs[string(mid)]
if !ok {
return nil, 0, false
}
tx, ok := mc.peertx[mid]
tx, ok := mc.peertx[string(mid)]
if !ok {
tx = make(map[peer.ID]int)
mc.peertx[mid] = tx
mc.peertx[string(mid)] = tx
}
tx[p]++
return m, tx[p], true
}
func (mc *MessageCache) GetGossipIDs(bitmask []byte) []string {
var mids []string
func (mc *MessageCache) GetGossipIDs(bitmask []byte) [][]byte {
var mids [][]byte
for _, entries := range mc.history[:mc.gossip] {
for _, entry := range entries {
if bytes.Equal(entry.bitmask, bitmask) {
@ -95,8 +95,8 @@ func (mc *MessageCache) GetGossipIDs(bitmask []byte) []string {
func (mc *MessageCache) Shift() {
last := mc.history[len(mc.history)-1]
for _, entry := range last {
delete(mc.msgs, entry.mid)
delete(mc.peertx, entry.mid)
delete(mc.msgs, string(entry.mid))
delete(mc.peertx, string(entry.mid))
}
for i := len(mc.history) - 2; i >= 0; i-- {
mc.history[i+1] = mc.history[i]

View File

@ -1,6 +1,7 @@
package blossomsub
import (
"bytes"
"encoding/binary"
"fmt"
"testing"
@ -33,14 +34,14 @@ func TestMessageCache(t *testing.T) {
}
}
gids := mcache.GetGossipIDs([]byte{0x7e, 0x57})
gids := mcache.GetGossipIDs([]byte{0x01, 0x00})
if len(gids) != 10 {
t.Fatalf("Expected 10 gossip IDs; got %d", len(gids))
}
for i := 0; i < 10; i++ {
mid := msgID(msgs[i])
if mid != gids[i] {
if !bytes.Equal(mid, gids[i]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
@ -62,21 +63,21 @@ func TestMessageCache(t *testing.T) {
}
}
gids = mcache.GetGossipIDs([]byte{0x7e, 0x57})
gids = mcache.GetGossipIDs([]byte{0x01, 0x00})
if len(gids) != 20 {
t.Fatalf("Expected 20 gossip IDs; got %d", len(gids))
}
for i := 0; i < 10; i++ {
mid := msgID(msgs[i])
if mid != gids[10+i] {
if !bytes.Equal(mid, gids[10+i]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
for i := 10; i < 20; i++ {
mid := msgID(msgs[i])
if mid != gids[i-10] {
if !bytes.Equal(mid, gids[i-10]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
@ -125,28 +126,28 @@ func TestMessageCache(t *testing.T) {
}
}
gids = mcache.GetGossipIDs([]byte{0x7e, 0x57})
gids = mcache.GetGossipIDs([]byte{0x01, 0x00})
if len(gids) != 30 {
t.Fatalf("Expected 30 gossip IDs; got %d", len(gids))
}
for i := 0; i < 10; i++ {
mid := msgID(msgs[50+i])
if mid != gids[i] {
if !bytes.Equal(mid, gids[i]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
for i := 10; i < 20; i++ {
mid := msgID(msgs[30+i])
if mid != gids[i] {
if !bytes.Equal(mid, gids[i]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
for i := 20; i < 30; i++ {
mid := msgID(msgs[10+i])
if mid != gids[i] {
if !bytes.Equal(mid, gids[i]) {
t.Fatalf("GossipID mismatch for message %d", i)
}
}
@ -157,11 +158,11 @@ func makeTestMessage(n int) *pb.Message {
seqno := make([]byte, 8)
binary.BigEndian.PutUint64(seqno, uint64(n))
data := []byte(fmt.Sprintf("%d", n))
bitmask := []byte{0x7e, 0x57}
bitmask := []byte{0x01, 0x00}
return &pb.Message{
Data: data,
Bitmask: bitmask,
From: []byte([]byte{0x7e, 0x57}),
From: []byte([]byte{0x01, 0x00}),
Seqno: seqno,
}
}

View File

@ -30,8 +30,8 @@ func (m *msgIDGenerator) Set(bitmask []byte, gen MsgIdFunction) {
}
// ID computes ID for the msg or short-circuits with the cached value.
func (m *msgIDGenerator) ID(msg *Message) string {
if msg.ID != "" {
func (m *msgIDGenerator) ID(msg *Message) []byte {
if len(msg.ID) != 0 {
return msg.ID
}
@ -40,7 +40,7 @@ func (m *msgIDGenerator) ID(msg *Message) string {
}
// RawID computes ID for the proto 'msg'.
func (m *msgIDGenerator) RawID(msg *pb.Message) string {
func (m *msgIDGenerator) RawID(msg *pb.Message) []byte {
m.bitmaskGensLk.RLock()
gen, ok := m.bitmaskGens[string(msg.GetBitmask())]
m.bitmaskGensLk.RUnlock()

View File

@ -1,75 +0,0 @@
package blossomsub
import (
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
ma "github.com/multiformats/go-multiaddr"
)
var _ network.Notifiee = (*PubSubNotif)(nil)
type PubSubNotif PubSub
func (p *PubSubNotif) OpenedStream(n network.Network, s network.Stream) {
}
func (p *PubSubNotif) ClosedStream(n network.Network, s network.Stream) {
}
func (p *PubSubNotif) Connected(n network.Network, c network.Conn) {
// ignore transient connections
if c.Stat().Limited {
return
}
go func() {
p.newPeersPrioLk.RLock()
p.newPeersMx.Lock()
p.newPeersPend[c.RemotePeer()] = struct{}{}
p.newPeersMx.Unlock()
p.newPeersPrioLk.RUnlock()
select {
case p.newPeers <- struct{}{}:
default:
}
}()
}
func (p *PubSubNotif) Disconnected(n network.Network, c network.Conn) {
}
func (p *PubSubNotif) Listen(n network.Network, _ ma.Multiaddr) {
}
func (p *PubSubNotif) ListenClose(n network.Network, _ ma.Multiaddr) {
}
func (p *PubSubNotif) Initialize() {
isTransient := func(pid peer.ID) bool {
for _, c := range p.host.Network().ConnsToPeer(pid) {
if !c.Stat().Limited {
return false
}
}
return true
}
p.newPeersPrioLk.RLock()
p.newPeersMx.Lock()
for _, pid := range p.host.Network().Peers() {
if isTransient(pid) {
continue
}
p.newPeersPend[pid] = struct{}{}
}
p.newPeersMx.Unlock()
p.newPeersPrioLk.RUnlock()
select {
case p.newPeers <- struct{}{}:
default:
}
}

View File

@ -246,9 +246,8 @@ type ControlIHave struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bitmask []byte `protobuf:"bytes,1,opt,name=bitmask,proto3" json:"bitmask,omitempty"`
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
MessageIDs []string `protobuf:"bytes,2,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
Bitmask []byte `protobuf:"bytes,1,opt,name=bitmask,proto3" json:"bitmask,omitempty"`
MessageIDs [][]byte `protobuf:"bytes,2,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
}
func (x *ControlIHave) Reset() {
@ -290,7 +289,7 @@ func (x *ControlIHave) GetBitmask() []byte {
return nil
}
func (x *ControlIHave) GetMessageIDs() []string {
func (x *ControlIHave) GetMessageIDs() [][]byte {
if x != nil {
return x.MessageIDs
}
@ -302,8 +301,7 @@ type ControlIWant struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
MessageIDs []string `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
MessageIDs [][]byte `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
}
func (x *ControlIWant) Reset() {
@ -338,7 +336,7 @@ func (*ControlIWant) Descriptor() ([]byte, []int) {
return file_rpc_proto_rawDescGZIP(), []int{4}
}
func (x *ControlIWant) GetMessageIDs() []string {
func (x *ControlIWant) GetMessageIDs() [][]byte {
if x != nil {
return x.MessageIDs
}
@ -612,10 +610,10 @@ var file_rpc_proto_rawDesc = []byte{
0x49, 0x48, 0x61, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12,
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x2e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x12,
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
0x28, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12,
0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x71, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,

View File

@ -34,13 +34,11 @@ message ControlMessage {
message ControlIHave {
bytes bitmask = 1;
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
repeated string messageIDs = 2;
repeated bytes messageIDs = 2;
}
message ControlIWant {
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
repeated string messageIDs = 1;
repeated bytes messageIDs = 1;
}
message ControlGraft {

View File

@ -23,19 +23,20 @@ const (
type TraceEvent_Type int32
const (
TraceEvent_PUBLISH_MESSAGE TraceEvent_Type = 0
TraceEvent_REJECT_MESSAGE TraceEvent_Type = 1
TraceEvent_DUPLICATE_MESSAGE TraceEvent_Type = 2
TraceEvent_DELIVER_MESSAGE TraceEvent_Type = 3
TraceEvent_ADD_PEER TraceEvent_Type = 4
TraceEvent_REMOVE_PEER TraceEvent_Type = 5
TraceEvent_RECV_RPC TraceEvent_Type = 6
TraceEvent_SEND_RPC TraceEvent_Type = 7
TraceEvent_DROP_RPC TraceEvent_Type = 8
TraceEvent_JOIN TraceEvent_Type = 9
TraceEvent_LEAVE TraceEvent_Type = 10
TraceEvent_GRAFT TraceEvent_Type = 11
TraceEvent_PRUNE TraceEvent_Type = 12
TraceEvent_PUBLISH_MESSAGE TraceEvent_Type = 0
TraceEvent_REJECT_MESSAGE TraceEvent_Type = 1
TraceEvent_DUPLICATE_MESSAGE TraceEvent_Type = 2
TraceEvent_DELIVER_MESSAGE TraceEvent_Type = 3
TraceEvent_ADD_PEER TraceEvent_Type = 4
TraceEvent_REMOVE_PEER TraceEvent_Type = 5
TraceEvent_RECV_RPC TraceEvent_Type = 6
TraceEvent_SEND_RPC TraceEvent_Type = 7
TraceEvent_DROP_RPC TraceEvent_Type = 8
TraceEvent_JOIN TraceEvent_Type = 9
TraceEvent_LEAVE TraceEvent_Type = 10
TraceEvent_GRAFT TraceEvent_Type = 11
TraceEvent_PRUNE TraceEvent_Type = 12
TraceEvent_UNDELIVERABLE_MESSAGE TraceEvent_Type = 13
)
// Enum value maps for TraceEvent_Type.
@ -54,21 +55,23 @@ var (
10: "LEAVE",
11: "GRAFT",
12: "PRUNE",
13: "UNDELIVERABLE_MESSAGE",
}
TraceEvent_Type_value = map[string]int32{
"PUBLISH_MESSAGE": 0,
"REJECT_MESSAGE": 1,
"DUPLICATE_MESSAGE": 2,
"DELIVER_MESSAGE": 3,
"ADD_PEER": 4,
"REMOVE_PEER": 5,
"RECV_RPC": 6,
"SEND_RPC": 7,
"DROP_RPC": 8,
"JOIN": 9,
"LEAVE": 10,
"GRAFT": 11,
"PRUNE": 12,
"PUBLISH_MESSAGE": 0,
"REJECT_MESSAGE": 1,
"DUPLICATE_MESSAGE": 2,
"DELIVER_MESSAGE": 3,
"ADD_PEER": 4,
"REMOVE_PEER": 5,
"RECV_RPC": 6,
"SEND_RPC": 7,
"DROP_RPC": 8,
"JOIN": 9,
"LEAVE": 10,
"GRAFT": 11,
"PRUNE": 12,
"UNDELIVERABLE_MESSAGE": 13,
}
)
@ -104,22 +107,23 @@ type TraceEvent struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type *TraceEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=blossomsub.pb.TraceEvent_Type,oneof" json:"type,omitempty"`
PeerID []byte `protobuf:"bytes,2,opt,name=peerID,proto3,oneof" json:"peerID,omitempty"`
Timestamp *int64 `protobuf:"varint,3,opt,name=timestamp,proto3,oneof" json:"timestamp,omitempty"`
PublishMessage *TraceEvent_PublishMessage `protobuf:"bytes,4,opt,name=publishMessage,proto3,oneof" json:"publishMessage,omitempty"`
RejectMessage *TraceEvent_RejectMessage `protobuf:"bytes,5,opt,name=rejectMessage,proto3,oneof" json:"rejectMessage,omitempty"`
DuplicateMessage *TraceEvent_DuplicateMessage `protobuf:"bytes,6,opt,name=duplicateMessage,proto3,oneof" json:"duplicateMessage,omitempty"`
DeliverMessage *TraceEvent_DeliverMessage `protobuf:"bytes,7,opt,name=deliverMessage,proto3,oneof" json:"deliverMessage,omitempty"`
AddPeer *TraceEvent_AddPeer `protobuf:"bytes,8,opt,name=addPeer,proto3,oneof" json:"addPeer,omitempty"`
RemovePeer *TraceEvent_RemovePeer `protobuf:"bytes,9,opt,name=removePeer,proto3,oneof" json:"removePeer,omitempty"`
RecvRPC *TraceEvent_RecvRPC `protobuf:"bytes,10,opt,name=recvRPC,proto3,oneof" json:"recvRPC,omitempty"`
SendRPC *TraceEvent_SendRPC `protobuf:"bytes,11,opt,name=sendRPC,proto3,oneof" json:"sendRPC,omitempty"`
DropRPC *TraceEvent_DropRPC `protobuf:"bytes,12,opt,name=dropRPC,proto3,oneof" json:"dropRPC,omitempty"`
Join *TraceEvent_Join `protobuf:"bytes,13,opt,name=join,proto3,oneof" json:"join,omitempty"`
Leave *TraceEvent_Leave `protobuf:"bytes,14,opt,name=leave,proto3,oneof" json:"leave,omitempty"`
Graft *TraceEvent_Graft `protobuf:"bytes,15,opt,name=graft,proto3,oneof" json:"graft,omitempty"`
Prune *TraceEvent_Prune `protobuf:"bytes,16,opt,name=prune,proto3,oneof" json:"prune,omitempty"`
Type *TraceEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=blossomsub.pb.TraceEvent_Type,oneof" json:"type,omitempty"`
PeerID []byte `protobuf:"bytes,2,opt,name=peerID,proto3,oneof" json:"peerID,omitempty"`
Timestamp *int64 `protobuf:"varint,3,opt,name=timestamp,proto3,oneof" json:"timestamp,omitempty"`
PublishMessage *TraceEvent_PublishMessage `protobuf:"bytes,4,opt,name=publishMessage,proto3,oneof" json:"publishMessage,omitempty"`
RejectMessage *TraceEvent_RejectMessage `protobuf:"bytes,5,opt,name=rejectMessage,proto3,oneof" json:"rejectMessage,omitempty"`
DuplicateMessage *TraceEvent_DuplicateMessage `protobuf:"bytes,6,opt,name=duplicateMessage,proto3,oneof" json:"duplicateMessage,omitempty"`
DeliverMessage *TraceEvent_DeliverMessage `protobuf:"bytes,7,opt,name=deliverMessage,proto3,oneof" json:"deliverMessage,omitempty"`
AddPeer *TraceEvent_AddPeer `protobuf:"bytes,8,opt,name=addPeer,proto3,oneof" json:"addPeer,omitempty"`
RemovePeer *TraceEvent_RemovePeer `protobuf:"bytes,9,opt,name=removePeer,proto3,oneof" json:"removePeer,omitempty"`
RecvRPC *TraceEvent_RecvRPC `protobuf:"bytes,10,opt,name=recvRPC,proto3,oneof" json:"recvRPC,omitempty"`
SendRPC *TraceEvent_SendRPC `protobuf:"bytes,11,opt,name=sendRPC,proto3,oneof" json:"sendRPC,omitempty"`
DropRPC *TraceEvent_DropRPC `protobuf:"bytes,12,opt,name=dropRPC,proto3,oneof" json:"dropRPC,omitempty"`
Join *TraceEvent_Join `protobuf:"bytes,13,opt,name=join,proto3,oneof" json:"join,omitempty"`
Leave *TraceEvent_Leave `protobuf:"bytes,14,opt,name=leave,proto3,oneof" json:"leave,omitempty"`
Graft *TraceEvent_Graft `protobuf:"bytes,15,opt,name=graft,proto3,oneof" json:"graft,omitempty"`
Prune *TraceEvent_Prune `protobuf:"bytes,16,opt,name=prune,proto3,oneof" json:"prune,omitempty"`
UndeliverableMessage *TraceEvent_UndeliverableMessage `protobuf:"bytes,17,opt,name=undeliverableMessage,proto3,oneof" json:"undeliverableMessage,omitempty"`
}
func (x *TraceEvent) Reset() {
@ -266,6 +270,13 @@ func (x *TraceEvent) GetPrune() *TraceEvent_Prune {
return nil
}
func (x *TraceEvent) GetUndeliverableMessage() *TraceEvent_UndeliverableMessage {
if x != nil {
return x.UndeliverableMessage
}
return nil
}
type TraceEventBatch struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1036,6 +1047,69 @@ func (x *TraceEvent_Prune) GetBitmask() []byte {
return nil
}
type TraceEvent_UndeliverableMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MessageID []byte `protobuf:"bytes,1,opt,name=messageID,proto3,oneof" json:"messageID,omitempty"`
Bitmask []byte `protobuf:"bytes,2,opt,name=bitmask,proto3,oneof" json:"bitmask,omitempty"`
ReceivedFrom []byte `protobuf:"bytes,3,opt,name=receivedFrom,proto3,oneof" json:"receivedFrom,omitempty"`
}
func (x *TraceEvent_UndeliverableMessage) Reset() {
*x = TraceEvent_UndeliverableMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TraceEvent_UndeliverableMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TraceEvent_UndeliverableMessage) ProtoMessage() {}
func (x *TraceEvent_UndeliverableMessage) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[15]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TraceEvent_UndeliverableMessage.ProtoReflect.Descriptor instead.
func (*TraceEvent_UndeliverableMessage) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 13}
}
func (x *TraceEvent_UndeliverableMessage) GetMessageID() []byte {
if x != nil {
return x.MessageID
}
return nil
}
func (x *TraceEvent_UndeliverableMessage) GetBitmask() []byte {
if x != nil {
return x.Bitmask
}
return nil
}
func (x *TraceEvent_UndeliverableMessage) GetReceivedFrom() []byte {
if x != nil {
return x.ReceivedFrom
}
return nil
}
type TraceEvent_RPCMeta struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1049,7 +1123,7 @@ type TraceEvent_RPCMeta struct {
func (x *TraceEvent_RPCMeta) Reset() {
*x = TraceEvent_RPCMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[15]
mi := &file_trace_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1062,7 +1136,7 @@ func (x *TraceEvent_RPCMeta) String() string {
func (*TraceEvent_RPCMeta) ProtoMessage() {}
func (x *TraceEvent_RPCMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[15]
mi := &file_trace_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1075,7 +1149,7 @@ func (x *TraceEvent_RPCMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_RPCMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_RPCMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 13}
return file_trace_proto_rawDescGZIP(), []int{0, 14}
}
func (x *TraceEvent_RPCMeta) GetMessages() []*TraceEvent_MessageMeta {
@ -1111,7 +1185,7 @@ type TraceEvent_MessageMeta struct {
func (x *TraceEvent_MessageMeta) Reset() {
*x = TraceEvent_MessageMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[16]
mi := &file_trace_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1124,7 +1198,7 @@ func (x *TraceEvent_MessageMeta) String() string {
func (*TraceEvent_MessageMeta) ProtoMessage() {}
func (x *TraceEvent_MessageMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[16]
mi := &file_trace_proto_msgTypes[17]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1137,7 +1211,7 @@ func (x *TraceEvent_MessageMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_MessageMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_MessageMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 14}
return file_trace_proto_rawDescGZIP(), []int{0, 15}
}
func (x *TraceEvent_MessageMeta) GetMessageID() []byte {
@ -1166,7 +1240,7 @@ type TraceEvent_SubMeta struct {
func (x *TraceEvent_SubMeta) Reset() {
*x = TraceEvent_SubMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[17]
mi := &file_trace_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1179,7 +1253,7 @@ func (x *TraceEvent_SubMeta) String() string {
func (*TraceEvent_SubMeta) ProtoMessage() {}
func (x *TraceEvent_SubMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[17]
mi := &file_trace_proto_msgTypes[18]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1192,7 +1266,7 @@ func (x *TraceEvent_SubMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_SubMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_SubMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 15}
return file_trace_proto_rawDescGZIP(), []int{0, 16}
}
func (x *TraceEvent_SubMeta) GetSubscribe() bool {
@ -1223,7 +1297,7 @@ type TraceEvent_ControlMeta struct {
func (x *TraceEvent_ControlMeta) Reset() {
*x = TraceEvent_ControlMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[18]
mi := &file_trace_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1236,7 +1310,7 @@ func (x *TraceEvent_ControlMeta) String() string {
func (*TraceEvent_ControlMeta) ProtoMessage() {}
func (x *TraceEvent_ControlMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[18]
mi := &file_trace_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1249,7 +1323,7 @@ func (x *TraceEvent_ControlMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_ControlMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 16}
return file_trace_proto_rawDescGZIP(), []int{0, 17}
}
func (x *TraceEvent_ControlMeta) GetIhave() []*TraceEvent_ControlIHaveMeta {
@ -1292,7 +1366,7 @@ type TraceEvent_ControlIHaveMeta struct {
func (x *TraceEvent_ControlIHaveMeta) Reset() {
*x = TraceEvent_ControlIHaveMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[19]
mi := &file_trace_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1305,7 +1379,7 @@ func (x *TraceEvent_ControlIHaveMeta) String() string {
func (*TraceEvent_ControlIHaveMeta) ProtoMessage() {}
func (x *TraceEvent_ControlIHaveMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[19]
mi := &file_trace_proto_msgTypes[20]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1318,7 +1392,7 @@ func (x *TraceEvent_ControlIHaveMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_ControlIHaveMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlIHaveMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 17}
return file_trace_proto_rawDescGZIP(), []int{0, 18}
}
func (x *TraceEvent_ControlIHaveMeta) GetBitmask() []byte {
@ -1346,7 +1420,7 @@ type TraceEvent_ControlIWantMeta struct {
func (x *TraceEvent_ControlIWantMeta) Reset() {
*x = TraceEvent_ControlIWantMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[20]
mi := &file_trace_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1359,7 +1433,7 @@ func (x *TraceEvent_ControlIWantMeta) String() string {
func (*TraceEvent_ControlIWantMeta) ProtoMessage() {}
func (x *TraceEvent_ControlIWantMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[20]
mi := &file_trace_proto_msgTypes[21]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1372,7 +1446,7 @@ func (x *TraceEvent_ControlIWantMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_ControlIWantMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlIWantMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 18}
return file_trace_proto_rawDescGZIP(), []int{0, 19}
}
func (x *TraceEvent_ControlIWantMeta) GetMessageIDs() [][]byte {
@ -1393,7 +1467,7 @@ type TraceEvent_ControlGraftMeta struct {
func (x *TraceEvent_ControlGraftMeta) Reset() {
*x = TraceEvent_ControlGraftMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[21]
mi := &file_trace_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1406,7 +1480,7 @@ func (x *TraceEvent_ControlGraftMeta) String() string {
func (*TraceEvent_ControlGraftMeta) ProtoMessage() {}
func (x *TraceEvent_ControlGraftMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[21]
mi := &file_trace_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1419,7 +1493,7 @@ func (x *TraceEvent_ControlGraftMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_ControlGraftMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlGraftMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 19}
return file_trace_proto_rawDescGZIP(), []int{0, 20}
}
func (x *TraceEvent_ControlGraftMeta) GetBitmask() []byte {
@ -1441,7 +1515,7 @@ type TraceEvent_ControlPruneMeta struct {
func (x *TraceEvent_ControlPruneMeta) Reset() {
*x = TraceEvent_ControlPruneMeta{}
if protoimpl.UnsafeEnabled {
mi := &file_trace_proto_msgTypes[22]
mi := &file_trace_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1454,7 +1528,7 @@ func (x *TraceEvent_ControlPruneMeta) String() string {
func (*TraceEvent_ControlPruneMeta) ProtoMessage() {}
func (x *TraceEvent_ControlPruneMeta) ProtoReflect() protoreflect.Message {
mi := &file_trace_proto_msgTypes[22]
mi := &file_trace_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1467,7 +1541,7 @@ func (x *TraceEvent_ControlPruneMeta) ProtoReflect() protoreflect.Message {
// Deprecated: Use TraceEvent_ControlPruneMeta.ProtoReflect.Descriptor instead.
func (*TraceEvent_ControlPruneMeta) Descriptor() ([]byte, []int) {
return file_trace_proto_rawDescGZIP(), []int{0, 20}
return file_trace_proto_rawDescGZIP(), []int{0, 21}
}
func (x *TraceEvent_ControlPruneMeta) GetBitmask() []byte {
@ -1488,7 +1562,7 @@ var File_trace_proto protoreflect.FileDescriptor
var file_trace_proto_rawDesc = []byte{
0x0a, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x62,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xfe, 0x1e, 0x0a,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xca, 0x21, 0x0a,
0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x6c, 0x6f, 0x73,
0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45,
@ -1555,38 +1629,106 @@ var file_trace_proto_rawDesc = []byte{
0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x2e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x48, 0x0f, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65,
0x88, 0x01, 0x01, 0x1a, 0x6c, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d,
0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74,
0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x1a, 0xcd, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c,
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12,
0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48,
0x02, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52,
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65,
0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x1a, 0xa8, 0x01, 0x0a, 0x10, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63,
0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88,
0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01,
0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42,
0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d,
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa6, 0x01, 0x0a,
0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x88, 0x01, 0x01, 0x12, 0x67, 0x0a, 0x14, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2e, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70,
0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x6e, 0x64,
0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x48, 0x10, 0x52, 0x14, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62,
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x1a, 0x6c, 0x0a, 0x0e,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21,
0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01,
0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01,
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xcd, 0x01, 0x0a, 0x0d, 0x52,
0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12,
0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46,
0x72, 0x6f, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa8, 0x01, 0x0a, 0x10, 0x44,
0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88,
0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72,
0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65,
0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63,
0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69,
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa6, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65,
0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65,
0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
0x48, 0x02, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d,
0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x42, 0x0f, 0x0a,
0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0x56,
0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65,
0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65,
0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x88, 0x01,
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x08, 0x0a, 0x06,
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01,
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x1a, 0x88, 0x01, 0x0a,
0x07, 0x52, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65,
0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01,
0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e,
0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65,
0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a,
0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x07,
0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x52,
0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12,
0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72,
0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61,
0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f,
0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a,
0x76, 0x0a, 0x07, 0x44, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65,
0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65,
0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73,
0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61,
0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07,
0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x31, 0x0a, 0x04, 0x4a, 0x6f, 0x69, 0x6e, 0x12,
0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x05, 0x4c, 0x65,
0x61, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88,
0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a,
0x0a, 0x05, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49,
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49,
0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x50, 0x72,
0x75, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01,
0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42,
0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xac, 0x01, 0x0a, 0x14, 0x55, 0x6e, 0x64, 0x65, 0x6c,
0x69, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88,
0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20,
@ -1596,156 +1738,109 @@ var file_trace_proto_rawDesc = []byte{
0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74,
0x6d, 0x61, 0x73, 0x6b, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0x56, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72,
0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a,
0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65,
0x72, 0x49, 0x44, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x0a,
0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65,
0x72, 0x49, 0x44, 0x1a, 0x88, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x12,
0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74,
0x61, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76,
0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e,
0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e,
0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88,
0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a,
0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76, 0x0a, 0x07, 0x44, 0x72, 0x6f, 0x70, 0x52, 0x50,
0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a,
0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61,
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48,
0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73,
0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x31,
0x0a, 0x04, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x1a, 0x32, 0x0a, 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69,
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69,
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12, 0x1b,
0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70,
0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65,
0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65,
0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d,
0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49,
0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xe5, 0x01,
0x0a, 0x07, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x08, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c,
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63,
0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65,
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0c,
0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e,
0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75,
0x62, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x1a, 0x69, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d,
0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x1a, 0x65, 0x0a, 0x07, 0x53, 0x75, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x73,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00,
0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d,
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a,
0x0a, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x95, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65,
0x74, 0x61, 0x52, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x77, 0x61,
0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73,
0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76,
0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74,
0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x77, 0x61, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x67,
0x72, 0x61, 0x66, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f,
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0xe5, 0x01, 0x0a, 0x07, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74,
0x61, 0x12, 0x41, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62,
0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f,
0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61,
0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x67, 0x72, 0x61, 0x66, 0x74, 0x12, 0x40, 0x0a,
0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0c, 0x73,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x07, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62,
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61,
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50,
0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x1a,
0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d,
0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88,
0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32,
0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65,
0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61,
0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e,
0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0xcf, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
0x13, 0x0a, 0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
0x47, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d,
0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c,
0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12,
0x13, 0x0a, 0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
0x47, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52,
0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45,
0x52, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10,
0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12,
0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a,
0x04, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45,
0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a,
0x05, 0x50, 0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70,
0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0c, 0x0a, 0x0a,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x10, 0x0a,
0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42,
0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61, 0x64, 0x64, 0x50,
0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65,
0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64,
0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x42,
0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x67, 0x72,
0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x22, 0x42, 0x0a,
0x0f, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68,
0x12, 0x2f, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x19, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e,
0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63,
0x68, 0x42, 0x43, 0x5a, 0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c,
0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69,
0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67,
0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
0x73, 0x75, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d,
0x65, 0x74, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x88, 0x01,
0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x1a, 0x69, 0x0a,
0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12,
0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c,
0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08,
0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x65, 0x0a, 0x07, 0x53, 0x75, 0x62, 0x4d,
0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
0x69, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
0x69, 0x62, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a,
0x95, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12,
0x40, 0x0a, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a,
0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54,
0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x68, 0x61, 0x76,
0x65, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x77, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62,
0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x77,
0x61, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x66, 0x74, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e,
0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05,
0x67, 0x72, 0x61, 0x66, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x04,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61,
0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07,
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d,
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a,
0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a,
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05,
0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65,
0x72, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0xea,
0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49,
0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e,
0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01,
0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45,
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56,
0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08,
0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45,
0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52,
0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e,
0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f,
0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12,
0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52,
0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c,
0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x41, 0x42, 0x4c,
0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0d, 0x42, 0x07, 0x0a, 0x05, 0x5f,
0x74, 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42,
0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a,
0x0f, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69,
0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61,
0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76,
0x65, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x52, 0x50,
0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a,
0x08, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f,
0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x42, 0x08, 0x0a, 0x06,
0x5f, 0x67, 0x72, 0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65,
0x42, 0x17, 0x0a, 0x15, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62,
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x42, 0x0a, 0x0f, 0x54, 0x72, 0x61,
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2f, 0x0a, 0x05,
0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x6c,
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63,
0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x42, 0x43, 0x5a,
0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69,
0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1761,32 +1856,33 @@ func file_trace_proto_rawDescGZIP() []byte {
}
var file_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
var file_trace_proto_goTypes = []interface{}{
(TraceEvent_Type)(0), // 0: blossomsub.pb.TraceEvent.Type
(*TraceEvent)(nil), // 1: blossomsub.pb.TraceEvent
(*TraceEventBatch)(nil), // 2: blossomsub.pb.TraceEventBatch
(*TraceEvent_PublishMessage)(nil), // 3: blossomsub.pb.TraceEvent.PublishMessage
(*TraceEvent_RejectMessage)(nil), // 4: blossomsub.pb.TraceEvent.RejectMessage
(*TraceEvent_DuplicateMessage)(nil), // 5: blossomsub.pb.TraceEvent.DuplicateMessage
(*TraceEvent_DeliverMessage)(nil), // 6: blossomsub.pb.TraceEvent.DeliverMessage
(*TraceEvent_AddPeer)(nil), // 7: blossomsub.pb.TraceEvent.AddPeer
(*TraceEvent_RemovePeer)(nil), // 8: blossomsub.pb.TraceEvent.RemovePeer
(*TraceEvent_RecvRPC)(nil), // 9: blossomsub.pb.TraceEvent.RecvRPC
(*TraceEvent_SendRPC)(nil), // 10: blossomsub.pb.TraceEvent.SendRPC
(*TraceEvent_DropRPC)(nil), // 11: blossomsub.pb.TraceEvent.DropRPC
(*TraceEvent_Join)(nil), // 12: blossomsub.pb.TraceEvent.Join
(*TraceEvent_Leave)(nil), // 13: blossomsub.pb.TraceEvent.Leave
(*TraceEvent_Graft)(nil), // 14: blossomsub.pb.TraceEvent.Graft
(*TraceEvent_Prune)(nil), // 15: blossomsub.pb.TraceEvent.Prune
(*TraceEvent_RPCMeta)(nil), // 16: blossomsub.pb.TraceEvent.RPCMeta
(*TraceEvent_MessageMeta)(nil), // 17: blossomsub.pb.TraceEvent.MessageMeta
(*TraceEvent_SubMeta)(nil), // 18: blossomsub.pb.TraceEvent.SubMeta
(*TraceEvent_ControlMeta)(nil), // 19: blossomsub.pb.TraceEvent.ControlMeta
(*TraceEvent_ControlIHaveMeta)(nil), // 20: blossomsub.pb.TraceEvent.ControlIHaveMeta
(*TraceEvent_ControlIWantMeta)(nil), // 21: blossomsub.pb.TraceEvent.ControlIWantMeta
(*TraceEvent_ControlGraftMeta)(nil), // 22: blossomsub.pb.TraceEvent.ControlGraftMeta
(*TraceEvent_ControlPruneMeta)(nil), // 23: blossomsub.pb.TraceEvent.ControlPruneMeta
(TraceEvent_Type)(0), // 0: blossomsub.pb.TraceEvent.Type
(*TraceEvent)(nil), // 1: blossomsub.pb.TraceEvent
(*TraceEventBatch)(nil), // 2: blossomsub.pb.TraceEventBatch
(*TraceEvent_PublishMessage)(nil), // 3: blossomsub.pb.TraceEvent.PublishMessage
(*TraceEvent_RejectMessage)(nil), // 4: blossomsub.pb.TraceEvent.RejectMessage
(*TraceEvent_DuplicateMessage)(nil), // 5: blossomsub.pb.TraceEvent.DuplicateMessage
(*TraceEvent_DeliverMessage)(nil), // 6: blossomsub.pb.TraceEvent.DeliverMessage
(*TraceEvent_AddPeer)(nil), // 7: blossomsub.pb.TraceEvent.AddPeer
(*TraceEvent_RemovePeer)(nil), // 8: blossomsub.pb.TraceEvent.RemovePeer
(*TraceEvent_RecvRPC)(nil), // 9: blossomsub.pb.TraceEvent.RecvRPC
(*TraceEvent_SendRPC)(nil), // 10: blossomsub.pb.TraceEvent.SendRPC
(*TraceEvent_DropRPC)(nil), // 11: blossomsub.pb.TraceEvent.DropRPC
(*TraceEvent_Join)(nil), // 12: blossomsub.pb.TraceEvent.Join
(*TraceEvent_Leave)(nil), // 13: blossomsub.pb.TraceEvent.Leave
(*TraceEvent_Graft)(nil), // 14: blossomsub.pb.TraceEvent.Graft
(*TraceEvent_Prune)(nil), // 15: blossomsub.pb.TraceEvent.Prune
(*TraceEvent_UndeliverableMessage)(nil), // 16: blossomsub.pb.TraceEvent.UndeliverableMessage
(*TraceEvent_RPCMeta)(nil), // 17: blossomsub.pb.TraceEvent.RPCMeta
(*TraceEvent_MessageMeta)(nil), // 18: blossomsub.pb.TraceEvent.MessageMeta
(*TraceEvent_SubMeta)(nil), // 19: blossomsub.pb.TraceEvent.SubMeta
(*TraceEvent_ControlMeta)(nil), // 20: blossomsub.pb.TraceEvent.ControlMeta
(*TraceEvent_ControlIHaveMeta)(nil), // 21: blossomsub.pb.TraceEvent.ControlIHaveMeta
(*TraceEvent_ControlIWantMeta)(nil), // 22: blossomsub.pb.TraceEvent.ControlIWantMeta
(*TraceEvent_ControlGraftMeta)(nil), // 23: blossomsub.pb.TraceEvent.ControlGraftMeta
(*TraceEvent_ControlPruneMeta)(nil), // 24: blossomsub.pb.TraceEvent.ControlPruneMeta
}
var file_trace_proto_depIdxs = []int32{
0, // 0: blossomsub.pb.TraceEvent.type:type_name -> blossomsub.pb.TraceEvent.Type
@ -1803,22 +1899,23 @@ var file_trace_proto_depIdxs = []int32{
13, // 11: blossomsub.pb.TraceEvent.leave:type_name -> blossomsub.pb.TraceEvent.Leave
14, // 12: blossomsub.pb.TraceEvent.graft:type_name -> blossomsub.pb.TraceEvent.Graft
15, // 13: blossomsub.pb.TraceEvent.prune:type_name -> blossomsub.pb.TraceEvent.Prune
1, // 14: blossomsub.pb.TraceEventBatch.batch:type_name -> blossomsub.pb.TraceEvent
16, // 15: blossomsub.pb.TraceEvent.RecvRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
16, // 16: blossomsub.pb.TraceEvent.SendRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
16, // 17: blossomsub.pb.TraceEvent.DropRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
17, // 18: blossomsub.pb.TraceEvent.RPCMeta.messages:type_name -> blossomsub.pb.TraceEvent.MessageMeta
18, // 19: blossomsub.pb.TraceEvent.RPCMeta.subscription:type_name -> blossomsub.pb.TraceEvent.SubMeta
19, // 20: blossomsub.pb.TraceEvent.RPCMeta.control:type_name -> blossomsub.pb.TraceEvent.ControlMeta
20, // 21: blossomsub.pb.TraceEvent.ControlMeta.ihave:type_name -> blossomsub.pb.TraceEvent.ControlIHaveMeta
21, // 22: blossomsub.pb.TraceEvent.ControlMeta.iwant:type_name -> blossomsub.pb.TraceEvent.ControlIWantMeta
22, // 23: blossomsub.pb.TraceEvent.ControlMeta.graft:type_name -> blossomsub.pb.TraceEvent.ControlGraftMeta
23, // 24: blossomsub.pb.TraceEvent.ControlMeta.prune:type_name -> blossomsub.pb.TraceEvent.ControlPruneMeta
25, // [25:25] is the sub-list for method output_type
25, // [25:25] is the sub-list for method input_type
25, // [25:25] is the sub-list for extension type_name
25, // [25:25] is the sub-list for extension extendee
0, // [0:25] is the sub-list for field type_name
16, // 14: blossomsub.pb.TraceEvent.undeliverableMessage:type_name -> blossomsub.pb.TraceEvent.UndeliverableMessage
1, // 15: blossomsub.pb.TraceEventBatch.batch:type_name -> blossomsub.pb.TraceEvent
17, // 16: blossomsub.pb.TraceEvent.RecvRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
17, // 17: blossomsub.pb.TraceEvent.SendRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
17, // 18: blossomsub.pb.TraceEvent.DropRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
18, // 19: blossomsub.pb.TraceEvent.RPCMeta.messages:type_name -> blossomsub.pb.TraceEvent.MessageMeta
19, // 20: blossomsub.pb.TraceEvent.RPCMeta.subscription:type_name -> blossomsub.pb.TraceEvent.SubMeta
20, // 21: blossomsub.pb.TraceEvent.RPCMeta.control:type_name -> blossomsub.pb.TraceEvent.ControlMeta
21, // 22: blossomsub.pb.TraceEvent.ControlMeta.ihave:type_name -> blossomsub.pb.TraceEvent.ControlIHaveMeta
22, // 23: blossomsub.pb.TraceEvent.ControlMeta.iwant:type_name -> blossomsub.pb.TraceEvent.ControlIWantMeta
23, // 24: blossomsub.pb.TraceEvent.ControlMeta.graft:type_name -> blossomsub.pb.TraceEvent.ControlGraftMeta
24, // 25: blossomsub.pb.TraceEvent.ControlMeta.prune:type_name -> blossomsub.pb.TraceEvent.ControlPruneMeta
26, // [26:26] is the sub-list for method output_type
26, // [26:26] is the sub-list for method input_type
26, // [26:26] is the sub-list for extension type_name
26, // [26:26] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
}
func init() { file_trace_proto_init() }
@ -2008,7 +2105,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_RPCMeta); i {
switch v := v.(*TraceEvent_UndeliverableMessage); i {
case 0:
return &v.state
case 1:
@ -2020,7 +2117,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_MessageMeta); i {
switch v := v.(*TraceEvent_RPCMeta); i {
case 0:
return &v.state
case 1:
@ -2032,7 +2129,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_SubMeta); i {
switch v := v.(*TraceEvent_MessageMeta); i {
case 0:
return &v.state
case 1:
@ -2044,7 +2141,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlMeta); i {
switch v := v.(*TraceEvent_SubMeta); i {
case 0:
return &v.state
case 1:
@ -2056,7 +2153,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlIHaveMeta); i {
switch v := v.(*TraceEvent_ControlMeta); i {
case 0:
return &v.state
case 1:
@ -2068,7 +2165,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlIWantMeta); i {
switch v := v.(*TraceEvent_ControlIHaveMeta); i {
case 0:
return &v.state
case 1:
@ -2080,7 +2177,7 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlGraftMeta); i {
switch v := v.(*TraceEvent_ControlIWantMeta); i {
case 0:
return &v.state
case 1:
@ -2092,6 +2189,18 @@ func file_trace_proto_init() {
}
}
file_trace_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlGraftMeta); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_trace_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TraceEvent_ControlPruneMeta); i {
case 0:
return &v.state
@ -2121,16 +2230,17 @@ func file_trace_proto_init() {
file_trace_proto_msgTypes[15].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[16].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[17].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[19].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[21].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[18].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[20].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[22].OneofWrappers = []interface{}{}
file_trace_proto_msgTypes[23].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_trace_proto_rawDesc,
NumEnums: 1,
NumMessages: 23,
NumMessages: 24,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -22,6 +22,7 @@ message TraceEvent {
optional Leave leave = 14;
optional Graft graft = 15;
optional Prune prune = 16;
optional UndeliverableMessage undeliverableMessage = 17;
enum Type {
PUBLISH_MESSAGE = 0;
@ -37,6 +38,7 @@ message TraceEvent {
LEAVE = 10;
GRAFT = 11;
PRUNE = 12;
UNDELIVERABLE_MESSAGE = 13;
}
message PublishMessage {
@ -105,6 +107,12 @@ message TraceEvent {
optional bytes bitmask = 2;
}
message UndeliverableMessage {
optional bytes messageID = 1;
optional bytes bitmask = 2;
optional bytes receivedFrom = 3;
}
message RPCMeta {
repeated MessageMeta messages = 1;
repeated SubMeta subscription = 2;

View File

@ -204,13 +204,12 @@ func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams)
func (pg *peerGater) background(ctx context.Context) {
tick := time.NewTicker(pg.params.DecayInterval)
defer tick.Stop()
for {
select {
case <-tick.C:
pg.decayStats()
case <-ctx.Done():
tick.Stop()
return
}
}
@ -218,7 +217,6 @@ func (pg *peerGater) background(ctx context.Context) {
func (pg *peerGater) decayStats() {
pg.Lock()
defer pg.Unlock()
pg.validate *= pg.params.GlobalDecay
if pg.validate < pg.params.DecayToZero {
@ -256,6 +254,8 @@ func (pg *peerGater) decayStats() {
delete(pg.ipStats, ip)
}
}
pg.Unlock()
}
func (pg *peerGater) getPeerStats(p peer.ID) *peerGaterStats {
@ -323,21 +323,23 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
}
pg.Lock()
defer pg.Unlock()
// check the quiet period; if the validation queue has not throttled for more than the Quiet
// interval, we turn off the circuit breaker and accept.
if time.Since(pg.lastThrottle) > pg.params.Quiet {
pg.Unlock()
return AcceptAll
}
// no throttle events -- or they have decayed; accept.
if pg.throttle == 0 {
pg.Unlock()
return AcceptAll
}
// check the throttle/validate ration; if it is below threshold we accept.
if pg.validate != 0 && pg.throttle/pg.validate < pg.params.Threshold {
pg.Unlock()
return AcceptAll
}
@ -346,6 +348,7 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
// compute the goodput of the peer; the denominator is the weighted mix of message counters
total := st.deliver + pg.params.DuplicateWeight*st.duplicate + pg.params.IgnoreWeight*st.ignore + pg.params.RejectWeight*st.reject
if total == 0 {
pg.Unlock()
return AcceptAll
}
@ -355,10 +358,12 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
// accepted; this is not a sinkhole/blacklist.
threshold := (1 + st.deliver) / (1 + total)
if rand.Float64() < threshold {
pg.Unlock()
return AcceptAll
}
log.Debugf("throttling peer %s with threshold %f", p, threshold)
pg.Unlock()
return AcceptControl
}
@ -368,21 +373,21 @@ var _ RawTracer = (*peerGater)(nil)
// tracer interface
func (pg *peerGater) AddPeer(p peer.ID, proto protocol.ID) {
pg.Lock()
defer pg.Unlock()
st := pg.getPeerStats(p)
st.connected++
pg.Unlock()
}
func (pg *peerGater) RemovePeer(p peer.ID) {
pg.Lock()
defer pg.Unlock()
st := pg.getPeerStats(p)
st.connected--
st.expire = time.Now().Add(pg.params.RetainStats)
delete(pg.peerStats, p)
pg.Unlock()
}
func (pg *peerGater) Join(bitmask []byte) {}
@ -392,14 +397,13 @@ func (pg *peerGater) Prune(p peer.ID, bitmask []byte) {}
func (pg *peerGater) ValidateMessage(msg *Message) {
pg.Lock()
defer pg.Unlock()
pg.validate++
pg.Unlock()
}
func (pg *peerGater) DeliverMessage(msg *Message) {
pg.Lock()
defer pg.Unlock()
st := pg.getPeerStats(msg.ReceivedFrom)
@ -411,11 +415,11 @@ func (pg *peerGater) DeliverMessage(msg *Message) {
}
st.deliver += weight
pg.Unlock()
}
func (pg *peerGater) RejectMessage(msg *Message, reason string) {
pg.Lock()
defer pg.Unlock()
switch reason {
case RejectValidationQueueFull:
@ -432,14 +436,15 @@ func (pg *peerGater) RejectMessage(msg *Message, reason string) {
st := pg.getPeerStats(msg.ReceivedFrom)
st.reject++
}
pg.Unlock()
}
func (pg *peerGater) DuplicateMessage(msg *Message) {
pg.Lock()
defer pg.Unlock()
st := pg.getPeerStats(msg.ReceivedFrom)
st.duplicate++
pg.Unlock()
}
func (pg *peerGater) ThrottlePeer(p peer.ID) {}

View File

@ -0,0 +1,112 @@
package blossomsub
import (
"context"
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
func (ps *PubSub) watchForNewPeers(ctx context.Context) {
// We don't bother subscribing to "connectivity" events because we always run identify after
// every new connection.
sub, err := ps.host.EventBus().Subscribe([]interface{}{
&event.EvtPeerIdentificationCompleted{},
&event.EvtPeerProtocolsUpdated{},
})
if err != nil {
log.Errorf("failed to subscribe to peer identification events: %v", err)
return
}
ps.newPeersPrioLk.RLock()
ps.newPeersMx.Lock()
for _, pid := range ps.host.Network().Peers() {
if ps.host.Network().Connectedness(pid) != network.Connected {
continue
}
ps.newPeersPend[pid] = struct{}{}
}
ps.newPeersMx.Unlock()
ps.newPeersPrioLk.RUnlock()
select {
case ps.newPeers <- struct{}{}:
default:
}
var supportsProtocol func(protocol.ID) bool
if ps.protoMatchFunc != nil {
var supportedProtocols []func(protocol.ID) bool
for _, proto := range ps.rt.Protocols() {
supportedProtocols = append(supportedProtocols, ps.protoMatchFunc(proto))
}
supportsProtocol = func(proto protocol.ID) bool {
for _, fn := range supportedProtocols {
if (fn)(proto) {
return true
}
}
return false
}
} else {
supportedProtocols := make(map[protocol.ID]struct{})
for _, proto := range ps.rt.Protocols() {
supportedProtocols[proto] = struct{}{}
}
supportsProtocol = func(proto protocol.ID) bool {
_, ok := supportedProtocols[proto]
return ok
}
}
for ctx.Err() == nil {
var ev any
select {
case <-ctx.Done():
sub.Close()
return
case ev = <-sub.Out():
}
var protos []protocol.ID
var peer peer.ID
switch ev := ev.(type) {
case event.EvtPeerIdentificationCompleted:
peer = ev.Peer
protos = ev.Protocols
case event.EvtPeerProtocolsUpdated:
peer = ev.Peer
protos = ev.Added
default:
continue
}
// We don't bother checking connectivity (connected and non-"limited") here because
// we'll check when actually handling the new peer.
for _, p := range protos {
if supportsProtocol(p) {
ps.notifyNewPeer(peer)
break
}
}
}
sub.Close()
}
func (ps *PubSub) notifyNewPeer(peer peer.ID) {
ps.newPeersPrioLk.RLock()
ps.newPeersMx.Lock()
ps.newPeersPend[peer] = struct{}{}
ps.newPeersMx.Unlock()
ps.newPeersPrioLk.RUnlock()
select {
case ps.newPeers <- struct{}{}:
default:
}
}

View File

@ -1,11 +1,14 @@
package blossomsub
import (
"bytes"
"context"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"math/rand"
"slices"
"sync"
"sync/atomic"
"time"
@ -23,8 +26,8 @@ import (
logging "github.com/ipfs/go-log/v2"
)
// DefaultMaximumMessageSize is 16.7 MB.
const DefaultMaxMessageSize = 1 << 24
// DefaultMaximumMessageSize is 1 MB.
const DefaultMaxMessageSize = 1 << 20
var (
// TimeCacheDuration specifies how long a message ID will be remembered as seen.
@ -147,7 +150,8 @@ type PubSub struct {
blacklist Blacklist
blacklistPeer chan peer.ID
peers map[peer.ID]chan *RPC
peers map[peer.ID]chan *RPC
peersMx sync.RWMutex
inboundStreamsMx sync.Mutex
inboundStreams map[peer.ID]network.Stream
@ -231,7 +235,7 @@ const (
type Message struct {
*pb.Message
ID string
ID []byte
ReceivedFrom peer.ID
ValidatorData interface{}
Local bool
@ -242,7 +246,7 @@ func (m *Message) GetFrom() peer.ID {
}
type RPC struct {
pb.RPC
*pb.RPC
// unexported on purpose, not sending this over the wire
from peer.ID
@ -263,7 +267,7 @@ func NewPubSub(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option
peerOutboundQueueSize: 32,
signID: h.ID(),
signKey: nil,
signPolicy: StrictSign,
signPolicy: LaxSign,
incoming: make(chan *RPC, 32),
newPeers: make(chan struct{}, 1),
newPeersPend: make(map[peer.ID]struct{}),
@ -330,20 +334,19 @@ func NewPubSub(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option
h.SetStreamHandler(id, ps.handleNewStream)
}
}
h.Network().Notify((*PubSubNotif)(ps))
go ps.watchForNewPeers(ctx)
ps.val.Start(ps)
go ps.processLoop(ctx)
(*PubSubNotif)(ps).Initialize()
return ps, nil
}
// MsgIdFunction returns a unique ID for the passed Message, and PubSub can be customized to use any
// implementation of this function by configuring it with the Option from WithMessageIdFn.
type MsgIdFunction func(pmsg *pb.Message) string
type MsgIdFunction func(pmsg *pb.Message) []byte
// WithMessageIdFn is an option to customize the way a message ID is computed for a pubsub message.
// The default ID function is DefaultMsgIdFn (concatenate source and seq nr.),
@ -495,14 +498,14 @@ func WithRawTracer(tracer RawTracer) Option {
}
// WithMaxMessageSize sets the global maximum message size for pubsub wire
// messages. The default value is 16.7MiB (DefaultMaxMessageSize).
// messages. The default value is 1MiB (DefaultMaxMessageSize).
//
// Observe the following warnings when setting this option.
//
// WARNING #1: Make sure to change the default protocol prefixes for floodsub
// (FloodSubID) and BlossomSub (BlossomSubID). This avoids accidentally joining
// the public default network, which uses the default max message size, and
// therefore will cause messages to be dropped.
// WARNING #1: Make sure to change the default protocol prefixes for BlossomSub
// (BlossomSubID). This avoids accidentally joining the public default network,
// which uses the default max message size, and therefore will cause messages to
// be dropped.
//
// WARNING #2: Reducing the default max message limit is fine, if you are
// certain that your application messages will not exceed the new limit.
@ -563,11 +566,13 @@ func WithAppSpecificRpcInspector(inspector func(peer.ID, *RPC) error) Option {
// processLoop handles all inputs arriving on the channels
func (p *PubSub) processLoop(ctx context.Context) {
defer func() {
p.peersMx.Lock()
// Clean up go routines.
for _, ch := range p.peers {
close(ch)
}
p.peers = nil
p.peersMx.Unlock()
p.bitmasks = nil
p.seenMessages.Done()
}()
@ -580,7 +585,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
case s := <-p.newPeerStream:
pid := s.Conn().RemotePeer()
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
if !ok {
log.Warn("new stream for unknown peer: ", pid)
s.Reset()
@ -590,7 +597,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
if p.blacklist.Contains(pid) {
log.Warn("closing stream for blacklisted peer: ", pid)
close(ch)
p.peersMx.Lock()
delete(p.peers, pid)
p.peersMx.Unlock()
s.Reset()
continue
}
@ -598,7 +607,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
p.rt.AddPeer(pid, s.Protocol())
case pid := <-p.newPeerError:
p.peersMx.Lock()
delete(p.peers, pid)
p.peersMx.Unlock()
case <-p.peerDead:
p.handleDeadPeers()
@ -622,22 +633,13 @@ func (p *PubSub) processLoop(ctx context.Context) {
case bitmask := <-p.rmRelay:
p.handleRemoveRelay([]byte(bitmask))
case preq := <-p.getPeers:
tmap, ok := p.bitmasks[string(preq.bitmask)]
if preq.bitmask != nil && !ok {
peers := p.getPeersInBitmask(preq.bitmask)
if len(peers) == 0 {
preq.resp <- nil
continue
} else {
preq.resp <- peers
}
var peers []peer.ID
for p := range p.peers {
if preq.bitmask != nil {
_, ok := tmap[p]
if !ok {
continue
}
}
peers = append(peers, p)
}
preq.resp <- peers
case rpc := <-p.incoming:
p.handleIncomingRPC(rpc)
@ -657,10 +659,14 @@ func (p *PubSub) processLoop(ctx context.Context) {
log.Infof("Blacklisting peer %s", pid)
p.blacklist.Add(pid)
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
if ok {
close(ch)
p.peersMx.Lock()
delete(p.peers, pid)
p.peersMx.Unlock()
for t, tmap := range p.bitmasks {
if _, ok := tmap[pid]; ok {
delete(tmap, pid)
@ -677,6 +683,49 @@ func (p *PubSub) processLoop(ctx context.Context) {
}
}
func (p *PubSub) getPeersInBitmask(bitmask []byte) []peer.ID {
bitmaskSlices := SliceBitmask(bitmask)
var peers []peer.ID
peerloop:
for _, slice := range bitmaskSlices {
tmap, ok := p.bitmasks[string(slice)]
if !ok {
peers = []peer.ID{}
break peerloop
}
var peerset []peer.ID
p.peersMx.RLock()
for p := range p.peers {
_, ok := tmap[p]
if !ok {
continue
}
peerset = append(peerset, p)
}
p.peersMx.RUnlock()
if len(peers) == 0 {
peers = peerset
} else {
var update []peer.ID
for _, p := range peers {
if slices.Contains(peerset, p) {
update = append(update, p)
}
}
peers = update
if len(update) == 0 {
break peerloop
}
}
}
return peers
}
func (p *PubSub) handlePendingPeers() {
p.newPeersPrioLk.Lock()
@ -694,10 +743,13 @@ func (p *PubSub) handlePendingPeers() {
continue
}
p.peersMx.RLock()
if _, ok := p.peers[pid]; ok {
p.peersMx.RUnlock()
log.Debug("already have connection to peer: ", pid)
continue
}
p.peersMx.RUnlock()
if p.blacklist.Contains(pid) {
log.Warn("ignoring connection from blacklisted peer: ", pid)
@ -707,7 +759,9 @@ func (p *PubSub) handlePendingPeers() {
messages := make(chan *RPC, p.peerOutboundQueueSize)
messages <- p.getHelloPacket()
go p.handleNewPeer(p.ctx, pid, messages)
p.peersMx.Lock()
p.peers[pid] = messages
p.peersMx.Unlock()
}
}
@ -724,13 +778,17 @@ func (p *PubSub) handleDeadPeers() {
p.peerDeadPrioLk.Unlock()
for pid := range deadPeers {
p.peersMx.RLock()
ch, ok := p.peers[pid]
p.peersMx.RUnlock()
if !ok {
continue
}
close(ch)
p.peersMx.Lock()
delete(p.peers, pid)
p.peersMx.Unlock()
for t, tmap := range p.bitmasks {
if _, ok := tmap[pid]; ok {
@ -753,7 +811,9 @@ func (p *PubSub) handleDeadPeers() {
log.Debugf("peer declared dead but still connected; respawning writer: %s", pid)
messages := make(chan *RPC, p.peerOutboundQueueSize)
messages <- p.getHelloPacket()
p.peersMx.Lock()
p.peers[pid] = messages
p.peersMx.Unlock()
go p.handleNewPeerWithBackoff(p.ctx, pid, backoffDelay, messages)
}
}
@ -917,6 +977,7 @@ func (p *PubSub) announce(bitmask []byte, sub bool) {
}
out := rpcWithSubs(subopt)
p.peersMx.RLock()
for pid, peer := range p.peers {
select {
case peer <- out:
@ -927,6 +988,7 @@ func (p *PubSub) announce(bitmask []byte, sub bool) {
go p.announceRetry(pid, bitmask, sub)
}
}
p.peersMx.RUnlock()
}
func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
@ -950,7 +1012,9 @@ func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
}
func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
p.peersMx.RLock()
peer, ok := p.peers[pid]
p.peersMx.RUnlock()
if !ok {
return
}
@ -975,14 +1039,13 @@ func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
// Only called from processLoop.
func (p *PubSub) notifySubs(msg *Message) {
bitmask := msg.GetBitmask()
subs := p.mySubs[string(bitmask)]
slices := SliceBitmask(bitmask)
// o := rand.Intn(len(slices))
subs := p.mySubs[string(slices[0])]
for f := range subs {
select {
case f.ch <- msg:
case <-time.After(5 * time.Millisecond):
// it's unreasonable to immediately fall over because a subscriber didn't
// answer, message delivery sometimes lands next nanosecond and dropping
// it when there's room is absurd.
default:
p.tracer.UndeliverableMessage(msg)
log.Infof("Can't deliver message to subscription for bitmask %x; subscriber too slow", bitmask)
}
@ -990,14 +1053,14 @@ func (p *PubSub) notifySubs(msg *Message) {
}
// seenMessage returns whether we already saw this message before
func (p *PubSub) seenMessage(id string) bool {
return p.seenMessages.Has(id)
func (p *PubSub) seenMessage(id []byte) bool {
return p.seenMessages.Has(string(id))
}
// markSeen marks a message as seen such that seenMessage returns `true' for the given id
// returns true if the message was freshly marked
func (p *PubSub) markSeen(id string) bool {
return p.seenMessages.Add(id)
func (p *PubSub) markSeen(id []byte) bool {
return p.seenMessages.Add(string(id))
}
// subscribedToMessage returns whether we are subscribed to one of the bitmasks
@ -1008,9 +1071,15 @@ func (p *PubSub) subscribedToMsg(msg *pb.Message) bool {
}
bitmask := msg.GetBitmask()
_, ok := p.mySubs[string(bitmask)]
slices := SliceBitmask(bitmask)
for _, slice := range slices {
_, ok := p.mySubs[string(slice)]
if !ok {
return false
}
}
return ok
return true
}
// canRelayMsg returns whether we are able to relay for one of the bitmasks
@ -1021,9 +1090,15 @@ func (p *PubSub) canRelayMsg(msg *pb.Message) bool {
}
bitmask := msg.GetBitmask()
relays := p.myRelays[string(bitmask)]
slices := SliceBitmask(bitmask)
for _, slice := range slices {
relays := p.myRelays[string(slice)]
if relays > 0 {
return true
}
}
return relays > 0
return false
}
func (p *PubSub) notifyLeave(bitmask []byte, pid peer.ID) {
@ -1049,7 +1124,7 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
var err error
subs, err = p.subFilter.FilterIncomingSubscriptions(rpc.from, subs)
if err != nil {
log.Debugf("subscription filter error: %s; ignoring RPC", err)
log.Debugf("subscription filter error: %s; ignoring RPC\n", err)
return
}
}
@ -1103,7 +1178,7 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
continue
}
p.pushMsg(&Message{pmsg, "", rpc.from, nil, false})
p.pushMsg(&Message{pmsg, []byte{}, rpc.from, nil, false})
}
}
@ -1111,8 +1186,10 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
}
// DefaultMsgIdFn returns a unique ID of the passed Message
func DefaultMsgIdFn(pmsg *pb.Message) string {
return string(pmsg.GetFrom()) + string(pmsg.GetSeqno())
func DefaultMsgIdFn(pmsg *pb.Message) []byte {
h := sha256.New()
h.Write(pmsg.Data)
return h.Sum([]byte{0x01})
}
// DefaultPeerFilter accepts all peers on all bitmasks
@ -1233,59 +1310,75 @@ func (p *PubSub) PeerScore(pr peer.ID) float64 {
return p.rt.PeerScore(pr)
}
// Join joins the bitmask and returns a Bitmask handle. Only one Bitmask handle should exist per bitmask, and Join will error if
// the Bitmask handle already exists.
func (p *PubSub) Join(bitmask []byte, opts ...BitmaskOpt) (*Bitmask, error) {
t, ok, err := p.tryJoin(bitmask, opts...)
if err != nil {
return nil, err
// Join joins the bitmasks and returns a set of Bitmask handles. Only one Bitmask
// handle should exist per bit, and Join will error if all the Bitmask handles already exist.
func (p *PubSub) Join(bitmask []byte, opts ...BitmaskOpt) ([]*Bitmask, error) {
ts, news, errs := p.tryJoin(bitmask, opts...)
if len(errs) != 0 {
return nil, errors.Join(errs...)
}
if !ok {
if !slices.Contains(news, true) {
return nil, fmt.Errorf("bitmask already exists")
}
return t, nil
return ts, nil
}
// tryJoin is an internal function that tries to join a bitmask
// Returns the bitmask if it can be created or found
// Returns true if the bitmask was newly created, false otherwise
// Can be removed once pubsub.Publish() and pubsub.Subscribe() are removed
func (p *PubSub) tryJoin(bitmask []byte, opts ...BitmaskOpt) (*Bitmask, bool, error) {
func (p *PubSub) tryJoin(bitmask []byte, opts ...BitmaskOpt) ([]*Bitmask, []bool, []error) {
if p.subFilter != nil && !p.subFilter.CanSubscribe(bitmask) {
return nil, false, fmt.Errorf("bitmask is not allowed by the subscription filter")
return nil, nil, []error{fmt.Errorf("bitmask is not allowed by the subscription filter")}
}
t := &Bitmask{
p: p,
bitmask: bitmask,
evtHandlers: make(map[*BitmaskEventHandler]struct{}),
}
sliced := SliceBitmask(bitmask)
var bitmasks []*Bitmask
var newBitmasks []bool
var errors []error
for _, opt := range opts {
err := opt(t)
if err != nil {
return nil, false, err
loop:
for _, slice := range sliced {
slice := slice
t := &Bitmask{
p: p,
bitmask: slice,
evtHandlers: make(map[*BitmaskEventHandler]struct{}),
}
for _, opt := range opts {
err := opt(t)
if err != nil {
errors = append(errors, err)
continue loop
}
}
resp := make(chan *Bitmask, 1)
select {
case t.p.addBitmask <- &addBitmaskReq{
bitmask: t,
resp: resp,
}:
case <-t.p.ctx.Done():
errors = append(errors, t.p.ctx.Err())
continue loop
}
returnedBitmask := <-resp
if returnedBitmask != t {
bitmasks = append(bitmasks, returnedBitmask)
newBitmasks = append(newBitmasks, false)
} else {
bitmasks = append(bitmasks, t)
newBitmasks = append(newBitmasks, true)
}
}
resp := make(chan *Bitmask, 1)
select {
case t.p.addBitmask <- &addBitmaskReq{
bitmask: t,
resp: resp,
}:
case <-t.p.ctx.Done():
return nil, false, t.p.ctx.Err()
}
returnedBitmask := <-resp
if returnedBitmask != t {
return returnedBitmask, false, nil
}
return t, true, nil
return bitmasks, newBitmasks, errors
}
type addSubReq struct {
@ -1300,14 +1393,24 @@ type SubOpt func(sub *Subscription) error
// before the subscription is processed by the pubsub main loop and propagated to our peers.
//
// Deprecated: use pubsub.Join() and bitmask.Subscribe() instead
func (p *PubSub) Subscribe(bitmask []byte, opts ...SubOpt) (*Subscription, error) {
func (p *PubSub) Subscribe(bitmask []byte, opts ...SubOpt) ([]*Subscription, error) {
// ignore whether the bitmask was newly created or not, since either way we have a valid bitmask to work with
bitmaskHandle, _, err := p.tryJoin(bitmask)
if err != nil {
return nil, err
bitmaskHandles, _, errs := p.tryJoin(bitmask)
if len(errs) != 0 {
return nil, errors.Join(errs...)
}
return bitmaskHandle.Subscribe(opts...)
var subs []*Subscription
for _, handle := range bitmaskHandles {
sub, err := handle.Subscribe(opts...)
if err != nil {
return nil, err
}
subs = append(subs, sub)
}
return subs, nil
}
// WithBufferSize is a Subscribe option to customize the size of the subscribe output buffer.
@ -1335,17 +1438,20 @@ func (p *PubSub) GetBitmasks() []string {
return <-out
}
// Publish publishes data to the given bitmask.
//
// Deprecated: use pubsub.Join() and bitmask.Publish() instead
func (p *PubSub) Publish(bitmask []byte, data []byte, opts ...PubOpt) error {
// ignore whether the bitmask was newly created or not, since either way we have a valid bitmask to work with
t, _, err := p.tryJoin(bitmask)
if err != nil {
return err
func (p *PubSub) Publish(ctx context.Context, bitmask []byte, data []byte, opts ...PubOpt) error {
peers := p.ListPeers(bitmask)
if len(peers) == 0 {
return ErrBitmaskClosed
}
return t.Publish(context.TODO(), data, opts...)
slices := SliceBitmask(bitmask)
o := rand.Intn(len(slices))
b, _, errs := p.tryJoin(slices[o])
if len(errs) != 0 {
return errors.Join(errs...)
}
return b[0].Publish(ctx, bitmask, data, opts...)
}
func (p *PubSub) nextSeqno() []byte {
@ -1430,3 +1536,34 @@ type addRelayReq struct {
bitmask []byte
resp chan RelayCancelFunc
}
func SliceBitmask(bitmask []byte) [][]byte {
sliced := [][]byte{}
if bytes.Equal(bitmask, make([]byte, len(bitmask))) {
sliced = append(sliced, bitmask)
} else {
for i, b := range bitmask {
if b == 0 {
continue
}
// fast: one bit in byte
if b&(b-1) == 0 {
slice := make([]byte, len(bitmask))
slice[i] = b
sliced = append(sliced, slice)
continue
}
for j := 7; j >= 0; j-- {
if (b>>j)&1 == 1 {
slice := make([]byte, len(bitmask))
slice[i] = 1 << j
sliced = append(sliced, slice)
}
}
}
}
return sliced
}

View File

@ -2,48 +2,98 @@ package blossomsub
import (
"context"
"crypto/rand"
"testing"
"time"
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
)
// See https://source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/issues/426
func TestPubSubRemovesBlacklistedPeer(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
hosts := getNetHosts(t, ctx, 2)
hosts := getDefaultHosts(t, 2)
bl := NewMapBlacklist()
psubs0 := getPubsub(ctx, hosts[0])
psubs1 := getPubsub(ctx, hosts[1], WithBlacklist(bl))
psubs0 := getBlossomSub(ctx, hosts[0])
psubs1 := getBlossomSub(ctx, hosts[1], WithBlacklist(bl))
connect(t, hosts[0], hosts[1])
// Bad peer is blacklisted after it has connected.
// Calling p.BlacklistPeer directly does the right thing but we should also clean
// up the peer if it has been added the the blacklist by another means.
bl.Add(hosts[0].ID())
_, err := psubs0.Subscribe([]byte{0x7e, 0x57})
bitmasks, err := psubs0.Join([]byte{0x01, 0x00})
if err != nil {
t.Fatal(err)
}
sub1, err := psubs1.Subscribe([]byte{0x7e, 0x57})
_, err = psubs0.Subscribe([]byte{0x01, 0x00})
if err != nil {
t.Fatal(err)
}
sub1, err := psubs1.Subscribe([]byte{0x01, 0x00})
if err != nil {
t.Fatal(err)
}
time.Sleep(time.Millisecond * 100)
psubs0.Publish([]byte{0x7e, 0x57}, []byte("message"))
bitmasks[0].Publish(ctx, []byte{0x01, 0x00}, []byte("message"))
wctx, cancel2 := context.WithTimeout(ctx, 1*time.Second)
defer cancel2()
_, _ = sub1.Next(wctx)
_, _ = sub1[0].Next(wctx)
// Explicitly cancel context so PubSub cleans up peer channels.
// Issue 426 reports a panic due to a peer channel being closed twice.
cancel()
time.Sleep(time.Millisecond * 100)
}
func TestSliceBitmask(t *testing.T) {
fullVector := []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
partialVector := []byte{
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
outputs := SliceBitmask(fullVector)
if len(outputs) != 256 {
t.Fatalf("output length mismatch: %d, expected %d", len(outputs), 256)
}
outputs = SliceBitmask(partialVector)
if len(outputs) != 4 {
t.Fatalf("output length mismatch: %d, expected %d", len(outputs), 4)
}
}
func TestDefaultMsgIdFn(t *testing.T) {
for i := 0; i < 10; i++ {
data := make([]byte, 1024)
rand.Read(data)
// for v2, prepends 0x01
out := DefaultMsgIdFn(&pb.Message{
Data: data,
})
if len(out) != 33 {
t.Fatalf("length mismatch for msg id fn: %d, expected %d\n", len(out), 33)
}
if out[0] != 0x01 {
t.Fatalf("missing prefix byte for msg id fn: %x, expected %x\n", out[:1], []byte{0x01})
}
}
}

View File

@ -1,172 +0,0 @@
package blossomsub
import (
"context"
"math"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
const (
RandomSubID = protocol.ID("/randomsub/1.0.0")
)
var (
RandomSubD = 6
)
// NewRandomSub returns a new PubSub object using RandomSubRouter as the router.
func NewRandomSub(ctx context.Context, h host.Host, size int, opts ...Option) (*PubSub, error) {
rt := &RandomSubRouter{
size: size,
peers: make(map[peer.ID]protocol.ID),
}
return NewPubSub(ctx, h, rt, opts...)
}
// RandomSubRouter is a router that implements a random propagation strategy.
// For each message, it selects the square root of the network size peers, with a min of RandomSubD,
// and forwards the message to them.
type RandomSubRouter struct {
p *PubSub
peers map[peer.ID]protocol.ID
size int
tracer *pubsubTracer
}
func (rs *RandomSubRouter) Protocols() []protocol.ID {
return []protocol.ID{RandomSubID, FloodSubID}
}
func (rs *RandomSubRouter) Attach(p *PubSub) {
rs.p = p
rs.tracer = p.tracer
}
func (rs *RandomSubRouter) PeerScore(p peer.ID) float64 {
return rs.p.PeerScore(p)
}
func (rs *RandomSubRouter) AddPeer(p peer.ID, proto protocol.ID) {
rs.tracer.AddPeer(p, proto)
rs.peers[p] = proto
}
func (rs *RandomSubRouter) RemovePeer(p peer.ID) {
rs.tracer.RemovePeer(p)
delete(rs.peers, p)
}
func (rs *RandomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
// check all peers in the bitmask
tmap, ok := rs.p.bitmasks[string(bitmask)]
if !ok {
return false
}
fsPeers := 0
rsPeers := 0
// count floodsub and randomsub peers
for p := range tmap {
switch rs.peers[p] {
case FloodSubID:
fsPeers++
case RandomSubID:
rsPeers++
}
}
if suggested == 0 {
suggested = RandomSubD
}
if fsPeers+rsPeers >= suggested {
return true
}
if rsPeers >= RandomSubD {
return true
}
return false
}
func (rs *RandomSubRouter) AcceptFrom(peer.ID) AcceptStatus {
return AcceptAll
}
func (rs *RandomSubRouter) HandleRPC(rpc *RPC) {}
func (rs *RandomSubRouter) Publish(msg *Message) {
from := msg.ReceivedFrom
tosend := make(map[peer.ID]struct{})
rspeers := make(map[peer.ID]struct{})
src := peer.ID(msg.GetFrom())
bitmask := msg.GetBitmask()
tmap, ok := rs.p.bitmasks[string(bitmask)]
if !ok {
return
}
for p := range tmap {
if p == from || p == src {
continue
}
if rs.peers[p] == FloodSubID {
tosend[p] = struct{}{}
} else {
rspeers[p] = struct{}{}
}
}
if len(rspeers) > RandomSubD {
target := RandomSubD
sqrt := int(math.Ceil(math.Sqrt(float64(rs.size))))
if sqrt > target {
target = sqrt
}
if target > len(rspeers) {
target = len(rspeers)
}
xpeers := peerMapToList(rspeers)
shufflePeers(xpeers)
xpeers = xpeers[:target]
for _, p := range xpeers {
tosend[p] = struct{}{}
}
} else {
for p := range rspeers {
tosend[p] = struct{}{}
}
}
out := rpcWithMessages(msg.Message)
for p := range tosend {
mch, ok := rs.p.peers[p]
if !ok {
continue
}
select {
case mch <- out:
rs.tracer.SendRPC(out, p)
default:
log.Infof("dropping message to peer %s: queue full", p)
rs.tracer.DropRPC(out, p)
}
}
}
func (rs *RandomSubRouter) Join(bitmask []byte) {
rs.tracer.Join(bitmask)
}
func (rs *RandomSubRouter) Leave(bitmask []byte) {
rs.tracer.Join(bitmask)
}

View File

@ -1,192 +0,0 @@
package blossomsub
import (
"context"
"fmt"
"testing"
"time"
"github.com/libp2p/go-libp2p/core/host"
)
func getRandomsub(ctx context.Context, h host.Host, size int, opts ...Option) *PubSub {
ps, err := NewRandomSub(ctx, h, size, opts...)
if err != nil {
panic(err)
}
return ps
}
func getRandomsubs(ctx context.Context, hs []host.Host, size int, opts ...Option) []*PubSub {
var psubs []*PubSub
for _, h := range hs {
psubs = append(psubs, getRandomsub(ctx, h, size, opts...))
}
return psubs
}
func tryReceive(sub *Subscription) *Message {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
m, err := sub.Next(ctx)
if err != nil {
return nil
} else {
return m
}
}
func TestRandomsubSmall(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 10)
psubs := getRandomsubs(ctx, hosts, 10)
connectAll(t, hosts)
var subs []*Subscription
for _, ps := range psubs {
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
if err != nil {
t.Fatal(err)
}
subs = append(subs, sub)
}
time.Sleep(time.Second)
count := 0
for i := 0; i < 10; i++ {
msg := []byte(fmt.Sprintf("message %d", i))
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
for _, sub := range subs {
if tryReceive(sub) != nil {
count++
}
}
}
if count < 7*len(hosts) {
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
}
}
func TestRandomsubBig(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 50)
psubs := getRandomsubs(ctx, hosts, 50)
connectSome(t, hosts, 12)
var subs []*Subscription
for _, ps := range psubs {
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
if err != nil {
t.Fatal(err)
}
subs = append(subs, sub)
}
time.Sleep(time.Second)
count := 0
for i := 0; i < 10; i++ {
msg := []byte(fmt.Sprintf("message %d", i))
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
for _, sub := range subs {
if tryReceive(sub) != nil {
count++
}
}
}
if count < 7*len(hosts) {
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
}
}
func TestRandomsubMixed(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 40)
fsubs := getPubsubs(ctx, hosts[:10])
rsubs := getRandomsubs(ctx, hosts[10:], 30)
psubs := append(fsubs, rsubs...)
connectSome(t, hosts, 12)
var subs []*Subscription
for _, ps := range psubs {
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
if err != nil {
t.Fatal(err)
}
subs = append(subs, sub)
}
time.Sleep(time.Second)
count := 0
for i := 0; i < 10; i++ {
msg := []byte(fmt.Sprintf("message %d", i))
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
for _, sub := range subs {
if tryReceive(sub) != nil {
count++
}
}
}
if count < 7*len(hosts) {
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
}
}
func TestRandomsubEnoughPeers(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 40)
fsubs := getPubsubs(ctx, hosts[:10])
rsubs := getRandomsubs(ctx, hosts[10:], 30)
psubs := append(fsubs, rsubs...)
connectSome(t, hosts, 12)
for _, ps := range psubs {
_, err := ps.Subscribe([]byte{0x7e, 0x57})
if err != nil {
t.Fatal(err)
}
}
time.Sleep(time.Second)
res := make(chan bool, 1)
rsubs[0].eval <- func() {
rs := rsubs[0].rt.(*RandomSubRouter)
res <- rs.EnoughPeers([]byte{0x7e, 0x57}, 0)
}
enough := <-res
if !enough {
t.Fatal("expected enough peers")
}
rsubs[0].eval <- func() {
rs := rsubs[0].rt.(*RandomSubRouter)
res <- rs.EnoughPeers([]byte{0x7e, 0x57}, 100)
}
enough = <-res
if !enough {
t.Fatal("expected enough peers")
}
}

View File

@ -105,7 +105,7 @@ type deliveryRecord struct {
}
type deliveryEntry struct {
id string
id []byte
expire time.Time
next *deliveryEntry
}
@ -200,12 +200,12 @@ func newPeerScore(params *PeerScoreParams) *peerScore {
// Note: assumes that the bitmask score parameters have already been validated
func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams) error {
ps.Lock()
defer ps.Unlock()
old, exist := ps.params.Bitmasks[string(bitmask)]
ps.params.Bitmasks[string(bitmask)] = p
if !exist {
ps.Unlock()
return nil
}
@ -218,6 +218,7 @@ func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams
recap = true
}
if !recap {
ps.Unlock()
return nil
}
@ -236,7 +237,7 @@ func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams
tstats.meshMessageDeliveries = p.MeshMessageDeliveriesCap
}
}
ps.Unlock()
return nil
}
@ -257,9 +258,10 @@ func (ps *peerScore) Score(p peer.ID) float64 {
}
ps.Lock()
defer ps.Unlock()
return ps.score(p)
score := ps.score(p)
ps.Unlock()
return score
}
func (ps *peerScore) score(p peer.ID) float64 {
@ -394,14 +396,15 @@ func (ps *peerScore) AddPenalty(p peer.ID, count int) {
}
ps.Lock()
defer ps.Unlock()
pstats, ok := ps.peerStats[p]
if !ok {
ps.Unlock()
return
}
pstats.behaviourPenalty += float64(count)
ps.Unlock()
}
// periodic maintenance
@ -503,7 +506,6 @@ func (ps *peerScore) inspectScoresExtended() {
// once their expiry has elapsed.
func (ps *peerScore) refreshScores() {
ps.Lock()
defer ps.Unlock()
now := time.Now()
for p, pstats := range ps.peerStats {
@ -562,12 +564,13 @@ func (ps *peerScore) refreshScores() {
pstats.behaviourPenalty = 0
}
}
ps.Unlock()
}
// refreshIPs refreshes IPs we know of peers we're tracking.
func (ps *peerScore) refreshIPs() {
ps.Lock()
defer ps.Unlock()
// peer IPs may change, so we periodically refresh them
//
@ -582,19 +585,20 @@ func (ps *peerScore) refreshIPs() {
pstats.ips = ips
}
}
ps.Unlock()
}
func (ps *peerScore) gcDeliveryRecords() {
ps.Lock()
defer ps.Unlock()
ps.deliveries.gc()
ps.Unlock()
}
// tracer interface
func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) {
ps.Lock()
defer ps.Unlock()
pstats, ok := ps.peerStats[p]
if !ok {
@ -606,14 +610,15 @@ func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) {
ips := ps.getIPs(p)
ps.setIPs(p, ips, pstats.ips)
pstats.ips = ips
ps.Unlock()
}
func (ps *peerScore) RemovePeer(p peer.ID) {
ps.Lock()
defer ps.Unlock()
pstats, ok := ps.peerStats[p]
if !ok {
ps.Unlock()
return
}
@ -622,6 +627,7 @@ func (ps *peerScore) RemovePeer(p peer.ID) {
if ps.score(p) > 0 {
ps.removeIPs(p, pstats.ips)
delete(ps.peerStats, p)
ps.Unlock()
return
}
@ -641,6 +647,7 @@ func (ps *peerScore) RemovePeer(p peer.ID) {
pstats.connected = false
pstats.expire = time.Now().Add(ps.params.RetainScore)
ps.Unlock()
}
func (ps *peerScore) Join(bitmask []byte) {}
@ -648,15 +655,16 @@ func (ps *peerScore) Leave(bitmask []byte) {}
func (ps *peerScore) Graft(p peer.ID, bitmask []byte) {
ps.Lock()
defer ps.Unlock()
pstats, ok := ps.peerStats[p]
if !ok {
ps.Unlock()
return
}
tstats, ok := pstats.getBitmaskStats(bitmask, ps.params)
if !ok {
ps.Unlock()
return
}
@ -664,19 +672,21 @@ func (ps *peerScore) Graft(p peer.ID, bitmask []byte) {
tstats.graftTime = time.Now()
tstats.meshTime = 0
tstats.meshMessageDeliveriesActive = false
ps.Unlock()
}
func (ps *peerScore) Prune(p peer.ID, bitmask []byte) {
ps.Lock()
defer ps.Unlock()
pstats, ok := ps.peerStats[p]
if !ok {
ps.Unlock()
return
}
tstats, ok := pstats.getBitmaskStats(bitmask, ps.params)
if !ok {
ps.Unlock()
return
}
@ -688,20 +698,20 @@ func (ps *peerScore) Prune(p peer.ID, bitmask []byte) {
}
tstats.inMesh = false
ps.Unlock()
}
func (ps *peerScore) ValidateMessage(msg *Message) {
ps.Lock()
defer ps.Unlock()
// the pubsub subsystem is beginning validation; create a record to track time in
// the validation pipeline with an accurate firstSeen time.
_ = ps.deliveries.getRecord(ps.idGen.ID(msg))
ps.Unlock()
}
func (ps *peerScore) DeliverMessage(msg *Message) {
ps.Lock()
defer ps.Unlock()
ps.markFirstMessageDelivery(msg.ReceivedFrom, msg)
@ -710,6 +720,7 @@ func (ps *peerScore) DeliverMessage(msg *Message) {
// defensive check that this is the first delivery trace -- delivery status should be unknown
if drec.status != deliveryUnknown {
log.Debugf("unexpected delivery trace: message from %s was first seen %s ago and has delivery status %d", msg.ReceivedFrom, time.Since(drec.firstSeen), drec.status)
ps.Unlock()
return
}
@ -723,11 +734,11 @@ func (ps *peerScore) DeliverMessage(msg *Message) {
ps.markDuplicateMessageDelivery(p, msg, time.Time{})
}
}
ps.Unlock()
}
func (ps *peerScore) RejectMessage(msg *Message, reason string) {
ps.Lock()
defer ps.Unlock()
switch reason {
// we don't track those messages, but we penalize the peer as they are clearly invalid
@ -741,18 +752,21 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
fallthrough
case RejectSelfOrigin:
ps.markInvalidMessageDelivery(msg.ReceivedFrom, msg)
ps.Unlock()
return
// we ignore those messages, so do nothing.
case RejectBlacklstedPeer:
fallthrough
case RejectBlacklistedSource:
ps.Unlock()
return
case RejectValidationQueueFull:
// the message was rejected before it entered the validation pipeline;
// we don't know if this message has a valid signature, and thus we also don't know if
// it has a valid message ID; all we can do is ignore it.
ps.Unlock()
return
}
@ -761,6 +775,7 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
// defensive check that this is the first rejection trace -- delivery status should be unknown
if drec.status != deliveryUnknown {
log.Debugf("unexpected rejection trace: message from %s was first seen %s ago and has delivery status %d", msg.ReceivedFrom, time.Since(drec.firstSeen), drec.status)
ps.Unlock()
return
}
@ -771,12 +786,14 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
drec.status = deliveryThrottled
// release the delivery time tracking map to free some memory early
drec.peers = nil
ps.Unlock()
return
case RejectValidationIgnored:
// we were explicitly instructed by the validator to ignore the message but not penalize
// the peer
drec.status = deliveryIgnored
drec.peers = nil
ps.Unlock()
return
}
@ -790,17 +807,18 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
// release the delivery time tracking map to free some memory early
drec.peers = nil
ps.Unlock()
}
func (ps *peerScore) DuplicateMessage(msg *Message) {
ps.Lock()
defer ps.Unlock()
drec := ps.deliveries.getRecord(ps.idGen.ID(msg))
_, ok := drec.peers[msg.ReceivedFrom]
if ok {
// we have already seen this duplicate!
ps.Unlock()
return
}
@ -824,6 +842,7 @@ func (ps *peerScore) DuplicateMessage(msg *Message) {
case deliveryIgnored:
// the message was ignored; do nothing
}
ps.Unlock()
}
func (ps *peerScore) ThrottlePeer(p peer.ID) {}
@ -837,8 +856,8 @@ func (ps *peerScore) DropRPC(rpc *RPC, p peer.ID) {}
func (ps *peerScore) UndeliverableMessage(msg *Message) {}
// message delivery records
func (d *messageDeliveries) getRecord(id string) *deliveryRecord {
rec, ok := d.records[id]
func (d *messageDeliveries) getRecord(id []byte) *deliveryRecord {
rec, ok := d.records[string(id)]
if ok {
return rec
}
@ -846,7 +865,7 @@ func (d *messageDeliveries) getRecord(id string) *deliveryRecord {
now := time.Now()
rec = &deliveryRecord{peers: make(map[peer.ID]struct{}), firstSeen: now}
d.records[id] = rec
d.records[string(id)] = rec
entry := &deliveryEntry{id: id, expire: now.Add(d.seenMsgTTL)}
if d.tail != nil {
@ -867,7 +886,7 @@ func (d *messageDeliveries) gc() {
now := time.Now()
for d.head != nil && now.After(d.head.expire) {
delete(d.records, d.head.id)
delete(d.records, string(d.head.id))
d.head = d.head.next
}

View File

@ -3,6 +3,7 @@ package blossomsub
import (
"fmt"
"google.golang.org/protobuf/proto"
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"github.com/libp2p/go-libp2p/core/crypto"
@ -26,10 +27,8 @@ const (
// StrictNoSign does not produce signatures and drops and penalises incoming messages that carry one
StrictNoSign = msgVerification
// LaxSign produces signatures and validates incoming signatures iff one is present
// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
LaxSign = msgSigning
// LaxNoSign does not produce signatures and validates incoming signatures iff one is present
// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
LaxNoSign = 0
)
@ -52,7 +51,7 @@ func verifyMessageSignature(m *pb.Message) error {
return err
}
xm := *m
xm := (proto.Clone(m)).(*pb.Message)
xm.Signature = nil
xm.Key = nil
bytes, err := xm.Marshal()

View File

@ -11,12 +11,25 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
)
func mustSubscribe(t *testing.T, ps *PubSub, bitmask []byte) *Subscription {
sub, err := ps.Subscribe(bitmask)
if err != nil {
t.Fatal(err)
}
if len(sub) != 1 {
t.Fatal("must subscribe only allows single bit bitmasks")
}
return sub[0]
}
func TestBasicSubscriptionFilter(t *testing.T) {
peerA := peer.ID("A")
bitmask1 := []byte{0xff, 0x00, 0x00, 0x00}
bitmask2 := []byte{0x00, 0xff, 0x00, 0x00}
bitmask3 := []byte{0x00, 0x00, 0xff, 0x00}
bitmask1 := []byte{0x00, 0x80, 0x00, 0x00}
bitmask2 := []byte{0x00, 0x20, 0x00, 0x00}
bitmask3 := []byte{0x00, 0x00, 0x02, 0x00}
yes := true
subs := []*pb.RPC_SubOpts{
&pb.RPC_SubOpts{
@ -69,9 +82,9 @@ func TestBasicSubscriptionFilter(t *testing.T) {
func TestSubscriptionFilterDeduplication(t *testing.T) {
peerA := peer.ID("A")
bitmask1 := []byte{0xff, 0x00, 0x00, 0x00}
bitmask2 := []byte{0x00, 0xff, 0x00, 0x00}
bitmask3 := []byte{0x00, 0x00, 0xff, 0x00}
bitmask1 := []byte{0x00, 0x80, 0x00, 0x00}
bitmask2 := []byte{0x00, 0x20, 0x00, 0x00}
bitmask3 := []byte{0x00, 0x00, 0x02, 0x00}
yes := true
no := false
subs := []*pb.RPC_SubOpts{
@ -117,17 +130,17 @@ func TestSubscriptionFilterRPC(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
hosts := getNetHosts(t, ctx, 2)
ps1 := getPubsub(ctx, hosts[0], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0xff, 0x00, 0x00, 0x00}, []byte{0x00, 0xff, 0x00, 0x00})))
ps2 := getPubsub(ctx, hosts[1], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0xff, 0x00, 0x00}, []byte{0x00, 0x00, 0xff, 0x00})))
hosts := getDefaultHosts(t, 2)
ps1 := getBlossomSub(ctx, hosts[0], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0x80, 0x00, 0x00}, []byte{0x00, 0x20, 0x00, 0x00})))
ps2 := getBlossomSub(ctx, hosts[1], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0x20, 0x00, 0x00}, []byte{0x00, 0x00, 0x02, 0x00})))
_ = mustSubscribe(t, ps1, []byte{0xff, 0x00, 0x00, 0x00})
_ = mustSubscribe(t, ps1, []byte{0x00, 0xff, 0x00, 0x00})
_ = mustSubscribe(t, ps2, []byte{0x00, 0xff, 0x00, 0x00})
_ = mustSubscribe(t, ps2, []byte{0x00, 0x00, 0xff, 0x00})
_ = mustSubscribe(t, ps1, []byte{0x00, 0x80, 0x00, 0x00})
_ = mustSubscribe(t, ps1, []byte{0x00, 0x20, 0x00, 0x00})
_ = mustSubscribe(t, ps2, []byte{0x00, 0x20, 0x00, 0x00})
_ = mustSubscribe(t, ps2, []byte{0x00, 0x00, 0x02, 0x00})
// check the rejection as well
_, err := ps1.Join([]byte{0x00, 0x00, 0xff, 0x00})
_, err := ps1.Join([]byte{0x00, 0x00, 0x02, 0x00})
if err == nil {
t.Fatal("expected subscription error")
}
@ -140,9 +153,9 @@ func TestSubscriptionFilterRPC(t *testing.T) {
ready := make(chan struct{})
ps1.eval <- func() {
_, sub1 = ps1.bitmasks[string([]byte{0xff, 0x00, 0x00, 0x00})][hosts[1].ID()]
_, sub2 = ps1.bitmasks[string([]byte{0x00, 0xff, 0x00, 0x00})][hosts[1].ID()]
_, sub3 = ps1.bitmasks[string([]byte{0x00, 0x00, 0xff, 0x00})][hosts[1].ID()]
_, sub1 = ps1.bitmasks[string([]byte{0x00, 0x80, 0x00, 0x00})][hosts[1].ID()]
_, sub2 = ps1.bitmasks[string([]byte{0x00, 0x20, 0x00, 0x00})][hosts[1].ID()]
_, sub3 = ps1.bitmasks[string([]byte{0x00, 0x00, 0x02, 0x00})][hosts[1].ID()]
ready <- struct{}{}
}
<-ready
@ -158,9 +171,9 @@ func TestSubscriptionFilterRPC(t *testing.T) {
}
ps2.eval <- func() {
_, sub1 = ps2.bitmasks[string([]byte{0xff, 0x00, 0x00, 0x00})][hosts[0].ID()]
_, sub2 = ps2.bitmasks[string([]byte{0x00, 0xff, 0x00, 0x00})][hosts[0].ID()]
_, sub3 = ps2.bitmasks[string([]byte{0x00, 0x00, 0xff, 0x00})][hosts[0].ID()]
_, sub1 = ps2.bitmasks[string([]byte{0x00, 0x80, 0x00, 0x00})][hosts[0].ID()]
_, sub2 = ps2.bitmasks[string([]byte{0x00, 0x20, 0x00, 0x00})][hosts[0].ID()]
_, sub3 = ps2.bitmasks[string([]byte{0x00, 0x00, 0x02, 0x00})][hosts[0].ID()]
ready <- struct{}{}
}
<-ready

View File

@ -109,9 +109,9 @@ func (t *tagTracer) addDeliveryTag(bitmask []byte) {
return
}
name := fmt.Sprintf("pubsub-deliveries:%s", bitmask)
name := "pubsub-deliveries:" + string(bitmask)
t.Lock()
defer t.Unlock()
tag, err := t.decayer.RegisterDecayingTag(
name,
BlossomSubConnTagDecayInterval,
@ -120,16 +120,19 @@ func (t *tagTracer) addDeliveryTag(bitmask []byte) {
if err != nil {
log.Warnf("unable to create decaying delivery tag: %s", err)
t.Unlock()
return
}
t.decaying[string(bitmask)] = tag
t.Unlock()
}
func (t *tagTracer) removeDeliveryTag(bitmask []byte) {
t.Lock()
defer t.Unlock()
tag, ok := t.decaying[string(bitmask)]
if !ok {
t.Unlock()
return
}
err := tag.Close()
@ -137,17 +140,20 @@ func (t *tagTracer) removeDeliveryTag(bitmask []byte) {
log.Warnf("error closing decaying connmgr tag: %s", err)
}
delete(t.decaying, string(bitmask))
t.Unlock()
}
func (t *tagTracer) bumpDeliveryTag(p peer.ID, bitmask []byte) error {
t.RLock()
defer t.RUnlock()
tag, ok := t.decaying[string(bitmask)]
if !ok {
t.RUnlock()
return fmt.Errorf("no decaying tag registered for bitmask %s", bitmask)
}
return tag.Bump(p, BlossomSubConnTagBumpMessageDelivery)
err := tag.Bump(p, BlossomSubConnTagBumpMessageDelivery)
t.RUnlock()
return err
}
func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) {
@ -161,15 +167,17 @@ func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) {
// nearFirstPeers returns the peers who delivered the message while it was still validating
func (t *tagTracer) nearFirstPeers(msg *Message) []peer.ID {
t.Lock()
defer t.Unlock()
peersMap, ok := t.nearFirst[t.idGen.ID(msg)]
peersMap, ok := t.nearFirst[string(t.idGen.ID(msg))]
if !ok {
t.Unlock()
return nil
}
peers := make([]peer.ID, 0, len(peersMap))
for p := range peersMap {
peers = append(peers, p)
}
t.Unlock()
return peers
}
@ -194,7 +202,7 @@ func (t *tagTracer) DeliverMessage(msg *Message) {
// delete the delivery state for this message
t.Lock()
delete(t.nearFirst, t.idGen.ID(msg))
delete(t.nearFirst, string(t.idGen.ID(msg)))
t.Unlock()
}
@ -212,31 +220,32 @@ func (t *tagTracer) Prune(p peer.ID, bitmask []byte) {
func (t *tagTracer) ValidateMessage(msg *Message) {
t.Lock()
defer t.Unlock()
// create map to start tracking the peers who deliver while we're validating
id := t.idGen.ID(msg)
if _, exists := t.nearFirst[id]; exists {
if _, exists := t.nearFirst[string(id)]; exists {
t.Unlock()
return
}
t.nearFirst[id] = make(map[peer.ID]struct{})
t.nearFirst[string(id)] = make(map[peer.ID]struct{})
t.Unlock()
}
func (t *tagTracer) DuplicateMessage(msg *Message) {
t.Lock()
defer t.Unlock()
id := t.idGen.ID(msg)
peers, ok := t.nearFirst[id]
peers, ok := t.nearFirst[string(id)]
if !ok {
t.Unlock()
return
}
peers[msg.ReceivedFrom] = struct{}{}
t.Unlock()
}
func (t *tagTracer) RejectMessage(msg *Message, reason string) {
t.Lock()
defer t.Unlock()
// We want to delete the near-first delivery tracking for messages that have passed through
// the validation pipeline. Other rejection reasons (missing signature, etc) skip the validation
@ -247,8 +256,9 @@ func (t *tagTracer) RejectMessage(msg *Message, reason string) {
case RejectValidationIgnored:
fallthrough
case RejectValidationFailed:
delete(t.nearFirst, t.idGen.ID(msg))
delete(t.nearFirst, string(t.idGen.ID(msg)))
}
t.Unlock()
}
func (t *tagTracer) RemovePeer(peer.ID) {}

View File

@ -55,9 +55,9 @@ func TestTagTracerDirectPeerTags(t *testing.T) {
tt.direct = make(map[peer.ID]struct{})
tt.direct[p1] = struct{}{}
tt.AddPeer(p1, BlossomSubID_v11)
tt.AddPeer(p2, BlossomSubID_v11)
tt.AddPeer(p3, BlossomSubID_v11)
tt.AddPeer(p1, BlossomSubID_v2)
tt.AddPeer(p2, BlossomSubID_v2)
tt.AddPeer(p3, BlossomSubID_v2)
tag := "pubsub:<direct>"
if !cmgr.IsProtected(p1, tag) {
@ -179,7 +179,7 @@ func TestTagTracerDeliveryTagsNearFirst(t *testing.T) {
tt := newTagTracer(cmgr)
bitmask := []byte{0x7e, 0x57}
bitmask := []byte{0x01, 0x00}
p := peer.ID("a-peer")
p2 := peer.ID("another-peer")

Some files were not shown because too many files have changed in this diff Show More