From 3d65852085cb4ffcedb34fbe55f39b5d9b1d10b3 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Thu, 23 Jan 2025 06:05:13 +0100 Subject: [PATCH 01/26] feat: add aux attribute in channel.Params Signed-off-by: Minh Huy Tran --- channel/params.go | 23 +- channel/test/backend.go | 1 + channel/test/randomizer.go | 3 +- channel/test/randomopts.go | 19 + client/proposal.go | 1 + client/proposalmsgs.go | 2 + client/proposalopts.go | 20 +- wire/perunio/serialize.go | 4 + wire/protobuf/proposalmsgs.go | 4 + wire/protobuf/updatemsgs.go | 8 +- wire/protobuf/wire.pb.go | 902 +++++++++++++++++----------------- wire/protobuf/wire.proto | 2 + 12 files changed, 541 insertions(+), 448 deletions(-) diff --git a/channel/params.go b/channel/params.go index d872b8ea6..dd70e13d7 100644 --- a/channel/params.go +++ b/channel/params.go @@ -40,6 +40,12 @@ const MinNumParts = 2 // Nonce is the channel parameters' nonce type. type Nonce = *big.Int +// AuxMaxLen is the maximum byte count of the auxiliary data. +const AuxMaxLen = 256 + +// Aux is the channel parameters' auxiliary data type. +type Aux = [AuxMaxLen]byte + // NonceFromBytes creates a nonce from a byte slice. func NonceFromBytes(b []byte) Nonce { if len(b) > MaxNonceLen { @@ -51,6 +57,8 @@ func NonceFromBytes(b []byte) Nonce { // Zero is the default channelID. var Zero = ID{} +var ZeroAux = Aux{} + var _ perunio.Serializer = (*Params)(nil) // Params are a channel's immutable parameters. A channel's id is the hash of @@ -73,6 +81,8 @@ type Params struct { LedgerChannel bool // VirtualChannel specifies whether this is a virtual channel. VirtualChannel bool + // Aux is an optional field that can be used to store additional information. + Aux Aux } // ID returns the channelID of this channel. @@ -84,11 +94,11 @@ func (p *Params) ID() ID { // appDef optional: if it is nil, it describes a payment channel. The channel id // is also calculated here and persisted because it probably is an expensive // hash operation. -func NewParams(challengeDuration uint64, parts []wallet.Address, app App, nonce Nonce, ledger bool, virtual bool) (*Params, error) { +func NewParams(challengeDuration uint64, parts []wallet.Address, app App, nonce Nonce, ledger bool, virtual bool, aux Aux) (*Params, error) { if err := ValidateParameters(challengeDuration, len(parts), app, nonce); err != nil { return nil, errors.WithMessage(err, "invalid parameter for NewParams") } - return NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual), nil + return NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual, aux), nil } // ValidateProposalParameters validates all parameters that are part of the @@ -130,7 +140,7 @@ func ValidateParameters(challengeDuration uint64, numParts int, app App, nonce N // NewParamsUnsafe creates Params from the given data and does NOT perform // sanity checks. The channel id is also calculated here and persisted because // it probably is an expensive hash operation. -func NewParamsUnsafe(challengeDuration uint64, parts []wallet.Address, app App, nonce Nonce, ledger bool, virtual bool) *Params { +func NewParamsUnsafe(challengeDuration uint64, parts []wallet.Address, app App, nonce Nonce, ledger bool, virtual bool, aux Aux) *Params { p := &Params{ ChallengeDuration: challengeDuration, Parts: parts, @@ -138,6 +148,7 @@ func NewParamsUnsafe(challengeDuration uint64, parts []wallet.Address, app App, Nonce: nonce, LedgerChannel: ledger, VirtualChannel: virtual, + Aux: aux, } // probably an expensive hash operation, do it only once during creation. p.id = CalcID(p) @@ -154,6 +165,7 @@ func (p *Params) Clone() *Params { Nonce: new(big.Int).Set(p.Nonce), LedgerChannel: p.LedgerChannel, VirtualChannel: p.VirtualChannel, + Aux: p.Aux, } } @@ -166,6 +178,7 @@ func (p *Params) Encode(w stdio.Writer) error { p.Nonce, p.LedgerChannel, p.VirtualChannel, + p.Aux, ) } @@ -178,6 +191,7 @@ func (p *Params) Decode(r stdio.Reader) error { nonce Nonce ledger bool virtual bool + aux Aux ) err := perunio.Decode(r, @@ -187,12 +201,13 @@ func (p *Params) Decode(r stdio.Reader) error { &nonce, &ledger, &virtual, + &aux, ) if err != nil { return err } - _p, err := NewParams(challengeDuration, parts, app, nonce, ledger, virtual) + _p, err := NewParams(challengeDuration, parts, app, nonce, ledger, virtual, aux) if err != nil { return err } diff --git a/channel/test/backend.go b/channel/test/backend.go index e109df926..c9856d96d 100644 --- a/channel/test/backend.go +++ b/channel/test/backend.go @@ -189,6 +189,7 @@ func appendModParams(a []channel.Params, modParams channel.Params) []channel.Par modParams.Nonce, modParams.LedgerChannel, modParams.VirtualChannel, + modParams.Aux, ) return append(a, *p) } diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index f1303f23e..99dee9648 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -173,8 +173,9 @@ func NewRandomParams(rng *rand.Rand, opts ...RandomOpt) *channel.Params { app := NewRandomApp(rng, opt) ledger := opt.LedgerChannel(rng) virtual := opt.VirtualChannel(rng) + aux := opt.Aux(rng) - params := channel.NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual) + params := channel.NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual, aux) updateOpts(opts, WithParams(params)) return params } diff --git a/channel/test/randomopts.go b/channel/test/randomopts.go index ba4d348e3..f0da0f174 100644 --- a/channel/test/randomopts.go +++ b/channel/test/randomopts.go @@ -211,6 +211,11 @@ func WithVirtualChannel(b bool) RandomOpt { return RandomOpt{nameVirtualChannel: b} } +// WithAux sets the `Aux` attribute. +func WithAux(aux channel.Aux) RandomOpt { + return RandomOpt{"aux": aux} +} + // Append inserts all `opts` into the receiving object and returns the result. // Overrides entries that occur more than once with the last occurrence. func (o RandomOpt) Append(opts ...RandomOpt) RandomOpt { @@ -423,6 +428,20 @@ func (o RandomOpt) VirtualChannel(rng io.Reader) bool { return o[nameVirtualChannel].(bool) } +// Aux returns the `Aux` value of the `RandomOpt`. +// If not present, a random value is generated with `rng` as entropy source. +func (o RandomOpt) Aux(rng io.Reader) channel.Aux { + if _, ok := o["aux"]; !ok { + a := make([]byte, channel.AuxMaxLen) + _, err := rng.Read(a) + if err != nil { + panic(err) + } + o["aux"] = channel.Aux(a) + } + return o["aux"].(channel.Aux) +} + // NumAssets returns the `NumAssets` value of the `RandomOpt`. // If not present, a random value is generated with `rng` as entropy source. func (o RandomOpt) NumAssets(rng *rand.Rand) int { diff --git a/client/proposal.go b/client/proposal.go index 39c524a2c..f888c5e85 100644 --- a/client/proposal.go +++ b/client/proposal.go @@ -584,6 +584,7 @@ func (c *Client) completeCPP( calcNonce(nonceShares(propBase.NonceShare, acc.Base().NonceShare)), prop.Type() == wire.LedgerChannelProposal, prop.Type() == wire.VirtualChannelProposal, + propBase.Aux, ) if c.channels.Has(params.ID()) { diff --git a/client/proposalmsgs.go b/client/proposalmsgs.go index 6115587ce..5164918e7 100644 --- a/client/proposalmsgs.go +++ b/client/proposalmsgs.go @@ -109,6 +109,7 @@ type ( InitData channel.Data // Initial App data. InitBals *channel.Allocation // Initial balances. FundingAgreement channel.Balances // Possibly different funding agreement from initial state's balances. + Aux channel.Aux // Auxiliary data. } // LedgerChannelProposalMsg is a channel proposal for ledger channels. @@ -177,6 +178,7 @@ func makeBaseChannelProposal( InitData: opt.AppData(), InitBals: initBals, FundingAgreement: fundingAgreement, + Aux: opt.aux(), }, nil } diff --git a/client/proposalopts.go b/client/proposalopts.go index ab1fbd48e..a3e0e45f2 100644 --- a/client/proposalopts.go +++ b/client/proposalopts.go @@ -27,7 +27,7 @@ import ( // NoData is set, and a random nonce share is generated. type ProposalOpts map[string]interface{} -var optNames = struct{ nonce, app, appData, fundingAgreement string }{nonce: "nonce", app: "app", appData: "appData", fundingAgreement: "fundingAgreement"} +var optNames = struct{ nonce, app, appData, fundingAgreement, aux string }{nonce: "nonce", app: "app", appData: "appData", fundingAgreement: "fundingAgreement", aux: "aux"} // App returns the option's configured app. func (o ProposalOpts) App() channel.App { @@ -92,6 +92,19 @@ func (o ProposalOpts) nonce() NonceShare { return share } +// aux returns the option's configured auxiliary data. +func (o ProposalOpts) aux() channel.Aux { + a, ok := o[optNames.aux] + if !ok { + return channel.ZeroAux + } + aux, ok := a.(channel.Aux) + if !ok { + log.Panicf("wrong type: expected []byte, got %T", a) + } + return aux +} + // isNonce returns whether a ProposalOpts contains a manually set nonce. func (o ProposalOpts) isNonce() bool { _, ok := o[optNames.nonce] @@ -122,6 +135,11 @@ func WithNonce(share NonceShare) ProposalOpts { return ProposalOpts{optNames.nonce: share} } +// WithAux configures an auxiliary data field. +func WithAux(aux []byte) ProposalOpts { + return ProposalOpts{optNames.aux: aux} +} + // WithNonceFrom reads a nonce share from a reader (should be random stream). func WithNonceFrom(r io.Reader) ProposalOpts { var share NonceShare diff --git a/wire/perunio/serialize.go b/wire/perunio/serialize.go index 259e0e805..d931ffcb0 100644 --- a/wire/perunio/serialize.go +++ b/wire/perunio/serialize.go @@ -43,6 +43,8 @@ func Encode(writer io.Writer, values ...interface{}) (err error) { //nolint: cyc err = BigInt{v}.Encode(writer) case [32]byte: _, err = writer.Write(v[:]) + case [256]byte: + _, err = writer.Write(v[:]) case []byte: err = ByteSlice(v).Encode(writer) case string: @@ -101,6 +103,8 @@ func Decode(reader io.Reader, values ...interface{}) (err error) { *v = d.Int case *[32]byte: _, err = io.ReadFull(reader, v[:]) + case *[256]byte: + _, err = io.ReadFull(reader, v[:]) case *[]byte: d := ByteSlice(*v) err = d.Decode(reader) diff --git a/wire/protobuf/proposalmsgs.go b/wire/protobuf/proposalmsgs.go index af1b6b44b..558f8d939 100644 --- a/wire/protobuf/proposalmsgs.go +++ b/wire/protobuf/proposalmsgs.go @@ -171,6 +171,7 @@ func ToBaseChannelProposal(protoProp *BaseChannelProposal) (prop client.BaseChan return prop, errors.WithMessage(err, "funding agreement") } prop.App, prop.InitData, err = ToAppAndData(protoProp.App, protoProp.InitData) + copy(prop.Aux[:], protoProp.Aux) return prop, err } @@ -404,6 +405,9 @@ func FromBaseChannelProposal(prop client.BaseChannelProposal) (protoProp *BaseCh protoProp.NonceShare = make([]byte, len(prop.NonceShare)) copy(protoProp.NonceShare, prop.NonceShare[:]) + protoProp.Aux = make([]byte, len(prop.Aux)) + copy(protoProp.Aux, prop.Aux[:]) + protoProp.ChallengeDuration = prop.ChallengeDuration protoProp.InitBals, err = FromAllocation(*prop.InitBals) diff --git a/wire/protobuf/updatemsgs.go b/wire/protobuf/updatemsgs.go index dc30b00ce..a0f7d15c3 100644 --- a/wire/protobuf/updatemsgs.go +++ b/wire/protobuf/updatemsgs.go @@ -127,13 +127,18 @@ func ToParams(protoParams *Params) (*channel.Params, error) { if err != nil { return nil, errors.WithMessage(err, "parts") } + + var aux channel.Aux + copy(aux[:], protoParams.Aux) params := channel.NewParamsUnsafe( protoParams.ChallengeDuration, parts, app, (new(big.Int)).SetBytes(protoParams.Nonce), protoParams.LedgerChannel, - protoParams.VirtualChannel) + protoParams.VirtualChannel, + aux, + ) return params, nil } @@ -255,6 +260,7 @@ func FromParams(params *channel.Params) (protoParams *Params, err error) { return nil, errors.WithMessage(err, "parts") } protoParams.App, err = FromApp(params.App) + protoParams.Aux = params.Aux[:] return protoParams, err } diff --git a/wire/protobuf/wire.pb.go b/wire/protobuf/wire.pb.go index 49a605f46..669d1fc7b 100644 --- a/wire/protobuf/wire.pb.go +++ b/wire/protobuf/wire.pb.go @@ -16,9 +16,9 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.32.0 // protoc v4.25.2 -// source: wire.proto +// source: wire/protobuf/wire.proto package protobuf @@ -50,6 +50,7 @@ type Envelope struct { // msg should contain on the valid message types. // // Types that are assignable to Msg: + // // *Envelope_PingMsg // *Envelope_PongMsg // *Envelope_ShutdownMsg @@ -73,7 +74,7 @@ type Envelope struct { func (x *Envelope) Reset() { *x = Envelope{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[0] + mi := &file_wire_protobuf_wire_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -86,7 +87,7 @@ func (x *Envelope) String() string { func (*Envelope) ProtoMessage() {} func (x *Envelope) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[0] + mi := &file_wire_protobuf_wire_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -99,7 +100,7 @@ func (x *Envelope) ProtoReflect() protoreflect.Message { // Deprecated: Use Envelope.ProtoReflect.Descriptor instead. func (*Envelope) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{0} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{0} } func (x *Envelope) GetSender() []byte { @@ -361,7 +362,7 @@ type Balance struct { func (x *Balance) Reset() { *x = Balance{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[1] + mi := &file_wire_protobuf_wire_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -374,7 +375,7 @@ func (x *Balance) String() string { func (*Balance) ProtoMessage() {} func (x *Balance) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[1] + mi := &file_wire_protobuf_wire_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -387,7 +388,7 @@ func (x *Balance) ProtoReflect() protoreflect.Message { // Deprecated: Use Balance.ProtoReflect.Descriptor instead. func (*Balance) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{1} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{1} } func (x *Balance) GetBalance() [][]byte { @@ -410,7 +411,7 @@ type Balances struct { func (x *Balances) Reset() { *x = Balances{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[2] + mi := &file_wire_protobuf_wire_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -423,7 +424,7 @@ func (x *Balances) String() string { func (*Balances) ProtoMessage() {} func (x *Balances) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[2] + mi := &file_wire_protobuf_wire_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -436,7 +437,7 @@ func (x *Balances) ProtoReflect() protoreflect.Message { // Deprecated: Use Balances.ProtoReflect.Descriptor instead. func (*Balances) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{2} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{2} } func (x *Balances) GetBalances() []*Balance { @@ -460,7 +461,7 @@ type IndexMap struct { func (x *IndexMap) Reset() { *x = IndexMap{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[3] + mi := &file_wire_protobuf_wire_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -473,7 +474,7 @@ func (x *IndexMap) String() string { func (*IndexMap) ProtoMessage() {} func (x *IndexMap) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[3] + mi := &file_wire_protobuf_wire_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -486,7 +487,7 @@ func (x *IndexMap) ProtoReflect() protoreflect.Message { // Deprecated: Use IndexMap.ProtoReflect.Descriptor instead. func (*IndexMap) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{3} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{3} } func (x *IndexMap) GetIndexMap() []uint32 { @@ -510,7 +511,7 @@ type SubAlloc struct { func (x *SubAlloc) Reset() { *x = SubAlloc{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[4] + mi := &file_wire_protobuf_wire_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -523,7 +524,7 @@ func (x *SubAlloc) String() string { func (*SubAlloc) ProtoMessage() {} func (x *SubAlloc) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[4] + mi := &file_wire_protobuf_wire_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -536,7 +537,7 @@ func (x *SubAlloc) ProtoReflect() protoreflect.Message { // Deprecated: Use SubAlloc.ProtoReflect.Descriptor instead. func (*SubAlloc) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{4} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{4} } func (x *SubAlloc) GetId() []byte { @@ -574,7 +575,7 @@ type Allocation struct { func (x *Allocation) Reset() { *x = Allocation{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[5] + mi := &file_wire_protobuf_wire_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -587,7 +588,7 @@ func (x *Allocation) String() string { func (*Allocation) ProtoMessage() {} func (x *Allocation) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[5] + mi := &file_wire_protobuf_wire_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -600,7 +601,7 @@ func (x *Allocation) ProtoReflect() protoreflect.Message { // Deprecated: Use Allocation.ProtoReflect.Descriptor instead. func (*Allocation) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{5} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{5} } func (x *Allocation) GetAssets() [][]byte { @@ -637,12 +638,13 @@ type BaseChannelProposal struct { InitData []byte `protobuf:"bytes,5,opt,name=init_data,json=initData,proto3" json:"init_data,omitempty"` InitBals *Allocation `protobuf:"bytes,6,opt,name=init_bals,json=initBals,proto3" json:"init_bals,omitempty"` FundingAgreement *Balances `protobuf:"bytes,7,opt,name=funding_agreement,json=fundingAgreement,proto3" json:"funding_agreement,omitempty"` + Aux []byte `protobuf:"bytes,8,opt,name=aux,proto3" json:"aux,omitempty"` } func (x *BaseChannelProposal) Reset() { *x = BaseChannelProposal{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[6] + mi := &file_wire_protobuf_wire_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -655,7 +657,7 @@ func (x *BaseChannelProposal) String() string { func (*BaseChannelProposal) ProtoMessage() {} func (x *BaseChannelProposal) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[6] + mi := &file_wire_protobuf_wire_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -668,7 +670,7 @@ func (x *BaseChannelProposal) ProtoReflect() protoreflect.Message { // Deprecated: Use BaseChannelProposal.ProtoReflect.Descriptor instead. func (*BaseChannelProposal) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{6} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{6} } func (x *BaseChannelProposal) GetProposalId() []byte { @@ -720,6 +722,13 @@ func (x *BaseChannelProposal) GetFundingAgreement() *Balances { return nil } +func (x *BaseChannelProposal) GetAux() []byte { + if x != nil { + return x.Aux + } + return nil +} + // BaseChannelProposalAcc represents client.BaseChannelProposalAcc. type BaseChannelProposalAcc struct { state protoimpl.MessageState @@ -733,7 +742,7 @@ type BaseChannelProposalAcc struct { func (x *BaseChannelProposalAcc) Reset() { *x = BaseChannelProposalAcc{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[7] + mi := &file_wire_protobuf_wire_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -746,7 +755,7 @@ func (x *BaseChannelProposalAcc) String() string { func (*BaseChannelProposalAcc) ProtoMessage() {} func (x *BaseChannelProposalAcc) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[7] + mi := &file_wire_protobuf_wire_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -759,7 +768,7 @@ func (x *BaseChannelProposalAcc) ProtoReflect() protoreflect.Message { // Deprecated: Use BaseChannelProposalAcc.ProtoReflect.Descriptor instead. func (*BaseChannelProposalAcc) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{7} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{7} } func (x *BaseChannelProposalAcc) GetProposalId() []byte { @@ -789,12 +798,13 @@ type Params struct { Nonce []byte `protobuf:"bytes,5,opt,name=nonce,proto3" json:"nonce,omitempty"` LedgerChannel bool `protobuf:"varint,6,opt,name=ledger_channel,json=ledgerChannel,proto3" json:"ledger_channel,omitempty"` VirtualChannel bool `protobuf:"varint,7,opt,name=virtual_channel,json=virtualChannel,proto3" json:"virtual_channel,omitempty"` + Aux []byte `protobuf:"bytes,8,opt,name=aux,proto3" json:"aux,omitempty"` } func (x *Params) Reset() { *x = Params{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[8] + mi := &file_wire_protobuf_wire_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -807,7 +817,7 @@ func (x *Params) String() string { func (*Params) ProtoMessage() {} func (x *Params) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[8] + mi := &file_wire_protobuf_wire_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -820,7 +830,7 @@ func (x *Params) ProtoReflect() protoreflect.Message { // Deprecated: Use Params.ProtoReflect.Descriptor instead. func (*Params) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{8} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{8} } func (x *Params) GetId() []byte { @@ -872,6 +882,13 @@ func (x *Params) GetVirtualChannel() bool { return false } +func (x *Params) GetAux() []byte { + if x != nil { + return x.Aux + } + return nil +} + // State represents channel.State. type State struct { state protoimpl.MessageState @@ -889,7 +906,7 @@ type State struct { func (x *State) Reset() { *x = State{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[9] + mi := &file_wire_protobuf_wire_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -902,7 +919,7 @@ func (x *State) String() string { func (*State) ProtoMessage() {} func (x *State) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[9] + mi := &file_wire_protobuf_wire_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -915,7 +932,7 @@ func (x *State) ProtoReflect() protoreflect.Message { // Deprecated: Use State.ProtoReflect.Descriptor instead. func (*State) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{9} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{9} } func (x *State) GetId() []byte { @@ -973,7 +990,7 @@ type Transaction struct { func (x *Transaction) Reset() { *x = Transaction{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[10] + mi := &file_wire_protobuf_wire_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -986,7 +1003,7 @@ func (x *Transaction) String() string { func (*Transaction) ProtoMessage() {} func (x *Transaction) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[10] + mi := &file_wire_protobuf_wire_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -999,7 +1016,7 @@ func (x *Transaction) ProtoReflect() protoreflect.Message { // Deprecated: Use Transaction.ProtoReflect.Descriptor instead. func (*Transaction) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{10} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{10} } func (x *Transaction) GetState() *State { @@ -1030,7 +1047,7 @@ type SignedState struct { func (x *SignedState) Reset() { *x = SignedState{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[11] + mi := &file_wire_protobuf_wire_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1043,7 +1060,7 @@ func (x *SignedState) String() string { func (*SignedState) ProtoMessage() {} func (x *SignedState) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[11] + mi := &file_wire_protobuf_wire_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1056,7 +1073,7 @@ func (x *SignedState) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedState.ProtoReflect.Descriptor instead. func (*SignedState) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{11} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{11} } func (x *SignedState) GetParams() *Params { @@ -1093,7 +1110,7 @@ type ChannelUpdate struct { func (x *ChannelUpdate) Reset() { *x = ChannelUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[12] + mi := &file_wire_protobuf_wire_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1106,7 +1123,7 @@ func (x *ChannelUpdate) String() string { func (*ChannelUpdate) ProtoMessage() {} func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[12] + mi := &file_wire_protobuf_wire_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1119,7 +1136,7 @@ func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdate.ProtoReflect.Descriptor instead. func (*ChannelUpdate) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{12} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{12} } func (x *ChannelUpdate) GetState() *State { @@ -1148,7 +1165,7 @@ type PingMsg struct { func (x *PingMsg) Reset() { *x = PingMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[13] + mi := &file_wire_protobuf_wire_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1161,7 +1178,7 @@ func (x *PingMsg) String() string { func (*PingMsg) ProtoMessage() {} func (x *PingMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[13] + mi := &file_wire_protobuf_wire_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1174,7 +1191,7 @@ func (x *PingMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use PingMsg.ProtoReflect.Descriptor instead. func (*PingMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{13} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{13} } func (x *PingMsg) GetCreated() int64 { @@ -1196,7 +1213,7 @@ type PongMsg struct { func (x *PongMsg) Reset() { *x = PongMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[14] + mi := &file_wire_protobuf_wire_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1209,7 +1226,7 @@ func (x *PongMsg) String() string { func (*PongMsg) ProtoMessage() {} func (x *PongMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[14] + mi := &file_wire_protobuf_wire_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1222,7 +1239,7 @@ func (x *PongMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use PongMsg.ProtoReflect.Descriptor instead. func (*PongMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{14} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{14} } func (x *PongMsg) GetCreated() int64 { @@ -1244,7 +1261,7 @@ type ShutdownMsg struct { func (x *ShutdownMsg) Reset() { *x = ShutdownMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[15] + mi := &file_wire_protobuf_wire_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1257,7 +1274,7 @@ func (x *ShutdownMsg) String() string { func (*ShutdownMsg) ProtoMessage() {} func (x *ShutdownMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[15] + mi := &file_wire_protobuf_wire_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1270,7 +1287,7 @@ func (x *ShutdownMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ShutdownMsg.ProtoReflect.Descriptor instead. func (*ShutdownMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{15} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{15} } func (x *ShutdownMsg) GetReason() string { @@ -1292,7 +1309,7 @@ type AuthResponseMsg struct { func (x *AuthResponseMsg) Reset() { *x = AuthResponseMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[16] + mi := &file_wire_protobuf_wire_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1305,7 +1322,7 @@ func (x *AuthResponseMsg) String() string { func (*AuthResponseMsg) ProtoMessage() {} func (x *AuthResponseMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[16] + mi := &file_wire_protobuf_wire_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1318,7 +1335,7 @@ func (x *AuthResponseMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthResponseMsg.ProtoReflect.Descriptor instead. func (*AuthResponseMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{16} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{16} } func (x *AuthResponseMsg) GetSignature() []byte { @@ -1342,7 +1359,7 @@ type LedgerChannelProposalMsg struct { func (x *LedgerChannelProposalMsg) Reset() { *x = LedgerChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[17] + mi := &file_wire_protobuf_wire_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1355,7 +1372,7 @@ func (x *LedgerChannelProposalMsg) String() string { func (*LedgerChannelProposalMsg) ProtoMessage() {} func (x *LedgerChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[17] + mi := &file_wire_protobuf_wire_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1368,7 +1385,7 @@ func (x *LedgerChannelProposalMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use LedgerChannelProposalMsg.ProtoReflect.Descriptor instead. func (*LedgerChannelProposalMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{17} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{17} } func (x *LedgerChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1405,7 +1422,7 @@ type LedgerChannelProposalAccMsg struct { func (x *LedgerChannelProposalAccMsg) Reset() { *x = LedgerChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[18] + mi := &file_wire_protobuf_wire_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1418,7 +1435,7 @@ func (x *LedgerChannelProposalAccMsg) String() string { func (*LedgerChannelProposalAccMsg) ProtoMessage() {} func (x *LedgerChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[18] + mi := &file_wire_protobuf_wire_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1431,7 +1448,7 @@ func (x *LedgerChannelProposalAccMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use LedgerChannelProposalAccMsg.ProtoReflect.Descriptor instead. func (*LedgerChannelProposalAccMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{18} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{18} } func (x *LedgerChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1461,7 +1478,7 @@ type SubChannelProposalMsg struct { func (x *SubChannelProposalMsg) Reset() { *x = SubChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[19] + mi := &file_wire_protobuf_wire_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1474,7 +1491,7 @@ func (x *SubChannelProposalMsg) String() string { func (*SubChannelProposalMsg) ProtoMessage() {} func (x *SubChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[19] + mi := &file_wire_protobuf_wire_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1487,7 +1504,7 @@ func (x *SubChannelProposalMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use SubChannelProposalMsg.ProtoReflect.Descriptor instead. func (*SubChannelProposalMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{19} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{19} } func (x *SubChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1516,7 +1533,7 @@ type SubChannelProposalAccMsg struct { func (x *SubChannelProposalAccMsg) Reset() { *x = SubChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[20] + mi := &file_wire_protobuf_wire_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1529,7 +1546,7 @@ func (x *SubChannelProposalAccMsg) String() string { func (*SubChannelProposalAccMsg) ProtoMessage() {} func (x *SubChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[20] + mi := &file_wire_protobuf_wire_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1542,7 +1559,7 @@ func (x *SubChannelProposalAccMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use SubChannelProposalAccMsg.ProtoReflect.Descriptor instead. func (*SubChannelProposalAccMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{20} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{20} } func (x *SubChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1568,7 +1585,7 @@ type VirtualChannelProposalMsg struct { func (x *VirtualChannelProposalMsg) Reset() { *x = VirtualChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[21] + mi := &file_wire_protobuf_wire_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1581,7 +1598,7 @@ func (x *VirtualChannelProposalMsg) String() string { func (*VirtualChannelProposalMsg) ProtoMessage() {} func (x *VirtualChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[21] + mi := &file_wire_protobuf_wire_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1611,7 @@ func (x *VirtualChannelProposalMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use VirtualChannelProposalMsg.ProtoReflect.Descriptor instead. func (*VirtualChannelProposalMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{21} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{21} } func (x *VirtualChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1645,7 +1662,7 @@ type VirtualChannelProposalAccMsg struct { func (x *VirtualChannelProposalAccMsg) Reset() { *x = VirtualChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[22] + mi := &file_wire_protobuf_wire_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1658,7 +1675,7 @@ func (x *VirtualChannelProposalAccMsg) String() string { func (*VirtualChannelProposalAccMsg) ProtoMessage() {} func (x *VirtualChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[22] + mi := &file_wire_protobuf_wire_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1671,7 +1688,7 @@ func (x *VirtualChannelProposalAccMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use VirtualChannelProposalAccMsg.ProtoReflect.Descriptor instead. func (*VirtualChannelProposalAccMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{22} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{22} } func (x *VirtualChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1701,7 +1718,7 @@ type ChannelProposalRejMsg struct { func (x *ChannelProposalRejMsg) Reset() { *x = ChannelProposalRejMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[23] + mi := &file_wire_protobuf_wire_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1714,7 +1731,7 @@ func (x *ChannelProposalRejMsg) String() string { func (*ChannelProposalRejMsg) ProtoMessage() {} func (x *ChannelProposalRejMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[23] + mi := &file_wire_protobuf_wire_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1727,7 +1744,7 @@ func (x *ChannelProposalRejMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelProposalRejMsg.ProtoReflect.Descriptor instead. func (*ChannelProposalRejMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{23} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{23} } func (x *ChannelProposalRejMsg) GetProposalId() []byte { @@ -1757,7 +1774,7 @@ type ChannelUpdateMsg struct { func (x *ChannelUpdateMsg) Reset() { *x = ChannelUpdateMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[24] + mi := &file_wire_protobuf_wire_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1770,7 +1787,7 @@ func (x *ChannelUpdateMsg) String() string { func (*ChannelUpdateMsg) ProtoMessage() {} func (x *ChannelUpdateMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[24] + mi := &file_wire_protobuf_wire_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1783,7 +1800,7 @@ func (x *ChannelUpdateMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdateMsg.ProtoReflect.Descriptor instead. func (*ChannelUpdateMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{24} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{24} } func (x *ChannelUpdateMsg) GetChannelUpdate() *ChannelUpdate { @@ -1815,7 +1832,7 @@ type VirtualChannelFundingProposalMsg struct { func (x *VirtualChannelFundingProposalMsg) Reset() { *x = VirtualChannelFundingProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[25] + mi := &file_wire_protobuf_wire_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1828,7 +1845,7 @@ func (x *VirtualChannelFundingProposalMsg) String() string { func (*VirtualChannelFundingProposalMsg) ProtoMessage() {} func (x *VirtualChannelFundingProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[25] + mi := &file_wire_protobuf_wire_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1841,7 +1858,7 @@ func (x *VirtualChannelFundingProposalMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use VirtualChannelFundingProposalMsg.ProtoReflect.Descriptor instead. func (*VirtualChannelFundingProposalMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{25} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{25} } func (x *VirtualChannelFundingProposalMsg) GetChannelUpdateMsg() *ChannelUpdateMsg { @@ -1879,7 +1896,7 @@ type VirtualChannelSettlementProposalMsg struct { func (x *VirtualChannelSettlementProposalMsg) Reset() { *x = VirtualChannelSettlementProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[26] + mi := &file_wire_protobuf_wire_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1892,7 +1909,7 @@ func (x *VirtualChannelSettlementProposalMsg) String() string { func (*VirtualChannelSettlementProposalMsg) ProtoMessage() {} func (x *VirtualChannelSettlementProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[26] + mi := &file_wire_protobuf_wire_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1905,7 +1922,7 @@ func (x *VirtualChannelSettlementProposalMsg) ProtoReflect() protoreflect.Messag // Deprecated: Use VirtualChannelSettlementProposalMsg.ProtoReflect.Descriptor instead. func (*VirtualChannelSettlementProposalMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{26} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{26} } func (x *VirtualChannelSettlementProposalMsg) GetChannelUpdateMsg() *ChannelUpdateMsg { @@ -1936,7 +1953,7 @@ type ChannelUpdateAccMsg struct { func (x *ChannelUpdateAccMsg) Reset() { *x = ChannelUpdateAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[27] + mi := &file_wire_protobuf_wire_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1949,7 +1966,7 @@ func (x *ChannelUpdateAccMsg) String() string { func (*ChannelUpdateAccMsg) ProtoMessage() {} func (x *ChannelUpdateAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[27] + mi := &file_wire_protobuf_wire_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1962,7 +1979,7 @@ func (x *ChannelUpdateAccMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdateAccMsg.ProtoReflect.Descriptor instead. func (*ChannelUpdateAccMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{27} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{27} } func (x *ChannelUpdateAccMsg) GetChannelId() []byte { @@ -2000,7 +2017,7 @@ type ChannelUpdateRejMsg struct { func (x *ChannelUpdateRejMsg) Reset() { *x = ChannelUpdateRejMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[28] + mi := &file_wire_protobuf_wire_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2013,7 +2030,7 @@ func (x *ChannelUpdateRejMsg) String() string { func (*ChannelUpdateRejMsg) ProtoMessage() {} func (x *ChannelUpdateRejMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[28] + mi := &file_wire_protobuf_wire_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2026,7 +2043,7 @@ func (x *ChannelUpdateRejMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdateRejMsg.ProtoReflect.Descriptor instead. func (*ChannelUpdateRejMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{28} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{28} } func (x *ChannelUpdateRejMsg) GetChannelId() []byte { @@ -2063,7 +2080,7 @@ type ChannelSyncMsg struct { func (x *ChannelSyncMsg) Reset() { *x = ChannelSyncMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[29] + mi := &file_wire_protobuf_wire_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2076,7 +2093,7 @@ func (x *ChannelSyncMsg) String() string { func (*ChannelSyncMsg) ProtoMessage() {} func (x *ChannelSyncMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[29] + mi := &file_wire_protobuf_wire_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2089,7 +2106,7 @@ func (x *ChannelSyncMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelSyncMsg.ProtoReflect.Descriptor instead. func (*ChannelSyncMsg) Descriptor() ([]byte, []int) { - return file_wire_proto_rawDescGZIP(), []int{29} + return file_wire_protobuf_wire_proto_rawDescGZIP(), []int{29} } func (x *ChannelSyncMsg) GetPhase() uint32 { @@ -2106,161 +2123,163 @@ func (x *ChannelSyncMsg) GetCurrentTx() *Transaction { return nil } -var File_wire_proto protoreflect.FileDescriptor - -var file_wire_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x22, 0xcd, 0x0c, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, - 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, - 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x69, - 0x6e, 0x67, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, - 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, - 0x48, 0x00, 0x52, 0x07, 0x70, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x2f, 0x0a, 0x08, 0x70, - 0x6f, 0x6e, 0x67, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x50, 0x6f, 0x6e, 0x67, 0x4d, 0x73, - 0x67, 0x48, 0x00, 0x52, 0x07, 0x70, 0x6f, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x3b, 0x0a, 0x0c, - 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, - 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x68, - 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x48, 0x0a, 0x11, 0x61, 0x75, 0x74, - 0x68, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, - 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, - 0x48, 0x00, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x4d, 0x73, 0x67, 0x12, 0x64, 0x0a, 0x1b, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, - 0x73, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, - 0x18, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x6e, 0x0a, 0x1f, 0x6c, 0x65, 0x64, - 0x67, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4c, +var File_wire_protobuf_wire_proto protoreflect.FileDescriptor + +var file_wire_protobuf_wire_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x77, 0x69, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x22, 0xcd, 0x0c, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, + 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x67, + 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, + 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x48, 0x00, + 0x52, 0x07, 0x70, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x6f, 0x6e, + 0x67, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, + 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x50, 0x6f, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x48, + 0x00, 0x52, 0x07, 0x70, 0x6f, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x68, + 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x68, 0x75, + 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x68, 0x75, 0x74, + 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x48, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x48, 0x00, + 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, + 0x67, 0x12, 0x64, 0x0a, 0x1b, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x18, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x1b, 0x6c, 0x65, - 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5b, 0x0a, 0x18, 0x73, 0x75, 0x62, - 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, - 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, - 0x15, 0x73, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x65, 0x0a, 0x1c, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, - 0x63, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, - 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, - 0x67, 0x48, 0x00, 0x52, 0x18, 0x73, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x67, 0x0a, - 0x1c, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x19, 0x76, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x71, 0x0a, 0x20, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, - 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x1c, 0x76, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5b, 0x0a, 0x18, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x72, 0x65, - 0x6a, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, - 0x15, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x4b, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x48, - 0x00, 0x52, 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4d, 0x73, 0x67, 0x12, 0x7d, 0x0a, 0x24, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, 0x69, - 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, - 0x52, 0x20, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, - 0x73, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x27, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, - 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, - 0x65, 0x74, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, - 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x23, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x55, 0x0a, 0x16, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x63, - 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x13, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, - 0x73, 0x67, 0x12, 0x55, 0x0a, 0x16, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x6a, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6a, 0x4d, - 0x73, 0x67, 0x48, 0x00, 0x52, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x45, 0x0a, 0x10, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, - 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, - 0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x23, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x0a, 0x08, - 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, - 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, - 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x08, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6d, 0x61, - 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, - 0x70, 0x22, 0x74, 0x0a, 0x08, 0x53, 0x75, 0x62, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x26, 0x0a, - 0x04, 0x62, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x04, 0x62, 0x61, 0x6c, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6d, - 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x52, 0x08, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x22, 0x82, 0x01, 0x0a, 0x0a, 0x41, 0x6c, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2f, - 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, - 0x2b, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x41, - 0x6c, 0x6c, 0x6f, 0x63, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0xab, 0x02, 0x0a, - 0x13, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, - 0x67, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x6e, 0x63, 0x65, - 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x69, 0x6e, 0x69, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x61, 0x6c, - 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, - 0x69, 0x6e, 0x69, 0x74, 0x42, 0x61, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x11, 0x66, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x10, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x41, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x5a, 0x0a, 0x16, 0x42, 0x61, + 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x6e, 0x0a, 0x1f, 0x6c, 0x65, 0x64, 0x67, 0x65, + 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4c, 0x65, 0x64, + 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x1b, 0x6c, 0x65, 0x64, 0x67, + 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5b, 0x0a, 0x18, 0x73, 0x75, 0x62, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, + 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x15, 0x73, + 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x65, 0x0a, 0x1c, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, + 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x65, 0x72, + 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, + 0x00, 0x52, 0x18, 0x73, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x67, 0x0a, 0x1c, 0x76, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x19, 0x76, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x71, 0x0a, 0x20, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x1c, 0x76, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5b, 0x0a, 0x18, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6a, 0x5f, + 0x6d, 0x73, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x15, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, + 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x4b, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, + 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, + 0x67, 0x12, 0x7d, 0x0a, 0x24, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x20, + 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x75, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, + 0x12, 0x86, 0x01, 0x0a, 0x27, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x56, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x74, + 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, + 0x73, 0x67, 0x48, 0x00, 0x52, 0x23, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x55, 0x0a, 0x16, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x5f, + 0x6d, 0x73, 0x67, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x13, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, + 0x12, 0x55, 0x0a, 0x16, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x72, 0x65, 0x6a, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, + 0x48, 0x00, 0x52, 0x13, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x45, 0x0a, 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x0e, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x42, 0x05, + 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x23, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x0a, 0x08, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x08, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, + 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6d, 0x61, 0x70, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x22, + 0x74, 0x0a, 0x08, 0x53, 0x75, 0x62, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x04, 0x62, + 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x62, + 0x61, 0x6c, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6d, 0x61, 0x70, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x52, 0x08, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x4d, 0x61, 0x70, 0x22, 0x82, 0x01, 0x0a, 0x0a, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x08, + 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x0a, + 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x41, 0x6c, 0x6c, + 0x6f, 0x63, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0xbd, 0x02, 0x0a, 0x13, 0x42, + 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, + 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x53, 0x68, + 0x61, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x69, 0x6e, 0x69, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x32, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x69, 0x6e, + 0x69, 0x74, 0x42, 0x61, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x11, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x5f, 0x61, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x10, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, + 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x75, 0x78, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x61, 0x75, 0x78, 0x22, 0x5a, 0x0a, 0x16, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x22, 0xd5, 0x01, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x22, 0xe7, 0x01, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, @@ -2273,180 +2292,181 @@ var file_wire_proto_rawDesc = []byte{ 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, - 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0xa9, - 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x61, 0x70, 0x70, 0x12, 0x35, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x0b, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x04, 0x73, 0x69, 0x67, 0x73, 0x22, 0x74, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, - 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, - 0x26, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x73, 0x22, 0x54, 0x0a, 0x0d, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x65, - 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, - 0x78, 0x22, 0x23, 0x0a, 0x07, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x23, 0x0a, 0x07, 0x50, 0x6f, 0x6e, 0x67, 0x4d, 0x73, - 0x67, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x25, 0x0a, 0x0b, 0x53, - 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x0f, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x18, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, - 0x12, 0x52, 0x0a, 0x15, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, - 0x13, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, - 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, - 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x9d, 0x01, 0x0a, - 0x1b, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5c, 0x0a, 0x19, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, - 0x63, 0x63, 0x52, 0x16, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, - 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x22, 0x83, 0x01, 0x0a, - 0x15, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x13, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x22, 0x78, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10, + 0x0a, 0x03, 0x61, 0x75, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x61, 0x75, 0x78, + 0x22, 0xa9, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x35, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x65, 0x72, + 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x0b, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x65, 0x72, + 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x73, 0x22, 0x74, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x73, 0x22, 0x54, 0x0a, + 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x26, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x5f, + 0x69, 0x64, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x49, 0x64, 0x78, 0x22, 0x23, 0x0a, 0x07, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x73, 0x67, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x23, 0x0a, 0x07, 0x50, 0x6f, 0x6e, 0x67, + 0x4d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x25, 0x0a, + 0x0b, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x0f, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x18, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, + 0x73, 0x67, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, + 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x52, 0x13, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, + 0x69, 0x70, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x9d, + 0x01, 0x0a, 0x1b, 0x4c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x5c, 0x0a, 0x19, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x52, 0x16, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x22, 0xef, 0x01, 0x0a, - 0x19, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, - 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x13, 0x62, 0x61, 0x73, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, - 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, - 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0c, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x0a, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x4d, 0x61, 0x70, 0x52, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x9a, - 0x01, 0x0a, 0x1c, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, - 0x5c, 0x0a, 0x19, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, - 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, - 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x61, 0x6c, 0x41, 0x63, 0x63, 0x52, 0x16, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x12, 0x1c, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x50, 0x0a, 0x15, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, - 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x65, 0x0a, - 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, - 0x67, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x65, 0x72, 0x75, - 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x73, 0x69, 0x67, 0x22, 0xd1, 0x01, 0x0a, 0x20, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x49, 0x0a, 0x12, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, - 0x73, 0x67, 0x52, 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4d, 0x73, 0x67, 0x12, 0x30, 0x0a, 0x07, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, - 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, - 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x52, 0x08, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x22, 0x9e, 0x01, 0x0a, 0x23, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, - 0x12, 0x49, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, - 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, - 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x60, 0x0a, 0x13, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, - 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x66, 0x0a, 0x13, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6a, 0x4d, + 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x12, 0x20, 0x0a, 0x0b, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x22, 0x83, + 0x01, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x13, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x22, 0x78, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, + 0x12, 0x5c, 0x0a, 0x19, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x52, 0x16, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x22, 0xef, + 0x01, 0x0a, 0x19, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x52, 0x0a, 0x15, + 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x65, + 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x13, 0x62, 0x61, 0x73, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, + 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x0a, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x73, + 0x22, 0x9a, 0x01, 0x0a, 0x1c, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x4d, 0x73, + 0x67, 0x12, 0x5c, 0x0a, 0x19, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x42, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x52, 0x16, 0x62, 0x61, 0x73, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x50, 0x0a, + 0x15, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x52, 0x65, 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, + 0x65, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x4d, 0x73, 0x67, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x65, + 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0xd1, 0x01, 0x0a, 0x20, 0x56, 0x69, 0x72, 0x74, 0x75, + 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x49, 0x0a, 0x12, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x73, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4d, 0x73, 0x67, 0x52, 0x10, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x30, 0x0a, 0x07, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x07, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x65, + 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, + 0x52, 0x08, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4d, 0x61, 0x70, 0x22, 0x9e, 0x01, 0x0a, 0x23, 0x56, + 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x74, + 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x4d, + 0x73, 0x67, 0x12, 0x49, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x52, 0x10, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x73, 0x67, 0x12, 0x2c, 0x0a, + 0x05, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, + 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x60, 0x0a, 0x13, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, - 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x54, 0x78, 0x42, 0x26, 0x5a, 0x24, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x2f, 0x67, 0x6f, 0x2d, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x2f, 0x77, 0x69, 0x72, - 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x73, + 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x66, 0x0a, + 0x13, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x6a, 0x4d, 0x73, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x35, 0x0a, + 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x54, 0x78, 0x42, 0x26, 0x5a, 0x24, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x2e, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x67, 0x6f, 0x2d, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x2f, 0x77, + 0x69, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( - file_wire_proto_rawDescOnce sync.Once - file_wire_proto_rawDescData = file_wire_proto_rawDesc + file_wire_protobuf_wire_proto_rawDescOnce sync.Once + file_wire_protobuf_wire_proto_rawDescData = file_wire_protobuf_wire_proto_rawDesc ) -func file_wire_proto_rawDescGZIP() []byte { - file_wire_proto_rawDescOnce.Do(func() { - file_wire_proto_rawDescData = protoimpl.X.CompressGZIP(file_wire_proto_rawDescData) +func file_wire_protobuf_wire_proto_rawDescGZIP() []byte { + file_wire_protobuf_wire_proto_rawDescOnce.Do(func() { + file_wire_protobuf_wire_proto_rawDescData = protoimpl.X.CompressGZIP(file_wire_protobuf_wire_proto_rawDescData) }) - return file_wire_proto_rawDescData + return file_wire_protobuf_wire_proto_rawDescData } -var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 30) -var file_wire_proto_goTypes = []interface{}{ +var file_wire_protobuf_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 30) +var file_wire_protobuf_wire_proto_goTypes = []interface{}{ (*Envelope)(nil), // 0: perunwire.Envelope (*Balance)(nil), // 1: perunwire.Balance (*Balances)(nil), // 2: perunwire.Balances @@ -2478,7 +2498,7 @@ var file_wire_proto_goTypes = []interface{}{ (*ChannelUpdateRejMsg)(nil), // 28: perunwire.ChannelUpdateRejMsg (*ChannelSyncMsg)(nil), // 29: perunwire.ChannelSyncMsg } -var file_wire_proto_depIdxs = []int32{ +var file_wire_protobuf_wire_proto_depIdxs = []int32{ 13, // 0: perunwire.Envelope.ping_msg:type_name -> perunwire.PingMsg 14, // 1: perunwire.Envelope.pong_msg:type_name -> perunwire.PongMsg 15, // 2: perunwire.Envelope.shutdown_msg:type_name -> perunwire.ShutdownMsg @@ -2529,13 +2549,13 @@ var file_wire_proto_depIdxs = []int32{ 0, // [0:43] is the sub-list for field type_name } -func init() { file_wire_proto_init() } -func file_wire_proto_init() { - if File_wire_proto != nil { +func init() { file_wire_protobuf_wire_proto_init() } +func file_wire_protobuf_wire_proto_init() { + if File_wire_protobuf_wire_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_wire_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Envelope); i { case 0: return &v.state @@ -2547,7 +2567,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Balance); i { case 0: return &v.state @@ -2559,7 +2579,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Balances); i { case 0: return &v.state @@ -2571,7 +2591,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*IndexMap); i { case 0: return &v.state @@ -2583,7 +2603,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubAlloc); i { case 0: return &v.state @@ -2595,7 +2615,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Allocation); i { case 0: return &v.state @@ -2607,7 +2627,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BaseChannelProposal); i { case 0: return &v.state @@ -2619,7 +2639,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BaseChannelProposalAcc); i { case 0: return &v.state @@ -2631,7 +2651,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Params); i { case 0: return &v.state @@ -2643,7 +2663,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*State); i { case 0: return &v.state @@ -2655,7 +2675,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Transaction); i { case 0: return &v.state @@ -2667,7 +2687,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SignedState); i { case 0: return &v.state @@ -2679,7 +2699,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelUpdate); i { case 0: return &v.state @@ -2691,7 +2711,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PingMsg); i { case 0: return &v.state @@ -2703,7 +2723,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PongMsg); i { case 0: return &v.state @@ -2715,7 +2735,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShutdownMsg); i { case 0: return &v.state @@ -2727,7 +2747,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AuthResponseMsg); i { case 0: return &v.state @@ -2739,7 +2759,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LedgerChannelProposalMsg); i { case 0: return &v.state @@ -2751,7 +2771,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LedgerChannelProposalAccMsg); i { case 0: return &v.state @@ -2763,7 +2783,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubChannelProposalMsg); i { case 0: return &v.state @@ -2775,7 +2795,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubChannelProposalAccMsg); i { case 0: return &v.state @@ -2787,7 +2807,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VirtualChannelProposalMsg); i { case 0: return &v.state @@ -2799,7 +2819,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VirtualChannelProposalAccMsg); i { case 0: return &v.state @@ -2811,7 +2831,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelProposalRejMsg); i { case 0: return &v.state @@ -2823,7 +2843,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelUpdateMsg); i { case 0: return &v.state @@ -2835,7 +2855,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VirtualChannelFundingProposalMsg); i { case 0: return &v.state @@ -2847,7 +2867,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VirtualChannelSettlementProposalMsg); i { case 0: return &v.state @@ -2859,7 +2879,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelUpdateAccMsg); i { case 0: return &v.state @@ -2871,7 +2891,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelUpdateRejMsg); i { case 0: return &v.state @@ -2883,7 +2903,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_wire_protobuf_wire_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ChannelSyncMsg); i { case 0: return &v.state @@ -2896,7 +2916,7 @@ func file_wire_proto_init() { } } } - file_wire_proto_msgTypes[0].OneofWrappers = []interface{}{ + file_wire_protobuf_wire_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Envelope_PingMsg)(nil), (*Envelope_PongMsg)(nil), (*Envelope_ShutdownMsg)(nil), @@ -2919,18 +2939,18 @@ func file_wire_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_wire_proto_rawDesc, + RawDescriptor: file_wire_protobuf_wire_proto_rawDesc, NumEnums: 0, NumMessages: 30, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_wire_proto_goTypes, - DependencyIndexes: file_wire_proto_depIdxs, - MessageInfos: file_wire_proto_msgTypes, + GoTypes: file_wire_protobuf_wire_proto_goTypes, + DependencyIndexes: file_wire_protobuf_wire_proto_depIdxs, + MessageInfos: file_wire_protobuf_wire_proto_msgTypes, }.Build() - File_wire_proto = out.File - file_wire_proto_rawDesc = nil - file_wire_proto_goTypes = nil - file_wire_proto_depIdxs = nil + File_wire_protobuf_wire_proto = out.File + file_wire_protobuf_wire_proto_rawDesc = nil + file_wire_protobuf_wire_proto_goTypes = nil + file_wire_protobuf_wire_proto_depIdxs = nil } diff --git a/wire/protobuf/wire.proto b/wire/protobuf/wire.proto index dec4edbcb..35a44f025 100644 --- a/wire/protobuf/wire.proto +++ b/wire/protobuf/wire.proto @@ -91,6 +91,7 @@ message BaseChannelProposal { bytes init_data = 5; Allocation init_bals = 6; Balances funding_agreement = 7; + bytes aux = 8; } // BaseChannelProposalAcc represents client.BaseChannelProposalAcc. @@ -108,6 +109,7 @@ message Params { bytes nonce = 5; bool ledger_channel = 6; bool virtual_channel = 7; + bytes aux = 8; } // State represents channel.State. From 2bb1a5521eac1adc63d56e8e9f70ffb74cf5a317 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Wed, 26 Feb 2025 14:33:02 +0100 Subject: [PATCH 02/26] feat(adjudicate): gather state map switchs to gather signed states Signed-off-by: Minh Huy Tran --- channel/adjudicator.go | 8 ++++---- client/adjudicate.go | 6 +++++- client/test/backend.go | 4 ++-- client/test/subchannel_dispute.go | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/channel/adjudicator.go b/channel/adjudicator.go index ddfd1a7b0..0752a73c9 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -204,7 +204,7 @@ type ( } // StateMap represents a channel state tree. - StateMap map[ID]*State + StateMap map[ID]*SignedState ) // NewProgressReq creates a new ProgressReq object. @@ -302,12 +302,12 @@ func (t *TimeTimeout) String() string { // MakeStateMap creates a new StateMap object. func MakeStateMap() StateMap { - return make(map[ID]*State) + return make(map[ID]*SignedState) } // Add adds the given states to the state map. -func (m StateMap) Add(states ...*State) { +func (m StateMap) Add(states ...*SignedState) { for _, s := range states { - m[s.ID] = s + m[s.State.ID] = s } } diff --git a/client/adjudicate.go b/client/adjudicate.go index 362a8f294..5a7b6285a 100644 --- a/client/adjudicate.go +++ b/client/adjudicate.go @@ -438,7 +438,11 @@ func (c *Channel) gatherSubChannelStates() (states []channel.SignedState, err er func (c *Channel) subChannelStateMap() (states channel.StateMap, err error) { states = channel.MakeStateMap() err = c.applyToSubChannelsRecursive(func(c *Channel) error { - states[c.ID()] = c.state() + states[c.ID()] = &channel.SignedState{ + Params: c.Params(), + State: c.machine.CurrentTX().State, + Sigs: c.machine.CurrentTX().Sigs, + } return nil }) return diff --git a/client/test/backend.go b/client/test/backend.go index 2ea22648e..43c8d478d 100644 --- a/client/test/backend.go +++ b/client/test/backend.go @@ -231,7 +231,7 @@ func (b *MockBackend) Progress(_ context.Context, req channel.ProgressReq) error func outcomeRecursive(state *channel.State, subStates channel.StateMap) (outcome channel.Balances) { outcome = state.Balances.Clone() for _, subAlloc := range state.Locked { - subOutcome := outcomeRecursive(subStates[subAlloc.ID], subStates) + subOutcome := outcomeRecursive(subStates[subAlloc.ID].State, subStates) for a, bals := range subOutcome { for p, bal := range bals { _p := p @@ -297,7 +297,7 @@ func (b *MockBackend) Withdraw(_ context.Context, req channel.AdjudicatorReq, su states[0] = req.Tx.State i := 1 for _, s := range subStates { - states[i] = s + states[i] = s.State i++ } if err := b.checkStates(states, checkWithdraw); err != nil { diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index ea6721724..c884fbd05 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -105,7 +105,7 @@ func (r *DisputeSusie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { r.log.Debug("Attempt withdrawing refuted state.") m := channel.MakeStateMap() - m.Add(subState0.State) + m.Add(&subState0) err = r.setup.Adjudicator.Withdraw(ctx, reqLedger, m) r.RequireTruef(err != nil, "withdraw should fail because other party should have refuted.") From 8c7df65ee60aea15f90c9fb12508fb52b4a693cd Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Wed, 2 Apr 2025 12:43:02 +0200 Subject: [PATCH 03/26] feat(vc_test): add aux params to virtual channel test for utxo chains Signed-off-by: Minh Huy Tran --- client/test/virtualchannel.go | 34 ++++++++++++++++++++++++++-------- client/virtual_channel_test.go | 2 ++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 5d1449160..2e1ee6e36 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -39,6 +39,8 @@ type VirtualChannelSetup struct { BalanceDelta channel.Bal Rng *rand.Rand WaitWatcherTimeout time.Duration + IsUTXO bool + Aux []byte } // TestVirtualChannelOptimistic tests virtual channel functionality in the @@ -289,14 +291,30 @@ func setupVirtualChannelTest( } indexMapAlice := []channel.Index{0, 1} indexMapBob := []channel.Index{1, 0} - vcp, err := client.NewVirtualChannelProposal( - setup.ChallengeDuration, - alice.WalletAddress, - &initAllocVirtual, - []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, - []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, - [][]channel.Index{indexMapAlice, indexMapBob}, - ) + + var vcp *client.VirtualChannelProposalMsg + if setup.IsUTXO { + // UTXO Chains need additional auxiliary data to be able to + // create a virtual channel. + vcp, err = client.NewVirtualChannelProposal( + setup.ChallengeDuration, + alice.WalletAddress, + &initAllocVirtual, + []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, + []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, + [][]channel.Index{indexMapAlice, indexMapBob}, + client.WithAux(setup.Aux), + ) + } else { + vcp, err = client.NewVirtualChannelProposal( + setup.ChallengeDuration, + alice.WalletAddress, + &initAllocVirtual, + []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, + []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, + [][]channel.Index{indexMapAlice, indexMapBob}, + ) + } require.NoError(err, "creating virtual channel proposal") vct.chAliceBob, err = alice.ProposeChannel(ctx, vcp) diff --git a/client/virtual_channel_test.go b/client/virtual_channel_test.go index 4e8eda21e..f91359c13 100644 --- a/client/virtual_channel_test.go +++ b/client/virtual_channel_test.go @@ -65,6 +65,8 @@ func makeVirtualChannelSetup(rng *rand.Rand) ctest.VirtualChannelSetup { BalanceDelta: big.NewInt(0), Rng: rng, WaitWatcherTimeout: 100 * time.Millisecond, + IsUTXO: false, + Aux: nil, } } From e363289b30c86134e430cfde06c740cbc0d4bdbc Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Wed, 2 Apr 2025 13:40:08 +0200 Subject: [PATCH 04/26] feat(vc test): send parent ID in aux instead for utxo chains Signed-off-by: Minh Huy Tran --- channel/params.go | 5 +++++ client/test/virtualchannel.go | 9 +++++++-- client/virtual_channel_test.go | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/channel/params.go b/channel/params.go index dd70e13d7..28027c762 100644 --- a/channel/params.go +++ b/channel/params.go @@ -46,6 +46,11 @@ const AuxMaxLen = 256 // Aux is the channel parameters' auxiliary data type. type Aux = [AuxMaxLen]byte +// ConvertIDToBytes converts an ID to a []byte slice. +func ConvertIDToBytes(id ID) []byte { + return id[:] +} + // NonceFromBytes creates a nonce from a byte slice. func NonceFromBytes(b []byte) Nonce { if len(b) > MaxNonceLen { diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 2e1ee6e36..1c2b12c0a 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -40,7 +40,6 @@ type VirtualChannelSetup struct { Rng *rand.Rand WaitWatcherTimeout time.Duration IsUTXO bool - Aux []byte } // TestVirtualChannelOptimistic tests virtual channel functionality in the @@ -149,6 +148,8 @@ type virtualChannelTest struct { errs chan error asset channel.Asset balancesBefore channel.Balances + isUTXO bool + parentIDs [2]channel.ID } // VirtualChannelBalances contains a description of the balances that will be @@ -180,6 +181,7 @@ func setupVirtualChannelTest( vct.finalBalsAlice = setup.Balances.FinalBalsAlice vct.finalBalsBob = setup.Balances.FinalBalsBob vct.finalBalIngrid = new(big.Int).Add(vct.finalBalsAlice[1], vct.finalBalsBob[1]) + vct.isUTXO = setup.IsUTXO const errBufferLen = 10 vct.errs = make(chan error, errBufferLen) @@ -292,6 +294,9 @@ func setupVirtualChannelTest( indexMapAlice := []channel.Index{0, 1} indexMapBob := []channel.Index{1, 0} + vct.parentIDs[0] = vct.chAliceIngrid.ID() + vct.parentIDs[1] = vct.chBobIngrid.ID() + var vcp *client.VirtualChannelProposalMsg if setup.IsUTXO { // UTXO Chains need additional auxiliary data to be able to @@ -303,7 +308,7 @@ func setupVirtualChannelTest( []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, [][]channel.Index{indexMapAlice, indexMapBob}, - client.WithAux(setup.Aux), + client.WithAux(append(vct.parentIDs[0][:], vct.parentIDs[1][:]...)), ) } else { vcp, err = client.NewVirtualChannelProposal( diff --git a/client/virtual_channel_test.go b/client/virtual_channel_test.go index f91359c13..526a746a2 100644 --- a/client/virtual_channel_test.go +++ b/client/virtual_channel_test.go @@ -66,7 +66,6 @@ func makeVirtualChannelSetup(rng *rand.Rand) ctest.VirtualChannelSetup { Rng: rng, WaitWatcherTimeout: 100 * time.Millisecond, IsUTXO: false, - Aux: nil, } } From 0edd2b7e01ccb6b83cd4c84133cdbcae44a22ad9 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Wed, 2 Apr 2025 14:25:01 +0200 Subject: [PATCH 05/26] fix(vc test): fix aux generation in virtual channel test Signed-off-by: Minh Huy Tran --- client/proposalopts.go | 2 +- client/test/virtualchannel.go | 6 +++++- client/virtual_channel_test.go | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/proposalopts.go b/client/proposalopts.go index a3e0e45f2..fa080534e 100644 --- a/client/proposalopts.go +++ b/client/proposalopts.go @@ -136,7 +136,7 @@ func WithNonce(share NonceShare) ProposalOpts { } // WithAux configures an auxiliary data field. -func WithAux(aux []byte) ProposalOpts { +func WithAux(aux channel.Aux) ProposalOpts { return ProposalOpts{optNames.aux: aux} } diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 1c2b12c0a..eebf1823e 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -301,6 +301,10 @@ func setupVirtualChannelTest( if setup.IsUTXO { // UTXO Chains need additional auxiliary data to be able to // create a virtual channel. + var aux channel.Aux + copy(aux[:channel.IDLen], vct.parentIDs[0][:]) + copy(aux[channel.IDLen:], vct.parentIDs[1][:]) + vcp, err = client.NewVirtualChannelProposal( setup.ChallengeDuration, alice.WalletAddress, @@ -308,7 +312,7 @@ func setupVirtualChannelTest( []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, [][]channel.Index{indexMapAlice, indexMapBob}, - client.WithAux(append(vct.parentIDs[0][:], vct.parentIDs[1][:]...)), + client.WithAux(aux), ) } else { vcp, err = client.NewVirtualChannelProposal( diff --git a/client/virtual_channel_test.go b/client/virtual_channel_test.go index 526a746a2..cb15b38b3 100644 --- a/client/virtual_channel_test.go +++ b/client/virtual_channel_test.go @@ -65,7 +65,7 @@ func makeVirtualChannelSetup(rng *rand.Rand) ctest.VirtualChannelSetup { BalanceDelta: big.NewInt(0), Rng: rng, WaitWatcherTimeout: 100 * time.Millisecond, - IsUTXO: false, + IsUTXO: true, } } From 634091b5744f9f9e8f2abf535505657860658657 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Thu, 10 Apr 2025 10:28:30 +0200 Subject: [PATCH 06/26] feat(test/virtualchannel): add wait time for processing opening virtual channel in some chains Signed-off-by: Minh Huy Tran --- client/test/virtualchannel.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index eebf1823e..e30d046e3 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -91,6 +91,8 @@ func TestVirtualChannelDispute( //nolint:revive // test.Test... stutters but OK assert := assert.New(t) waitTimeout := setup.WaitWatcherTimeout + time.Sleep(waitTimeout) // Sleep to ensure that events have been processed and local client states have been updated. + chs := []*client.Channel{vct.chAliceIngrid, vct.chIngridAlice, vct.chBobIngrid, vct.chIngridBob} // Register the channels in a random order. perm := rand.Perm(len(chs)) From 1eb5cc35ed8d5fd4d241d98d8f56a99010e76a48 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Thu, 10 Apr 2025 15:28:29 +0200 Subject: [PATCH 07/26] feat: add log and proper secondary settle for virtual channel test dispute Signed-off-by: Minh Huy Tran --- client/test/virtualchannel.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index e30d046e3..5bc8c68d0 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -103,9 +103,20 @@ func TestVirtualChannelDispute( //nolint:revive // test.Test... stutters but OK time.Sleep(waitTimeout) // Sleep to ensure that events have been processed and local client states have been updated. } + isSecondary := [2]bool{false, false} + // Settle the channels in a random order. - for _, i := range rand.Perm(len(chs)) { - err := chs[i].Settle(ctx, false) + perm = rand.Perm(len(chs)) + t.Logf("Settle order = %v", perm) + for _, i := range perm { + var err error + if i < 2 { + err = chs[i].Settle(ctx, isSecondary[0]) + isSecondary[0] = true + } else { + err = chs[i].Settle(ctx, isSecondary[1]) + isSecondary[1] = true + } assert.NoErrorf(err, "settle channel: %d", i) } From 61bf760655bd172c497342a46bbcb5ca25be9d18 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 14 Apr 2025 15:10:12 +0200 Subject: [PATCH 08/26] feat: add additional payment channel test setup Signed-off-by: Minh Huy Tran --- client/payment_test.go | 44 ++++++ client/test/paymentchannel.go | 254 ++++++++++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 client/test/paymentchannel.go diff --git a/client/payment_test.go b/client/payment_test.go index 027693200..fe964ea12 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -19,11 +19,13 @@ import ( "math/big" "math/rand" "testing" + "time" chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" ctest "perun.network/go-perun/client/test" "perun.network/go-perun/wire" + "polycry.pt/poly-go/test" pkgtest "polycry.pt/poly-go/test" ) @@ -65,3 +67,45 @@ func TestPaymentDispute(t *testing.T) { } ctest.ExecuteTwoPartyTest(ctx, t, roles, cfg) } + +func TestPaymentChannelsOptimistic(t *testing.T) { + rng := test.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), testDuration) + defer cancel() + + setup := makePaymentChannelSetup(rng) + ctest.TestPaymentChannelOptimistic(ctx, t, setup) +} + +func TestPaymentChannelsDispute(t *testing.T) { + rng := test.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), testDuration) + defer cancel() + + setup := makePaymentChannelSetup(rng) + ctest.TestPaymentChannelDispute(ctx, t, setup) +} + +func makePaymentChannelSetup(rng *rand.Rand) ctest.PaymentChannelSetup { + return ctest.PaymentChannelSetup{ + Clients: createPaymentChannelClients(rng), + ChallengeDuration: challengeDuration, + Asset: chtest.NewRandomAsset(rng), + Balances: ctest.PaymentChannelBalances{ + InitBalsAliceBob: []*big.Int{big.NewInt(5), big.NewInt(5)}, + BalsUpdated: []*big.Int{big.NewInt(2), big.NewInt(8)}, + FinalBals: []*big.Int{big.NewInt(2), big.NewInt(8)}, + }, + BalanceDelta: big.NewInt(0), + Rng: rng, + WaitWatcherTimeout: 100 * time.Millisecond, + IsUTXO: true, + } +} + +func createPaymentChannelClients(rng *rand.Rand) [2]ctest.RoleSetup { + var setupsArray [2]ctest.RoleSetup + setups := NewSetups(rng, []string{"Alice", "Bob"}) + copy(setupsArray[:], setups) + return setupsArray +} diff --git a/client/test/paymentchannel.go b/client/test/paymentchannel.go new file mode 100644 index 000000000..2d456467b --- /dev/null +++ b/client/test/paymentchannel.go @@ -0,0 +1,254 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "context" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "perun.network/go-perun/channel" + "perun.network/go-perun/client" + "perun.network/go-perun/wire" +) + +// PaymentChannelSetup contains the setup for a payment channel test. +type PaymentChannelSetup struct { + Clients [2]RoleSetup + ChallengeDuration uint64 + Asset channel.Asset + Balances PaymentChannelBalances + BalanceDelta channel.Bal + Rng *rand.Rand + WaitWatcherTimeout time.Duration + IsUTXO bool +} + +// PaymentChannelBalances contains a description of the balances that will be +// used during a virtual channel test. +type PaymentChannelBalances struct { + InitBalsAliceBob []*big.Int + BalsUpdated []*big.Int + FinalBals []*big.Int +} + +type paymentChannelTest struct { + alice *Client + bob *Client + chAliceBob *client.Channel + chBobAlice *client.Channel + balsUpdated []*big.Int + initBalsAlice *big.Int + initBalsBob *big.Int + finalBalsAlice *big.Int + finalBalsBob *big.Int + errs chan error + asset channel.Asset + balancesBefore channel.Balances + isUTXO bool + parentIDs [2]channel.ID +} + +// TestPaymentChannelOptimistic tests payment channel functionality in the happy case. +func TestPaymentChannelOptimistic( //nolint:revive // test.Test... stutters but OK here. + ctx context.Context, + t *testing.T, + setup PaymentChannelSetup, +) { + pct := setupPaymentChannelTest(t, ctx, setup) + assert := assert.New(t) + + err := pct.chAliceBob.Update(ctx, func(s *channel.State) { + s.IsFinal = true + }) + assert.NoError(err, "update final state") + + isSecondary := false + + // Settle the channels in a random order. + chs := []*client.Channel{pct.chAliceBob, pct.chBobAlice} + perm := rand.Perm(len(chs)) + t.Logf("Settle order = %v", perm) + for _, i := range perm { + var err error + err = chs[i].Settle(ctx, isSecondary) + isSecondary = true + + assert.NoErrorf(err, "settle channel: %d", i) + } + + // Check final balances. + balancesAfter := channel.Balances{ + { + pct.alice.BalanceReader.Balance(pct.asset), + pct.bob.BalanceReader.Balance(pct.asset), + }, + } + + balancesDiff := balancesAfter.Sub(pct.balancesBefore) + expectedBalancesDiff := channel.Balances{ + { + new(big.Int).Sub(pct.finalBalsAlice, pct.initBalsAlice), + new(big.Int).Sub(pct.finalBalsBob, pct.initBalsBob), + }, + } + balanceDelta := setup.BalanceDelta + eq := EqualBalancesWithDelta(expectedBalancesDiff, balancesDiff, balanceDelta) + assert.Truef(eq, "final ledger balances incorrect: expected balance difference %v +- %v, got %v", expectedBalancesDiff, balanceDelta, balancesDiff) +} + +// TestPaymentChannelDispute tests payment channel functionality in the dispute case. +func TestPaymentChannelDispute( //nolint:revive // test.Test... stutters but OK here. { + ctx context.Context, + t *testing.T, + setup PaymentChannelSetup, +) { + pct := setupPaymentChannelTest(t, ctx, setup) + assert := assert.New(t) + waitTimeout := setup.WaitWatcherTimeout + chs := []*client.Channel{pct.chAliceBob, pct.chBobAlice} + // Register the channels in a random order. + perm := rand.Perm(len(chs)) + t.Logf("perm = %v", perm) + for _, i := range perm { + err := client.NewTestChannel(chs[i]).Register(ctx) + assert.NoErrorf(err, "register channel: %d", i) + time.Sleep(waitTimeout) // Sleep to ensure that events have been processed and local client states have been updated. + } + + isSecondary := false + + // Settle the channels in a random order. + perm = rand.Perm(len(chs)) + t.Logf("Settle order = %v", perm) + for _, i := range perm { + var err error + err = chs[i].Settle(ctx, isSecondary) + isSecondary = true + + assert.NoErrorf(err, "settle channel: %d", i) + } + + // Check final balances. + balancesAfter := channel.Balances{ + { + pct.alice.BalanceReader.Balance(pct.asset), + pct.bob.BalanceReader.Balance(pct.asset), + }, + } + + balancesDiff := balancesAfter.Sub(pct.balancesBefore) + expectedBalancesDiff := channel.Balances{ + { + new(big.Int).Sub(pct.finalBalsAlice, pct.initBalsAlice), + new(big.Int).Sub(pct.finalBalsBob, pct.initBalsBob), + }, + } + balanceDelta := setup.BalanceDelta + eq := EqualBalancesWithDelta(expectedBalancesDiff, balancesDiff, balanceDelta) + assert.Truef(eq, "final ledger balances incorrect: expected balance difference %v +- %v, got %v", expectedBalancesDiff, balanceDelta, balancesDiff) +} + +func setupPaymentChannelTest( + t *testing.T, + ctx context.Context, + setup PaymentChannelSetup, +) (pct paymentChannelTest) { + t.Helper() + require := require.New(t) + + // Set test values. + asset := setup.Asset + pct.asset = asset + pct.initBalsAlice = setup.Balances.InitBalsAliceBob[0] + pct.initBalsBob = setup.Balances.InitBalsAliceBob[1] + pct.balsUpdated = setup.Balances.BalsUpdated + pct.finalBalsAlice = setup.Balances.FinalBals[0] + pct.finalBalsBob = setup.Balances.FinalBals[1] + pct.isUTXO = setup.IsUTXO + + const errBufferLen = 10 + pct.errs = make(chan error, errBufferLen) + + // Setup clients. + roles := setup.Clients + clients := NewClients(t, setup.Rng, roles[:]) + alice, bob := clients[0], clients[1] + pct.alice, pct.bob = alice, bob + + // Store client balances before running test. + pct.balancesBefore = channel.Balances{ + { + pct.alice.BalanceReader.Balance(pct.asset), + pct.bob.BalanceReader.Balance(pct.asset), + }, + } + + // Setup Bob's proposal and update handler. + channelsBob := make(chan *client.Channel, 1) + var openingProposalHandlerBob client.ProposalHandlerFunc = func(cp client.ChannelProposal, pr *client.ProposalResponder) { + switch cp := cp.(type) { + case *client.LedgerChannelProposalMsg: + ch, err := pr.Accept(ctx, cp.Accept(bob.WalletAddress, client.WithRandomNonce())) + if err != nil { + pct.errs <- errors.WithMessage(err, "accepting ledger channel proposal") + } + channelsBob <- ch + default: + pct.errs <- errors.Errorf("invalid channel proposal: %v", cp) + } + } + var updateProposalHandlerBob client.UpdateHandlerFunc = func( + s *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, + ) { + err := ur.Accept(ctx) + if err != nil { + pct.errs <- errors.WithMessage(err, "Bob: accepting channel update") + } + } + go bob.Client.Handle(openingProposalHandlerBob, updateProposalHandlerBob) + + // Establish ledger channel between Alice and Ingrid. + peersAlice := []wire.Address{alice.Identity.Address(), bob.Identity.Address()} + initAllocAlice := channel.NewAllocation(len(peersAlice), asset) + initAllocAlice.SetAssetBalances(asset, []channel.Bal{pct.initBalsAlice, pct.initBalsBob}) + lcpAlice, err := client.NewLedgerChannelProposal( + setup.ChallengeDuration, + alice.WalletAddress, + initAllocAlice, + peersAlice, + ) + require.NoError(err, "creating ledger channel proposal") + + pct.chAliceBob, err = alice.ProposeChannel(ctx, lcpAlice) + require.NoError(err, "opening channel between Alice and Ingrid") + select { + case pct.chBobAlice = <-channelsBob: + case err := <-pct.errs: + t.Fatalf("Error in go-routine: %v", err) + } + + err = pct.chAliceBob.Update(ctx, func(s *channel.State) { + s.Balances = channel.Balances{pct.balsUpdated} + }) + require.NoError(err, "updating virtual channel") + + return pct +} From 15bf682341163f5f871e4b53591f12a3d287328c Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 15 Apr 2025 10:37:39 +0200 Subject: [PATCH 09/26] feat: add proper clean up for virtual channel optimistic test Signed-off-by: Minh Huy Tran --- client/test/paymentchannel.go | 7 ++---- client/test/virtualchannel.go | 41 +++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/client/test/paymentchannel.go b/client/test/paymentchannel.go index 2d456467b..c120d917f 100644 --- a/client/test/paymentchannel.go +++ b/client/test/paymentchannel.go @@ -133,16 +133,13 @@ func TestPaymentChannelDispute( //nolint:revive // test.Test... stutters but OK time.Sleep(waitTimeout) // Sleep to ensure that events have been processed and local client states have been updated. } - isSecondary := false - // Settle the channels in a random order. + isSecondary := false perm = rand.Perm(len(chs)) t.Logf("Settle order = %v", perm) for _, i := range perm { - var err error - err = chs[i].Settle(ctx, isSecondary) + err := chs[i].Settle(ctx, isSecondary) isSecondary = true - assert.NoErrorf(err, "settle channel: %d", i) } diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 5bc8c68d0..3eb04dd55 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -78,6 +78,44 @@ func TestVirtualChannelOptimistic( //nolint:revive // test.Test... stutters but assert.NoError(t, err, "Alice: invalid final balances") err = vct.chBobIngrid.State().Balances.AssertEqual(channel.Balances{vct.finalBalsBob}) assert.NoError(t, err, "Bob: invalid final balances") + + // Close the parents. + chs = []*client.Channel{vct.chAliceIngrid, vct.chIngridAlice, vct.chBobIngrid, vct.chIngridBob} + isSecondary := [2]bool{false, false} + perm := rand.Perm(len(chs)) + t.Logf("Settle order = %v", perm) + for _, i := range perm { + var err error + if i < 2 { + err = chs[i].Settle(ctx, isSecondary[0]) + isSecondary[0] = true + } else { + err = chs[i].Settle(ctx, isSecondary[1]) + isSecondary[1] = true + } + assert.NoErrorf(t, err, "settle channel: %d", i) + } + + // Check final balances. + balancesAfter := channel.Balances{ + { + vct.alice.BalanceReader.Balance(vct.asset), + vct.bob.BalanceReader.Balance(vct.asset), + vct.ingrid.BalanceReader.Balance(vct.asset), + }, + } + + balancesDiff := balancesAfter.Sub(vct.balancesBefore) + expectedBalancesDiff := channel.Balances{ + { + new(big.Int).Sub(vct.finalBalsAlice[0], vct.initBalsAlice[0]), + new(big.Int).Sub(vct.finalBalsBob[0], vct.initBalsBob[0]), + big.NewInt(0), + }, + } + balanceDelta := setup.BalanceDelta + eq := EqualBalancesWithDelta(expectedBalancesDiff, balancesDiff, balanceDelta) + assert.Truef(t, eq, "final ledger balances incorrect: expected balance difference %v +- %v, got %v", expectedBalancesDiff, balanceDelta, balancesDiff) } // TestVirtualChannelDispute tests virtual channel functionality in the dispute @@ -103,9 +141,8 @@ func TestVirtualChannelDispute( //nolint:revive // test.Test... stutters but OK time.Sleep(waitTimeout) // Sleep to ensure that events have been processed and local client states have been updated. } - isSecondary := [2]bool{false, false} - // Settle the channels in a random order. + isSecondary := [2]bool{false, false} perm = rand.Perm(len(chs)) t.Logf("Settle order = %v", perm) for _, i := range perm { From 4d68d2869b94bcbdba731a5f9fdf63a284509abe Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 15 Apr 2025 11:00:22 +0200 Subject: [PATCH 10/26] feat: add client's handler for virtual channel test cleanup Signed-off-by: Minh Huy Tran --- client/test/virtualchannel.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 3eb04dd55..95c8a5fcc 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -80,6 +80,14 @@ func TestVirtualChannelOptimistic( //nolint:revive // test.Test... stutters but assert.NoError(t, err, "Bob: invalid final balances") // Close the parents. + err = vct.chIngridAlice.Update(ctx, func(s *channel.State) { + s.IsFinal = true + }) + assert.NoError(t, err, "Alice: close channel") + vct.chIngridBob.Update(ctx, func(s *channel.State) { + s.IsFinal = true + }) + assert.NoError(t, err, "Bob: close channel") chs = []*client.Channel{vct.chAliceIngrid, vct.chIngridAlice, vct.chBobIngrid, vct.chIngridBob} isSecondary := [2]bool{false, false} perm := rand.Perm(len(chs)) @@ -336,6 +344,17 @@ func setupVirtualChannelTest( } go bob.Client.Handle(openingProposalHandlerBob, updateProposalHandlerBob) + // Setup Alice's handlers. + var updateProposalHandlerAlice client.UpdateHandlerFunc = func( + s *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, + ) { + err := ur.Accept(ctx) + if err != nil { + vct.errs <- errors.WithMessage(err, "Bob: accepting channel update") + } + } + go alice.Client.Handle(openingProposalHandlerBob, updateProposalHandlerAlice) + // Establish virtual channel between Alice and Bob via Ingrid. initAllocVirtual := channel.Allocation{ Assets: []channel.Asset{asset}, From 22cdc688710b6ffd89cf9da997166aee81a2a1bf Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Fri, 25 Apr 2025 12:23:08 +0200 Subject: [PATCH 11/26] refactor(lint): update and refactor golangci-lint Signed-off-by: Minh Huy Tran --- .github/workflows/ci.yml | 12 +- .golangci.json | 103 ++++++ .golangci.yml | 81 ----- apps/payment/app_internal_test.go | 10 +- apps/payment/randomizer_internal_test.go | 3 +- apps/payment/resolver_internal_test.go | 4 +- backend/sim/channel/app.go | 12 +- backend/sim/channel/asset.go | 13 +- backend/sim/channel/asset_test.go | 2 +- backend/sim/wallet/address.go | 56 ++-- backend/sim/wallet/address_internal_test.go | 11 +- backend/sim/wallet/wallet.go | 20 +- backend/sim/wallet/wallet_internal_test.go | 18 +- backend/sim/wallet/wallet_test.go | 2 +- backend/sim/wire/account.go | 14 +- backend/sim/wire/address.go | 20 +- channel/actionmachine.go | 14 +- channel/allocation.go | 185 ++++++----- channel/allocation_test.go | 4 +- channel/app.go | 4 +- channel/app_test.go | 5 +- channel/appregistry_internal_test.go | 3 +- channel/funder_internal_test.go | 5 +- channel/machine.go | 168 +++++----- channel/mock_app_internal_test.go | 15 +- channel/multi/adjudicator.go | 2 +- channel/multi/funder.go | 9 +- channel/multi/subscription.go | 2 +- channel/params.go | 56 ++-- channel/persistence/keyvalue/cache.go | 7 +- .../persistence/keyvalue/persistrestorer.go | 14 +- .../keyvalue/persistrestorer_internal_test.go | 6 +- channel/persistence/keyvalue/restorer.go | 38 +-- channel/persistence/statemachine_test.go | 4 +- channel/persistence/test/channel.go | 17 +- .../persistence/test/channel_internal_test.go | 2 +- channel/persistence/test/peerchans.go | 16 +- channel/persistence/test/persistrestorer.go | 22 +- .../persistence/test/persistrestorertest.go | 12 +- channel/state.go | 2 +- channel/statemachine.go | 18 +- channel/test/app_randomizer_internal_test.go | 4 +- channel/test/backend.go | 26 +- channel/test/randomizer.go | 33 +- channel/test/randomopts.go | 21 +- channel/transaction.go | 2 +- channel/transaction_test.go | 2 +- client/channel.go | 22 +- client/channelconn.go | 107 +++--- client/client.go | 42 +-- client/client_internal_test.go | 3 +- client/client_role_test.go | 4 +- client/client_test.go | 2 +- client/clientconn.go | 30 +- client/payment_test.go | 5 +- client/proposal.go | 10 +- client/proposal_internal_test.go | 2 +- client/proposalmsgs.go | 166 +++++----- client/restore_internal_test.go | 8 +- client/serialize.go | 19 +- client/subchannel.go | 8 +- client/sync.go | 9 +- client/test/alice.go | 4 +- client/test/backend.go | 308 +++++++++--------- client/test/bob.go | 4 +- client/test/carol.go | 18 +- client/test/channel.go | 32 +- client/test/fund.go | 1 + client/test/handler.go | 2 +- client/test/mallory.go | 5 +- client/test/multiledger.go | 16 +- client/test/multiledger_dispute.go | 7 +- client/test/multiledger_happy.go | 2 + client/test/paymentchannel.go | 15 +- client/test/persistence.go | 12 +- client/test/progression.go | 10 +- client/test/proposalmsgs.go | 12 +- client/test/role.go | 148 ++++----- client/test/subchannel.go | 12 +- client/test/subchannel_dispute.go | 18 +- client/test/syncmsgs.go | 9 +- client/test/updatemsgs.go | 21 +- client/test/virtualchannel.go | 22 +- client/testchannel.go | 2 +- client/update.go | 7 +- client/virtual_channel.go | 13 +- client/virtual_channel_settlement.go | 10 +- log/levellified.go | 22 +- log/log.go | 2 + log/logrus/logrus_internal_test.go | 8 +- wallet/address.go | 29 +- wallet/sig.go | 4 +- wallet/test/address.go | 6 +- wallet/test/wallet.go | 18 +- wallet/test/walletbench.go | 6 +- watcher/local/adjudicatorpubsub.go | 32 +- watcher/local/watcher.go | 108 +++--- watcher/local/watcher_test.go | 87 ++--- wire/account.go | 7 +- wire/address.go | 23 +- wire/cache_internal_test.go | 2 +- wire/controlmsgs.go | 20 +- wire/encode_test.go | 2 +- wire/hybridbus.go | 1 - wire/net/bus_test.go | 5 +- wire/net/endpoint.go | 44 +-- wire/net/endpoint_internal_test.go | 3 +- wire/net/endpoint_registry.go | 86 ++--- wire/net/endpoint_registry_external_test.go | 8 +- wire/net/endpoint_registry_internal_test.go | 16 +- wire/net/exchange_addr_internal_test.go | 5 +- wire/net/simple/account.go | 34 +- wire/net/simple/address.go | 55 ++-- wire/net/simple/dialer.go | 16 +- wire/net/simple/dialer_internal_test.go | 20 +- wire/net/simple/listener_internal_test.go | 6 +- wire/net/simple/simple_exchange_addr_test.go | 3 +- wire/net/test/connhub_internal_test.go | 2 +- wire/net/test/dialer_internal_test.go | 3 +- wire/net/test/dialerlist_internal_test.go | 4 +- wire/net/test/listener_internal_test.go | 5 +- wire/net/test/listenermap_internal_test.go | 13 +- wire/perunio/bigint.go | 4 +- wire/perunio/bigint_external_test.go | 11 +- wire/perunio/byteslice_external_test.go | 3 +- wire/perunio/serialize.go | 2 + wire/perunio/string.go | 12 +- wire/perunio/string_internal_test.go | 6 +- wire/perunio/wire_internal_test.go | 3 +- wire/protobuf/controlmsgs.go | 8 +- wire/protobuf/proposalmsgs.go | 164 +++++----- wire/protobuf/serializer.go | 17 +- wire/protobuf/syncmsgs.go | 21 +- wire/protobuf/test/serializertest.go | 2 +- wire/protobuf/updatemsgs.go | 92 +++--- wire/receiver.go | 14 +- wire/receiver_internal_test.go | 5 +- wire/relay.go | 66 ++-- wire/relay_internal_test.go | 20 +- wire/relay_test.go | 3 +- wire/test/address.go | 8 +- wire/test/bustest.go | 19 +- 142 files changed, 1845 insertions(+), 1586 deletions(-) create mode 100644 .golangci.json delete mode 100644 .golangci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b8e27396..6679361ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: release: env: - go-version: 1.18 + go-version: 1.22 jobs: check-copyright: @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Authors @@ -26,20 +26,20 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Check vanity import run: .scripts/check-vanity-imports.sh $GITHUB_WORKSPACE - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ${{ env.go-version }} - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v7 with: - version: v1.45 + version: v2.1 - name: Lint proto files uses: plexsystems/protolint-action@v0.6.0 diff --git a/.golangci.json b/.golangci.json new file mode 100644 index 000000000..493d210a8 --- /dev/null +++ b/.golangci.json @@ -0,0 +1,103 @@ +{ + "formatters": { + "enable": [ + "gofmt", + "gofumpt", + "goimports" + ], + "exclusions": { + "generated": "lax", + "paths": [ + "third_party$", + "builtin$", + "examples$" + ] + } + }, + "linters": { + "default": "all", + "disable": [ + "err113", + "errorlint", + "gochecknoglobals", + "gochecknoinits", + "ireturn", + "lll", + "nlreturn", + "paralleltest", + "promlinter", + "tparallel", + "varnamelen", + "wrapcheck", + "wsl", + "testifylint", + "revive", + "recvcheck", + "prealloc", + "depguard", + "nonamedreturns", + "inamedparam", + "exhaustruct" + ], + "exclusions": { + "generated": "lax", + "paths": [ + "third_party$", + "builtin$", + "examples$" + ], + "presets": [ + "common-false-positives", + "legacy", + "std-error-handling" + ], + "rules": [ + { + "linters": [ + "err113", + "forcetypeassert", + "funlen" + ], + "path": "test" + }, + { + "linters": [ + "revive" + ], + "path": "test", + "text": "context-as-argument" + } + ] + }, + "settings": { + "cyclop": { + "max-complexity": 15 + }, + "forbidigo": { + "forbid": [ + { + "pattern": "^[Ee]quals$" + }, + { + "pattern": "^print.*$" + }, + { + "pattern": "fmt\\.Print.*" + } + ] + }, + "goheader": { + "template-path": ".scripts/copyright-notice", + "values": { + "regexp": { + "ANY_YEAR": "20(19|2\\d)" + } + } + } + } + }, + "run": { + "modules-download-mode": "readonly" + }, + "version": "2" +} \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 668e8353e..000000000 --- a/.golangci.yml +++ /dev/null @@ -1,81 +0,0 @@ -run: - timeout: 2m - - # Skip the eth bindings. - skip-dirs: - - backend/ethereum/bindings/ - - # Do not change go.{mod,sum}. - modules-download-mode: readonly - -linters: - enable-all: true - disable: - # Disabled as a style choice: - - wrapcheck # We do not rewrap all errors from other packages. - - varnamelen # We often use single letter arguments. - - exhaustivestruct # We often have uninitialized fields of structs. - - errorlint # We have interface errors which an impl needs to return. - - lll # We use long lines. - - gochecknoglobals # We have a lot of globals. - - nlreturn # "new line before return"; not useful. - - paralleltest # We like parallel tests. - - ireturn # We often return interfaces. - - wsl # Formatting disabled for now. - - gochecknoinits # We use init functions. - - gci # We have our own import order. - - goerr113 # We do not strictly require static errors. - - promlinter # Disabled because unstable. - - # These could be enabled in the future: - - ifshort # we often don't use `if err := โ€ฆ` for readability. - - tparallel # We don't always use parallel tests. - - # Deprecated: - - maligned - - scopelint - - golint - - interfacer - -linters-settings: - cyclop: - # 15 is used by go-report-card as well. - max-complexity: 15 - goheader: - values: - regexp: - ANY_YEAR: "20(19|2\\d)" # 2019-2029 - template-path: ".scripts/copyright-notice" - forbidigo: - forbid: - # Forbid functions to start with "get" or "Get". - - ^[Gg]et.*$ - - ^[Ee]quals$ - # Default values: - - ^print.*$ - - 'fmt\.Print.*' - exclude-rules: - - path: ".*\\.pb\\.go$" # Exclude protobuf generated files. -issues: - # Re-enable default excludes. - include: - - EXC0012 # revive: exported (.+) should have comment or be unexported - - EXC0013 # revive: package comment should be of the form "(.+)..." - - EXC0014 # revive: comment on exported (.+) should be of the form "(.+)..." - - EXC0015 # revive: package should have comment - - exclude-rules: - - path: test - linters: - # We often have long test or test helper functions. - - funlen - # Can use panicing interface casts. - - forcetypeassert - # Can create dynamic errors with errors.New. - - goerr113 - - # We always use testing.T as first argument in testing functions, - # therefore disable the "context must be the first argument" check in tests. - - path: test - linters: [revive] - text: "context-as-argument" diff --git a/apps/payment/app_internal_test.go b/apps/payment/app_internal_test.go index 19c46d5e4..7bcac626a 100644 --- a/apps/payment/app_internal_test.go +++ b/apps/payment/app_internal_test.go @@ -42,7 +42,7 @@ func TestApp_ValidInit(t *testing.T) { assert.Panics(func() { app.ValidInit(nil, wrongdata) }) //nolint:errcheck data := &channel.State{Data: Data()} - assert.Nil(app.ValidInit(nil, data)) + assert.NoError(app.ValidInit(nil, data)) } func TestApp_ValidTransition(t *testing.T) { @@ -93,9 +93,9 @@ func TestApp_ValidTransition(t *testing.T) { test.WithBalances(asBalances(tt.from...)...), ) numParticipants := len(tt.from[0]) - for i := 0; i < numParticipants; i++ { + for i := range numParticipants { // valid self-transition - assert.NoError(app.ValidTransition(nil, from, from, channel.Index(i))) + assert.NoError(app.ValidTransition(nil, from, from, channel.Index(i))) //nolint:gosec } for _, tto := range tt.tos { @@ -104,8 +104,8 @@ func TestApp_ValidTransition(t *testing.T) { test.WithAppData(Data()), test.WithBalances(asBalances(tto.alloc...)...), ) - for i := 0; i < numParticipants; i++ { - err := app.ValidTransition(nil, from, to, channel.Index(i)) + for i := range numParticipants { + err := app.ValidTransition(nil, from, to, channel.Index(i)) //nolint:gosec if i == tto.valid { assert.NoError(err) } else { diff --git a/apps/payment/randomizer_internal_test.go b/apps/payment/randomizer_internal_test.go index c178314cf..a16e5d679 100644 --- a/apps/payment/randomizer_internal_test.go +++ b/apps/payment/randomizer_internal_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" _ "perun.network/go-perun/backend/sim" // backend init "perun.network/go-perun/channel" @@ -31,7 +32,7 @@ func TestRandomizer(t *testing.T) { app := r.NewRandomApp(rng, channel.TestBackendID) channel.RegisterApp(app) regApp, err := channel.Resolve(app.Def()) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, app.Def().Equal(regApp.Def())) assert.True(t, IsData(r.NewRandomData(rng))) } diff --git a/apps/payment/resolver_internal_test.go b/apps/payment/resolver_internal_test.go index 9508a0346..e1428f21c 100644 --- a/apps/payment/resolver_internal_test.go +++ b/apps/payment/resolver_internal_test.go @@ -36,7 +36,7 @@ func TestResolver(t *testing.T) { channel.RegisterAppResolver(def.Equal, &Resolver{}) app, err := channel.Resolve(def) - assert.NoError(err) + require.NoError(err) require.NotNil(app) assert.True(def.Equal(app.Def())) } @@ -47,7 +47,7 @@ func TestData(t *testing.T) { assert.NotPanics(func() { data := Data() _, err := data.MarshalBinary() - assert.Nil(err) + assert.NoError(err) }) assert.NotPanics(func() { diff --git a/backend/sim/channel/app.go b/backend/sim/channel/app.go index 2c32d7e7c..ec7b70129 100644 --- a/backend/sim/channel/app.go +++ b/backend/sim/channel/app.go @@ -26,6 +26,12 @@ type AppID struct { *wallet.Address } +// NewRandomAppID generates a new random app identifier. +func NewRandomAppID(rng *rand.Rand) AppID { + addr := wallet.NewRandomAddress(rng) + return AppID{Address: addr} +} + // Equal returns whether the object is equal to the given object. func (id AppID) Equal(b channel.AppID) bool { bTyped, ok := b.(AppID) @@ -44,9 +50,3 @@ func (id AppID) Key() channel.AppIDKey { } return channel.AppIDKey(b) } - -// NewRandomAppID generates a new random app identifier. -func NewRandomAppID(rng *rand.Rand) AppID { - addr := wallet.NewRandomAddress(rng) - return AppID{Address: addr} -} diff --git a/backend/sim/channel/asset.go b/backend/sim/channel/asset.go index 2e66db169..403b07379 100644 --- a/backend/sim/channel/asset.go +++ b/backend/sim/channel/asset.go @@ -17,8 +17,10 @@ package channel import ( "encoding/binary" "fmt" + "math" "math/rand" + "github.com/pkg/errors" "perun.network/go-perun/channel" ) @@ -44,6 +46,9 @@ func NewRandomAsset(rng *rand.Rand) *Asset { // MarshalBinary marshals the address into its binary representation. func (a Asset) MarshalBinary() ([]byte, error) { data := make([]byte, assetLen) + if a.ID < 0 { + return nil, errors.New("asset ID must be non-negative") + } byteOrder.PutUint64(data, uint64(a.ID)) return data, nil } @@ -51,9 +56,13 @@ func (a Asset) MarshalBinary() ([]byte, error) { // UnmarshalBinary unmarshals the asset from its binary representation. func (a *Asset) UnmarshalBinary(data []byte) error { if len(data) != assetLen { - return fmt.Errorf("unexpected length %d, want %d", len(data), assetLen) //nolint:goerr113 // We do not want to define this as constant error. + return fmt.Errorf("unexpected length %d, want %d", len(data), assetLen) // We do not want to define this as constant error. + } + id := byteOrder.Uint64(data) + if id > math.MaxInt64 { + return fmt.Errorf("asset ID %d is too large", id) } - a.ID = int64(byteOrder.Uint64(data)) + a.ID = int64(id) return nil } diff --git a/backend/sim/channel/asset_test.go b/backend/sim/channel/asset_test.go index 7761e391e..b710b2e96 100644 --- a/backend/sim/channel/asset_test.go +++ b/backend/sim/channel/asset_test.go @@ -24,7 +24,7 @@ import ( func Test_Asset_GenericMarshaler(t *testing.T) { rng := pkgtest.Prng(t) - for n := 0; n < 10; n++ { + for range 10 { test.GenericMarshalerTest(t, &channel.Asset{ID: rng.Int63()}) } } diff --git a/backend/sim/wallet/address.go b/backend/sim/wallet/address.go index 562f517dd..9642181d2 100644 --- a/backend/sim/wallet/address.go +++ b/backend/sim/wallet/address.go @@ -29,6 +29,21 @@ import ( // Address represents a simulated address. type Address ecdsa.PublicKey +// NewRandomAddress creates a new address using the randomness +// provided by rng. +func NewRandomAddress(rng io.Reader) *Address { + privateKey, err := ecdsa.GenerateKey(curve, rng) + if err != nil { + log.Panicf("Creation of account failed with error", err) + } + + return &Address{ + Curve: privateKey.Curve, + X: privateKey.X, + Y: privateKey.Y, + } +} + // BackendID returns the backend id of the address. func (a Address) BackendID() wallet.BackendID { return channel.TestBackendID @@ -46,21 +61,6 @@ const ( // compile time check that we implement the perun Address interface. var _ wallet.Address = (*Address)(nil) -// NewRandomAddress creates a new address using the randomness -// provided by rng. -func NewRandomAddress(rng io.Reader) *Address { - privateKey, err := ecdsa.GenerateKey(curve, rng) - if err != nil { - log.Panicf("Creation of account failed with error", err) - } - - return &Address{ - Curve: privateKey.Curve, - X: privateKey.X, - Y: privateKey.Y, - } -} - // NewRandomAddresses creates a new address using the randomness // provided by rng. func NewRandomAddresses(rng io.Reader) map[wallet.BackendID]wallet.Address { @@ -82,19 +82,6 @@ func (a *Address) Bytes() []byte { return data[:] } -// byteArray converts an address into a 64-byte array. The returned array -// consists of two 32-byte chunks representing the public key's X and Y values. -func (a *Address) byteArray() (data [addrLen]byte) { - xb := a.X.Bytes() - yb := a.Y.Bytes() - - // Left-pad with 0 bytes. - copy(data[elemLen-len(xb):elemLen], xb) - copy(data[addrLen-len(yb):addrLen], yb) - - return data -} - // String converts this address to a human-readable string. func (a *Address) String() string { return fmt.Sprintf("0x%x", a.byteArray()) @@ -150,3 +137,16 @@ func (a *Address) UnmarshalBinary(data []byte) error { return nil } + +// byteArray converts an address into a 64-byte array. The returned array +// consists of two 32-byte chunks representing the public key's X and Y values. +func (a *Address) byteArray() (data [addrLen]byte) { + xb := a.X.Bytes() + yb := a.Y.Bytes() + + // Left-pad with 0 bytes. + copy(data[elemLen-len(xb):elemLen], xb) + copy(data[addrLen-len(yb):addrLen], yb) + + return data +} diff --git a/backend/sim/wallet/address_internal_test.go b/backend/sim/wallet/address_internal_test.go index c3f5e703f..41d9c6494 100644 --- a/backend/sim/wallet/address_internal_test.go +++ b/backend/sim/wallet/address_internal_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" pkgtest "polycry.pt/poly-go/test" "perun.network/go-perun/wire/test" @@ -26,7 +27,7 @@ import ( func TestGenericMarshaler(t *testing.T) { rng := pkgtest.Prng(t) - for n := 0; n < 10; n++ { + for range 10 { test.GenericMarshalerTest(t, NewRandomAddress(rng)) } } @@ -43,13 +44,13 @@ func TestAddressMarshalling(t *testing.T) { Y: new(big.Int).SetBytes(dest[elemLen:]), } result, err := addr.MarshalBinary() - assert.NoError(t, err, "marshaling address should not error") + require.NoError(t, err, "marshaling address should not error") assert.Equal(t, result, dest[:]) }) t.Run("half length", func(t *testing.T) { const zeros = 5 - for i := 0; i < zeros; i++ { + for i := range zeros { dest[i] = 0 dest[i+elemLen] = 0 } @@ -59,7 +60,7 @@ func TestAddressMarshalling(t *testing.T) { Y: new(big.Int).SetBytes(dest[elemLen:]), } result, err := addr.MarshalBinary() - assert.NoError(t, err, "marshaling address should not error") + require.NoError(t, err, "marshaling address should not error") assert.Equal(t, result, dest[:]) }) } @@ -73,7 +74,7 @@ func TestAddressOrdering(t *testing.T) { expected int } var cases []addrTest - for i := 0; i < 10; i++ { + for range 10 { addr := NewRandomAddress(rng) ltX := CloneAddr(addr) ltY := CloneAddr(addr) diff --git a/backend/sim/wallet/wallet.go b/backend/sim/wallet/wallet.go index 37f431153..3665471d6 100644 --- a/backend/sim/wallet/wallet.go +++ b/backend/sim/wallet/wallet.go @@ -27,6 +27,16 @@ import ( var _ wallet.Wallet = (*Wallet)(nil) +// Wallet is a collection of accounts. Query accounts using Unlock, track their +// usage using IncrementUsage and DecrementUsage, and lock them using LockAll. +// Create new accounts using NewRandomAccount, and add existing accounts using +// AddAccount. Check whether the wallet owns a particular account via +// HasAccount. +type Wallet struct { + accMutex sync.RWMutex + accs map[wallet.AddrKey]*Account +} + // NewWallet creates a new empty wallet. func NewWallet() *Wallet { return &Wallet{accs: make(map[wallet.AddrKey]*Account)} @@ -48,16 +58,6 @@ func NewRestoredWallet(accounts ...*Account) *Wallet { return w } -// Wallet is a collection of accounts. Query accounts using Unlock, track their -// usage using IncrementUsage and DecrementUsage, and lock them using LockAll. -// Create new accounts using NewRandomAccount, and add existing accounts using -// AddAccount. Check whether the wallet owns a particular account via -// HasAccount. -type Wallet struct { - accMutex sync.RWMutex - accs map[wallet.AddrKey]*Account -} - // Unlock retrieves the account belonging to the supplied address, and unlocks // it. If the address does not have a corresponding account in the wallet, // returns an error. diff --git a/backend/sim/wallet/wallet_internal_test.go b/backend/sim/wallet/wallet_internal_test.go index b6f6a4d7f..9dbea3284 100644 --- a/backend/sim/wallet/wallet_internal_test.go +++ b/backend/sim/wallet/wallet_internal_test.go @@ -33,7 +33,7 @@ func TestSignatureSerialize(t *testing.T) { rng := pkgtest.Prng(t) // More iterations are better for catching value dependent bugs - for i := 0; i < 10; i++ { + for range 10 { rBytes := make([]byte, 32) sBytes := make([]byte, 32) @@ -45,11 +45,11 @@ func TestSignatureSerialize(t *testing.T) { s := new(big.Int).SetBytes(sBytes) sig, err1 := serializeSignature(r, s) - a.Nil(err1, "Serialization should not fail") - a.Equal(curve.Params().BitSize/4, len(sig), "Signature has wrong size") + a.NoError(err1, "Serialization should not fail") + a.Len(sig, curve.Params().BitSize/4, "Signature has wrong size") R, S, err2 := deserializeSignature(sig) - a.Nil(err2, "Deserialization should not fail") + a.NoError(err2, "Deserialization should not fail") a.Equal(r, R, "Serialized and deserialized r values should be equal") a.Equal(s, S, "Serialized and deserialized s values should be equal") } @@ -71,7 +71,7 @@ func TestGenericTests(t *testing.T) { // NewRandomAddress is also tested in channel_test but since they are two packages, // we also need to test it here rng := pkgtest.Prng(t) - for i := 0; i < 10; i++ { + for range 10 { addr0 := NewRandomAddress(rng) addr1 := NewRandomAddress(rng) assert.NotEqual( @@ -80,10 +80,10 @@ func TestGenericTests(t *testing.T) { addrStrLen := addrLen*2 + 2 // hex encoded and prefixed with 0x str0 := addr0.String() str1 := addr1.String() - assert.Equal( - t, addrStrLen, len(str0), "First address '%v' has wrong length", str0) - assert.Equal( - t, addrStrLen, len(str1), "Second address '%v' has wrong length", str1) + assert.Len( + t, str0, addrStrLen, "First address '%v' has wrong length", str0) + assert.Len( + t, str1, addrStrLen, "Second address '%v' has wrong length", str1) assert.NotEqual( t, str0, str1, "Printed addresses are unlikely to be identical") } diff --git a/backend/sim/wallet/wallet_test.go b/backend/sim/wallet/wallet_test.go index 1f1989b00..c71ec6b91 100644 --- a/backend/sim/wallet/wallet_test.go +++ b/backend/sim/wallet/wallet_test.go @@ -30,7 +30,7 @@ func TestWallet_AddAccount(t *testing.T) { acc := wallet.NewRandomAccount(rng) assert.False(t, w.HasAccount(acc)) - assert.NoError(t, w.AddAccount(acc)) + require.NoError(t, w.AddAccount(acc)) assert.True(t, w.HasAccount(acc)) assert.Error(t, w.AddAccount(acc)) } diff --git a/backend/sim/wire/account.go b/backend/sim/wire/account.go index 191cbcd17..bb509d9c4 100644 --- a/backend/sim/wire/account.go +++ b/backend/sim/wire/account.go @@ -25,6 +25,13 @@ type Account struct { addr wire.Address } +// NewRandomAccount generates a new random account. +func NewRandomAccount(rng *rand.Rand) *Account { + return &Account{ + addr: NewRandomAddress(rng), + } +} + // Address returns the account's address. func (acc *Account) Address() wire.Address { return acc.addr @@ -34,10 +41,3 @@ func (acc *Account) Address() wire.Address { func (acc *Account) Sign(msg []byte) ([]byte, error) { return []byte("Authenticate"), nil } - -// NewRandomAccount generates a new random account. -func NewRandomAccount(rng *rand.Rand) *Account { - return &Account{ - addr: NewRandomAddress(rng), - } -} diff --git a/backend/sim/wire/address.go b/backend/sim/wire/address.go index 4c241dc5c..d43d50c61 100644 --- a/backend/sim/wire/address.go +++ b/backend/sim/wire/address.go @@ -33,6 +33,16 @@ func NewAddress() *Address { return &Address{} } +// NewRandomAddress returns a new random peer address. +func NewRandomAddress(rng *rand.Rand) *Address { + addr := Address{} + _, err := rng.Read(addr[:]) + if err != nil { + panic(err) + } + return &addr +} + // MarshalBinary marshals the address to binary. func (a Address) MarshalBinary() (data []byte, err error) { return a[:], nil @@ -70,13 +80,3 @@ func (a Address) Verify(msg, sig []byte) error { } return nil } - -// NewRandomAddress returns a new random peer address. -func NewRandomAddress(rng *rand.Rand) *Address { - addr := Address{} - _, err := rng.Read(addr[:]) - if err != nil { - panic(err) - } - return &addr -} diff --git a/channel/actionmachine.go b/channel/actionmachine.go index 453b67a1e..ecd119f23 100644 --- a/channel/actionmachine.go +++ b/channel/actionmachine.go @@ -114,13 +114,6 @@ func (m *ActionMachine) Update() error { return nil } -// setStaging sets the current staging phase and state and additionally clears -// the staging actions. -func (m *ActionMachine) setStaging(phase Phase, state *State) { - m.stagingActions = make([]Action, m.N()) - m.machine.setStaging(phase, state) -} - // Clone returns a deep copy of ActionMachine. func (m *ActionMachine) Clone() *ActionMachine { clonedActions := make([]Action, m.N()) @@ -144,3 +137,10 @@ func (m *ActionMachine) Clone() *ActionMachine { stagingActions: clonedActions, } } + +// setStaging sets the current staging phase and state and additionally clears +// the staging actions. +func (m *ActionMachine) setStaging(phase Phase, state *State) { + m.stagingActions = make([]Action, m.N()) + m.machine.setStaging(phase, state) +} diff --git a/channel/allocation.go b/channel/allocation.go index 8727ca2ed..4b21c4273 100644 --- a/channel/allocation.go +++ b/channel/allocation.go @@ -18,6 +18,7 @@ import ( "encoding" "io" "log" + "math" "math/big" "perun.network/go-perun/wallet" @@ -127,6 +128,9 @@ func NewAllocation(numParts int, backends []wallet.BackendID, assets ...Asset) * func (a *Allocation) AssetIndex(asset Asset) (Index, bool) { for idx, _asset := range a.Assets { if asset.Equal(_asset) { + if idx < 0 || idx >= math.MaxUint16 { + log.Panicf("asset index out of bounds: %d", idx) + } return Index(idx), true } } @@ -298,6 +302,80 @@ func (b Balances) Sub(a Balances) Balances { ) } +// Sum returns the sum of each asset over all participants. +func (b Balances) Sum() []Bal { + n := len(b) + totals := make([]*big.Int, n) + for i := range n { + totals[i] = new(big.Int) + } + + for i, asset := range b { + for _, bal := range asset { + totals[i].Add(totals[i], bal) + } + } + return totals +} + +// Decode decodes a Balances from an io.Reader. +func (b *Balances) Decode(r io.Reader) error { + var numAssets, numParts Index + if err := perunio.Decode(r, &numAssets, &numParts); err != nil { + return errors.WithMessage(err, "decoding dimensions") + } + if numAssets > MaxNumAssets { + return errors.Errorf("expected maximum number of assets %d, got %d", MaxNumAssets, numAssets) + } + if numParts > MaxNumParts { + return errors.Errorf("expected maximum number of parts %d, got %d", MaxNumParts, numParts) + } + + *b = make(Balances, numAssets) + for i := range *b { + (*b)[i] = make([]Bal, numParts) + for j := range (*b)[i] { + (*b)[i][j] = new(big.Int) + if err := perunio.Decode(r, &(*b)[i][j]); err != nil { + return errors.WithMessagef( + err, "decoding balance of asset %d of participant %d", i, j) + } + } + } + return nil +} + +// Encode encodes these balances into an io.Writer. +func (b Balances) Encode(w io.Writer) error { + numAssets := len(b) + numParts := 0 + + if numAssets > 0 { + numParts = len(b[0]) + } + if numAssets > MaxNumAssets { + return errors.Errorf("expected maximum number of assets %d, got %d", MaxNumAssets, numAssets) + } + if numParts > MaxNumParts { + return errors.Errorf("expected maximum number of parts %d, got %d", MaxNumParts, numParts) + } + if numParts < 0 || numParts > math.MaxUint16 { + return errors.New("too many participants") + } + if err := perunio.Encode(w, Index(numAssets), Index(numParts)); err != nil { + return errors.WithMessage(err, "encoding dimensions") + } + for i := range b { + for j := range b[i] { + if err := perunio.Encode(w, b[i][j]); err != nil { + return errors.WithMessagef( + err, "encoding balance of asset %d of participant %d", i, j) + } + } + } + return nil +} + // operate returns op(b, a). It panics if the dimensions do not match. func (b Balances) operate(a Balances, op func(Bal, Bal) Bal) Balances { if len(a) != len(b) { @@ -325,12 +403,29 @@ func (a Allocation) Encode(w io.Writer) error { err, "invalid allocations cannot be encoded, got %v", a) } // encode dimensions - if err := perunio.Encode(w, Index(len(a.Assets)), Index(len(a.Balances[0])), Index(len(a.Locked))); err != nil { + assetLen := len(a.Assets) + if assetLen > math.MaxUint16 { + return errors.New("too many assets") + } + balanceLen := len(a.Balances[0]) + if balanceLen > math.MaxUint16 { + return errors.New("too many participants") + } + lockedLen := len(a.Locked) + if lockedLen > math.MaxUint16 { + return errors.New("too many sub-allocations") + } + + if err := perunio.Encode(w, Index(assetLen), Index(balanceLen), Index(lockedLen)); err != nil { return err } // encode assets for i, asset := range a.Assets { - if err := perunio.Encode(w, uint32(a.Backends[i])); err != nil { + id := int(a.Backends[i]) + if id < 0 || id > math.MaxUint32 { + return errors.New("asset index out of bounds") + } + if err := perunio.Encode(w, uint32(id)); err != nil { return errors.WithMessagef(err, "encoding backends %d", i) } if err := perunio.Encode(w, asset); err != nil { @@ -394,62 +489,6 @@ func (a *Allocation) Decode(r io.Reader) error { return a.Valid() } -// Decode decodes a Balances from an io.Reader. -func (b *Balances) Decode(r io.Reader) error { - var numAssets, numParts Index - if err := perunio.Decode(r, &numAssets, &numParts); err != nil { - return errors.WithMessage(err, "decoding dimensions") - } - if numAssets > MaxNumAssets { - return errors.Errorf("expected maximum number of assets %d, got %d", MaxNumAssets, numAssets) - } - if numParts > MaxNumParts { - return errors.Errorf("expected maximum number of parts %d, got %d", MaxNumParts, numParts) - } - - *b = make(Balances, numAssets) - for i := range *b { - (*b)[i] = make([]Bal, numParts) - for j := range (*b)[i] { - (*b)[i][j] = new(big.Int) - if err := perunio.Decode(r, &(*b)[i][j]); err != nil { - return errors.WithMessagef( - err, "decoding balance of asset %d of participant %d", i, j) - } - } - } - return nil -} - -// Encode encodes these balances into an io.Writer. -func (b Balances) Encode(w io.Writer) error { - numAssets := len(b) - numParts := 0 - - if numAssets > 0 { - numParts = len(b[0]) - } - if numAssets > MaxNumAssets { - return errors.Errorf("expected maximum number of assets %d, got %d", MaxNumAssets, numAssets) - } - if numParts > MaxNumParts { - return errors.Errorf("expected maximum number of parts %d, got %d", MaxNumParts, numParts) - } - - if err := perunio.Encode(w, Index(numAssets), Index(numParts)); err != nil { - return errors.WithMessage(err, "encoding dimensions") - } - for i := range b { - for j := range b[i] { - if err := perunio.Encode(w, b[i][j]); err != nil { - return errors.WithMessagef( - err, "encoding balance of asset %d of participant %d", i, j) - } - } - } - return nil -} - // CloneBals creates a deep copy of a balance array. func CloneBals(orig []Bal) []Bal { if orig == nil { @@ -535,22 +574,6 @@ func (a Allocation) Sum() []Bal { return totals } -// Sum returns the sum of each asset over all participants. -func (b Balances) Sum() []Bal { - n := len(b) - totals := make([]*big.Int, n) - for i := 0; i < n; i++ { - totals[i] = new(big.Int) - } - - for i, asset := range b { - for _, bal := range asset { - totals[i].Add(totals[i], bal) - } - } - return totals -} - // NewSubAlloc creates a new sub-allocation. func NewSubAlloc(id ID, bals []Bal, indexMap []Index) *SubAlloc { if indexMap == nil { @@ -607,7 +630,7 @@ func (a *Allocation) Equal(b *Allocation) error { } // Compare Balances - if err := a.Balances.AssertEqual(b.Balances); err != nil { + if err := a.AssertEqual(b.Balances); err != nil { return errors.WithMessage(err, "comparing balances") } @@ -667,7 +690,11 @@ func (s SubAlloc) Encode(w io.Writer) error { err, "invalid sub-allocations cannot be encoded, got %v", s) } // encode ID and dimension - if err := perunio.Encode(w, s.ID, Index(len(s.Bals))); err != nil { + balsLen := len(s.Bals) + if balsLen > math.MaxUint16 { + return errors.New("too many bals") + } + if err := perunio.Encode(w, s.ID, Index(balsLen)); err != nil { return errors.WithMessagef( err, "encoding sub-allocation ID or dimension, id %v", s.ID) } @@ -679,7 +706,11 @@ func (s SubAlloc) Encode(w io.Writer) error { } } // Encode IndexMap. - if err := perunio.Encode(w, Index(len(s.IndexMap))); err != nil { + idxMapLen := len(s.IndexMap) + if idxMapLen > math.MaxUint16 { + return errors.New("too many index map entries") + } + if err := perunio.Encode(w, Index(idxMapLen)); err != nil { return errors.WithMessage(err, "encoding length of index map") } for i, x := range s.IndexMap { diff --git a/channel/allocation_test.go b/channel/allocation_test.go index 74f8cabcc..d9904a86a 100644 --- a/channel/allocation_test.go +++ b/channel/allocation_test.go @@ -163,7 +163,7 @@ func TestBalancesGreaterOrEqual(t *testing.T) { func TestBalancesEqualSum(t *testing.T) { rng := pkgtest.Prng(t) - for i := 0; i < 10; i++ { + for range 10 { // Two random balances are different by chance. a, b := test.NewRandomBalances(rng), test.NewRandomBalances(rng) ok, err := pkgbig.EqualSum(a, b) @@ -233,7 +233,7 @@ func testBalancesOperation(t *testing.T, op func(channel.Balances, channel.Balan func TestBalancesSerialization(t *testing.T) { rng := pkgtest.Prng(t) - for n := 0; n < 10; n++ { + for range 10 { alloc := test.NewRandomAllocation(rng) if alloc.Valid() == nil { peruniotest.GenericSerializerTest(t, alloc) diff --git a/channel/app.go b/channel/app.go index cd9d5305f..eaf8135c1 100644 --- a/channel/app.go +++ b/channel/app.go @@ -161,7 +161,7 @@ func (e OptAppEnc) Encode(w io.Writer) error { if IsNoApp(e.App) { return perunio.Encode(w, false) } - return perunio.Encode(w, true, e.App.Def()) + return perunio.Encode(w, true, e.Def()) } // Encode encodes an optional App value. @@ -221,7 +221,7 @@ func (d OptAppDecMap) Decode(r io.Reader) (err error) { return err } *d.App = make(map[int]App, mapLen) - for i := 0; i < mapLen; i++ { + for range mapLen { var key int if err := perunio.Decode(r, &key); err != nil { return err diff --git a/channel/app_test.go b/channel/app_test.go index 65c593263..c106efd66 100644 --- a/channel/app_test.go +++ b/channel/app_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" pkgtest "polycry.pt/poly-go/test" @@ -31,8 +32,8 @@ func TestAppShouldEqual(t *testing.T) { assert.EqualError(t, channel.AppShouldEqual(app1, app2), "different App definitions") assert.EqualError(t, channel.AppShouldEqual(app2, app1), "different App definitions") - assert.NoError(t, channel.AppShouldEqual(app1, app1)) + require.NoError(t, channel.AppShouldEqual(app1, app1)) assert.EqualError(t, channel.AppShouldEqual(app1, napp), "(non-)nil App definitions") assert.EqualError(t, channel.AppShouldEqual(napp, app1), "(non-)nil App definitions") - assert.NoError(t, channel.AppShouldEqual(napp, napp)) + require.NoError(t, channel.AppShouldEqual(napp, napp)) } diff --git a/channel/appregistry_internal_test.go b/channel/appregistry_internal_test.go index 08e74606e..b81d51d56 100644 --- a/channel/appregistry_internal_test.go +++ b/channel/appregistry_internal_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "polycry.pt/poly-go/test" ) @@ -97,7 +98,7 @@ func testAppRegistryIdentity(t *testing.T, rng *rand.Rand) { func assertIdentity(t *testing.T, expected App) { t.Helper() actual, err := Resolve(expected.Def()) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, actual.Def().Equal(expected.Def())) } diff --git a/channel/funder_internal_test.go b/channel/funder_internal_test.go index ee362113d..83060e452 100644 --- a/channel/funder_internal_test.go +++ b/channel/funder_internal_test.go @@ -34,7 +34,7 @@ func TestAssetFundingError(t *testing.T) { assert.Equal(Index(2), perr.TimedOutPeers[1]) assert.Equal(Index(3), perr.TimedOutPeers[2]) assert.Equal(Index(4), perr.TimedOutPeers[3]) - assert.Equal(4, len(perr.TimedOutPeers)) + assert.Len(perr.TimedOutPeers, 4) assert.Equal(perr.Error(), "Funding Error on asset [42] peers: [1], [2], [3], [4], did not fund channel in time") assert.False(IsAssetFundingError(errors.New("not a asset funding error"))) } @@ -61,7 +61,8 @@ func TestFundingTimeoutError(t *testing.T) { assert.Equal(Index(1), perr.Errors[2].TimedOutPeers[0]) assert.Equal(Index(3), perr.Errors[2].TimedOutPeers[1]) assert.Equal(3, len(perr.Errors)) - assert.Equal(perr.Error(), "Funding Error on asset [42] peers: [1], [2], did not fund channel in time; Funding Error on asset [1337] peers: [0], [2], did not fund channel in time; Funding Error on asset [7531] peers: [1], [3], did not fund channel in time; ") + assert.Equal("Funding Error on asset [42] peers: [1], [2], did not fund channel in time; Funding Error on asset [1337] peers: [0], [2], did not fund channel in time; Funding Error on asset [7531] peers: [1], [3], did not fund channel in time; ", + perr.Error()) // test no funding timeout error assert.False(IsFundingTimeoutError(errors.New("no FundingTimeoutError"))) // nil input should not return error diff --git a/channel/machine.go b/channel/machine.go index 333ca8205..2d41ff728 100644 --- a/channel/machine.go +++ b/channel/machine.go @@ -17,6 +17,7 @@ package channel import ( "fmt" stdio "io" + "math" "github.com/pkg/errors" @@ -147,6 +148,9 @@ func newMachine(acc map[wallet.BackendID]wallet.Account, params Params) (*machin if idx < 0 { return nil, errors.New("account not part of participant set") } + if idx > math.MaxUint16 { + return nil, errors.New("index out of bounds") + } return &machine{ phase: InitActing, @@ -190,7 +194,11 @@ func (m *machine) Params() *Params { // N returns the number of participants of the channel parameters of this machine. func (m *machine) N() Index { - return Index(len(m.params.Parts)) + partsLen := len(m.params.Parts) + if partsLen > math.MaxUint16 { + panic("number of participants exceeds uint16") + } + return Index(partsLen) } // Phase returns the current phase. @@ -198,12 +206,6 @@ func (m *machine) Phase() Phase { return m.phase } -// setPhase is internally used to set the phase. -func (m *machine) setPhase(p Phase) { - m.Log().Tracef("phase transition: %v", PhaseTransition{m.phase, p}) - m.phase = p -} - // inPhase returns whether phase is in phases. func inPhase(phase Phase, phases []Phase) bool { for _, p := range phases { @@ -243,7 +245,7 @@ func (m *machine) State() *State { return m.currentTX.State } -// CurrentTX returns the current current transaction. +// CurrentTX returns the current transaction. func (m *machine) CurrentTX() Transaction { return m.currentTX } @@ -303,12 +305,6 @@ func (m *machine) AddSig(idx Index, sig wallet.Sig) error { return nil } -// setStaging sets the given phase and state as staging state. -func (m *machine) setStaging(phase Phase, state *State) { - m.stagingTX = *m.newTransaction(state) - m.setPhase(phase) -} - // DiscardUpdate discards the current staging transaction and sets the machine's // phase back to Acting. This method is useful in the case where a valid update // request is rejected. @@ -340,35 +336,6 @@ func (m *machine) EnableFinal() error { return m.enableStaged(PhaseTransition{Signing, Final}) } -// enableStaged checks that -// 1. the current phase is `expected.From` and -// 2. all signatures of the staging transactions have been set. -// -// If successful, the staging transaction is promoted to be the current -// transaction. If not, an error is returned. -func (m *machine) enableStaged(expected PhaseTransition) error { - if err := m.expect(expected); err != nil { - return errors.WithMessage(err, "no staging phase") - } - - // Assert that we transition to phase Final iff state.IsFinal. - if (expected.To == Final) != m.stagingTX.State.IsFinal { - return m.phaseErrorf(expected, "State.IsFinal and target phase don't match") - } - - // Assert that all signatures are present. - for i, sig := range m.stagingTX.Sigs { - if sig == nil { - return m.phaseErrorf(expected, "signature %d missing from staging TX", i) - } - } - - m.setPhase(expected.To) - m.addTx(&m.stagingTX) - - return nil -} - // SetFunded tells the state machine that the channel got funded and progresses // to the Acting phase. func (m *machine) SetFunded() error { @@ -437,15 +404,6 @@ func (m *machine) SetWithdrawn() error { return m.simplePhaseTransition(Withdrawing, Withdrawn) } -func (m *machine) simplePhaseTransition(from, to Phase) error { - if err := m.expect(PhaseTransition{from, to}); err != nil { - return err - } - - m.setPhase(to) - return nil -} - var validPhaseTransitions = map[PhaseTransition]struct{}{ {InitActing, InitSigning}: {}, {InitSigning, Funding}: {}, @@ -469,14 +427,25 @@ var validPhaseTransitions = map[PhaseTransition]struct{}{ {Withdrawing, Withdrawn}: {}, } -func (m *machine) expect(tr PhaseTransition) error { - if m.phase != tr.From { - return m.phaseErrorf(tr, "not in correct phase") +func (m *machine) Clone() *machine { + var prevTXs []Transaction + if m.prevTXs != nil { + prevTXs = make([]Transaction, len(m.prevTXs)) + for i, tx := range m.prevTXs { + prevTXs[i] = tx.Clone() + } } - if _, ok := validPhaseTransitions[PhaseTransition{m.phase, tr.To}]; !ok { - return m.phaseErrorf(tr, "forbidden phase transition") + + return &machine{ + phase: m.phase, + acc: m.acc, + idx: m.idx, + params: *m.params.Clone(), + stagingTX: m.stagingTX.Clone(), + currentTX: m.currentTX.Clone(), + prevTXs: prevTXs, + Embedding: m.Embedding, } - return nil } // ValidTransition checks that the transition from the current to the provided @@ -506,7 +475,7 @@ func (m *machine) ValidTransition(to *State) error { return newError(fmt.Sprintf("expected version %d, got version %d", m.currentTX.Version+1, to.Version)) } - if err := to.Allocation.Valid(); err != nil { + if err := to.Valid(); err != nil { return newError(fmt.Sprintf("invalid allocation: %v", err)) } @@ -523,6 +492,60 @@ func (m *machine) ValidTransition(to *State) error { return nil } +func (m *machine) IsRegistered() bool { + return m.phase >= Registered +} + +// setPhase is internally used to set the phase. +func (m *machine) setPhase(p Phase) { + m.Log().Tracef("phase transition: %v", PhaseTransition{m.phase, p}) + m.phase = p +} + +// setStaging sets the given phase and state as staging state. +func (m *machine) setStaging(phase Phase, state *State) { + m.stagingTX = *m.newTransaction(state) + m.setPhase(phase) +} + +// enableStaged checks that +// 1. the current phase is `expected.From` and +// 2. all signatures of the staging transactions have been set. +// +// If successful, the staging transaction is promoted to be the current +// transaction. If not, an error is returned. +func (m *machine) enableStaged(expected PhaseTransition) error { + if err := m.expect(expected); err != nil { + return errors.WithMessage(err, "no staging phase") + } + + // Assert that we transition to phase Final iff state.IsFinal. + if (expected.To == Final) != m.stagingTX.IsFinal { + return m.phaseErrorf(expected, "State.IsFinal and target phase don't match") + } + + // Assert that all signatures are present. + for i, sig := range m.stagingTX.Sigs { + if sig == nil { + return m.phaseErrorf(expected, "signature %d missing from staging TX", i) + } + } + + m.setPhase(expected.To) + m.addTx(&m.stagingTX) + + return nil +} + +func (m *machine) simplePhaseTransition(from, to Phase) error { + if err := m.expect(PhaseTransition{from, to}); err != nil { + return err + } + + m.setPhase(to) + return nil +} + // phaseErrorf constructs a new PhaseTransitionError. func (m *machine) phaseErrorf(expected PhaseTransition, format string, args ...interface{}) error { return newPhaseTransitionErrorf(m.params.ID(), m.phase, expected, format, args...) @@ -533,29 +556,14 @@ func (m *machine) selfTransition() PhaseTransition { return PhaseTransition{m.phase, m.phase} } -func (m *machine) Clone() *machine { - var prevTXs []Transaction - if m.prevTXs != nil { - prevTXs = make([]Transaction, len(m.prevTXs)) - for i, tx := range m.prevTXs { - prevTXs[i] = tx.Clone() - } +func (m *machine) expect(tr PhaseTransition) error { + if m.phase != tr.From { + return m.phaseErrorf(tr, "not in correct phase") } - - return &machine{ - phase: m.phase, - acc: m.acc, - idx: m.idx, - params: *m.params.Clone(), - stagingTX: m.stagingTX.Clone(), - currentTX: m.currentTX.Clone(), - prevTXs: prevTXs, - Embedding: m.Embedding, + if _, ok := validPhaseTransitions[PhaseTransition{m.phase, tr.To}]; !ok { + return m.phaseErrorf(tr, "forbidden phase transition") } -} - -func (m *machine) IsRegistered() bool { - return m.phase >= Registered + return nil } func (m *machine) newTransaction(s *State) *Transaction { diff --git a/channel/mock_app_internal_test.go b/channel/mock_app_internal_test.go index c021eacc3..6c949273a 100644 --- a/channel/mock_app_internal_test.go +++ b/channel/mock_app_internal_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" wiretest "perun.network/go-perun/wire/test" pkgtest "polycry.pt/poly-go/test" @@ -70,19 +71,19 @@ func MockStateAppTest(t *testing.T, app MockApp) { t.Run("ValidTransition", func(t *testing.T) { // ValidTransition only checks the first state. - assert.NoError(t, app.ValidTransition(nil, stateValid, nil, 0)) + require.NoError(t, app.ValidTransition(nil, stateValid, nil, 0)) assert.Error(t, app.ValidTransition(nil, stateErr, nil, 0)) assert.True(t, IsStateTransitionError(app.ValidTransition(nil, stateTransErr, nil, 0))) assert.True(t, IsActionError(app.ValidTransition(nil, stateActErr, nil, 0))) - assert.Panics(t, func() { assert.NoError(t, app.ValidTransition(nil, statePanic, nil, 0)) }) + assert.Panics(t, func() { require.NoError(t, app.ValidTransition(nil, statePanic, nil, 0)) }) }) t.Run("ValidInit", func(t *testing.T) { - assert.NoError(t, app.ValidInit(nil, stateValid)) + require.NoError(t, app.ValidInit(nil, stateValid)) assert.Error(t, app.ValidInit(nil, stateErr)) assert.True(t, IsStateTransitionError(app.ValidInit(nil, stateTransErr))) assert.True(t, IsActionError(app.ValidInit(nil, stateActErr))) - assert.Panics(t, func() { assert.NoError(t, app.ValidInit(nil, statePanic)) }) + assert.Panics(t, func() { require.NoError(t, app.ValidInit(nil, statePanic)) }) }) } @@ -99,7 +100,7 @@ func MockActionAppTest(t *testing.T, app MockApp) { t.Run("InitState", func(t *testing.T) { _, _, err := app.InitState(nil, []Action{actValid}) // Sadly we can not check Allocation.valid() here, since it is private. - assert.NoError(t, err) + require.NoError(t, err) _, _, err = app.InitState(nil, []Action{actErr}) assert.Error(t, err) @@ -114,7 +115,7 @@ func MockActionAppTest(t *testing.T, app MockApp) { }) t.Run("ValidAction", func(t *testing.T) { - assert.NoError(t, app.ValidAction(nil, nil, 0, actValid)) + require.NoError(t, app.ValidAction(nil, nil, 0, actValid)) assert.Error(t, app.ValidAction(nil, nil, 0, actErr)) assert.True(t, IsStateTransitionError(app.ValidAction(nil, nil, 0, actTransErr))) assert.True(t, IsActionError(app.ValidAction(nil, nil, 0, actActErr))) @@ -125,7 +126,7 @@ func MockActionAppTest(t *testing.T, app MockApp) { // ApplyActions increments the Version counter, so we cant pass nil as state. retState, err := app.ApplyActions(nil, state, []Action{actValid}) assert.Equal(t, retState.Version, state.Version+1) - assert.NoError(t, err) + require.NoError(t, err) _, err = app.ApplyActions(nil, state, []Action{actErr}) assert.Error(t, err) diff --git a/channel/multi/adjudicator.go b/channel/multi/adjudicator.go index 60fe2512f..80c9467bc 100644 --- a/channel/multi/adjudicator.go +++ b/channel/multi/adjudicator.go @@ -112,7 +112,7 @@ func (a *Adjudicator) dispatch(assetIds []LedgerBackendID, f func(channel.Adjudi }(l) } - for i := 0; i < n; i++ { + for range n { err := <-errs if err != nil { return err diff --git a/channel/multi/funder.go b/channel/multi/funder.go index 455ea182d..e18048c6a 100644 --- a/channel/multi/funder.go +++ b/channel/multi/funder.go @@ -17,6 +17,7 @@ package multi import ( "context" "fmt" + "math" "time" "perun.network/go-perun/channel" @@ -63,7 +64,11 @@ func (f *Funder) SetEgoisticPart(index int) { // channel. If any of the funder calls fails, the method returns an error. func (f *Funder) Fund(ctx context.Context, request channel.FundingReq) error { // Define funding timeout. - d := time.Duration(request.Params.ChallengeDuration) * time.Second + duration := request.Params.ChallengeDuration + if duration > math.MaxInt64 { + return fmt.Errorf("challenge duration %d is too large", duration) + } + d := time.Duration(duration) * time.Second ctx, cancel := context.WithTimeout(ctx, d) defer cancel() @@ -122,7 +127,7 @@ func fundLedgers(ctx context.Context, request channel.FundingReq, assetIDs []Led } // Collect errors - for i := 0; i < n; i++ { + for range n { err := <-errs if err != nil { return err diff --git a/channel/multi/subscription.go b/channel/multi/subscription.go index fa801aa1a..2e90df0c9 100644 --- a/channel/multi/subscription.go +++ b/channel/multi/subscription.go @@ -75,7 +75,7 @@ func (s *AdjudicatorSubscription) Next() channel.AdjudicatorEvent { // Err blocks until an error occurred and returns it. func (s *AdjudicatorSubscription) Err() error { - for i := 0; i < len(s.subs); i++ { + for range len(s.subs) { err := <-s.errors if err != nil { return err diff --git a/channel/params.go b/channel/params.go index 94df168b4..58b170b53 100644 --- a/channel/params.go +++ b/channel/params.go @@ -89,11 +89,6 @@ type Params struct { Aux Aux } -// ID returns the channelID of this channel. -func (p *Params) ID() ID { - return p.id -} - // NewParams creates Params from the given data and performs sanity checks. The // appDef optional: if it is nil, it describes a payment channel. The channel id // is also calculated here and persisted because it probably is an expensive @@ -115,6 +110,34 @@ func NewParams(challengeDuration uint64, parts []map[wallet.BackendID]wallet.Add return NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual, aux), nil } +// NewParamsUnsafe creates Params from the given data and does NOT perform +// sanity checks. The channel id is also calculated here and persisted because +// it probably is an expensive hash operation. +func NewParamsUnsafe(challengeDuration uint64, parts []map[wallet.BackendID]wallet.Address, app App, nonce Nonce, ledger bool, virtual bool, aux Aux) *Params { + p := &Params{ + ChallengeDuration: challengeDuration, + Parts: parts, + App: app, + Nonce: nonce, + LedgerChannel: ledger, + VirtualChannel: virtual, + Aux: aux, + } + + // probably an expensive hash operation, do it only once during creation. + id, err := CalcID(p) + if err != nil || id == Zero { + log.Panicf("Could not calculate channel id: %v", err) + } + p.id = id + return p +} + +// ID returns the channelID of this channel. +func (p *Params) ID() ID { + return p.id +} + // ValidateProposalParameters validates all parameters that are part of the // proposal message in the MPCPP. Checks the following conditions: // * non-zero ChallengeDuration @@ -151,29 +174,6 @@ func ValidateParameters(challengeDuration uint64, numParts int, app App, nonce N return nil } -// NewParamsUnsafe creates Params from the given data and does NOT perform -// sanity checks. The channel id is also calculated here and persisted because -// it probably is an expensive hash operation. -func NewParamsUnsafe(challengeDuration uint64, parts []map[wallet.BackendID]wallet.Address, app App, nonce Nonce, ledger bool, virtual bool, aux Aux) *Params { - p := &Params{ - ChallengeDuration: challengeDuration, - Parts: parts, - App: app, - Nonce: nonce, - LedgerChannel: ledger, - VirtualChannel: virtual, - Aux: aux, - } - - // probably an expensive hash operation, do it only once during creation. - id, err := CalcID(p) - if err != nil || id == Zero { - log.Panicf("Could not calculate channel id: %v", err) - } - p.id = id - return p -} - // CloneAddresses returns a clone of an Address using its binary marshaling // implementation. It panics if an error occurs during binary (un)marshaling. func CloneAddresses(as []map[wallet.BackendID]wallet.Address) []map[wallet.BackendID]wallet.Address { diff --git a/channel/persistence/keyvalue/cache.go b/channel/persistence/keyvalue/cache.go index 5b0ad8cf1..cdd75da67 100644 --- a/channel/persistence/keyvalue/cache.go +++ b/channel/persistence/keyvalue/cache.go @@ -22,7 +22,7 @@ import ( "perun.network/go-perun/wire" ) -//nolint:deadcode +//nolint:unused func newChannelCache() *channelCache { return &channelCache{ peers: make(map[channel.ID][]wire.Address), @@ -31,12 +31,15 @@ func newChannelCache() *channelCache { } // channelCache contains all channels. +// +//nolint:unused type channelCache struct { mutex stdsync.RWMutex peers map[channel.ID][]wire.Address // Used when closing a channel. peerChannels map[string]map[channel.ID]struct{} // Address -> Set } +//nolint:unused func (c *channelCache) addPeerChannel(addr wire.Address, chID channel.ID) { c.mutex.Lock() defer c.mutex.Unlock() @@ -56,6 +59,7 @@ func (c *channelCache) addPeerChannel(addr wire.Address, chID channel.ID) { } } +//nolint:unused func (c *channelCache) deleteChannel(id channel.ID) []wire.Address { c.mutex.Lock() defer c.mutex.Unlock() @@ -78,6 +82,7 @@ func (c *channelCache) deleteChannel(id channel.ID) []wire.Address { return peers } +//nolint:unused func (c *channelCache) clear() { c.mutex.Lock() defer c.mutex.Unlock() diff --git a/channel/persistence/keyvalue/persistrestorer.go b/channel/persistence/keyvalue/persistrestorer.go index 9e2b94be3..9a3ecee22 100644 --- a/channel/persistence/keyvalue/persistrestorer.go +++ b/channel/persistence/keyvalue/persistrestorer.go @@ -27,6 +27,13 @@ type PersistRestorer struct { db sortedkv.Database } +// NewPersistRestorer creates a new PersistRestorer for the supplied database. +func NewPersistRestorer(db sortedkv.Database) *PersistRestorer { + return &PersistRestorer{ + db: db, + } +} + // Close closes the PersistRestorer and releases all resources it holds. func (pr *PersistRestorer) Close() error { if err := pr.db.Close(); err != nil { @@ -35,13 +42,6 @@ func (pr *PersistRestorer) Close() error { return nil } -// NewPersistRestorer creates a new PersistRestorer for the supplied database. -func NewPersistRestorer(db sortedkv.Database) *PersistRestorer { - return &PersistRestorer{ - db: db, - } -} - var prefix = struct{ ChannelDB, PeerDB, SigKey, Peers string }{ ChannelDB: "Chan:", PeerDB: "Peer:", diff --git a/channel/persistence/keyvalue/persistrestorer_internal_test.go b/channel/persistence/keyvalue/persistrestorer_internal_test.go index de878154a..604ebec76 100644 --- a/channel/persistence/keyvalue/persistrestorer_internal_test.go +++ b/channel/persistence/keyvalue/persistrestorer_internal_test.go @@ -16,7 +16,6 @@ package keyvalue import ( "context" - "os" "testing" "github.com/stretchr/testify/assert" @@ -31,8 +30,7 @@ import ( ) func TestPersistRestorer_Generic(t *testing.T) { - tmpdir, err := os.MkdirTemp("", "perun-test-kvpersistrestorer-db-*") - require.NoError(t, err) + tmpdir := t.TempDir() lvldb, err := leveldb.LoadDatabase(tmpdir) require.NoError(t, err) @@ -62,5 +60,5 @@ func TestChannelIterator_Next_Empty(t *testing.T) { var success bool assert.NotPanics(t, func() { success = it.Next(context.Background()) }) assert.False(t, success) - assert.NoError(t, it.err) + require.NoError(t, it.err) } diff --git a/channel/persistence/keyvalue/restorer.go b/channel/persistence/keyvalue/restorer.go index bfb949768..5139eabac 100644 --- a/channel/persistence/keyvalue/restorer.go +++ b/channel/persistence/keyvalue/restorer.go @@ -69,7 +69,7 @@ func (pr *PersistRestorer) channelPeers(id channel.ID) ([]map[wallet.BackendID]w if err != nil { return nil, errors.WithMessage(err, "unable to get peerlist from db") } - return ps, errors.WithMessage(perunio.Decode(bytes.NewBuffer([]byte(peers)), &ps), + return ps, errors.WithMessage(perunio.Decode(bytes.NewBufferString(peers), &ps), "decoding peerlist") } @@ -169,6 +169,24 @@ func (i *ChannelIterator) Next(context.Context) bool { return i.decodeNext("staging:state", &PersistedState{&i.ch.StagingTXV.State}, allowEmpty) } +// Channel returns the iterator's current channel. +func (i *ChannelIterator) Channel() *persistence.Channel { + return i.ch +} + +// Close closes the iterator and releases its resources. It returns the last +// error that occurred when advancing the iterator. +func (i *ChannelIterator) Close() error { + for it := range i.its { + if err := i.its[it].Close(); err != nil && i.err == nil { + i.err = err + } + } + i.its = nil + + return i.err +} + // recoverFromEmptyIterator is called when there is no iterator or when the // current iterator just ended. allowEnd signifies whether this situation is // allowed. It returns whether it could recover a new iterator. @@ -217,21 +235,3 @@ func (i *ChannelIterator) decodeNext(key string, v interface{}, opts decOpts) bo return i.err == nil } - -// Channel returns the iterator's current channel. -func (i *ChannelIterator) Channel() *persistence.Channel { - return i.ch -} - -// Close closes the iterator and releases its resources. It returns the last -// error that occurred when advancing the iterator. -func (i *ChannelIterator) Close() error { - for it := range i.its { - if err := i.its[it].Close(); err != nil && i.err == nil { - i.err = err - } - } - i.its = nil - - return i.err -} diff --git a/channel/persistence/statemachine_test.go b/channel/persistence/statemachine_test.go index cb675ba70..c5b812bdc 100644 --- a/channel/persistence/statemachine_test.go +++ b/channel/persistence/statemachine_test.go @@ -76,7 +76,7 @@ func TestStateMachine(t *testing.T) { sig, err = channel.Sign(acc, csm.StagingState(), b) require.NoError(err) } - err = sm.AddSig(ctx, channel.Index(i), sig) + err = sm.AddSig(ctx, channel.Index(i), sig) //nolint:gosec require.NoError(err) tpr.AssertEqual(csm) } @@ -152,7 +152,7 @@ func TestStateMachine(t *testing.T) { // Set Progressed timeout := ctest.NewRandomTimeout(rng) - idx := channel.Index(rng.Intn(s.NumParts())) + idx := channel.Index(rng.Intn(s.NumParts())) //nolint:gosec e := channel.NewProgressedEvent(s.ID, timeout, s, idx) err = sm.SetProgressed(ctx, e) require.NoError(err) diff --git a/channel/persistence/test/channel.go b/channel/persistence/test/channel.go index 75638edef..26c8e3f79 100644 --- a/channel/persistence/test/channel.go +++ b/channel/persistence/test/channel.go @@ -18,6 +18,7 @@ import ( "context" "math/rand" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "perun.network/go-perun/channel" @@ -89,7 +90,7 @@ func NewRandomChannel( } func requireEqualPeers(t require.TestingT, expected, actual []map[wallet.BackendID]wire.Address) { - require.Equal(t, len(expected), len(actual)) + assert.Equal(t, len(expected), len(actual)) for i, p := range expected { if !channel.EqualWireMaps(p, actual[i]) { t.Errorf("restored peers for channel do not match\nexpected: %v\nactual: %v", @@ -106,10 +107,10 @@ func requireEqualPeers(t require.TestingT, expected, actual []map[wallet.Backend func (c *Channel) AssertPersisted(ctx context.Context, t require.TestingT) { ch, err := c.pr.RestoreChannel(ctx, c.ID()) require.NoError(t, err) - require.NotNil(t, ch) + assert.NotNil(t, ch) c.RequireEqual(t, ch) requireEqualPeers(t, c.peers, ch.PeersV) - require.Equal(t, c.parent, ch.Parent) + assert.Equal(t, c.parent, ch.Parent) } // RequireEqual asserts that the channel is equal to the provided channel state. @@ -182,7 +183,7 @@ func (c *Channel) SignAll(ctx context.Context, t require.TestingT) { for i := range c.accounts { sig, err := channel.Sign(c.accounts[i][channel.TestBackendID], c.StagingState(), channel.TestBackendID) require.NoError(t, err) - c.AddSig(ctx, channel.Index(i), sig) //nolint:errcheck + c.AddSig(ctx, channel.Index(i), sig) //nolint:errcheck, gosec c.AssertPersisted(ctx, t) } } @@ -232,14 +233,14 @@ func (c *Channel) SetRegistered(t require.TestingT) { // SetWithdrawing calls SetWithdrawing on the state machine and then checks the persistence. func (c *Channel) SetWithdrawing(t require.TestingT) { - require.NoError(t, c.StateMachine.SetWithdrawing(c.ctx)) + assert.NoError(t, c.StateMachine.SetWithdrawing(c.ctx)) c.AssertPersisted(c.ctx, t) } // SetWithdrawn calls SetWithdrawn on the state machine and then checks the persistence. func (c *Channel) SetWithdrawn(t require.TestingT) { - require.NoError(t, c.StateMachine.SetWithdrawn(c.ctx)) + assert.NoError(t, c.StateMachine.SetWithdrawn(c.ctx)) rc, err := c.pr.RestoreChannel(c.ctx, c.ID()) - require.Error(t, err, "restoring of a non-existing channel") - require.Nil(t, rc, "restoring of a non-existing channel") + assert.Error(t, err, "restoring of a non-existing channel") + assert.Nil(t, rc, "restoring of a non-existing channel") } diff --git a/channel/persistence/test/channel_internal_test.go b/channel/persistence/test/channel_internal_test.go index 56a10e348..fe25b9e70 100644 --- a/channel/persistence/test/channel_internal_test.go +++ b/channel/persistence/test/channel_internal_test.go @@ -59,7 +59,7 @@ func initSigSlice(length int, prng *rand.Rand) []wallet.Sig { s := make([]wallet.Sig, length) for i := range s { s[i] = make(wallet.Sig, 32) - for j := 0; j < 32; j++ { + for j := range 32 { s[i][j] = byte(prng.Int()) } } diff --git a/channel/persistence/test/peerchans.go b/channel/persistence/test/peerchans.go index 58de9d738..6377f4d2c 100644 --- a/channel/persistence/test/peerchans.go +++ b/channel/persistence/test/peerchans.go @@ -51,13 +51,6 @@ func (pc peerChans) Add(id channel.ID, ps ...map[wallet.BackendID]wire.Address) } } -// Don't use add, use Add. -func (pc peerChans) add(id channel.ID, p map[wallet.BackendID]wire.Address) { - pk := peerKey(p) - ids := pc[pk] // nil ok, since we append - pc[pk] = append(ids, id) -} - func (pc peerChans) Delete(id channel.ID) { for pk, ids := range pc { for i, pid := range ids { @@ -78,6 +71,13 @@ func (pc peerChans) Delete(id channel.ID) { } } +// Don't use add, use Add. +func (pc peerChans) add(id channel.ID, p map[wallet.BackendID]wire.Address) { + pk := peerKey(p) + ids := pc[pk] // nil ok, since we append + pc[pk] = append(ids, id) +} + func peerKey(a map[wallet.BackendID]wire.Address) string { key := new(bytes.Buffer) err := perunio.Encode(key, wire.AddressDecMap(a)) @@ -90,7 +90,7 @@ func peerKey(a map[wallet.BackendID]wire.Address) string { func peerFromKey(s string) (map[wallet.BackendID]wire.Address, error) { p := make(map[wallet.BackendID]wire.Address) decMap := wire.AddressDecMap(p) - err := perunio.Decode(bytes.NewBuffer([]byte(s)), &decMap) + err := perunio.Decode(bytes.NewBufferString(s), &decMap) if err != nil { return nil, fmt.Errorf("error decoding peer key: %w", err) } diff --git a/channel/persistence/test/persistrestorer.go b/channel/persistence/test/persistrestorer.go index 81f23360a..775462d13 100644 --- a/channel/persistence/test/persistrestorer.go +++ b/channel/persistence/test/persistrestorer.go @@ -170,17 +170,6 @@ func (pr *PersistRestorer) AssertNotExists(id channel.ID) { assert.Falsef(pr.t, ok, "channel shouldn't exist: %x", id) } -// channel is a mutexed access to the Channel stored at the given id. -// Since persister access is guaranteed to be single-threaded per channel, it -// makes sense for the Persister implementation methods to use this getter to -// channel the pointer to the channel storage. -func (pr *PersistRestorer) channel(id channel.ID) (*persistence.Channel, bool) { - pr.mu.Lock() - defer pr.mu.Unlock() - ch, ok := pr.chans[id] - return ch, ok -} - // Restorer implementation // ActivePeers returns all peers that channels are persisted for. @@ -239,3 +228,14 @@ func (i *chanIter) Close() error { i.chans = nil // GC return nil } + +// channel is a mutexed access to the Channel stored at the given id. +// Since persister access is guaranteed to be single-threaded per channel, it +// makes sense for the Persister implementation methods to use this getter to +// channel the pointer to the channel storage. +func (pr *PersistRestorer) channel(id channel.ID) (*persistence.Channel, bool) { + pr.mu.Lock() + defer pr.mu.Unlock() + ch, ok := pr.chans[id] + return ch, ok +} diff --git a/channel/persistence/test/persistrestorertest.go b/channel/persistence/test/persistrestorertest.go index ffbe252a6..01e4ccd15 100644 --- a/channel/persistence/test/persistrestorertest.go +++ b/channel/persistence/test/persistrestorertest.go @@ -67,7 +67,7 @@ func (c *Client) NewChannel(t require.TestingT, p map[wallet.BackendID]wire.Addr c.ctx, t, c.pr, - channel.Index(idx), + channel.Index(idx), //nolint:gosec peers, parent, c.rng) @@ -89,7 +89,7 @@ func GenericPersistRestorerTest( t.Helper() t.Run("RestoreChannel error", func(t *testing.T) { var id channel.ID - ch, err := pr.RestoreChannel(context.Background(), id) + ch, err := pr.RestoreChannel(ctx, id) assert.Error(t, err) assert.Nil(t, ch) }) @@ -100,9 +100,9 @@ func GenericPersistRestorerTest( channels := make([]map[channel.ID]*Channel, numPeers) var prevCh *Channel - for p := 0; p < numPeers; p++ { + for p := range numPeers { channels[p] = make(map[channel.ID]*Channel) - for i := 0; i < numChans; i++ { + for i := range numChans { var parent *Channel // Every second channel is set to have a parent. if i&1 == 1 { @@ -118,9 +118,7 @@ func GenericPersistRestorerTest( subSeed := rng.Int63() iterIdx := 0 for idx := range peers { - idx := idx for _, ch := range channels[idx] { - ch := ch iterIdx++ iterIdx := iterIdx go ct.StageN("testing", numChans*numPeers, func(t pkgtest.ConcT) { @@ -198,5 +196,5 @@ peerLoop: ps, err := pr.ActivePeers(ctx) require.NoError(t, err) - require.Len(t, ps, 0) + require.Empty(t, ps) } diff --git a/channel/state.go b/channel/state.go index 9216b0066..ec5397303 100644 --- a/channel/state.go +++ b/channel/state.go @@ -159,7 +159,7 @@ func (s *State) Equal(t *State) error { // ToSubAlloc creates a SubAlloc from the state that can be added to the // parent channel's locked funds. func (s *State) ToSubAlloc() *SubAlloc { - return NewSubAlloc(s.ID, s.Allocation.Sum(), nil) + return NewSubAlloc(s.ID, s.Sum(), nil) } // EqualWireMaps compares two wire.Address maps for equality. diff --git a/channel/statemachine.go b/channel/statemachine.go index 2508b3475..57352b77a 100644 --- a/channel/statemachine.go +++ b/channel/statemachine.go @@ -125,6 +125,14 @@ func (m *StateMachine) CheckUpdate( return nil } +// Clone returns a deep copy of StateMachine. +func (m *StateMachine) Clone() *StateMachine { + return &StateMachine{ + machine: m.machine.Clone(), + app: m.app, + } +} + // validTransition makes all the default transition checks and additionally // checks for a valid application specific transition. // This is where a StateMachine and ActionMachine differ. In an ActionMachine, @@ -135,7 +143,7 @@ func (m *StateMachine) validTransition(to *State, actor Index) (err error) { if actor >= m.N() { return errors.New("actor index is out of range") } - if err := m.machine.ValidTransition(to); err != nil { + if err := m.ValidTransition(to); err != nil { return err } @@ -144,11 +152,3 @@ func (m *StateMachine) validTransition(to *State, actor Index) (err error) { } return errors.WithMessagef(err, "runtime error in application's ValidTransition()") } - -// Clone returns a deep copy of StateMachine. -func (m *StateMachine) Clone() *StateMachine { - return &StateMachine{ - machine: m.machine.Clone(), - app: m.app, - } -} diff --git a/channel/test/app_randomizer_internal_test.go b/channel/test/app_randomizer_internal_test.go index 3416841a2..67e28d253 100644 --- a/channel/test/app_randomizer_internal_test.go +++ b/channel/test/app_randomizer_internal_test.go @@ -32,11 +32,11 @@ func TestAppRandomizerSet(t *testing.T) { assert.NotPanics(t, func() { SetAppRandomizer(NewMockAppRandomizer()) }, "first SetAppRandomizer() should work") assert.True(t, isAppRandomizerSet, "isAppRandomizerSet should be true") assert.NotNil(t, appRandomizer, "appRandomizer should not be nil") - assert.False(t, old == appRandomizer, "appRandomizer should have changed") + assert.NotEqual(t, old, appRandomizer, "appRandomizer should have changed") old = appRandomizer assert.Panics(t, func() { SetAppRandomizer(NewMockAppRandomizer()) }, "second SetAppRandomizer() should panic") assert.True(t, isAppRandomizerSet, "isAppRandomizerSet should be true") assert.NotNil(t, appRandomizer, "appRandomizer should not be nil") - assert.True(t, old == appRandomizer, "appRandomizer should not have changed") + assert.Equal(t, old, appRandomizer, "appRandomizer should not have changed") } diff --git a/channel/test/backend.go b/channel/test/backend.go index c19bfbec2..11c0903c8 100644 --- a/channel/test/backend.go +++ b/channel/test/backend.go @@ -97,7 +97,7 @@ func genericChannelIDTest(t *testing.T, s *Setup) { t.Helper() require.NotNil(t, s.Params.Parts, "params.Parts can not be nil") _, err := channel.CalcID(s.Params) - assert.NoError(t, err, "CalcID should not return an error") + require.NoError(t, err, "CalcID should not return an error") // Check that modifying the state changes the id for _, modParams := range buildModifiedParams(s.Params, s.Params2, s) { @@ -110,7 +110,7 @@ func genericChannelIDTest(t *testing.T, s *Setup) { func genericSignTest(t *testing.T, s *Setup) { t.Helper() _, err := channel.Sign(s.Account, s.State, s.State.Backends[0]) - assert.NoError(t, err, "Sign should not return an error") + require.NoError(t, err, "Sign should not return an error") } func genericVerifyTest(t *testing.T, s *Setup, opts ...GenericTestOption) { @@ -123,22 +123,22 @@ func genericVerifyTest(t *testing.T, s *Setup, opts ...GenericTestOption) { require.NoError(t, err, "Sign should not return an error") ok, err := channel.Verify(addr, s.State, sig) - assert.NoError(t, err, "Verify should not return an error") + require.NoError(t, err, "Verify should not return an error") assert.True(t, ok, "Verify should return true") for i, _modState := range buildModifiedStates(s.State, s.State2, append(opts, IgnoreApp)...) { modState := _modState ok, err = channel.Verify(addr, &modState, sig) assert.Falsef(t, ok, "Verify should return false: index %d", i) - assert.NoError(t, err, "Verify should not return an error") + require.NoError(t, err, "Verify should not return an error") } // Different address and same state and params - for i := 0; i < 10; i++ { + for range 10 { add := s.RandomAddress() for _, a := range add { ok, err := channel.Verify(a, s.State, sig) - assert.NoError(t, err, "Verify should not return an error") + require.NoError(t, err, "Verify should not return an error") assert.False(t, ok, "Verify should return false") } } @@ -253,8 +253,8 @@ func buildModifiedStates(s1, s2 *channel.State, _opts ...GenericTestOption) (ret // Modify Assets[0] { modState := s1.Clone() - modState.Allocation.Assets[0] = s2.Allocation.Assets[0] - modState.Allocation.Backends[0] = s2.Allocation.Backends[0] + modState.Assets[0] = s2.Assets[0] + modState.Backends[0] = s2.Backends[0] ret = append(ret, *modState) } } @@ -270,14 +270,14 @@ func buildModifiedStates(s1, s2 *channel.State, _opts ...GenericTestOption) (ret // Modify Balances[0] { modState := s1.Clone() - modState.Allocation.Balances[0] = s2.Allocation.Balances[0] + modState.Balances[0] = s2.Balances[0] modState = ensureConsistentBalances(modState) ret = append(ret, *modState) } // Modify Balances[0][0] { modState := s1.Clone() - modState.Allocation.Balances[0][0] = s2.Allocation.Balances[0][0] + modState.Balances[0][0] = s2.Balances[0][0] ret = append(ret, *modState) } } @@ -286,7 +286,7 @@ func buildModifiedStates(s1, s2 *channel.State, _opts ...GenericTestOption) (ret // Modify complete Locked { modState := s1.Clone() - modState.Allocation.Locked = s2.Clone().Locked + modState.Locked = s2.Clone().Locked modState = ensureConsistentBalances(modState) ret = append(ret, *modState) } @@ -371,8 +371,8 @@ func ensureBalanceVectorLength(bals []channel.Bal, l int) []channel.Bal { // GenericStateEqualTest tests the State.Equal function. func GenericStateEqualTest(t *testing.T, s1, s2 *channel.State, opts ...GenericTestOption) { t.Helper() - assert.NoError(t, s1.Equal(s1)) //nolint:gocritic - assert.NoError(t, s2.Equal(s2)) //nolint:gocritic + require.NoError(t, s1.Equal(s1)) //nolint:gocritic + require.NoError(t, s2.Equal(s2)) //nolint:gocritic for _, differentState := range buildModifiedStates(s1, s2, opts...) { assert.Error(t, differentState.Equal(s1)) diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index 584aa1103..510c61af2 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -18,6 +18,7 @@ import ( crand "crypto/rand" "fmt" "log" + "math" "math/big" "math/rand" "time" @@ -48,7 +49,11 @@ func SetRandomizer(r Randomizer, bID wallet.BackendID) { // NewRandomPhase generates a random channel machine phase. func NewRandomPhase(rng *rand.Rand) channel.Phase { - return channel.Phase(rng.Intn(channel.LastPhase + 1)) + phase := rng.Intn(channel.LastPhase + 1) + if phase < 0 || phase > math.MaxUint8 { + panic("LastPhase is too large") + } + return channel.Phase(phase) } // NewRandomAsset generates a new random `channel.Asset`. @@ -262,7 +267,11 @@ func NewRandomChannelIDs(rng *rand.Rand, n int) (ids []channel.ID) { func NewRandomIndexMap(rng *rand.Rand, numParts int, numPartsParent int) (m []channel.Index) { m = make([]channel.Index, numParts) for i := range m { - m[i] = channel.Index(rng.Intn(numPartsParent)) + idx := rng.Intn(numPartsParent) + if idx < 0 || idx >= math.MaxUint16 { + panic("index out of bounds") + } + m[i] = channel.Index(idx) } return } @@ -280,24 +289,25 @@ func NewRandomIndexMaps(rng *rand.Rand, numParts int, numPartsParent int) (maps // Options: `WithBalancesRange`. func NewRandomBal(rng *rand.Rand, opts ...RandomOpt) channel.Bal { opt := mergeRandomOpts(opts...) - min, max := opt.BalancesRange() - if min == nil { + var balanceMin, balanceMax channel.Bal + balanceMin, balanceMax = opt.BalancesRange() + if balanceMin == nil { // Use 1 here since 0 is nearly impossible anyway and many // test assume != 0. - min = big.NewInt(1) + balanceMin = big.NewInt(1) } - if max == nil { - max = maxRandomBalance + if balanceMax == nil { + balanceMax = maxRandomBalance } - // rng(max - min + 1) - bal, err := crand.Int(rng, new(big.Int).Add(new(big.Int).Sub(max, min), big.NewInt(1))) + // rng(balanceMax - balanceMin + 1) + bal, err := crand.Int(rng, new(big.Int).Add(new(big.Int).Sub(balanceMax, balanceMin), big.NewInt(1))) if err != nil { panic(fmt.Sprintf("Error creating random big.Int: %v", err)) } - // min + rng(max - min + 1) - return new(big.Int).Add(min, bal) + // balanceMin + rng(balanceMax - balanceMin + 1) + return new(big.Int).Add(balanceMin, bal) } // NewRandomBals generates new random `channel.Bal`s. @@ -394,7 +404,6 @@ func NewRandomTransaction(rng *rand.Rand, sigMask []bool, opts ...RandomOpt) *ch func ShuffleBalances(rng *rand.Rand, b channel.Balances) channel.Balances { ret := b.Clone() for _, a := range ret { - a := a rng.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] }) diff --git a/channel/test/randomopts.go b/channel/test/randomopts.go index 1f7ba9f43..b20c75204 100644 --- a/channel/test/randomopts.go +++ b/channel/test/randomopts.go @@ -15,6 +15,7 @@ package test import ( + "errors" "fmt" "io" "math/big" @@ -29,10 +30,10 @@ var ( // It is set to 2 ^ 128 - 1 since when 2 ^ 256 - 1 is used, the faucet // key is depleted. // The production limit can be found in `go-perun/channel.MaxBalance`. - MaxBalance = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1)) //nolint:gomnd + MaxBalance = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1)) //nolint:mnd // Highest balance that is returned by NewRandomBal. Set to MaxBalance / 2^30. - maxRandomBalance = new(big.Int).Rsh(MaxBalance, 30) //nolint:gomnd + maxRandomBalance = new(big.Int).Rsh(MaxBalance, 30) //nolint:mnd ) const ( @@ -112,8 +113,8 @@ func WithBalances(balances ...[]channel.Bal) RandomOpt { } // WithBalancesInRange sets the range within which balances are randomly generated to [min, max]. -func WithBalancesInRange(min, max channel.Bal) RandomOpt { - return RandomOpt{"balanceRange": []channel.Bal{min, max}} +func WithBalancesInRange(balanceMin, balanceMax channel.Bal) RandomOpt { + return RandomOpt{"balanceRange": []channel.Bal{balanceMin, balanceMax}} } // WithChallengeDuration sets the `ChallengeDuration` that should be used. @@ -309,7 +310,7 @@ func (o RandomOpt) Assets() []channel.Asset { // Backend returns the `Backend` value of the `RandomOpt`. func (o RandomOpt) Backend() (wallet.BackendID, error) { if _, ok := o["backend"]; !ok { - return 0, fmt.Errorf("backend not set") + return 0, errors.New("backend not set") } return o["backend"].(wallet.BackendID), nil } @@ -317,7 +318,7 @@ func (o RandomOpt) Backend() (wallet.BackendID, error) { // BackendID returns the `BackendID` value from `Allocation` of the `RandomOpt`. func (o RandomOpt) BackendID() ([]wallet.BackendID, error) { if _, ok := o["backendIDs"]; !ok { - return []wallet.BackendID{0}, fmt.Errorf("backend not set") + return []wallet.BackendID{0}, errors.New("backend not set") } return o["backendIDs"].([]wallet.BackendID), nil } @@ -333,7 +334,7 @@ func (o RandomOpt) Balances() channel.Balances { // BalancesRange returns the `BalancesRange` value of the `RandomOpt`. // If not present, returns nil,nil. -func (o RandomOpt) BalancesRange() (min, max channel.Bal) { +func (o RandomOpt) BalancesRange() (balanceMin, balanceMax channel.Bal) { if _, ok := o["balanceRange"]; !ok { return nil, nil } @@ -372,7 +373,7 @@ func (o RandomOpt) FirstPart() map[wallet.BackendID]wallet.Address { // If not present, a random value is generated with `rng` as entropy source. func (o RandomOpt) IsFinal(rng *rand.Rand) bool { if _, ok := o["isFinal"]; !ok { - o["isFinal"] = (rng.Int31n(2) == 0) //nolint:gomnd + o["isFinal"] = (rng.Int31n(2) == 0) //nolint:mnd } return o["isFinal"].(bool) } @@ -435,7 +436,7 @@ func (o RandomOpt) LedgerChannel(rng io.Reader) bool { if err != nil { panic(err) } - o[nameLedgerChannel] = a[0]%2 == 0 //nolint:gomnd + o[nameLedgerChannel] = a[0]%2 == 0 //nolint:mnd } return o[nameLedgerChannel].(bool) } @@ -449,7 +450,7 @@ func (o RandomOpt) VirtualChannel(rng io.Reader) bool { if err != nil { panic(err) } - o[nameVirtualChannel] = a[0]%2 == 0 //nolint:gomnd + o[nameVirtualChannel] = a[0]%2 == 0 //nolint:mnd } return o[nameVirtualChannel].(bool) } diff --git a/channel/transaction.go b/channel/transaction.go index 1539d2f86..e616ac859 100644 --- a/channel/transaction.go +++ b/channel/transaction.go @@ -76,7 +76,7 @@ func (t *Transaction) Decode(r io.Reader) error { return errors.WithMessage(err, "decoding state") } - t.Sigs = make([]wallet.Sig, t.State.NumParts()) + t.Sigs = make([]wallet.Sig, t.NumParts()) return wallet.DecodeSparseSigs(r, &t.Sigs) } diff --git a/channel/transaction_test.go b/channel/transaction_test.go index 70d2a2726..63fde0062 100644 --- a/channel/transaction_test.go +++ b/channel/transaction_test.go @@ -33,7 +33,7 @@ func TestTransactionSerialization(t *testing.T) { tests = append(tests, newUniformBoolSlice(l, true), newUniformBoolSlice(l, false), newStripedBoolSlice(l, true), newStripedBoolSlice(l, false)) - for i := 0; i < l; i++ { + for i := range l { tests = append(tests, newAlmostUniformBoolSlice(i, l, true)) tests = append(tests, newAlmostUniformBoolSlice(i, l, false)) } diff --git a/client/channel.go b/client/channel.go index 72e107052..ada79e101 100644 --- a/client/channel.go +++ b/client/channel.go @@ -127,10 +127,6 @@ func (c *Channel) Ctx() context.Context { return c.conn.r.Ctx() } -func (c *Channel) logPeer(idx channel.Index) log.Logger { - return c.Log().WithField("peerIdx", idx) -} - // ID returns the channel ID. func (c *Channel) ID() channel.ID { return c.machine.ID() @@ -156,13 +152,6 @@ func (c *Channel) State() *channel.State { return c.state() } -// state returns a pointer to the current state. -// Assumes that the machine mutex has been locked. -// Clone the state if you want to modify it. -func (c *Channel) state() *channel.State { - return c.machine.State() -} - // Phase returns the current phase of the channel state machine. // Can not be called from an update handler. func (c *Channel) Phase() channel.Phase { @@ -244,3 +233,14 @@ func (c *Channel) initExchangeSigsAndEnable(ctx context.Context) error { func (c *Channel) hasLockedFunds() bool { return len(c.machine.State().Locked) > 0 } + +// state returns a pointer to the current state. +// Assumes that the machine mutex has been locked. +// Clone the state if you want to modify it. +func (c *Channel) state() *channel.State { + return c.machine.State() +} + +func (c *Channel) logPeer(idx channel.Index) log.Logger { + return c.Log().WithField("peerIdx", idx) +} diff --git a/client/channelconn.go b/client/channelconn.go index bbde16335..ffc099b25 100644 --- a/client/channelconn.go +++ b/client/channelconn.go @@ -16,6 +16,7 @@ package client import ( "context" + "math" "github.com/pkg/errors" "golang.org/x/sync/errgroup" @@ -41,6 +42,57 @@ type channelConn struct { log log.Logger } +// Send broadcasts the message to all channel participants. +func (c *channelConn) Send(ctx context.Context, msg wire.Msg) error { + var eg errgroup.Group + for i, peer := range c.peers { + if i < 0 || i > math.MaxUint16 { + return errors.Errorf("channel connection peer index out of bounds: %d", i) + } + if channel.Index(i) == c.idx { + continue // skip own peer + } + c.log.WithField("peer", peer).Debugf("channelConn: publishing message: %v: %+v", msg.Type(), msg) + env := &wire.Envelope{ + Sender: c.sender(), + Recipient: peer, + Msg: msg, + } + eg.Go(func() error { return c.pub.Publish(ctx, env) }) + } + return errors.WithMessage(eg.Wait(), "publishing message") +} + +// Peers returns the ordered list of peer addresses. Note that the own peer is +// included in the list. +func (c *channelConn) Peers() []map[wallet.BackendID]wire.Address { + return c.peers +} + +// newUpdateResRecv creates a new update response receiver for the given version. +// The receiver should be closed after all expected responses are received. +// The receiver is also closed when the channel connection is closed. +func (c *channelConn) NewUpdateResRecv(version uint64) (*channelMsgRecv, error) { + recv := wire.NewReceiver() + if err := c.r.Subscribe(recv, func(e *wire.Envelope) bool { + resMsg, ok := e.Msg.(channelUpdateResMsg) + return ok && resMsg.Ver() == version + }); err != nil { + return nil, errors.WithMessagef(err, "subscribing update response receiver") + } + + return &channelMsgRecv{ + Receiver: recv, + peers: c.peers, + log: c.log.WithField("version", version), + }, nil +} + +// Close closes the broadcaster and update request receiver. +func (c *channelConn) Close() error { + return c.r.Close() +} + // newChannelConn creates a new channel connection for the given channel ID. It // subscribes on the subscriber to all messages regarding this channel. func newChannelConn(id channel.ID, peers []map[wallet.BackendID]wire.Address, idx channel.Index, sub wire.Subscriber, pub wire.Publisher) (_ *channelConn, err error) { @@ -85,62 +137,14 @@ func newChannelConn(id channel.ID, peers []map[wallet.BackendID]wire.Address, id }, nil } -func (c *channelConn) sender() map[wallet.BackendID]wire.Address { - return c.peers[c.idx] -} - // SetLog sets the logger of the channel connection. It is assumed to be // called once before usage of the connection, so it isn't thread-safe. func (c *channelConn) SetLog(l log.Logger) { c.log = l } -// Close closes the broadcaster and update request receiver. -func (c *channelConn) Close() error { - return c.r.Close() -} - -// Send broadcasts the message to all channel participants. -func (c *channelConn) Send(ctx context.Context, msg wire.Msg) error { - var eg errgroup.Group - for i, peer := range c.peers { - if channel.Index(i) == c.idx { - continue // skip own peer - } - c.log.WithField("peer", peer).Debugf("channelConn: publishing message: %v: %+v", msg.Type(), msg) - env := &wire.Envelope{ - Sender: c.sender(), - Recipient: peer, - Msg: msg, - } - eg.Go(func() error { return c.pub.Publish(ctx, env) }) - } - return errors.WithMessage(eg.Wait(), "publishing message") -} - -// Peers returns the ordered list of peer addresses. Note that the own peer is -// included in the list. -func (c *channelConn) Peers() []map[wallet.BackendID]wire.Address { - return c.peers -} - -// newUpdateResRecv creates a new update response receiver for the given version. -// The receiver should be closed after all expected responses are received. -// The receiver is also closed when the channel connection is closed. -func (c *channelConn) NewUpdateResRecv(version uint64) (*channelMsgRecv, error) { - recv := wire.NewReceiver() - if err := c.r.Subscribe(recv, func(e *wire.Envelope) bool { - resMsg, ok := e.Msg.(channelUpdateResMsg) - return ok && resMsg.Ver() == version - }); err != nil { - return nil, errors.WithMessagef(err, "subscribing update response receiver") - } - - return &channelMsgRecv{ - Receiver: recv, - peers: c.peers, - log: c.log.WithField("version", version), - }, nil +func (c *channelConn) sender() map[wallet.BackendID]wire.Address { + return c.peers[c.idx] } type ( @@ -168,5 +172,8 @@ func (r *channelMsgRecv) Next(ctx context.Context) (channel.Index, ChannelMsg, e if !ok { return 0, nil, errors.Errorf("unexpected message type: expected ChannelMsg, got %T", env.Msg) } + if idx < 0 || idx > math.MaxUint16 { + return 0, nil, errors.Errorf("channel connection received message from unexpected peer %v", env.Sender) + } return channel.Index(idx), msg, nil } diff --git a/client/client.go b/client/client.go index 2557d0ce9..efe54ce26 100644 --- a/client/client.go +++ b/client/client.go @@ -128,6 +128,27 @@ func (c *Client) Close() error { return err } +// Restore restores all channels from persistence. Channels are restored in +// parallel. Newly restored channels should be acquired through the +// OnNewChannel callback. +func (c *Client) Restore(ctx context.Context) error { + ps, err := c.pr.ActivePeers(ctx) + if err != nil { + return errors.WithMessage(err, "restoring active peers") + } + + var eg errgroup.Group + for _, p := range ps { + if channel.EqualWireMaps(p, c.address) { + continue // skip own peer + } + + eg.Go(func() error { return c.restorePeerChannels(ctx, p) }) + } + + return eg.Wait() +} + // OnNewChannel sets a callback to be called whenever a new channel is created // or restored. Only one such handler can be set at a time, and repeated calls // to this function will overwrite the currently existing handler. This function @@ -208,24 +229,3 @@ func (c *Client) logPeer(p map[wallet.BackendID]wire.Address) log.Logger { func (c *Client) logChan(id channel.ID) log.Logger { return c.log.WithField("channel", id) } - -// Restore restores all channels from persistence. Channels are restored in -// parallel. Newly restored channels should be acquired through the -// OnNewChannel callback. -func (c *Client) Restore(ctx context.Context) error { - ps, err := c.pr.ActivePeers(ctx) - if err != nil { - return errors.WithMessage(err, "restoring active peers") - } - - var eg errgroup.Group - for _, p := range ps { - if channel.EqualWireMaps(p, c.address) { - continue // skip own peer - } - p := p - eg.Go(func() error { return c.restorePeerChannels(ctx, p) }) - } - - return eg.Wait() -} diff --git a/client/client_internal_test.go b/client/client_internal_test.go index 7ef75c15a..673abcc30 100644 --- a/client/client_internal_test.go +++ b/client/client_internal_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" channeltest "perun.network/go-perun/channel/test" wiretest "perun.network/go-perun/wire/test" @@ -46,6 +47,6 @@ func TestClient_Channel(t *testing.T) { ch2, err := c.Channel(cID) assert.Same(t, ch2, ch1) - assert.NoError(t, err) + require.NoError(t, err) }) } diff --git a/client/client_role_test.go b/client/client_role_test.go index 09d0904d8..0de26193b 100644 --- a/client/client_role_test.go +++ b/client/client_role_test.go @@ -49,7 +49,7 @@ func NewSetups(rng *rand.Rand, names []string, bID wallet.BackendID) []ctest.Rol backend = ctest.NewMockBackend(rng, "1337") ) - for i := 0; i < n; i++ { + for i := range n { watcher, err := local.NewWatcher(backend) if err != nil { panic("Error initializing watcher: " + err.Error()) @@ -77,7 +77,7 @@ func NewSetups(rng *rand.Rand, names []string, bID wallet.BackendID) []ctest.Rol func runAliceBobTest(ctx context.Context, t *testing.T, setup func(*rand.Rand) ([]ctest.RoleSetup, [2]ctest.Executer)) { t.Helper() rng := test.Prng(t) - for i := 0; i < 2; i++ { + for i := range 2 { setups, roles := setup(rng) app := client.WithoutApp() if i == 1 { diff --git a/client/client_test.go b/client/client_test.go index 0963289e9..93bf7b18c 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -86,7 +86,7 @@ func TestClient_New(t *testing.T) { require.NoError(t, err, "initializing the watcher should not error") c, err := client.New(wiretest.NewRandomAddress(rng), &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, map[wallet.BackendID]wallet.Wallet{channel.TestBackendID: wtest.RandomWallet(channel.TestBackendID)}, watcher) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, c) } diff --git a/client/clientconn.go b/client/clientconn.go index 9ba36e369..02bd47880 100644 --- a/client/clientconn.go +++ b/client/clientconn.go @@ -34,6 +34,20 @@ type clientConn struct { log.Embedding } +// Publish publishes the message on the bus. Makes clientConn implement the +// wire.Publisher interface. +func (c *clientConn) Publish(ctx context.Context, env *wire.Envelope) error { + return c.bus.Publish(ctx, env) +} + +func (c *clientConn) Close() error { + err := c.Relay.Close() + if rerr := c.reqRecv.Close(); err == nil { + err = errors.WithMessage(rerr, "closing proposal receiver") + } + return err +} + func makeClientConn(address map[wallet.BackendID]wire.Address, bus wire.Bus) (c clientConn, err error) { c.Embedding = log.MakeEmbedding(log.WithField("id", address)) c.sender = address @@ -47,7 +61,7 @@ func makeClientConn(address map[wallet.BackendID]wire.Address, bus wire.Bus) (c } }() - c.Relay.SetDefaultMsgHandler(func(m *wire.Envelope) { + c.SetDefaultMsgHandler(func(m *wire.Envelope) { log.Debugf("Received %T message without subscription: %v", m.Msg, m) }) if err := bus.SubscribeClient(c, c.sender); err != nil { @@ -86,17 +100,3 @@ func (c *clientConn) pubMsg(ctx context.Context, msg wire.Msg, rec map[wallet.Ba Msg: msg, }) } - -// Publish publishes the message on the bus. Makes clientConn implement the -// wire.Publisher interface. -func (c *clientConn) Publish(ctx context.Context, env *wire.Envelope) error { - return c.bus.Publish(ctx, env) -} - -func (c *clientConn) Close() error { - err := c.Relay.Close() - if rerr := c.reqRecv.Close(); err == nil { - err = errors.WithMessage(rerr, "closing proposal receiver") - } - return err -} diff --git a/client/payment_test.go b/client/payment_test.go index e96b6c3da..5865eec4b 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -29,7 +29,6 @@ import ( "perun.network/go-perun/client" ctest "perun.network/go-perun/client/test" "perun.network/go-perun/wire" - "polycry.pt/poly-go/test" pkgtest "polycry.pt/poly-go/test" ) @@ -74,7 +73,7 @@ func TestPaymentDispute(t *testing.T) { } func TestPaymentChannelsOptimistic(t *testing.T) { - rng := test.Prng(t) + rng := pkgtest.Prng(t) ctx, cancel := context.WithTimeout(context.Background(), testDuration) defer cancel() @@ -83,7 +82,7 @@ func TestPaymentChannelsOptimistic(t *testing.T) { } func TestPaymentChannelsDispute(t *testing.T) { - rng := test.Prng(t) + rng := pkgtest.Prng(t) ctx, cancel := context.WithTimeout(context.Background(), testDuration) defer cancel() diff --git a/client/proposal.go b/client/proposal.go index 5c7fe3706..ed6ba1d92 100644 --- a/client/proposal.go +++ b/client/proposal.go @@ -190,7 +190,7 @@ func (c *Client) ProposeChannel(ctx context.Context, prop ChannelProposal) (*Cha // cache version 1 updates until channel is opened c.enableVer1Cache() // replay cached version 1 updates - defer c.releaseVer1Cache() + defer c.releaseVer1Cache() //nolint:contextcheck ch, err := c.proposeTwoPartyChannel(ctx, prop) if err != nil { return nil, errors.WithMessage(err, "channel proposal") @@ -267,7 +267,7 @@ func (c *Client) handleChannelProposalAcc( // cache version 1 updates c.enableVer1Cache() // replay cached version 1 updates - defer c.releaseVer1Cache() + defer c.releaseVer1Cache() //nolint:contextcheck if ch, err = c.acceptChannelProposal(ctx, prop, p, acc); err != nil { return ch, errors.WithMessage(err, "accept channel proposal") @@ -408,7 +408,7 @@ func (c *Client) validTwoPartyProposal( return errors.Errorf("expected 2 peers, got %d", len(peers)) } - if !(ourIdx == ProposerIdx || ourIdx == ProposeeIdx) { + if ourIdx != ProposerIdx && ourIdx != ProposeeIdx { return errors.Errorf("invalid index: %d", ourIdx) } @@ -454,7 +454,7 @@ func (c *Client) validSubChannelProposal(proposal *SubChannelProposalMsg) error return errors.New("parent channel and sub-channel backends do not match") } - if err := parentState.Balances.AssertGreaterOrEqual(base.InitBals.Balances); err != nil { + if err := parentState.AssertGreaterOrEqual(base.InitBals.Balances); err != nil { return errors.WithMessage(err, "insufficient funds") } @@ -501,7 +501,7 @@ func (c *Client) validVirtualChannelProposal(prop *VirtualChannelProposalMsg, ou } virtualBals := transformBalances(prop.InitBals.Balances, parentState.NumParts(), indexMap) - if err := parentState.Balances.AssertGreaterOrEqual(virtualBals); err != nil { + if err := parentState.AssertGreaterOrEqual(virtualBals); err != nil { return errors.WithMessage(err, "insufficient funds") } return nil diff --git a/client/proposal_internal_test.go b/client/proposal_internal_test.go index ed45afaf0..498bc7b34 100644 --- a/client/proposal_internal_test.go +++ b/client/proposal_internal_test.go @@ -105,7 +105,7 @@ func TestChannelProposal_assertValidNumParts(t *testing.T) { func TestProposalResponder_Accept_Nil(t *testing.T) { p := new(ProposalResponder) - _, err := p.Accept(nil, new(LedgerChannelProposalAccMsg)) + _, err := p.Accept(nil, new(LedgerChannelProposalAccMsg)) //nolint:staticcheck assert.Error(t, err, "context") } diff --git a/client/proposalmsgs.go b/client/proposalmsgs.go index 4184d306a..8c7c07b1c 100644 --- a/client/proposalmsgs.go +++ b/client/proposalmsgs.go @@ -126,60 +126,29 @@ type ( } ) -// proposalPeers returns the wire addresses of a proposed channel's -// participants. -func (c *Client) proposalPeers(p ChannelProposal) (peers []map[wallet.BackendID]wire.Address) { - switch prop := p.(type) { - case *LedgerChannelProposalMsg: - peers = prop.Peers - case *SubChannelProposalMsg: - ch, ok := c.channels.Channel(prop.Parent) - if !ok { - c.log.Panic("ProposalPeers: invalid parent channel") - } - peers = ch.Peers() - case *VirtualChannelProposalMsg: - peers = prop.Peers - default: - c.log.Panicf("ProposalPeers: unhandled proposal type %T") - } - return -} - -// makeBaseChannelProposal creates a BaseChannelProposal and applies the supplied -// options. For more information, see ProposalOpts. -func makeBaseChannelProposal( +// NewLedgerChannelProposal creates a ledger channel proposal and applies the +// supplied options. +// challengeDuration is the on-chain challenge duration in seconds. +// participant is our wallet address. +// initBals are the initial balances. +// peers are the wire addresses of the channel participants. +// For more information, see ProposalOpts. +func NewLedgerChannelProposal( challengeDuration uint64, + participant map[wallet.BackendID]wallet.Address, initBals *channel.Allocation, + peers []map[wallet.BackendID]wire.Address, opts ...ProposalOpts, -) (BaseChannelProposal, error) { - opt := union(opts...) - - fundingAgreement := initBals.Balances - if opt.isFundingAgreement() { - fundingAgreement = opt.fundingAgreement() - if equal, err := perunbig.EqualSum(initBals.Balances, fundingAgreement); err != nil { - return BaseChannelProposal{}, errors.WithMessage(err, "comparing FundingAgreement and initial balances sum") - } else if !equal { - return BaseChannelProposal{}, errors.New("FundingAgreement and initial balances differ") - } - } - - var proposalID ProposalID - if _, err := io.ReadFull(rand.Reader, proposalID[:]); err != nil { - return BaseChannelProposal{}, errors.Wrap(err, "generating proposal ID") +) (prop *LedgerChannelProposalMsg, err error) { + prop = &LedgerChannelProposalMsg{ + Participant: participant, + Peers: peers, } - - return BaseChannelProposal{ - ChallengeDuration: challengeDuration, - ProposalID: proposalID, - NonceShare: opt.nonce(), - App: opt.App(), - InitData: opt.AppData(), - InitBals: initBals, - FundingAgreement: fundingAgreement, - Aux: opt.aux(), - }, nil + prop.BaseChannelProposal, err = makeBaseChannelProposal( + challengeDuration, + initBals, + opts...) + return } // Base returns the channel proposal's common values. @@ -252,31 +221,6 @@ func (LedgerChannelProposalMsg) Matches(acc ChannelProposalAccept) bool { return ok } -// NewLedgerChannelProposal creates a ledger channel proposal and applies the -// supplied options. -// challengeDuration is the on-chain challenge duration in seconds. -// participant is our wallet address. -// initBals are the initial balances. -// peers are the wire addresses of the channel participants. -// For more information, see ProposalOpts. -func NewLedgerChannelProposal( - challengeDuration uint64, - participant map[wallet.BackendID]wallet.Address, - initBals *channel.Allocation, - peers []map[wallet.BackendID]wire.Address, - opts ...ProposalOpts, -) (prop *LedgerChannelProposalMsg, err error) { - prop = &LedgerChannelProposalMsg{ - Participant: participant, - Peers: peers, - } - prop.BaseChannelProposal, err = makeBaseChannelProposal( - challengeDuration, - initBals, - opts...) - return -} - // Type returns wire.LedgerChannelProposal. func (LedgerChannelProposalMsg) Type() wire.Type { return wire.LedgerChannelProposal @@ -306,14 +250,6 @@ func (p *LedgerChannelProposalMsg) Decode(r io.Reader) error { return p.assertValidNumParts() } -func (p LedgerChannelProposalMsg) assertValidNumParts() error { - if len(p.Peers) < 2 || len(p.Peers) > channel.MaxNumParts { - return errors.Errorf("expected 2-%d participants, got %d", - channel.MaxNumParts, len(p.Peers)) - } - return nil -} - // Valid checks whether the participant address is nil. func (p LedgerChannelProposalMsg) Valid() error { if err := p.BaseChannelProposal.Valid(); err != nil { @@ -381,6 +317,70 @@ func (SubChannelProposalMsg) Matches(acc ChannelProposalAccept) bool { return ok } +// proposalPeers returns the wire addresses of a proposed channel's +// participants. +func (c *Client) proposalPeers(p ChannelProposal) (peers []map[wallet.BackendID]wire.Address) { + switch prop := p.(type) { + case *LedgerChannelProposalMsg: + peers = prop.Peers + case *SubChannelProposalMsg: + ch, ok := c.channels.Channel(prop.Parent) + if !ok { + c.log.Panic("ProposalPeers: invalid parent channel") + } + peers = ch.Peers() + case *VirtualChannelProposalMsg: + peers = prop.Peers + default: + c.log.Panicf("ProposalPeers: unhandled proposal type %T") + } + return +} + +// makeBaseChannelProposal creates a BaseChannelProposal and applies the supplied +// options. For more information, see ProposalOpts. +func makeBaseChannelProposal( + challengeDuration uint64, + initBals *channel.Allocation, + opts ...ProposalOpts, +) (BaseChannelProposal, error) { + opt := union(opts...) + + fundingAgreement := initBals.Balances + if opt.isFundingAgreement() { + fundingAgreement = opt.fundingAgreement() + if equal, err := perunbig.EqualSum(initBals.Balances, fundingAgreement); err != nil { + return BaseChannelProposal{}, errors.WithMessage(err, "comparing FundingAgreement and initial balances sum") + } else if !equal { + return BaseChannelProposal{}, errors.New("FundingAgreement and initial balances differ") + } + } + + var proposalID ProposalID + if _, err := io.ReadFull(rand.Reader, proposalID[:]); err != nil { + return BaseChannelProposal{}, errors.Wrap(err, "generating proposal ID") + } + + return BaseChannelProposal{ + ChallengeDuration: challengeDuration, + ProposalID: proposalID, + NonceShare: opt.nonce(), + App: opt.App(), + InitData: opt.AppData(), + InitBals: initBals, + FundingAgreement: fundingAgreement, + Aux: opt.aux(), + }, nil +} + +func (p LedgerChannelProposalMsg) assertValidNumParts() error { + if len(p.Peers) < 2 || len(p.Peers) > channel.MaxNumParts { + return errors.Errorf("expected 2-%d participants, got %d", + channel.MaxNumParts, len(p.Peers)) + } + return nil +} + type ( // ChannelProposalAccept is the generic interface for channel proposal // accept messages. diff --git a/client/restore_internal_test.go b/client/restore_internal_test.go index 902e1a986..c31917804 100644 --- a/client/restore_internal_test.go +++ b/client/restore_internal_test.go @@ -87,7 +87,7 @@ func TestRestoreChannelCollection(t *testing.T) { // Generate multiple trees of channels into one collection. db := make(map[channel.ID]*persistence.Channel) - for i := 0; i < 3; i++ { + for range 3 { mkRndChanTree(rng, 3, 1, 3, db, channel.TestBackendID) } @@ -104,7 +104,7 @@ func TestRestoreChannelCollection(t *testing.T) { // Restore all channels into the client and check the published channels. c.restoreChannelCollection(db, patchChFromSource) - require.Equal(t, len(witnessedChans), len(db), "channel count mismatch") + require.Len(t, witnessedChans, len(db), "channel count mismatch") // Duplicates should be ignored and there should be no missing channels. c.OnNewChannel(func(*Channel) { @@ -120,7 +120,7 @@ func mkRndChan(rng *rand.Rand, bID wallet.BackendID) *persistence.Channel { parts[i] = map[wallet.BackendID]wallet.Address{bID: wallettest.NewRandomAccount(rng, bID).Address()} } ch := persistence.NewChannel() - ch.IdxV = channel.Index(rng.Intn(channel.MaxNumParts)) + ch.IdxV = channel.Index(rng.Intn(channel.MaxNumParts)) //nolint:gosec ch.ParamsV = test.NewRandomParams(rng, test.WithParts(parts)) sigs := make([]bool, channel.MaxNumParts) opts := test.WithParams(ch.ParamsV) @@ -147,7 +147,7 @@ func mkRndChanTree( if minChildren > 0 { minChildren-- } - for i := 0; i < children; i++ { + for range children { t := mkRndChanTree(rng, depth-1, minChildren, maxChildren-1, db, bID) t.Parent = new(channel.ID) *t.Parent = root.ID() diff --git a/client/serialize.go b/client/serialize.go index 365fc133a..e4c752228 100644 --- a/client/serialize.go +++ b/client/serialize.go @@ -16,6 +16,7 @@ package client import ( "io" + "math" "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -31,7 +32,11 @@ type ( // Encode encodes the object to the writer. func (a channelIDsWithLen) Encode(w io.Writer) (err error) { - err = perunio.Encode(w, sliceLen(len(a))) + l := len(a) + if l > math.MaxUint16 { + return errors.New("slice length too long") + } + err = perunio.Encode(w, sliceLen(l)) if err != nil { return } @@ -66,7 +71,11 @@ func (a *channelIDsWithLen) Decode(r io.Reader) (err error) { // Encode encodes the object to the writer. func (a indexMapsWithLen) Encode(w io.Writer) (err error) { - err = perunio.Encode(w, sliceLen(len(a))) + l := len(a) + if l > math.MaxUint16 { + return errors.New("slice length too long") + } + err = perunio.Encode(w, sliceLen(l)) if err != nil { return } @@ -98,7 +107,11 @@ func (a *indexMapsWithLen) Decode(r io.Reader) (err error) { // Encode encodes the object to the writer. func (a indexMapWithLen) Encode(w io.Writer) (err error) { - err = perunio.Encode(w, sliceLen(len(a))) + l := len(a) + if l > math.MaxUint16 { + return errors.New("slice length too long") + } + err = perunio.Encode(w, sliceLen(l)) if err != nil { return } diff --git a/client/subchannel.go b/client/subchannel.go index 117428202..73ad92b39 100644 --- a/client/subchannel.go +++ b/client/subchannel.go @@ -53,7 +53,7 @@ func (c *Channel) fundSubChannel(ctx context.Context, id channel.ID, alloc *chan // equal assets and sufficient balances are already checked when validating the sub-channel proposal // withdraw initial balances into sub-allocation - state.Allocation.Balances = state.Allocation.Balances.Sub(alloc.Balances) + state.Balances = state.Sub(alloc.Balances) state.AddSubAlloc(*channel.NewSubAlloc(id, alloc.Balances.Sum(), nil)) return nil @@ -97,19 +97,19 @@ func (c *Channel) withdrawSubChannel(ctx context.Context, sub *Channel) error { c.Log().Panicf("sub-allocation %x not found", subAlloc.ID) } - if !subAlloc.BalancesEqual(sub.machine.State().Allocation.Sum()) { + if !subAlloc.BalancesEqual(sub.machine.State().Sum()) { c.Log().Panic("sub-allocation does not equal accumulated sub-channel outcome") } // asset types of this channel and parent channel are assumed to be the same for a, assetBalances := range sub.machine.State().Balances { for u, userBalance := range assetBalances { - parentBalance := parentState.Allocation.Balances[a][u] + parentBalance := parentState.Balances[a][u] parentBalance.Add(parentBalance, userBalance) } } - if err := parentState.Allocation.RemoveSubAlloc(subAlloc); err != nil { + if err := parentState.RemoveSubAlloc(subAlloc); err != nil { c.Log().WithError(err).Panicf("removing sub-allocation with id %x", subAlloc.ID) } diff --git a/client/sync.go b/client/sync.go index ab852e416..7ec29e784 100644 --- a/client/sync.go +++ b/client/sync.go @@ -67,8 +67,10 @@ func (c *Client) handleSyncMsg(peer map[wallet.BackendID]wire.Address, msg *Chan } } -// syncChannel synchronizes the channel state with the given peer and modifies +// SyncChannel synchronizes the channel state with the given peer and modifies // the current state if required. +// +//nolint:unused func (c *Client) syncChannel(ctx context.Context, ch *persistence.Channel, p map[wallet.BackendID]wire.Address) (err error) { recv := wire.NewReceiver() defer recv.Close() // ignore error @@ -119,7 +121,7 @@ func (c *Client) syncChannel(ctx context.Context, ch *persistence.Channel, p map // validateMessage validates the remote channel sync message. // -//nolint:nestif +//nolint:nestif, unused func validateMessage(ch *persistence.Channel, msg *ChannelSyncMsg) error { v := ch.CurrentTX().Version mv := msg.CurrentTX.Version @@ -128,7 +130,7 @@ func validateMessage(ch *persistence.Channel, msg *ChannelSyncMsg) error { return errors.New("channel ID mismatch") } if mv == v { - if err := msg.CurrentTX.State.Equal(ch.CurrentTX().State); err != nil { + if err := msg.CurrentTX.Equal(ch.CurrentTX().State); err != nil { return errors.WithMessage(err, "different states for same version") } } else if mv > v { @@ -151,6 +153,7 @@ func validateMessage(ch *persistence.Channel, msg *ChannelSyncMsg) error { return nil } +//nolint:unused func revisePhase(ch *persistence.Channel) error { //nolint:gocritic if ch.PhaseV <= channel.Funding && ch.CurrentTXV.Version == 0 { diff --git a/client/test/alice.go b/client/test/alice.go index 2c633e3a3..69e41271a 100644 --- a/client/test/alice.go +++ b/client/test/alice.go @@ -50,14 +50,14 @@ func (r *Alice) exec(_cfg ExecConfig, ch *paymentChannel) { we, them := r.Idxs(cfg.Peers()) // 1st Alice receives some updates from Bob - for i := 0; i < cfg.NumPayments[them]; i++ { + for i := range cfg.NumPayments[them] { ch.recvTransfer(cfg.TxAmounts[them], fmt.Sprintf("Bob#%d", i)) } // 2nd stage r.waitStage() // 2nd Alice sends some updates to Bob - for i := 0; i < cfg.NumPayments[we]; i++ { + for i := range cfg.NumPayments[we] { ch.sendTransfer(cfg.TxAmounts[we], fmt.Sprintf("Alice#%d", i)) } // 3rd stage diff --git a/client/test/backend.go b/client/test/backend.go index 669c679ca..1825c3d49 100644 --- a/client/test/backend.go +++ b/client/test/backend.go @@ -18,6 +18,7 @@ import ( "context" "encoding" "fmt" + "math" "math/big" "math/rand" "time" @@ -191,7 +192,11 @@ func (b *MockBackend) Register(_ context.Context, req channel.AdjudicatorReq, su }, }, subChannels...) - timeout := time.Now().Add(time.Duration(req.Params.ChallengeDuration) * time.Millisecond) + duration := req.Params.ChallengeDuration + if duration > math.MaxInt64 { + return fmt.Errorf("challenge duration %d is too large", duration) + } + timeout := time.Now().Add(time.Duration(duration) * time.Millisecond) for _, ch := range channels { b.setLatestEvent( ch.Params.ID(), @@ -207,20 +212,23 @@ func (b *MockBackend) Register(_ context.Context, req channel.AdjudicatorReq, su return nil } -func (b *MockBackend) setLatestEvent(ch channel.ID, e channel.AdjudicatorEvent) { - b.latestEvents[ch] = e - // Update subscriptions. - if channelSubs, ok := b.eventSubs[ch]; ok { - for _, sub := range channelSubs { - // Remove previous latest event. - select { - case <-sub.events: - default: - } - // Add latest event. - sub.events <- e - } +// Subscribe creates an event subscription. +func (b *MockBackend) Subscribe(ctx context.Context, chID channel.ID) (channel.AdjudicatorSubscription, error) { + b.log.Infof("SubscribeRegistered: %+v", chID) + + b.mu.Lock() + defer b.mu.Unlock() + + sub := NewMockSubscription(ctx) + sub.onClose = func() { b.removeSubscription(chID, sub) } + b.eventSubs[chID] = append(b.eventSubs[chID], sub) + + // Feed latest event if any. + if e, ok := b.latestEvents[chID]; ok { + sub.events <- e } + + return sub, nil } // Progress progresses the channel state. @@ -229,8 +237,11 @@ func (b *MockBackend) Progress(_ context.Context, req channel.ProgressReq) error b.mu.Lock() defer b.mu.Unlock() - - timeout := time.Now().Add(time.Duration(req.Params.ChallengeDuration) * time.Millisecond) + duration := req.Params.ChallengeDuration + if duration > math.MaxInt64 { + return fmt.Errorf("challenge duration %d is too large", duration) + } + timeout := time.Now().Add(time.Duration(duration) * time.Millisecond) b.setLatestEvent( req.Params.ID(), channel.NewProgressedEvent( @@ -243,6 +254,108 @@ func (b *MockBackend) Progress(_ context.Context, req channel.ProgressReq) error return nil } +// Withdraw withdraws the channel funds. +func (b *MockBackend) Withdraw(_ context.Context, req channel.AdjudicatorReq, subStates channel.StateMap, acc wallet.Address) error { + b.mu.Lock() + defer b.mu.Unlock() + + // Check withdraw requirements. + states := make([]*channel.State, 1+len(subStates)) + states[0] = req.Tx.State + i := 1 + for _, s := range subStates { + states[i] = s.State + i++ + } + if err := b.checkStates(states, checkWithdraw); err != nil { + return err + } + + // Redistribute balances if not done already. + b.assetHolder.mtx.Lock() + defer b.assetHolder.mtx.Unlock() + ch := req.Params.ID() + if !b.isConcluded(ch) { + outcome := outcomeRecursive(req.Tx.State, subStates) + b.log.Infof("Withdraw: %+v, %+v, %+v", req, subStates, outcome) + outcomeSum := outcome.Sum() + + funding := b.assetHolder.balances[ch] + if funding == nil { + funding = channel.MakeBalances(len(req.Tx.Assets), req.Tx.NumParts()) + } + fundingSum := funding.Sum() + + for a, assetOutcome := range outcome { + // If underfunded, don't redistribute balances. + if fundingSum[a].Cmp(outcomeSum[a]) < 0 { + continue + } + for p, amount := range assetOutcome { + funding[a][p].Set(amount) + } + } + } + + // Payout balances. + balances := b.assetHolder.balances[ch] + for a, assetBalances := range balances { + asset := req.Tx.Assets[a] + p := req.Idx + amount := assetBalances[p] + b.addBalance(acc, asset, amount) + amount.Set(big.NewInt(0)) + } + + if !b.isConcluded(ch) { + b.setLatestEvent(ch, channel.NewConcludedEvent(ch, &channel.ElapsedTimeout{}, req.Tx.Version)) + } + return nil +} + +// MockBalanceReader is a balance reader used for testing. At initialization, it +// is associated with a given account. +type MockBalanceReader struct { + b *MockBackend + acc wallet.Address +} + +// Balance returns the balance of the associated account for the given asset. +func (br *MockBalanceReader) Balance(asset channel.Asset) channel.Bal { + return br.b.Balance(br.acc, asset) +} + +// NewBalanceReader creates balance for the given account. +func (b *MockBackend) NewBalanceReader(acc wallet.Address) *MockBalanceReader { + return &MockBalanceReader{ + b: b, + acc: acc, + } +} + +// Balance returns the balance for the participant and asset. +func (b *MockBackend) Balance(p wallet.Address, a channel.Asset) *big.Int { + b.mu.Lock() + defer b.mu.Unlock() + return b.balance(p, a) +} + +func (b *MockBackend) setLatestEvent(ch channel.ID, e channel.AdjudicatorEvent) { + b.latestEvents[ch] = e + // Update subscriptions. + if channelSubs, ok := b.eventSubs[ch]; ok { + for _, sub := range channelSubs { + // Remove previous latest event. + select { + case <-sub.events: + default: + } + // Add latest event. + sub.events <- e + } + } +} + // outcomeRecursive returns the accumulated outcome of the channel and its sub-channels. func outcomeRecursive(state *channel.State, subStates channel.StateMap) (outcome channel.Balances) { outcome = state.Balances.Clone() @@ -303,63 +416,16 @@ func (b *MockBackend) checkState(s *channel.State, op checkStateFunc) error { return nil } -// Withdraw withdraws the channel funds. -func (b *MockBackend) Withdraw(_ context.Context, req channel.AdjudicatorReq, subStates channel.StateMap, acc wallet.Address) error { - b.mu.Lock() - defer b.mu.Unlock() - - // Check withdraw requirements. - states := make([]*channel.State, 1+len(subStates)) - states[0] = req.Tx.State - i := 1 - for _, s := range subStates { - states[i] = s.State - i++ - } - if err := b.checkStates(states, checkWithdraw); err != nil { - return err - } - - // Redistribute balances if not done already. - b.assetHolder.mtx.Lock() - defer b.assetHolder.mtx.Unlock() - ch := req.Params.ID() - if !b.isConcluded(ch) { - outcome := outcomeRecursive(req.Tx.State, subStates) - b.log.Infof("Withdraw: %+v, %+v, %+v", req, subStates, outcome) - outcomeSum := outcome.Sum() - - funding := b.assetHolder.balances[ch] - if funding == nil { - funding = channel.MakeBalances(len(req.Tx.Assets), req.Tx.NumParts()) - } - fundingSum := funding.Sum() - - for a, assetOutcome := range outcome { - // If underfunded, don't redistribute balances. - if fundingSum[a].Cmp(outcomeSum[a]) < 0 { - continue - } - for p, amount := range assetOutcome { - funding[a][p].Set(amount) - } - } - } - - // Payout balances. - balances := b.assetHolder.balances[ch] - for a, assetBalances := range balances { - asset := req.Tx.Allocation.Assets[a] - p := req.Idx - amount := assetBalances[p] - b.addBalance(acc, asset, amount) - amount.Set(big.NewInt(0)) - } - - if !b.isConcluded(ch) { - b.setLatestEvent(ch, channel.NewConcludedEvent(ch, &channel.ElapsedTimeout{}, req.Tx.Version)) +func (b *MockBackend) setBalance(p wallet.Address, a channel.Asset, v *big.Int) { + partKey := newAddressMapKey(p) + partBals, ok := b.balances[partKey] + if !ok { + log.Debug("part not found", p) + partBals = make(map[string]*big.Int) + b.balances[partKey] = partBals } - return nil + log.Debug("set balance:", p, v) + partBals[newAssetMapKey(a)] = new(big.Int).Set(v) } func (b *MockBackend) isConcluded(ch channel.ID) bool { @@ -418,64 +484,6 @@ func encodableAsString(e encoding.BinaryMarshaler) string { return string(buff) } -// MockBalanceReader is a balance reader used for testing. At initialization, it -// is associated with a given account. -type MockBalanceReader struct { - b *MockBackend - acc wallet.Address -} - -// Balance returns the balance of the associated account for the given asset. -func (br *MockBalanceReader) Balance(asset channel.Asset) channel.Bal { - return br.b.Balance(br.acc, asset) -} - -// NewBalanceReader creates balance for the given account. -func (b *MockBackend) NewBalanceReader(acc wallet.Address) *MockBalanceReader { - return &MockBalanceReader{ - b: b, - acc: acc, - } -} - -// Balance returns the balance for the participant and asset. -func (b *MockBackend) Balance(p wallet.Address, a channel.Asset) *big.Int { - b.mu.Lock() - defer b.mu.Unlock() - return b.balance(p, a) -} - -func (b *MockBackend) setBalance(p wallet.Address, a channel.Asset, v *big.Int) { - partKey := newAddressMapKey(p) - partBals, ok := b.balances[partKey] - if !ok { - log.Debug("part not found", p) - partBals = make(map[string]*big.Int) - b.balances[partKey] = partBals - } - log.Debug("set balance:", p, v) - partBals[newAssetMapKey(a)] = new(big.Int).Set(v) -} - -// Subscribe creates an event subscription. -func (b *MockBackend) Subscribe(ctx context.Context, chID channel.ID) (channel.AdjudicatorSubscription, error) { - b.log.Infof("SubscribeRegistered: %+v", chID) - - b.mu.Lock() - defer b.mu.Unlock() - - sub := NewMockSubscription(ctx) - sub.onClose = func() { b.removeSubscription(chID, sub) } - b.eventSubs[chID] = append(b.eventSubs[chID], sub) - - // Feed latest event if any. - if e, ok := b.latestEvents[chID]; ok { - sub.events <- e - } - - return sub, nil -} - func (b *MockBackend) removeSubscription(ch channel.ID, sub *MockSubscription) { b.mu.Lock() defer b.mu.Unlock() @@ -516,23 +524,6 @@ func newAssetHolder(rng rng) *assetHolder { } } -// initFund initializes the funded WaitGroups for a channel if not already -// initialized. -// -// Must be called before using the Funder for a funding request. -func (f *assetHolder) initFund(req channel.FundingReq) { - f.mtx.Lock() - defer f.mtx.Unlock() - - if f.fundedWgs[req.Params.ID()] == nil { - f.fundedWgs[req.Params.ID()] = &sync.WaitGroup{} - f.fundedWgs[req.Params.ID()].Add(len(req.Params.Parts)) - } - if f.balances[req.Params.ID()] == nil { - f.balances[req.Params.ID()] = channel.MakeBalances(len(req.State.Assets), req.State.NumParts()) - } -} - // Fund simulates funding the channel. func (f *assetHolder) Fund(req channel.FundingReq, b *MockBackend, acc wallet.Address) { f.initFund(req) @@ -561,7 +552,11 @@ func (f *assetHolder) Fund(req channel.FundingReq, b *MockBackend, acc wallet.Ad // WaitForFunding waits until all participants have funded the channel. func (f *assetHolder) WaitForFunding(ctx context.Context, req channel.FundingReq) error { - challengeDuration := time.Duration(req.Params.ChallengeDuration) * time.Second + duration := req.Params.ChallengeDuration + if duration > math.MaxInt64 { + return fmt.Errorf("challenge duration %d is too large", duration) + } + challengeDuration := time.Duration(duration) * time.Second fundCtx, cancel := context.WithTimeout(ctx, challengeDuration) defer cancel() @@ -574,6 +569,23 @@ func (f *assetHolder) WaitForFunding(ctx context.Context, req channel.FundingReq } } +// initFund initializes the funded WaitGroups for a channel if not already +// initialized. +// +// Must be called before using the Funder for a funding request. +func (f *assetHolder) initFund(req channel.FundingReq) { + f.mtx.Lock() + defer f.mtx.Unlock() + + if f.fundedWgs[req.Params.ID()] == nil { + f.fundedWgs[req.Params.ID()] = &sync.WaitGroup{} + f.fundedWgs[req.Params.ID()].Add(len(req.Params.Parts)) + } + if f.balances[req.Params.ID()] == nil { + f.balances[req.Params.ID()] = channel.MakeBalances(len(req.State.Assets), req.State.NumParts()) + } +} + // MockSubscription is a subscription for MockBackend. type MockSubscription struct { events chan channel.AdjudicatorEvent diff --git a/client/test/bob.go b/client/test/bob.go index de215f6bf..89dcbda86 100644 --- a/client/test/bob.go +++ b/client/test/bob.go @@ -40,14 +40,14 @@ func (r *Bob) exec(_cfg ExecConfig, ch *paymentChannel, propHandler *acceptNextP we, them := r.Idxs(cfg.Peers()) // 1st Bob sends some updates to Alice - for i := 0; i < cfg.NumPayments[we]; i++ { + for i := range cfg.NumPayments[we] { ch.sendTransfer(cfg.TxAmounts[we], fmt.Sprintf("Bob#%d", i)) } // 2nd stage r.waitStage() // 2nd Bob receives some updates from Alice - for i := 0; i < cfg.NumPayments[them]; i++ { + for i := range cfg.NumPayments[them] { ch.recvTransfer(cfg.TxAmounts[them], fmt.Sprintf("Alice#%d", i)) } // 3rd stage diff --git a/client/test/carol.go b/client/test/carol.go index 518de1f5c..2fef85195 100644 --- a/client/test/carol.go +++ b/client/test/carol.go @@ -27,14 +27,6 @@ type Carol struct { registered chan *channel.RegisteredEvent } -// HandleAdjudicatorEvent is the callback for adjudicator event handling. -func (r *Carol) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { - r.log.Infof("HandleAdjudicatorEvent: %v", e) - if e, ok := e.(*channel.RegisteredEvent); ok { - r.registered <- e - } -} - // NewCarol creates a new Responder that executes the Carol protocol. func NewCarol(t *testing.T, setup RoleSetup) *Carol { t.Helper() @@ -44,6 +36,14 @@ func NewCarol(t *testing.T, setup RoleSetup) *Carol { } } +// HandleAdjudicatorEvent is the callback for adjudicator event handling. +func (r *Carol) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { + r.log.Infof("HandleAdjudicatorEvent: %v", e) + if e, ok := e.(*channel.RegisteredEvent); ok { + r.registered <- e + } +} + // Execute executes the Carol protocol. func (r *Carol) Execute(cfg ExecConfig) { r.Responder.Execute(cfg, r.exec) @@ -64,7 +64,7 @@ func (r *Carol) exec(_cfg ExecConfig, ch *paymentChannel, propHandler *acceptNex r.waitStage() // Carol receives some updates from Mallory - for i := 0; i < cfg.NumPayments[them]; i++ { + for i := range cfg.NumPayments[them] { ch.recvTransfer(cfg.TxAmounts[them], fmt.Sprintf("Mallory#%d", i)) } // 2nd stage - txs received diff --git a/client/test/channel.go b/client/test/channel.go index 2162d851d..28b7c6c9e 100644 --- a/client/test/channel.go +++ b/client/test/channel.go @@ -61,6 +61,22 @@ func newPaymentChannel(ch *client.Channel, r *role) *paymentChannel { } } +// The payment channel is its own update handler. +func (ch *paymentChannel) Handle(up client.ChannelUpdate, res *client.UpdateResponder) { + ch.log.Infof("Incoming channel update: %v", up) + ctx, cancel := context.WithTimeout(context.Background(), ch.r.timeout) + defer cancel() + + accept := <-ch.handler + if accept { + ch.log.Debug("Accepting...") + ch.res <- handlerRes{up, res.Accept(ctx)} + } else { + ch.log.Debug("Rejecting...") + ch.res <- handlerRes{up, res.Reject(ctx, "Rejection")} + } +} + func (ch *paymentChannel) openSubChannel( rng io.Reader, cfg ExecConfig, @@ -198,22 +214,6 @@ func (ch *paymentChannel) settleImpl(secondary bool) { } } -// The payment channel is its own update handler. -func (ch *paymentChannel) Handle(up client.ChannelUpdate, res *client.UpdateResponder) { - ch.log.Infof("Incoming channel update: %v", up) - ctx, cancel := context.WithTimeout(context.Background(), ch.r.timeout) - defer cancel() - - accept := <-ch.handler - if accept { - ch.log.Debug("Accepting...") - ch.res <- handlerRes{up, res.Accept(ctx)} - } else { - ch.log.Debug("Rejecting...") - ch.res <- handlerRes{up, res.Reject(ctx, "Rejection")} - } -} - func transferBal(bals []channel.Bal, ourIdx channel.Index, amount *big.Int) { a := new(big.Int).Set(amount) // local copy because we mutate it otherIdx := ourIdx ^ 1 diff --git a/client/test/fund.go b/client/test/fund.go index 448e047a9..4f61669a8 100644 --- a/client/test/fund.go +++ b/client/test/fund.go @@ -109,6 +109,7 @@ func runFredFridaTest( // New channels and errors are passed via the corresponding Go channels. chsFred := make(chan *client.Channel, 1) errsFred := make(chan error, 1) + //nolint:contextcheck go fred.Handle( AlwaysAcceptChannelHandler(ctx, fredWalletAddr, chsFred, errsFred), AlwaysRejectUpdateHandler(ctx, errsFred), diff --git a/client/test/handler.go b/client/test/handler.go index 72c815f6c..aadfd2196 100644 --- a/client/test/handler.go +++ b/client/test/handler.go @@ -70,7 +70,7 @@ func AlwaysAcceptUpdateHandler(ctx context.Context, errs chan error) client.Upda // AlwaysRejectUpdateHandler returns a channel update handler that rejects all // channel updates. func AlwaysRejectUpdateHandler(ctx context.Context, errs chan error) client.UpdateHandlerFunc { - return func(state *channel.State, update client.ChannelUpdate, responder *client.UpdateResponder) { + return func(_ *channel.State, update client.ChannelUpdate, responder *client.UpdateResponder) { err := responder.Reject(ctx, "") if err != nil { errs <- errors.WithMessage(err, "rejecting channel update") diff --git a/client/test/mallory.go b/client/test/mallory.go index ef30ec27d..0075ebac8 100644 --- a/client/test/mallory.go +++ b/client/test/mallory.go @@ -59,14 +59,15 @@ func (r *Mallory) exec(_cfg ExecConfig, ch *paymentChannel) { r.waitStage() // Mallory sends some updates to Carol - for i := 0; i < cfg.NumPayments[we]; i++ { + for i := range cfg.NumPayments[we] { ch.sendTransfer(cfg.TxAmounts[we], fmt.Sprintf("Mallory#%d", i)) } // 2nd stage - txs sent r.waitStage() // Register version 0 AdjudicatorReq - challengeDuration := time.Duration(ch.Channel.Params().ChallengeDuration) * time.Second + duration := ch.Channel.Params().ChallengeDuration + challengeDuration := time.Duration(duration) * time.Second //nolint:gosec regCtx, regCancel := context.WithTimeout(context.Background(), r.timeout) defer regCancel() r.log.Debug("Registering version 0 state.") diff --git a/client/test/multiledger.go b/client/test/multiledger.go index 7aa275136..f991ea894 100644 --- a/client/test/multiledger.go +++ b/client/test/multiledger.go @@ -71,17 +71,17 @@ func SetupMultiLedgerTest(t *testing.T) MultiLedgerSetup { Client2: c2, Asset1: a1, Asset2: a2, - //nolint:gomnd // We allow the balances to be magic numbers. + //nolint:mnd // We allow the balances to be magic numbers. InitBalances: channel.Balances{ {big.NewInt(10), big.NewInt(0)}, // Asset 1. {big.NewInt(0), big.NewInt(10)}, // Asset 2. }, - //nolint:gomnd + //nolint:mnd UpdateBalances1: channel.Balances{ {big.NewInt(5), big.NewInt(5)}, // Asset 1. {big.NewInt(3), big.NewInt(7)}, // Asset 2. }, - //nolint:gomnd + //nolint:mnd UpdateBalances2: channel.Balances{ {big.NewInt(1), big.NewInt(9)}, // Asset 1. {big.NewInt(5), big.NewInt(5)}, // Asset 2. @@ -96,11 +96,6 @@ type MultiLedgerAsset struct { asset channel.Asset } -// LedgerBackendID returns the asset's ID. -func (a *MultiLedgerAsset) LedgerBackendID() multi.LedgerBackendID { - return a.id -} - // NewMultiLedgerAsset returns a new multi-ledger asset. func NewMultiLedgerAsset(id multi.LedgerBackendID, asset channel.Asset) *MultiLedgerAsset { return &MultiLedgerAsset{ @@ -109,6 +104,11 @@ func NewMultiLedgerAsset(id multi.LedgerBackendID, asset channel.Asset) *MultiLe } } +// LedgerBackendID returns the asset's ID. +func (a *MultiLedgerAsset) LedgerBackendID() multi.LedgerBackendID { + return a.id +} + // Equal returns whether the two assets are equal. func (a *MultiLedgerAsset) Equal(b channel.Asset) bool { bm, ok := b.(*MultiLedgerAsset) diff --git a/client/test/multiledger_dispute.go b/client/test/multiledger_dispute.go index cabf66c90..129dd8c2c 100644 --- a/client/test/multiledger_dispute.go +++ b/client/test/multiledger_dispute.go @@ -79,10 +79,12 @@ func TestMultiLedgerDispute( // Setup proposal handler. channels := make(chan *client.Channel, 1) errs := make(chan error) + //nolint:contextcheck go alice.Handle( AlwaysRejectChannelHandler(ctx, errs), AlwaysAcceptUpdateHandler(ctx, errs), ) + //nolint:contextcheck go bob.Handle( AlwaysAcceptChannelHandler(ctx, bob.WalletAddress, channels, errs), AlwaysAcceptUpdateHandler(ctx, errs), @@ -99,11 +101,12 @@ func TestMultiLedgerDispute( } // Start Bob's watcher. + //nolint:contextcheck go func() { errs <- chBobAlice.Watch(bob) }() // Wait until watcher is active. - time.Sleep(100 * time.Millisecond) //nolint:gomnd // The 100ms is a guess on how long the watcher needs to setup. + time.Sleep(100 * time.Millisecond) //nolint:mnd // The 100ms is a guess on how long the watcher needs to setup. // Notify Bob when an update is complete. done := make(chan struct{}, 1) @@ -119,7 +122,7 @@ func TestMultiLedgerDispute( // Wait until Bob's watcher processed the update. <-done - time.Sleep(100 * time.Millisecond) //nolint:gomnd // The 100ms is a guess on how long the watcher needs to catch up. + time.Sleep(100 * time.Millisecond) //nolint:mnd // The 100ms is a guess on how long the watcher needs to catch up. // Alice registers state on L1 adjudicator. req1 := client.NewTestChannel(chAliceBob).AdjudicatorReq() diff --git a/client/test/multiledger_happy.go b/client/test/multiledger_happy.go index 36375428e..2aa0be4da 100644 --- a/client/test/multiledger_happy.go +++ b/client/test/multiledger_happy.go @@ -57,10 +57,12 @@ func TestMultiLedgerHappy(ctx context.Context, t *testing.T, mlt MultiLedgerSetu // Setup proposal handler. channels := make(chan *client.Channel, 1) errs := make(chan error) + //nolint:contextcheck // This is a test. go alice.Handle( AlwaysRejectChannelHandler(ctx, errs), AlwaysAcceptUpdateHandler(ctx, errs), ) + //nolint:contextcheck // This is a test. go bob.Handle( AlwaysAcceptChannelHandler(ctx, bob.WalletAddress, channels, errs), AlwaysAcceptUpdateHandler(ctx, errs), diff --git a/client/test/paymentchannel.go b/client/test/paymentchannel.go index a63845e09..72897439c 100644 --- a/client/test/paymentchannel.go +++ b/client/test/paymentchannel.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -64,7 +64,6 @@ type paymentChannelTest struct { asset channel.Asset balancesBefore channel.Balances isUTXO bool - parentIDs [2]channel.ID } // TestPaymentChannelOptimistic tests payment channel functionality in the happy case. @@ -88,8 +87,7 @@ func TestPaymentChannelOptimistic( //nolint:revive // test.Test... stutters but perm := rand.Perm(len(chs)) t.Logf("Settle order = %v", perm) for _, i := range perm { - var err error - err = chs[i].Settle(ctx, isSecondary) + err := chs[i].Settle(ctx, isSecondary) isSecondary = true assert.NoErrorf(err, "settle channel: %d", i) @@ -170,7 +168,6 @@ func setupPaymentChannelTest( setup PaymentChannelSetup, ) (pct paymentChannelTest) { t.Helper() - require := require.New(t) // Set test values. asset := setup.Asset @@ -221,7 +218,7 @@ func setupPaymentChannelTest( pct.errs <- errors.WithMessage(err, "Bob: accepting channel update") } } - go bob.Client.Handle(openingProposalHandlerBob, updateProposalHandlerBob) + go bob.Handle(openingProposalHandlerBob, updateProposalHandlerBob) //nolint:contextcheck // Establish ledger channel between Alice and Ingrid. peersAlice := []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(alice.Identity), wire.AddressMapfromAccountMap(bob.Identity)} @@ -238,10 +235,10 @@ func setupPaymentChannelTest( initAllocAlice, peersAlice, ) - require.NoError(err, "creating ledger channel proposal") + require.NoError(t, err, "creating ledger channel proposal") pct.chAliceBob, err = alice.ProposeChannel(ctx, lcpAlice) - require.NoError(err, "opening channel between Alice and Ingrid") + require.NoError(t, err, "opening channel between Alice and Ingrid") select { case pct.chBobAlice = <-channelsBob: case err := <-pct.errs: @@ -251,7 +248,7 @@ func setupPaymentChannelTest( err = pct.chAliceBob.Update(ctx, func(s *channel.State) { s.Balances = channel.Balances{pct.balsUpdated} }) - require.NoError(err, "updating virtual channel") + require.NoError(t, err, "updating virtual channel") return pct } diff --git a/client/test/persistence.go b/client/test/persistence.go index b80c93c28..4ecd00b98 100644 --- a/client/test/persistence.go +++ b/client/test/persistence.go @@ -195,6 +195,11 @@ func (r *Robert) Execute(cfg ExecConfig) { r.RequireNoError(r.Close()) } +// Errors returns the error channel. +func (r *multiClientRole) Errors() <-chan error { + return r.errs +} + func (r *multiClientRole) assertPersistedPeerAndChannel(cfg ExecConfig, state *channel.State) { _, them := r.Idxs(cfg.Peers()) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) @@ -212,12 +217,7 @@ func (r *multiClientRole) assertPersistedPeerAndChannel(cfg ExecConfig, state *c restoredCh := chIt.Channel() r.RequireNoError(chIt.Close()) r.RequireTrue(restoredCh.ID() == state.ID) - r.RequireNoError(restoredCh.CurrentTXV.State.Equal(state)) -} - -// Errors returns the error channel. -func (r *multiClientRole) Errors() <-chan error { - return r.errs + r.RequireNoError(restoredCh.CurrentTXV.Equal(state)) } type addresses []map[wallet.BackendID]wire.Address diff --git a/client/test/progression.go b/client/test/progression.go index be498dd9c..bfe728868 100644 --- a/client/test/progression.go +++ b/client/test/progression.go @@ -80,7 +80,7 @@ func (r *Paul) Execute(cfg ExecConfig) { r.Proposer.Execute(cfg, r.exec) } -func (r *Paul) exec(_cfg ExecConfig, ch *paymentChannel) { +func (r *Paul) exec(_ ExecConfig, ch *paymentChannel) { ctx := r.Ctx() assetIdx := 0 @@ -98,7 +98,7 @@ func (r *Paul) exec(_cfg ExecConfig, ch *paymentChannel) { bal := func(user channel.Index) int64 { return s.Balances[assetIdx][user].Int64() } - half := (bal(0) + bal(1)) / 2 //nolint:gomnd + half := (bal(0) + bal(1)) / 2 //nolint:mnd s.Balances[assetIdx][0] = big.NewInt(half) s.Balances[assetIdx][1] = big.NewInt(half) })) @@ -112,7 +112,7 @@ func (r *Paul) exec(_cfg ExecConfig, ch *paymentChannel) { // Await progressed event 2. r.log.Debugf("%v awaiting progressed event 2", r.setup.Name) e = <-r.progressed - r.RequireTruef(e.Version() == 2, "expected version 2, got version %v", e.Version()) //nolint:gomnd + r.RequireTruef(e.Version() == 2, "expected version 2, got version %v", e.Version()) //nolint:mnd r.waitStage() // withdraw @@ -166,7 +166,7 @@ func (r *Paula) exec(_cfg ExecConfig, ch *paymentChannel, _ *acceptNextPropHandl bal := func(user channel.Index) int64 { return s.Balances[assetIdx][user].Int64() } - half := (bal(0) + bal(1)) / 2 //nolint:gomnd + half := (bal(0) + bal(1)) / 2 //nolint:mnd s.Balances[assetIdx][0] = big.NewInt(half + paulPaulaBalTransferAmount) s.Balances[assetIdx][1] = big.NewInt(half - paulPaulaBalTransferAmount) })) @@ -174,7 +174,7 @@ func (r *Paula) exec(_cfg ExecConfig, ch *paymentChannel, _ *acceptNextPropHandl // Await progressed event 2. r.log.Debugf("%v awaiting progressed event 2", r.setup.Name) e = <-r.progressed - r.RequireTruef(e.Version() == 2, "expected version 2, got version %v", e.Version()) //nolint:gomnd + r.RequireTruef(e.Version() == 2, "expected version 2, got version %v", e.Version()) //nolint:mnd r.waitStage() // await ready to conclude diff --git a/client/test/proposalmsgs.go b/client/test/proposalmsgs.go index 840c3d99e..39efd718b 100644 --- a/client/test/proposalmsgs.go +++ b/client/test/proposalmsgs.go @@ -40,7 +40,7 @@ func ProposalMsgsSerializationTest(t *testing.T, serializerTest func(t *testing. func channelProposalReqSerializationTest(t *testing.T, serializerTest func(t *testing.T, msg wire.Msg)) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 16; i++ { + for i := range 16 { var ( app client.ProposalOpts m wire.Msg @@ -55,7 +55,7 @@ func channelProposalReqSerializationTest(t *testing.T, serializerTest func(t *te case 1: m, err = NewRandomSubChannelProposal(rng, client.WithNonceFrom(rng), app) require.NoError(t, err) - case 2: //nolint: gomnd // This is not a magic number. + case 2: //nolint: mnd // This is not a magic number. m, err = NewRandomVirtualChannelProposal(rng, client.WithNonceFrom(rng), app) require.NoError(t, err) } @@ -67,14 +67,14 @@ func channelProposalAccSerializationTest(t *testing.T, serializerTest func(t *te t.Helper() rng := pkgtest.Prng(t) t.Run("ledger channel", func(t *testing.T) { - for i := 0; i < 16; i++ { + for range 16 { proposal := NewRandomLedgerChannelProposal(rng) m := proposal.Accept(wallettest.NewRandomAddresses(rng, channel.TestBackendID), client.WithNonceFrom(rng)) serializerTest(t, m) } }) t.Run("sub channel", func(t *testing.T) { - for i := 0; i < 16; i++ { + for range 16 { var err error proposal, err := NewRandomSubChannelProposal(rng) require.NoError(t, err) @@ -83,7 +83,7 @@ func channelProposalAccSerializationTest(t *testing.T, serializerTest func(t *te } }) t.Run("virtual channel", func(t *testing.T) { - for i := 0; i < 16; i++ { + for range 16 { var err error proposal, err := NewRandomVirtualChannelProposal(rng) require.NoError(t, err) @@ -98,7 +98,7 @@ func channelProposalRejSerializationTest(t *testing.T, serializerTest func(t *te minLen := 16 maxLenDiff := 16 rng := pkgtest.Prng(t) - for i := 0; i < 16; i++ { + for range 16 { m := &client.ChannelProposalRejMsg{ ProposalID: newRandomProposalID(rng), Reason: newRandomASCIIString(rng, minLen, maxLenDiff), diff --git a/client/test/role.go b/client/test/role.go index 2791c3b1e..9f74a92c6 100644 --- a/client/test/role.go +++ b/client/test/role.go @@ -28,7 +28,7 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" "perun.network/go-perun/channel/persistence" "perun.network/go-perun/client" @@ -134,7 +134,7 @@ func NewClients(t *testing.T, rng *rand.Rand, setups []RoleSetup) []*Client { setupWallet[i] = w } cl, err := client.New(wire.AddressMapfromAccountMap(setup.Identity), setup.Bus, setup.Funder, setup.Adjudicator, setupWallet, setup.Watcher) - assert.NoError(t, err) + require.NoError(t, err) walletAddress := make(map[wallet.BackendID]wallet.Address) for i, w := range setup.Wallet { walletAddress[i] = w.NewRandomAccount(rng).Address() @@ -160,7 +160,7 @@ func ExecuteTwoPartyTest(ctx context.Context, t *testing.T, role [2]Executer, cf var wg pkgsync.WaitGroup // start clients - for i := 0; i < len(role); i++ { + for i := range role { wg.Add(1) go func(i int) { log.Infof("Executing role %d", i) @@ -224,59 +224,6 @@ func (c *BaseExecConfig) App() client.ProposalOpts { return c.app } -// makeRole creates a client for the given setup and wraps it into a Role. -func makeRole(t *testing.T, setup RoleSetup, numStages int) (r role) { - t.Helper() - r = role{ - chans: &channelMap{entries: make(map[channel.ID]*paymentChannel)}, - setup: setup, - timeout: setup.Timeout, - errs: setup.Errors, - numStages: numStages, - challengeDuration: setup.ChallengeDuration, - } - setupWallet := make(map[wallet.BackendID]wallet.Wallet) - for i, w := range r.setup.Wallet { - setupWallet[i] = w - } - cl, err := client.New(wire.AddressMapfromAccountMap(r.setup.Identity), - r.setup.Bus, r.setup.Funder, r.setup.Adjudicator, setupWallet, r.setup.Watcher) - if err != nil { - t.Fatal("Error creating client: ", err) - } - r.setClient(cl) // init client - return r -} - -func (r *role) setClient(cl *client.Client) { - if r.setup.PR != nil { - cl.EnablePersistence(r.setup.PR) - } - cl.OnNewChannel(func(_ch *client.Channel) { - ch := newPaymentChannel(_ch, r) - r.chans.add(ch) - if r.newChan != nil { - r.newChan(ch) // forward callback - } - }) - r.Client = cl - // Append role field to client logger and set role logger to client logger. - r.log = log.AppendField(cl, "role", r.setup.Name) -} - -func (chs *channelMap) channel(ch channel.ID) (_ch *paymentChannel, ok bool) { - chs.RLock() - defer chs.RUnlock() - _ch, ok = chs.entries[ch] - return -} - -func (chs *channelMap) add(ch *paymentChannel) { - chs.Lock() - defer chs.Unlock() - chs.entries[ch.ID()] = ch -} - func (r *role) OnNewChannel(callback func(ch *paymentChannel)) { r.newChan = callback } @@ -307,15 +254,6 @@ func (r *role) SetStages(st Stages) { } } -func (r *role) waitStage() { - if r.stages != nil { - r.numStages-- - stage := &r.stages[r.numStages] - stage.Done() - stage.Wait() - } -} - // Idxs maps the passed addresses to the indices in the 2-party-channel. If the // setup's Identity is not found in peers, Idxs panics. func (r *role) Idxs(peers [2]map[wallet.BackendID]wire.Address) (our, their channel.Index) { @@ -358,7 +296,7 @@ func (r *role) RequireNoErrorf(err error, msg string, args ...interface{}) { func (r *role) RequireTrue(b bool) { if !b { - r.errs <- fmt.Errorf("expected true, got false") + r.errs <- errors.New("expected true, got false") } } @@ -457,14 +395,6 @@ func (r *role) SubChannelProposal( return prop } -func (r *role) acceptNextPropHandler(rng *rand.Rand) *acceptNextPropHandler { - return &acceptNextPropHandler{ - r: r, - props: make(chan proposalAndResponder), - rng: rng, - } -} - func (h *acceptNextPropHandler) HandleProposal(prop client.ChannelProposal, res *client.ProposalResponder) { select { case h.props <- proposalAndResponder{prop, res}: @@ -530,3 +460,73 @@ func (h *roleUpdateHandler) HandleUpdate(_ *channel.State, up client.ChannelUpda (*role)(h).RequireTruef(ok, "unknown channel: %v", up.State.ID) ch.Handle(up, res) } + +// makeRole creates a client for the given setup and wraps it into a Role. +func makeRole(t *testing.T, setup RoleSetup, numStages int) (r role) { + t.Helper() + r = role{ + chans: &channelMap{entries: make(map[channel.ID]*paymentChannel)}, + setup: setup, + timeout: setup.Timeout, + errs: setup.Errors, + numStages: numStages, + challengeDuration: setup.ChallengeDuration, + } + setupWallet := make(map[wallet.BackendID]wallet.Wallet) + for i, w := range r.setup.Wallet { + setupWallet[i] = w + } + cl, err := client.New(wire.AddressMapfromAccountMap(r.setup.Identity), + r.setup.Bus, r.setup.Funder, r.setup.Adjudicator, setupWallet, r.setup.Watcher) + if err != nil { + t.Fatal("Error creating client: ", err) + } + r.setClient(cl) // init client + return r +} + +func (r *role) setClient(cl *client.Client) { + if r.setup.PR != nil { + cl.EnablePersistence(r.setup.PR) + } + cl.OnNewChannel(func(_ch *client.Channel) { + ch := newPaymentChannel(_ch, r) + r.chans.add(ch) + if r.newChan != nil { + r.newChan(ch) // forward callback + } + }) + r.Client = cl + // Append role field to client logger and set role logger to client logger. + r.log = log.AppendField(cl, "role", r.setup.Name) +} + +func (chs *channelMap) channel(ch channel.ID) (_ch *paymentChannel, ok bool) { + chs.RLock() + defer chs.RUnlock() + _ch, ok = chs.entries[ch] + return +} + +func (chs *channelMap) add(ch *paymentChannel) { + chs.Lock() + defer chs.Unlock() + chs.entries[ch.ID()] = ch +} + +func (r *role) acceptNextPropHandler(rng *rand.Rand) *acceptNextPropHandler { + return &acceptNextPropHandler{ + r: r, + props: make(chan proposalAndResponder), + rng: rng, + } +} + +func (r *role) waitStage() { + if r.stages != nil { + r.numStages-- + stage := &r.stages[r.numStages] + stage.Done() + stage.Wait() + } +} diff --git a/client/test/subchannel.go b/client/test/subchannel.go index 061840fa6..782489a4c 100644 --- a/client/test/subchannel.go +++ b/client/test/subchannel.go @@ -34,8 +34,8 @@ type SusieTimExecConfig struct { // NewSusieTimExecConfig creates a new object from the given parameters. func NewSusieTimExecConfig( base BaseExecConfig, - numSubChannels int, - numSubSubChannels int, + _ int, + _ int, subChannelFunds [][2]*big.Int, subSubChannelFunds [][2]*big.Int, leafChannelApp client.ProposalOpts, @@ -81,13 +81,13 @@ func (r *Susie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { } var subChannels []*paymentChannel - for i := 0; i < len(cfg.SubChannelFunds); i++ { + for i := range len(cfg.SubChannelFunds) { c := openSubChannel(ledgerChannel, cfg.SubChannelFunds[i][:], cfg.App()) subChannels = append(subChannels, c) } var subSubChannels []*paymentChannel - for i := 0; i < len(cfg.SubSubChannelFunds); i++ { + for i := range len(cfg.SubSubChannelFunds) { c := openSubChannel(subChannels[0], cfg.SubSubChannelFunds[i][:], cfg.LeafChannelApp) subSubChannels = append(subSubChannels, c) } @@ -175,13 +175,13 @@ func (r *Tim) exec(_cfg ExecConfig, ledgerChannel *paymentChannel, propHandler * } var subChannels []*paymentChannel - for i := 0; i < len(cfg.SubChannelFunds); i++ { + for i := range len(cfg.SubChannelFunds) { c := acceptNext(ledgerChannel, cfg.SubChannelFunds[i][:]) subChannels = append(subChannels, c) } var subSubChannels []*paymentChannel - for i := 0; i < len(cfg.SubSubChannelFunds); i++ { + for i := range len(cfg.SubSubChannelFunds) { c := acceptNext(subChannels[0], cfg.SubSubChannelFunds[i][:]) subSubChannels = append(subSubChannels, c) } diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index 043f48a33..45692d66a 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -124,6 +124,15 @@ type DisputeTim struct { subCh channel.ID } +// NewDisputeTim creates a new Responder that executes the DisputeTim protocol. +func NewDisputeTim(t *testing.T, setup RoleSetup) *DisputeTim { + t.Helper() + return &DisputeTim{ + Responder: *NewResponder(t, setup, nStagesDisputeSusieTime), + registered: make(chan *channel.RegisteredEvent), + } +} + // time to wait until a parent channel watcher becomes active. const channelWatcherWait = 100 * time.Millisecond @@ -135,15 +144,6 @@ func (r *DisputeTim) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { } } -// NewDisputeTim creates a new Responder that executes the DisputeTim protocol. -func NewDisputeTim(t *testing.T, setup RoleSetup) *DisputeTim { - t.Helper() - return &DisputeTim{ - Responder: *NewResponder(t, setup, nStagesDisputeSusieTime), - registered: make(chan *channel.RegisteredEvent), - } -} - // Execute executes the DisputeTim protocol. func (r *DisputeTim) Execute(cfg ExecConfig) { r.Responder.Execute(cfg, r.exec) diff --git a/client/test/syncmsgs.go b/client/test/syncmsgs.go index 4d9e87063..77c256111 100644 --- a/client/test/syncmsgs.go +++ b/client/test/syncmsgs.go @@ -15,6 +15,7 @@ package test import ( + "math" "testing" "perun.network/go-perun/channel" @@ -28,10 +29,14 @@ import ( func ChannelSyncMsgSerializationTest(t *testing.T, serializerTest func(t *testing.T, msg wire.Msg)) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 4; i++ { + for range 4 { state := test.NewRandomState(rng) + phase := rng.Intn(channel.LastPhase) + if phase < 0 || phase > math.MaxUint8 { + panic("LastPhase is too large") + } m := &client.ChannelSyncMsg{ - Phase: channel.Phase(rng.Intn(channel.LastPhase)), + Phase: channel.Phase(phase), CurrentTX: channel.Transaction{ State: state, Sigs: newRandomSigs(rng, state.NumParts()), diff --git a/client/test/updatemsgs.go b/client/test/updatemsgs.go index e1a4e1332..deeaa8bd0 100644 --- a/client/test/updatemsgs.go +++ b/client/test/updatemsgs.go @@ -15,6 +15,7 @@ package test import ( + "math" "math/rand" "testing" @@ -41,7 +42,7 @@ func channelUpdateSerializationTest(t *testing.T, serializerTest func(t *testing ) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 4; i++ { + for range 4 { m := newRandomMsgChannelUpdate(rng) serializerTest(t, m) } @@ -53,7 +54,7 @@ func virtualChannelFundingProposalSerializationTest( ) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 4; i++ { + for range 4 { msgUp := newRandomMsgChannelUpdate(rng) params, state := test.NewRandomParamsAndState(rng) m := &client.VirtualChannelFundingProposalMsg{ @@ -75,7 +76,7 @@ func virtualChannelSettlementProposalSerializationTest( ) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 4; i++ { + for range 4 { msgUp := newRandomMsgChannelUpdate(rng) params, state := test.NewRandomParamsAndState(rng) m := &client.VirtualChannelSettlementProposalMsg{ @@ -93,11 +94,11 @@ func virtualChannelSettlementProposalSerializationTest( func channelUpdateAccSerializationTest(t *testing.T, serializerTest func(t *testing.T, msg wire.Msg)) { t.Helper() rng := pkgtest.Prng(t) - for i := 0; i < 4; i++ { + for range 4 { sig := newRandomSig(rng, channel.TestBackendID) m := &client.ChannelUpdateAccMsg{ ChannelID: test.NewRandomChannelID(rng), - Version: uint64(rng.Int63()), + Version: rng.Uint64(), Sig: sig, } serializerTest(t, m) @@ -109,10 +110,10 @@ func channelUpdateRejSerializationTest(t *testing.T, serializerTest func(t *test rng := pkgtest.Prng(t) minLen := 16 maxLenDiff := 16 - for i := 0; i < 4; i++ { + for range 4 { m := &client.ChannelUpdateRejMsg{ ChannelID: test.NewRandomChannelID(rng), - Version: uint64(rng.Int63()), + Version: rng.Uint64(), Reason: newRandomASCIIString(rng, minLen, maxLenDiff), } serializerTest(t, m) @@ -122,10 +123,14 @@ func channelUpdateRejSerializationTest(t *testing.T, serializerTest func(t *test func newRandomMsgChannelUpdate(rng *rand.Rand) *client.ChannelUpdateMsg { state := test.NewRandomState(rng) sig := newRandomSig(rng, channel.TestBackendID) + idx := rng.Intn(state.NumParts()) + if idx < 0 || idx > math.MaxUint16 { + panic("index out of bounds") + } return &client.ChannelUpdateMsg{ ChannelUpdate: client.ChannelUpdate{ State: state, - ActorIdx: channel.Index(rng.Intn(state.NumParts())), + ActorIdx: channel.Index(idx), }, Sig: sig, } diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 005d8f82c..a9751e0b8 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -75,9 +75,9 @@ func TestVirtualChannelOptimistic( //nolint:revive // test.Test... stutters but } // Test final balances. - err := vct.chAliceIngrid.State().Balances.AssertEqual(channel.Balances{vct.finalBalsAlice}) + err := vct.chAliceIngrid.State().AssertEqual(channel.Balances{vct.finalBalsAlice}) assert.NoError(t, err, "Alice: invalid final balances") - err = vct.chBobIngrid.State().Balances.AssertEqual(channel.Balances{vct.finalBalsBob}) + err = vct.chBobIngrid.State().AssertEqual(channel.Balances{vct.finalBalsBob}) assert.NoError(t, err, "Bob: invalid final balances") // Close the parents. @@ -85,7 +85,7 @@ func TestVirtualChannelOptimistic( //nolint:revive // test.Test... stutters but s.IsFinal = true }) assert.NoError(t, err, "Alice: close channel") - vct.chIngridBob.Update(ctx, func(s *channel.State) { + err = vct.chIngridBob.Update(ctx, func(s *channel.State) { s.IsFinal = true }) assert.NoError(t, err, "Bob: close channel") @@ -95,7 +95,7 @@ func TestVirtualChannelOptimistic( //nolint:revive // test.Test... stutters but t.Logf("Settle order = %v", perm) for _, i := range perm { var err error - if i < 2 { + if i < 2 { //nolint:mnd err = chs[i].Settle(ctx, isSecondary[0]) isSecondary[0] = true } else { @@ -156,7 +156,7 @@ func TestVirtualChannelDispute( //nolint:revive // test.Test... stutters but OK t.Logf("Settle order = %v", perm) for _, i := range perm { var err error - if i < 2 { + if i < 2 { //nolint:mnd err = chs[i].Settle(ctx, isSecondary[0]) isSecondary[0] = true } else { @@ -222,7 +222,7 @@ type VirtualChannelBalances struct { FinalBalsBob []*big.Int } -func setupVirtualChannelTest( +func setupVirtualChannelTest( //nolint t *testing.T, ctx context.Context, setup VirtualChannelSetup, @@ -274,10 +274,10 @@ func setupVirtualChannelTest( } } var updateProposalHandlerIngrid client.UpdateHandlerFunc = func( - s *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, + _ *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, ) { } - go ingrid.Client.Handle(openingProposalHandlerIngrid, updateProposalHandlerIngrid) + go ingrid.Handle(openingProposalHandlerIngrid, updateProposalHandlerIngrid) //nolint:contextcheck // Establish ledger channel between Alice and Ingrid. peersAlice := []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(alice.Identity), wire.AddressMapfromAccountMap(ingrid.Identity)} @@ -341,14 +341,14 @@ func setupVirtualChannelTest( } } var updateProposalHandlerBob client.UpdateHandlerFunc = func( - s *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, + _ *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, ) { err := ur.Accept(ctx) if err != nil { vct.errs <- errors.WithMessage(err, "Bob: accepting channel update") } } - go bob.Client.Handle(openingProposalHandlerBob, updateProposalHandlerBob) + go bob.Handle(openingProposalHandlerBob, updateProposalHandlerBob) //nolint:contextcheck // Setup Alice's handlers. channelsAlice := make(chan *client.Channel, 1) @@ -374,7 +374,7 @@ func setupVirtualChannelTest( vct.errs <- errors.WithMessage(err, "Alice: accepting channel update") } } - go alice.Client.Handle(openingProposalHandlerAlice, updateProposalHandlerAlice) + go alice.Handle(openingProposalHandlerAlice, updateProposalHandlerAlice) //nolint:contextcheck // Establish virtual channel between Alice and Bob via Ingrid. initAllocVirtual := channel.Allocation{ diff --git a/client/testchannel.go b/client/testchannel.go index 3a56af781..8f4e785cc 100644 --- a/client/testchannel.go +++ b/client/testchannel.go @@ -40,5 +40,5 @@ func (c *TestChannel) AdjudicatorReq() channel.AdjudicatorReq { // Register exposes dispute registration. func (c *TestChannel) Register(ctx context.Context) error { - return c.Channel.registerDispute(ctx) + return c.registerDispute(ctx) } diff --git a/client/update.go b/client/update.go index f75efa0ee..2087d97f6 100644 --- a/client/update.go +++ b/client/update.go @@ -16,7 +16,6 @@ package client import ( "context" - "fmt" "github.com/pkg/errors" @@ -49,7 +48,7 @@ func (c *Client) cacheVersion1Update(uh UpdateHandler, p map[wallet.BackendID]wi c.version1Cache.mu.Lock() defer c.version1Cache.mu.Unlock() - if !(m.Base().State.Version == 1 && c.version1Cache.enabled > 0) { + if m.Base().State.Version != 1 || c.version1Cache.enabled <= 0 { return false } @@ -119,7 +118,7 @@ func (r *UpdateResponder) Accept(ctx context.Context) error { return errors.New("context must not be nil") } if !r.called.TrySet() { - return fmt.Errorf("multiple calls on channel update responder") + return errors.New("multiple calls on channel update responder") } return r.channel.acceptUpdate(ctx, r.pidx, r.req) @@ -135,7 +134,7 @@ func (r *UpdateResponder) Reject(ctx context.Context, reason string) error { return errors.New("context must not be nil") } if !r.called.TrySet() { - return fmt.Errorf("multiple calls on channel update responder") + return errors.New("multiple calls on channel update responder") } return r.channel.rejectUpdate(ctx, r.pidx, r.req, reason) diff --git a/client/virtual_channel.go b/client/virtual_channel.go index c42feb9c0..2826cbe71 100644 --- a/client/virtual_channel.go +++ b/client/virtual_channel.go @@ -17,6 +17,7 @@ package client import ( "context" "fmt" + "math" "time" "github.com/pkg/errors" @@ -60,7 +61,7 @@ func (c *Channel) proposeVirtualChannelFunding(ctx context.Context, virtual *Cha // Deposit initial balances into sub-allocation balances := virtual.translateBalances(indexMap) - state.Allocation.Balances = state.Allocation.Balances.Sub(balances) + state.Balances = state.Sub(balances) state.AddSubAlloc(*channel.NewSubAlloc(virtual.ID(), balances.Sum(), indexMap)) err := c.updateGeneric(ctx, state, func(mcu *ChannelUpdateMsg) wire.Msg { @@ -185,6 +186,9 @@ func (c *Client) persistVirtualChannel(ctx context.Context, parent *Channel, pee } for i, sig := range sigs { + if i < 0 || i > math.MaxUint16 { + return nil, errors.Errorf("index out of bounds: %d", i) + } err = ch.machine.AddSig(ctx, channel.Index(i), sig) if err != nil { return nil, err @@ -223,6 +227,9 @@ func (c *Channel) pushVirtualUpdate(ctx context.Context, state *channel.State, s } for i, sig := range sigs { + if i < 0 || i > math.MaxUint16 { + return errors.Errorf("index out of bounds: %d", i) + } idx := channel.Index(i) if err := m.AddSig(ctx, idx, sig); err != nil { return err @@ -238,7 +245,6 @@ func (c *Channel) pushVirtualUpdate(ctx context.Context, state *channel.State, s return err } -//nolint:funlen func (c *Client) validateVirtualChannelFundingProposal( ch *Channel, prop *VirtualChannelFundingProposalMsg, @@ -300,7 +306,7 @@ func (c *Client) validateVirtualChannelFundingProposal( // Assert sufficient funds in parent channel. virtual := transformBalances(prop.Initial.State.Balances, ch.state().NumParts(), subAlloc.IndexMap) - if err := ch.state().Balances.AssertGreaterOrEqual(virtual); err != nil { + if err := ch.state().AssertGreaterOrEqual(virtual); err != nil { return errors.WithMessage(err, "insufficient funds") } @@ -359,6 +365,7 @@ func (c *Client) matchFundingProposal(ctx context.Context, a, b interface{}) boo return false } + //nolint:contextcheck go func() { // The context will be derived from the channel context. err := virtual.watchVirtual() diff --git a/client/virtual_channel_settlement.go b/client/virtual_channel_settlement.go index 28982e0cf..02254a122 100644 --- a/client/virtual_channel_settlement.go +++ b/client/virtual_channel_settlement.go @@ -16,6 +16,7 @@ package client import ( "context" + "math" "github.com/pkg/errors" @@ -40,16 +41,16 @@ func (c *Channel) withdrawVirtualChannel(ctx context.Context, virtual *Channel) c.Log().Panicf("sub-allocation %x not found", virtualAlloc.ID) } - if !virtualAlloc.BalancesEqual(virtual.state().Allocation.Sum()) { + if !virtualAlloc.BalancesEqual(virtual.state().Sum()) { c.Log().Panic("sub-allocation does not equal accumulated sub-channel outcome") } virtualBalsRemapped := virtual.translateBalances(virtualAlloc.IndexMap) // We assume that the asset types of parent channel and virtual channel are the same. - state.Allocation.Balances = state.Allocation.Balances.Add(virtualBalsRemapped) + state.Balances = state.Add(virtualBalsRemapped) - if err := state.Allocation.RemoveSubAlloc(virtualAlloc); err != nil { + if err := state.RemoveSubAlloc(virtualAlloc); err != nil { c.Log().WithError(err).Panicf("removing sub-allocation with id %x", virtualAlloc.ID) } @@ -214,6 +215,9 @@ func (c *Channel) forceFinalState(ctx context.Context, final channel.SignedState return err } for i, sig := range final.Sigs { + if i < 0 || i > math.MaxUint16 { + return errors.Errorf("index out of bounds: %d", i) + } if err := c.machine.AddSig(ctx, channel.Index(i), sig); err != nil { return err } diff --git a/log/levellified.go b/log/levellified.go index 1fa57e1ee..2364fb69e 100644 --- a/log/levellified.go +++ b/log/levellified.go @@ -69,14 +69,14 @@ func (l *Levellified) Tracef(format string, args ...interface{}) { // Trace implements log level trace. func (l *Levellified) Trace(args ...interface{}) { if l.Lvl >= TraceLevel { - l.StdLogger.Print(prepend("[trace] ", args)...) + l.Print(prepend("[trace] ", args)...) } } // Traceln implements log.TraceLn with white spaces in between arguments. func (l *Levellified) Traceln(args ...interface{}) { if l.Lvl >= TraceLevel { - l.StdLogger.Println(prepend("[trace]", args)...) + l.Println(prepend("[trace]", args)...) } } @@ -88,14 +88,14 @@ func (l *Levellified) Debugf(format string, args ...interface{}) { // Debug implements log level debug. func (l *Levellified) Debug(args ...interface{}) { if l.Lvl >= DebugLevel { - l.StdLogger.Print(prepend("[debug] ", args)...) + l.Print(prepend("[debug] ", args)...) } } // Debugln implements log.Debugln with white spaces in between arguments. func (l *Levellified) Debugln(args ...interface{}) { if l.Lvl >= DebugLevel { - l.StdLogger.Println(prepend("[debug]", args)...) + l.Println(prepend("[debug]", args)...) } } @@ -107,14 +107,14 @@ func (l *Levellified) Infof(format string, args ...interface{}) { // Info implements log level info. func (l *Levellified) Info(args ...interface{}) { if l.Lvl >= InfoLevel { - l.StdLogger.Print(prepend("[info] ", args)...) + l.Print(prepend("[info] ", args)...) } } // Infoln implements log.Infoln with white spaces in between arguments. func (l *Levellified) Infoln(args ...interface{}) { if l.Lvl >= InfoLevel { - l.StdLogger.Println(prepend("[info]", args)...) + l.Println(prepend("[info]", args)...) } } @@ -126,14 +126,14 @@ func (l *Levellified) Warnf(format string, args ...interface{}) { // Warn implements log level warn. func (l *Levellified) Warn(args ...interface{}) { if l.Lvl >= WarnLevel { - l.StdLogger.Print(prepend("[warn] ", args)...) + l.Print(prepend("[warn] ", args)...) } } // Warnln implements log.Warnln with white spaces in between arguments. func (l *Levellified) Warnln(args ...interface{}) { if l.Lvl >= WarnLevel { - l.StdLogger.Println(prepend("[warn]", args)...) + l.Println(prepend("[warn]", args)...) } } @@ -145,20 +145,20 @@ func (l *Levellified) Errorf(format string, args ...interface{}) { // Error implements log level error. func (l *Levellified) Error(args ...interface{}) { if l.Lvl >= ErrorLevel { - l.StdLogger.Print(prepend("[error] ", args)...) + l.Print(prepend("[error] ", args)...) } } // Errorln implements log.Errorln with white spaces in between arguments. func (l *Levellified) Errorln(args ...interface{}) { if l.Lvl >= ErrorLevel { - l.StdLogger.Println(prepend("[error]", args)...) + l.Println(prepend("[error]", args)...) } } func (l *Levellified) lprintf(lvl Level, format string, args ...interface{}) { if l.Lvl >= lvl { - l.StdLogger.Printf("[%v] "+format, prepend(lvl, args)...) + l.Printf("[%v] "+format, prepend(lvl, args)...) } } diff --git a/log/log.go b/log/log.go index c84497659..c58c75631 100644 --- a/log/log.go +++ b/log/log.go @@ -72,6 +72,8 @@ type StdLogger interface { } // LevelLogger is an extension to the StdLogger with different verbosity levels. +// +//nolint:interfacebloat type LevelLogger interface { StdLogger // Tracef logs a message at level Trace. Arguments are handled in the manner of fmt.Printf. diff --git a/log/logrus/logrus_internal_test.go b/log/logrus/logrus_internal_test.go index 874f313d3..6f5b4bebe 100644 --- a/log/logrus/logrus_internal_test.go +++ b/log/logrus/logrus_internal_test.go @@ -45,9 +45,9 @@ func testLogrusInfo(t *testing.T) { logger, hook := test.NewNullLogger() FromLogrus(logger).Println("Anton Ausdemhaus") - assert.Equal(t, len(hook.Entries), 1) - assert.Equal(t, hook.LastEntry().Level, logrus.InfoLevel) - assert.Equal(t, hook.LastEntry().Message, "Anton Ausdemhaus") + assert.Len(t, hook.Entries, 1) + assert.Equal(t, logrus.InfoLevel, hook.LastEntry().Level) + assert.Equal(t, "Anton Ausdemhaus", hook.LastEntry().Message) } func testLogrusStringer(t *testing.T) { @@ -83,7 +83,7 @@ func testLogrusWithField(t *testing.T) { logger.SetLevel(logrus.DebugLevel) FromLogrus(logger).WithField("field", 123456).Debugln("Bertha Bremsweg") - assert.Equal(t, len(hook.Entries), 1) + assert.Len(t, hook.Entries, 1) assert.Equal(t, hook.LastEntry().Level, logrus.DebugLevel) assert.Equal(t, hook.LastEntry().Message, "Bertha Bremsweg") assert.Contains(t, hook.LastEntry().Data, "field") diff --git a/wallet/address.go b/wallet/address.go index 429983b36..047db6887 100644 --- a/wallet/address.go +++ b/wallet/address.go @@ -19,6 +19,7 @@ import ( "encoding" "fmt" stdio "io" + "math" "strings" "github.com/pkg/errors" @@ -134,12 +135,20 @@ type AddrKey string // Encode encodes first the length of the map, // then all Addresses and their key in the map. func (a AddressDecMap) Encode(w stdio.Writer) error { - length := int32(len(a)) + l := len(a) + if l < math.MinInt32 || l > math.MaxInt32 { + return errors.New("map length out of bounds") + } + length := int32(l) if err := perunio.Encode(w, length); err != nil { return errors.WithMessage(err, "encoding map length") } for i, addr := range a { - if err := perunio.Encode(w, int32(i)); err != nil { + id := int(i) + if id < math.MinInt32 || id > math.MaxInt32 { + return errors.New("map index out of bounds") + } + if err := perunio.Encode(w, int32(id)); err != nil { return errors.WithMessage(err, "encoding map index") } if err := perunio.Encode(w, addr); err != nil { @@ -152,7 +161,11 @@ func (a AddressDecMap) Encode(w stdio.Writer) error { // Encode encodes first the length of the array, // then all AddressDecMaps in the array. func (a AddressMapArray) Encode(w stdio.Writer) error { - length := int32(len(a.Addr)) + l := len(a.Addr) + if l > math.MaxInt32 { + return errors.New("array length out of bounds") + } + length := int32(l) if err := perunio.Encode(w, length); err != nil { return errors.WithMessage(err, "encoding array length") } @@ -172,7 +185,7 @@ func (a *AddressDecMap) Decode(r stdio.Reader) (err error) { return errors.WithMessage(err, "decoding map length") } *a = make(map[BackendID]Address, mapLen) - for i := 0; i < int(mapLen); i++ { + for i := range mapLen { var idx int32 if err := perunio.Decode(r, &idx); err != nil { return errors.WithMessage(err, "decoding map index") @@ -193,7 +206,7 @@ func (a *AddressMapArray) Decode(r stdio.Reader) (err error) { return errors.WithMessage(err, "decoding array length") } a.Addr = make([]map[BackendID]Address, mapLen) - for i := 0; i < int(mapLen); i++ { + for i := range mapLen { if err := perunio.Decode(r, (*AddressDecMap)(&a.Addr[i])); err != nil { return errors.WithMessagef(err, "decoding %d-th address map entry", i) } @@ -206,7 +219,11 @@ func (a *AddressMapArray) Decode(r stdio.Reader) (err error) { // Panics when the `Address` can't be encoded. func Key(a Address) AddrKey { var buff strings.Builder - if err := perunio.Encode(&buff, uint32(a.BackendID())); err != nil { + id := int(a.BackendID()) + if id < 0 || id > math.MaxUint32 { + panic("backend ID out of range") + } + if err := perunio.Encode(&buff, uint32(id)); err != nil { panic("Could not encode id in AddrKey: " + err.Error()) } if err := perunio.Encode(&buff, a); err != nil { diff --git a/wallet/sig.go b/wallet/sig.go index 881d2e4d1..27beb51b0 100644 --- a/wallet/sig.go +++ b/wallet/sig.go @@ -57,7 +57,7 @@ func (s SigDec) Decode(r io.Reader) (err error) { return err } -// EncodeSparseSigs encodes a collection of signatures in the form ( mask, sig, sig, sig, ...). +// EncodeSparseSigs encodes a collection of signatures in the form ( mask, sig, ...). func EncodeSparseSigs(w io.Writer, sigs []Sig) error { n := len(sigs) @@ -83,7 +83,7 @@ func EncodeSparseSigs(w io.Writer, sigs []Sig) error { return nil } -// DecodeSparseSigs decodes a collection of signatures in the form (mask, sig, sig, sig, ...). +// DecodeSparseSigs decodes a collection of signatures in the form (mask, sig, ...). func DecodeSparseSigs(r io.Reader, sigs *[]Sig) (err error) { masklen := int(math.Ceil(float64(len(*sigs)) / float64(bitsPerByte))) mask := make([]uint8, masklen) diff --git a/wallet/test/address.go b/wallet/test/address.go index bbd45ab7f..8986dc150 100644 --- a/wallet/test/address.go +++ b/wallet/test/address.go @@ -29,7 +29,7 @@ import ( func TestAddress(t *testing.T, s *Setup) { //nolint:revive // `test.Test...` stutters, but we accept that here. null := s.ZeroAddress addr := s.Backend.NewAddress() - assert.NoError(t, addr.UnmarshalBinary(s.AddressMarshalled), "Byte deserialization of address should work") + require.NoError(t, addr.UnmarshalBinary(s.AddressMarshalled), "Byte deserialization of address should work") // Test Address.String. nullString := null.String() @@ -44,9 +44,9 @@ func TestAddress(t *testing.T, s *Setup) { //nolint:revive // `test.Test...` stu // Test Address.Bytes. addrBytes, err := addr.MarshalBinary() - assert.NoError(t, err, "Marshaling address should not error") + require.NoError(t, err, "Marshaling address should not error") nullBytes, err := null.MarshalBinary() - assert.NoError(t, err, "Marshaling zero address should not error") + require.NoError(t, err, "Marshaling zero address should not error") assert.False(t, bytes.Equal(addrBytes, nullBytes), "Expected inequality of byte representations of nonzero and zero address") // a.Equal(Decode(Encode(a))) diff --git a/wallet/test/wallet.go b/wallet/test/wallet.go index a520388f9..663d8059f 100644 --- a/wallet/test/wallet.go +++ b/wallet/test/wallet.go @@ -45,20 +45,20 @@ type Setup struct { // This function should be called by every implementation of the wallet interface. func TestAccountWithWalletAndBackend(t *testing.T, s *Setup) { //nolint:revive // `test.Test...` stutters, but we accept that here. acc, err := s.Wallet.Unlock(s.AddressInWallet) - assert.NoError(t, err) + require.NoError(t, err) // Check unlocked account sig, err := acc.SignData(s.DataToSign) - assert.NoError(t, err, "Sign with unlocked account should succeed") + require.NoError(t, err, "Sign with unlocked account should succeed") valid, err := s.Backend.VerifySignature(s.DataToSign, sig, s.AddressInWallet) assert.True(t, valid, "Verification should succeed") - assert.NoError(t, err, "Verification should not produce error") + require.NoError(t, err, "Verification should not produce error") addr := s.Backend.NewAddress() err = addr.UnmarshalBinary(s.AddressMarshalled) - assert.NoError(t, err, "Binary unmarshalling of address should work") + require.NoError(t, err, "Binary unmarshalling of address should work") valid, err = s.Backend.VerifySignature(s.DataToSign, sig, addr) assert.False(t, valid, "Verification with wrong address should fail") - assert.NoError(t, err, "Verification of valid signature should not produce error") + require.NoError(t, err, "Verification of valid signature should not produce error") tampered := make([]byte, len(sig)) copy(tampered, sig) @@ -90,7 +90,7 @@ func TestAccountWithWalletAndBackend(t *testing.T, s *Setup) { //nolint:revive / err = perunio.Encode(buff, sig) require.NoError(t, err, "encode sig") sign2, err := s.Backend.DecodeSig(buff) - assert.NoError(t, err, "Decoded signature should work") + require.NoError(t, err, "Decoded signature should work") assert.Equal(t, sig, sign2, "Decoded signature should be equal to the original") // Test DecodeSig on short stream @@ -113,13 +113,13 @@ func GenericSignatureSizeTest(t *testing.T, s *Setup) { // Test that signatures have constant length l := len(sign) - for i := 0; i < 8; i++ { + for range 8 { t.Run("parallel signing", func(t *testing.T) { t.Parallel() - for i := 0; i < 256; i++ { + for range 256 { sign, err := acc.SignData(s.DataToSign) require.NoError(t, err, "Sign with unlocked account should succeed") - require.Equal(t, l, len(sign), "Signatures should have constant length: %d vs %d", l, len(sign)) + require.Len(t, sign, l, "Signatures should have constant length: %d vs %d", l, len(sign)) } }) } diff --git a/wallet/test/walletbench.go b/wallet/test/walletbench.go index b5cc5709f..563a1ffa9 100644 --- a/wallet/test/walletbench.go +++ b/wallet/test/walletbench.go @@ -32,7 +32,7 @@ func benchAccountSign(b *testing.B, s *Setup) { perunAcc, err := s.Wallet.Unlock(s.AddressInWallet) require.NoError(b, err) - for n := 0; n < b.N; n++ { + for range b.N { _, err := perunAcc.SignData(s.DataToSign) if err != nil { b.Fatal(err) @@ -61,7 +61,7 @@ func benchBackendVerifySig(b *testing.B, s *Setup) { require.NoError(b, err) b.StartTimer() - for n := 0; n < b.N; n++ { + for range b.N { ok, err := s.Backend.VerifySignature(s.DataToSign, signature, perunAcc.Address()) if !ok { @@ -72,7 +72,7 @@ func benchBackendVerifySig(b *testing.B, s *Setup) { func benchUnmarshalAddress(b *testing.B, s *Setup) { b.Helper() - for n := 0; n < b.N; n++ { + for range b.N { addr := s.Backend.NewAddress() err := addr.UnmarshalBinary(s.AddressMarshalled) if err != nil { diff --git a/watcher/local/adjudicatorpubsub.go b/watcher/local/adjudicatorpubsub.go index 3a7fca63d..84fdaf393 100644 --- a/watcher/local/adjudicatorpubsub.go +++ b/watcher/local/adjudicatorpubsub.go @@ -45,6 +45,22 @@ func newAdjudicatorEventsPubSub() *adjudicatorPubSub { } } +// EventStream returns a channel for consuming the published adjudicator +// events. It always returns the same channel and does not support +// multiplexing. +// +// The channel will be closed when the pub-sub instance is closed and Err +// should tell the possible error. +func (a *adjudicatorPubSub) EventStream() <-chan channel.AdjudicatorEvent { + return a.pipe +} + +// Err always returns nil. Because, there will be no errors when closing a +// local subscription. +func (a *adjudicatorPubSub) Err() error { + return nil +} + // publish publishes the given adjudicator event to the subscriber. // // Panics if the pub-sub instance is already closed. It is implemented this @@ -65,19 +81,3 @@ func (a *adjudicatorPubSub) publish(e channel.AdjudicatorEvent) { func (a *adjudicatorPubSub) close() { a.once.Do(func() { close(a.pipe) }) } - -// EventStream returns a channel for consuming the published adjudicator -// events. It always returns the same channel and does not support -// multiplexing. -// -// The channel will be closed when the pub-sub instance is closed and Err -// should tell the possible error. -func (a *adjudicatorPubSub) EventStream() <-chan channel.AdjudicatorEvent { - return a.pipe -} - -// Err always returns nil. Because, there will be no errors when closing a -// local subscription. -func (a *adjudicatorPubSub) Err() error { - return nil -} diff --git a/watcher/local/watcher.go b/watcher/local/watcher.go index 45e466c01..53d1c3a08 100644 --- a/watcher/local/watcher.go +++ b/watcher/local/watcher.go @@ -160,7 +160,7 @@ func (w *Watcher) StartWatchingSubChannel( parent channel.ID, signedState channel.SignedState, ) (watcher.StatesPub, watcher.AdjudicatorSub, error) { - parentCh, ok := w.registry.retrieve(parent) + parentCh, ok := w.retrieve(parent) if !ok { return nil, nil, errors.New("parent channel not registered with the watcher") } @@ -177,6 +177,53 @@ func (w *Watcher) StartWatchingSubChannel( return statesPub, eventsSub, nil } +// StopWatching stops watching for adjudicator events, closes the pub-sub +// instances and removes the channel from the registry. +// +// The client should invoke stop watching for all the sub-channels before +// invoking for the parent ledger channel. +// +// In case of stop watching for sub-channels, watcher ensures that, when it +// receives a registered event for its parent channel or any other sub-channels +// of the parent channel, it is able to successfully refute with the latest +// states for the ledger channel and all its sub-channels (even if the watcher +// has stopped watching for some of the sub-channel). +// +// Context is not used, it is for implementing watcher.Watcher interface. +func (w *Watcher) StopWatching(_ context.Context, id channel.ID) error { + ch, ok := w.retrieve(id) + if !ok { + return errors.New("channel not registered with the watcher") + } + + parent := ch + if ch.isSubChannel() { + parent = ch.parent + } + parent.subChsAccess.Lock() + defer parent.subChsAccess.Unlock() + if ch.isClosed { + // Channel could have been closed while were waiting for the mutex locked. + return errors.New("channel not registered with the watcher") + } + close(ch.done) + + if ch.isSubChannel() { + latestParentTx := ch.parent.txRetriever.retrieve() + if _, ok := latestParentTx.SubAlloc(id); ok { + parent.archivedSubChStates[id] = makeSignedState(ch.params, ch.txRetriever.retrieve()) + } + delete(parent.subChs, id) + } else if len(ch.subChs) > 0 { + return errors.WithMessagef(ErrSubChannelsPresent, "cannot de-register: %d %v", len(ch.subChs), ch.id) + } + + closePubSubs(ch) + w.remove(ch.id) + ch.isClosed = true + return nil +} + func (w *Watcher) startWatching( ctx context.Context, parent *ch, @@ -197,7 +244,7 @@ func (w *Watcher) startWatching( return newCh(id, parent, signedState.Params, eventsFromChainSub, eventsToClientPubSub, statesPubSub, multiLedger), nil } - ch, err := w.registry.addIfSucceeds(id, chInitializer1) + ch, err := w.addIfSucceeds(id, chInitializer1) if err != nil { return nil, nil, err } @@ -206,7 +253,7 @@ func (w *Watcher) startWatching( Sigs: signedState.Sigs, } ch.Go(func() { ch.handleStatesFromClient(initialTx) }) - ch.Go(func() { ch.handleEventsFromChain(w.rs, w.registry) }) + ch.Go(func() { ch.handleEventsFromChain(w.rs, w.registry) }) //nolint:contextcheck return statesPubSub, eventsToClientPubSub, nil } @@ -415,15 +462,15 @@ func registerDispute(ctx context.Context, r *registry, registerer channel.Regist func retrieveLatestSubStates(r *registry, parent *ch) (channel.Transaction, []channel.SignedState) { parentTx := parent.txRetriever.retrieve() - subStates := make([]channel.SignedState, len(parentTx.Allocation.Locked)) - for i := range parentTx.Allocation.Locked { + subStates := make([]channel.SignedState, len(parentTx.Locked)) + for i := range parentTx.Locked { // Can be done concurrently. - subCh, ok := r.retrieve(parentTx.Allocation.Locked[i].ID) + subCh, ok := r.retrieve(parentTx.Locked[i].ID) if ok { subChTx := subCh.txRetriever.retrieve() subStates[i] = makeSignedState(subCh.params, subChTx) } else { - subStates[i] = parent.archivedSubChStates[parentTx.Allocation.Locked[i].ID] + subStates[i] = parent.archivedSubChStates[parentTx.Locked[i].ID] } } return parentTx, subStates @@ -445,53 +492,6 @@ func makeAdjudicatorReq(params *channel.Params, tx channel.Transaction) channel. } } -// StopWatching stops watching for adjudicator events, closes the pub-sub -// instances and removes the channel from the registry. -// -// The client should invoke stop watching for all the sub-channels before -// invoking for the parent ledger channel. -// -// In case of stop watching for sub-channels, watcher ensures that, when it -// receives a registered event for its parent channel or any other sub-channels -// of the parent channel, it is able to successfully refute with the latest -// states for the ledger channel and all its sub-channels (even if the watcher -// has stopped watching for some of the sub-channel). -// -// Context is not used, it is for implementing watcher.Watcher interface. -func (w *Watcher) StopWatching(_ context.Context, id channel.ID) error { - ch, ok := w.retrieve(id) - if !ok { - return errors.New("channel not registered with the watcher") - } - - parent := ch - if ch.isSubChannel() { - parent = ch.parent - } - parent.subChsAccess.Lock() - defer parent.subChsAccess.Unlock() - if ch.isClosed { - // Channel could have been closed while were waiting for the mutex locked. - return errors.New("channel not registered with the watcher") - } - close(ch.done) - - if ch.isSubChannel() { - latestParentTx := ch.parent.txRetriever.retrieve() - if _, ok := latestParentTx.SubAlloc(id); ok { - parent.archivedSubChStates[id] = makeSignedState(ch.params, ch.txRetriever.retrieve()) - } - delete(parent.subChs, id) - } else if len(ch.subChs) > 0 { - return errors.WithMessagef(ErrSubChannelsPresent, "cannot de-register: %d %v", len(ch.subChs), ch.id) - } - - closePubSubs(ch) - w.remove(ch.id) - ch.isClosed = true - return nil -} - func closePubSubs(ch *ch) { if err := ch.eventsFromChainSub.Close(); err != nil { err := errors.WithMessage(err, "closing events from chain sub") diff --git a/watcher/local/watcher_test.go b/watcher/local/watcher_test.go index 5e026b684..abeec70b4 100644 --- a/watcher/local/watcher_test.go +++ b/watcher/local/watcher_test.go @@ -242,7 +242,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) // Add sub-channel to allocation. This transaction represents funding of the sub-channel. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[2])...) @@ -262,7 +262,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { // Child: Publish both the states to the watcher. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - statesPubChild, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + statesPubChild, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) require.NoError(t, statesPubChild.Publish(context.Background(), childTxs[1])) require.NoError(t, statesPubChild.Publish(context.Background(), childTxs[2])) @@ -281,7 +281,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) // Add sub-channel to allocation. This transaction represents funding of the sub-channel. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[2])...) @@ -301,7 +301,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { // Child: Publish only one of the two newly created off-chain states to the watcher. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - statesPubChild, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + statesPubChild, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) require.NoError(t, statesPubChild.Publish(context.Background(), childTxs[1])) // Parent, Child: Trigger adjudicator events with a state newer than @@ -325,7 +325,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) // Add sub-channel to allocation. This transaction represents funding of the sub-channel. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[1], parentTxs[2])...) @@ -347,7 +347,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { // Child: Publish both the states to the watcher. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - childStatesPub, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + childStatesPub, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) require.NoError(t, childStatesPub.Publish(context.Background(), childTxs[1])) require.NoError(t, childStatesPub.Publish(context.Background(), childTxs[2])) @@ -384,7 +384,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 4) // Add sub-channel to allocation. This transaction represents funding of the sub-channel. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[1], parentTxs[2])...) @@ -408,7 +408,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { // Child: Publish both the states to the watcher. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - childStatesPub, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + childStatesPub, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) require.NoError(t, childStatesPub.Publish(context.Background(), childTxs[1])) require.NoError(t, childStatesPub.Publish(context.Background(), childTxs[2])) @@ -441,7 +441,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { // Setup: Generate the params and off-chain states for a ledger channel and a sub-channel. parentParams, parentTxs := randomTxsForSingleCh(rng, 2) childParams, childTxs := randomTxsForSingleCh(rng, 2) - parentTxs[1].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // Add sub-channel to allocation. + parentTxs[1].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // Add sub-channel to allocation. // Setup: Adjudicator event subscription for the ledger and sub-channel. adjSubParent := &mocks.AdjudicatorSubscription{} @@ -457,7 +457,7 @@ func Test_Watcher_WithSubchannel(t *testing.T) { parentSignedState := makeSignedStateWDummySigs(parentParams, parentTxs[0].State) _, eventsForClientParent := startWatchingForLedgerChannel(t, w, parentSignedState) childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - _, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + _, eventsForClientChild := startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) // Trigger the events for both (the ledger channel and the sub-channel) and, assert if they are relayed to // the adjudicator subscription (eventsForClient). @@ -495,7 +495,7 @@ func Test_Watcher_StopWatching(t *testing.T) { w := newWatcher(t, rs) startWatchingForLedgerChannel(t, w, makeSignedStateWDummySigs(params, txs[0].State)) - require.NoError(t, w.StopWatching(context.Background(), txs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), txs[0].ID)) rs.AssertExpectations(t) } @@ -517,10 +517,10 @@ func Test_Watcher_StopWatching(t *testing.T) { startWatchingForLedgerChannel(t, w, makeSignedStateWDummySigs(params, txs[0].State)) wg := sync.WaitGroup{} - for i := 0; i < 2; i++ { + for range 2 { wg.Add(1) go func() { - w.StopWatching(context.Background(), txs[0].State.ID) //nolint:errcheck + w.StopWatching(context.Background(), txs[0].ID) //nolint:errcheck wg.Done() }() } @@ -534,8 +534,8 @@ func Test_Watcher_StopWatching(t *testing.T) { defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 1) - parentTxs[1].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{} // sub-channel withdrawal. + parentTxs[1].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. + parentTxs[2].Locked = []channel.SubAlloc{} // sub-channel withdrawal. adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent) @@ -556,12 +556,12 @@ func Test_Watcher_StopWatching(t *testing.T) { // Child: Start watching. Parent: Publish sub-channel withdrawal transaction childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) require.NoError(t, statesPub.Publish(context.Background(), parentTxs[2])) // Child, then Parent: Stop Watching. - require.NoError(t, w.StopWatching(context.Background(), childTxs[0].State.ID)) - require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), childTxs[0].ID)) + require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].ID)) rs.AssertExpectations(t) }) @@ -570,7 +570,7 @@ func Test_Watcher_StopWatching(t *testing.T) { defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[2])[0]) @@ -592,14 +592,14 @@ func Test_Watcher_StopWatching(t *testing.T) { // Child: Start watching. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) // Child: Stop watching. - require.NoError(t, w.StopWatching(context.Background(), childTxs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), childTxs[0].ID)) // Parent: Trigger event with latest state and expect refutation with parent, child transactions. triggerAdjEventAndExpectNotification(t, triggerParent, eventsForClientParent) // Parent: Stop watching. - require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].ID)) rs.AssertExpectations(t) }) @@ -608,7 +608,7 @@ func Test_Watcher_StopWatching(t *testing.T) { defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent, makeRegisteredEvents(parentTxs[1])[0]) @@ -631,14 +631,14 @@ func Test_Watcher_StopWatching(t *testing.T) { // Child: Start watching. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) // Child: Stop watching. - require.NoError(t, w.StopWatching(context.Background(), childTxs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), childTxs[0].ID)) // Parent: Trigger event with latest state and expect refutation with parent, child transactions. triggerAdjEventAndExpectNotification(t, triggerParent, eventsForClientParent) // Parent: Stop watching. - require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].State.ID)) + require.NoError(t, w.StopWatching(context.Background(), parentTxs[0].ID)) rs.AssertExpectations(t) }) @@ -646,7 +646,7 @@ func Test_Watcher_StopWatching(t *testing.T) { t.Run("error/stopParent_woSettleOnParent_woStopChild", func(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) - parentTxs[2].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. + parentTxs[2].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent) @@ -668,10 +668,10 @@ func Test_Watcher_StopWatching(t *testing.T) { // Child: Start watching. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) // Parent: Stop watching (error). - require.Error(t, w.StopWatching(context.Background(), parentTxs[0].State.ID)) + require.Error(t, w.StopWatching(context.Background(), parentTxs[0].ID)) rs.AssertExpectations(t) }) @@ -679,8 +679,8 @@ func Test_Watcher_StopWatching(t *testing.T) { t.Run("error/stopParent_SettleOnParent_woStopChild", func(t *testing.T) { parentParams, parentTxs := randomTxsForSingleCh(rng, 3) childParams, childTxs := randomTxsForSingleCh(rng, 3) - parentTxs[1].Allocation.Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. - parentTxs[2].Allocation.Locked = []channel.SubAlloc{} // sub-channel withdrawal. + parentTxs[1].Locked = []channel.SubAlloc{{ID: childTxs[0].ID}} // sub-channel funding. + parentTxs[2].Locked = []channel.SubAlloc{} // sub-channel withdrawal. adjSubParent := &mocks.AdjudicatorSubscription{} triggerParent := setExpectationNextCall(adjSubParent) @@ -702,10 +702,10 @@ func Test_Watcher_StopWatching(t *testing.T) { // Child: Start watching. childSignedState := makeSignedStateWDummySigs(childParams, childTxs[0].State) - startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].State.ID) + startWatchingForSubChannel(t, w, childSignedState, parentTxs[0].ID) // Parent: Stop watching (error). - require.Error(t, w.StopWatching(context.Background(), parentTxs[0].State.ID)) + require.Error(t, w.StopWatching(context.Background(), parentTxs[0].ID)) rs.AssertExpectations(t) }) @@ -733,7 +733,10 @@ func randomTxsForSingleCh(rng *rand.Rand, n int) (*channel.Params, []channel.Tra txs := make([]channel.Transaction, n) for i := range txs { txs[i] = channel.Transaction{State: initialState.Clone()} - txs[i].State.Version = uint64(i) + if i < 0 { + panic("i < 0") + } + txs[i].Version = uint64(i) } return params, txs } @@ -774,6 +777,8 @@ func (t *adjEventSource) close() { // the "Subscribe" method to be called once. The adjSub and the err are set as // the return values for the call and will be returned when the method is // called. +// +//nolint:unparam func setExpectationSubscribeCall(rs *mocks.RegisterSubscriber, adjSub channel.AdjudicatorSubscription, err error) { rs.On("Subscribe", testifyMock.Anything, testifyMock.Anything).Return(adjSub, err).Once() } @@ -865,7 +870,7 @@ func setExpectationRegisterCalls(t *testing.T, rs *mocks.RegisterSubscriber, cha func assertEqualAdjudicatorReq(t *testing.T, got channel.AdjudicatorReq, want *channel.State) bool { t.Helper() - if nil != got.Tx.State.Equal(want) { + if nil != got.Tx.Equal(want) { t.Logf("Got %+v, expected %+v", got.Tx.State, want) return false } @@ -894,9 +899,9 @@ func makeRegisteredEvents(txs ...channel.Transaction) []channel.AdjudicatorEvent State: tx.State, Sigs: tx.Sigs, AdjudicatorEventBase: channel.AdjudicatorEventBase{ - IDV: tx.State.ID, + IDV: tx.ID, TimeoutV: &channel.ElapsedTimeout{}, - VersionV: tx.State.Version, + VersionV: tx.Version, }, } } @@ -910,9 +915,9 @@ func makeProgressedEvents(txs ...channel.Transaction) []channel.AdjudicatorEvent State: tx.State, Idx: channel.Index(0), AdjudicatorEventBase: channel.AdjudicatorEventBase{ - IDV: tx.State.ID, + IDV: tx.ID, TimeoutV: &channel.ElapsedTimeout{}, - VersionV: tx.State.Version, + VersionV: tx.Version, }, } } @@ -924,9 +929,9 @@ func makeConcludedEvents(txs ...channel.Transaction) []channel.AdjudicatorEvent for i, tx := range txs { events[i] = &channel.ConcludedEvent{ AdjudicatorEventBase: channel.AdjudicatorEventBase{ - IDV: tx.State.ID, + IDV: tx.ID, TimeoutV: &channel.ElapsedTimeout{}, - VersionV: tx.State.Version, + VersionV: tx.Version, }, } } diff --git a/wire/account.go b/wire/account.go index 117b99259..f864fc6fb 100644 --- a/wire/account.go +++ b/wire/account.go @@ -18,6 +18,7 @@ import ( "encoding/binary" "fmt" "io" + "math" "perun.network/go-perun/wallet" ) @@ -58,7 +59,11 @@ func (m *AuthResponseMsg) Type() Type { // It writes the signature to the writer. func (m *AuthResponseMsg) Encode(w io.Writer) error { // Write the length of the signature - err := binary.Write(w, binary.BigEndian, uint32(len(m.Signature))) + l := len(m.Signature) + if l > math.MaxUint32 { + return fmt.Errorf("signature length out of bounds: %d", len(m.Signature)) + } + err := binary.Write(w, binary.BigEndian, uint32(l)) if err != nil { return fmt.Errorf("failed to write signature length: %w", err) } diff --git a/wire/address.go b/wire/address.go index 39492fc52..5d58bc1b4 100644 --- a/wire/address.go +++ b/wire/address.go @@ -17,6 +17,7 @@ package wire import ( "encoding" stdio "io" + "math" "sort" "strings" @@ -58,12 +59,20 @@ type AddressDecMap map[wallet.BackendID]Address // Encode encodes first the length of the map, // then all Addresses and their key in the map. func (a AddressDecMap) Encode(w stdio.Writer) error { - length := int32(len(a)) + l := len(a) + if l > math.MaxInt32 { + return errors.New("map length out of bounds") + } + length := int32(l) if err := perunio.Encode(w, length); err != nil { return errors.WithMessage(err, "encoding map length") } for i, addr := range a { - if err := perunio.Encode(w, int32(i)); err != nil { + id := int(i) + if id < math.MinInt32 || id > math.MaxInt32 { + return errors.New("map index out of bounds") + } + if err := perunio.Encode(w, int32(id)); err != nil { return errors.WithMessage(err, "encoding map index") } if err := perunio.Encode(w, addr); err != nil { @@ -76,7 +85,11 @@ func (a AddressDecMap) Encode(w stdio.Writer) error { // Encode encodes first the length of the array, // then all AddressDecMaps in the array. func (a AddressMapArray) Encode(w stdio.Writer) error { - length := int32(len(a)) + l := len(a) + if l < math.MinInt32 || l > math.MaxInt32 { + return errors.New("array length out of bounds") + } + length := int32(l) if err := perunio.Encode(w, length); err != nil { return errors.WithMessage(err, "encoding array length") } @@ -95,7 +108,7 @@ func (a *AddressDecMap) Decode(r stdio.Reader) (err error) { return errors.WithMessage(err, "decoding map length") } *a = make(map[wallet.BackendID]Address, mapLen) - for i := 0; i < int(mapLen); i++ { + for i := range mapLen { var idx int32 if err := perunio.Decode(r, &idx); err != nil { return errors.WithMessage(err, "decoding map index") @@ -117,7 +130,7 @@ func (a *AddressMapArray) Decode(r stdio.Reader) (err error) { return errors.WithMessage(err, "decoding array length") } *a = make([]map[wallet.BackendID]Address, mapLen) - for i := 0; i < int(mapLen); i++ { + for i := range mapLen { if err := perunio.Decode(r, (*AddressDecMap)(&(*a)[i])); err != nil { return errors.WithMessagef(err, "decoding %d-th address map entry", i) } diff --git a/wire/cache_internal_test.go b/wire/cache_internal_test.go index 4de3acff7..10cb3c3d5 100644 --- a/wire/cache_internal_test.go +++ b/wire/cache_internal_test.go @@ -61,7 +61,7 @@ func TestCache(t *testing.T) { c.Release(&isPing) assert.False(c.Put(ping2), "Put into cache with canceled predicate") assert.Equal(2, c.Size()) - assert.Len(c.preds, 0, "internal: Put should have removed canceled predicate") + assert.Empty(c.preds, "internal: Put should have removed canceled predicate") msgs := c.Messages(func(e *Envelope) bool { return e.Msg.Type() == Ping && diff --git a/wire/controlmsgs.go b/wire/controlmsgs.go index 9d0c955ef..6b96b9af4 100644 --- a/wire/controlmsgs.go +++ b/wire/controlmsgs.go @@ -61,16 +61,16 @@ type PingMsg struct { PingPongMsg } -// Type returns Ping. -func (m *PingMsg) Type() Type { - return Ping -} - // NewPingMsg creates a new Ping message. func NewPingMsg() *PingMsg { return &PingMsg{newPingPongMsg()} } +// Type returns Ping. +func (m *PingMsg) Type() Type { + return Ping +} + // PongMsg is the response to a ping message. // It contains the time at which it was sent, so that the recipient knows how // long the ping request took to be transmitted, and how quickly the response @@ -79,16 +79,16 @@ type PongMsg struct { PingPongMsg } -// Type returns Pong. -func (m *PongMsg) Type() Type { - return Pong -} - // NewPongMsg creates a new Pong message. func NewPongMsg() *PongMsg { return &PongMsg{newPingPongMsg()} } +// Type returns Pong. +func (m *PongMsg) Type() Type { + return Pong +} + // ShutdownMsg is sent when orderly shutting down a connection. // // Reason should be a UTF-8 encodable string. diff --git a/wire/encode_test.go b/wire/encode_test.go index fc586c617..efd32e60d 100644 --- a/wire/encode_test.go +++ b/wire/encode_test.go @@ -25,7 +25,7 @@ import ( "polycry.pt/poly-go/test" ) -var nilDecoder = func(io.Reader) (wire.Msg, error) { return nil, nil } +var nilDecoder = func(io.Reader) (wire.Msg, error) { return nil, nil } //nolint:nilnil func TestType_Valid_String(t *testing.T) { test.OnlyOnce(t) diff --git a/wire/hybridbus.go b/wire/hybridbus.go index 6015d56ec..49bab8803 100644 --- a/wire/hybridbus.go +++ b/wire/hybridbus.go @@ -66,7 +66,6 @@ func (b *hybridBus) Publish(ctx context.Context, e *Envelope) error { errg := errors.NewGatherer() for _, bus := range b.buses { - bus := bus errg.Go(func() error { err := bus.Publish(sending, e) // If sending was successful, abort all other send operations, and diff --git a/wire/net/bus_test.go b/wire/net/bus_test.go index de169d6bd..cd98325eb 100644 --- a/wire/net/bus_test.go +++ b/wire/net/bus_test.go @@ -17,10 +17,9 @@ package net_test import ( "testing" + "github.com/stretchr/testify/require" "perun.network/go-perun/wallet" - "github.com/stretchr/testify/assert" - "perun.network/go-perun/wire" "perun.network/go-perun/wire/net" nettest "perun.network/go-perun/wire/net/test" @@ -41,5 +40,5 @@ func TestBus(t *testing.T) { return bus, bus }, numClients, numMsgs) - assert.NoError(t, hub.Close()) + require.NoError(t, hub.Close()) } diff --git a/wire/net/endpoint.go b/wire/net/endpoint.go index 902c101ab..28bc1a225 100644 --- a/wire/net/endpoint.go +++ b/wire/net/endpoint.go @@ -42,28 +42,6 @@ type Endpoint struct { sending sync.Mutex // Blocks multiple Send calls. } -// recvLoop continuously receives messages from an Endpoint until it is closed. -// Received messages are relayed via the Endpoint's subscription system. This is -// called by the registry when the Endpoint is registered. -// -// Does not return an error when the Endpoint closing fails or when -// conn.Recv returns io.EOF, which indicates connection closing for TCP. -func (p *Endpoint) recvLoop(c wire.Consumer) error { - for { - e, err := p.conn.Recv() - if err != nil { - p.Close() // Ignore double close. - // Check for graceful TCP connection close. - if errors.Cause(err) == io.EOF { - return nil - } - return err - } - // Emit the received envelope. - c.Put(e) - } -} - // Send sends a single message to an Endpoint. // Fails if the Endpoint is closed via Close() or the transmission fails. // @@ -109,3 +87,25 @@ func newEndpoint(addr map[wallet.BackendID]wire.Address, conn Conn) *Endpoint { func (p *Endpoint) String() string { return fmt.Sprint(p.Address) } + +// recvLoop continuously receives messages from an Endpoint until it is closed. +// Received messages are relayed via the Endpoint's subscription system. This is +// called by the registry when the Endpoint is registered. +// +// Does not return an error when the Endpoint closing fails or when +// conn.Recv returns io.EOF, which indicates connection closing for TCP. +func (p *Endpoint) recvLoop(c wire.Consumer) error { + for { + e, err := p.conn.Recv() + if err != nil { + p.Close() // Ignore double close. + // Check for graceful TCP connection close. + if errors.Cause(err) == io.EOF { + return nil + } + return err + } + // Emit the received envelope. + c.Put(e) + } +} diff --git a/wire/net/endpoint_internal_test.go b/wire/net/endpoint_internal_test.go index 0abe4734f..f37ab09c9 100644 --- a/wire/net/endpoint_internal_test.go +++ b/wire/net/endpoint_internal_test.go @@ -26,6 +26,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" _ "perun.network/go-perun/backend/sim" // backend init "perun.network/go-perun/wire" @@ -120,7 +121,7 @@ func TestEndpoint_Close(t *testing.T) { found := s.alice.Registry.find(bobAddr) assert.Equal(t, s.alice.endpoint, found) // Close Alice's connection to Bob. - assert.NoError(t, s.alice.endpoint.Close(), "closing a peer once must succeed") + require.NoError(t, s.alice.endpoint.Close(), "closing a peer once must succeed") assert.Error(t, s.alice.endpoint.Close(), "closing peers twice must fail") // Sending over closed peers (not connections) must fail. diff --git a/wire/net/endpoint_registry.go b/wire/net/endpoint_registry.go index 79be8eee9..6539ca528 100644 --- a/wire/net/endpoint_registry.go +++ b/wire/net/endpoint_registry.go @@ -165,29 +165,6 @@ func (r *EndpointRegistry) Listen(listener Listener) { } } -// setupConn authenticates a fresh connection, and if successful, adds it to the -// registry. -func (r *EndpointRegistry) setupConn(conn Conn) error { - ctx, cancel := context.WithTimeout(r.Ctx(), exchangeAddrsTimeout) - defer cancel() - - var peerAddr map[wallet.BackendID]wire.Address - var err error - if peerAddr, err = ExchangeAddrsPassive(ctx, r.id, conn); err != nil { - conn.Close() - r.Log().WithField("peer", peerAddr).Error("could not authenticate peer:", err) - return err - } - - if channel.EqualWireMaps(peerAddr, wire.AddressMapfromAccountMap(r.id)) { - r.Log().Error("dialed by self") - return errors.New("dialed by self") - } - - r.addEndpoint(peerAddr, conn, false) - return nil -} - // Endpoint looks up an Endpoint via its perun address. If the Endpoint does not // exist yet, it is dialed. Does not return until the peer is dialed or the // context is closed. @@ -219,6 +196,26 @@ func (r *EndpointRegistry) Endpoint(ctx context.Context, addr map[wallet.Backend return e, errors.WithMessage(err, "failed to dial peer") } +// NumPeers returns the current number of peers in the registry including +// placeholder peers (cf. Registry.Get). +func (r *EndpointRegistry) NumPeers() int { + r.mutex.RLock() + defer r.mutex.RUnlock() + return len(r.endpoints) +} + +// Has return true if and only if there is a peer with the given address in the +// registry. The function does not differentiate between regular and +// placeholder peers. +func (r *EndpointRegistry) Has(addr map[wallet.BackendID]wire.Address) bool { + r.mutex.Lock() + defer r.mutex.Unlock() + + _, ok := r.endpoints[wire.Keys(addr)] + + return ok +} + func (r *EndpointRegistry) authenticatedDial( ctx context.Context, addr map[wallet.BackendID]wire.Address, @@ -276,26 +273,6 @@ func (r *EndpointRegistry) dialingEndpoint(a map[wallet.BackendID]wire.Address) return entry, !ok } -// NumPeers returns the current number of peers in the registry including -// placeholder peers (cf. Registry.Get). -func (r *EndpointRegistry) NumPeers() int { - r.mutex.RLock() - defer r.mutex.RUnlock() - return len(r.endpoints) -} - -// Has return true if and only if there is a peer with the given address in the -// registry. The function does not differentiate between regular and -// placeholder peers. -func (r *EndpointRegistry) Has(addr map[wallet.BackendID]wire.Address) bool { - r.mutex.Lock() - defer r.mutex.Unlock() - - _, ok := r.endpoints[wire.Keys(addr)] - - return ok -} - // addEndpoint adds a new peer to the registry. func (r *EndpointRegistry) addEndpoint(addr map[wallet.BackendID]wire.Address, conn Conn, dialer bool) *Endpoint { r.Log().WithField("peer", addr).Trace("EndpointRegistry.addEndpoint") @@ -394,3 +371,26 @@ func (r *EndpointRegistry) find(addr map[wallet.BackendID]wire.Address) *Endpoin } return nil } + +// setupConn authenticates a fresh connection, and if successful, adds it to the +// registry. +func (r *EndpointRegistry) setupConn(conn Conn) error { + ctx, cancel := context.WithTimeout(r.Ctx(), exchangeAddrsTimeout) + defer cancel() + + var peerAddr map[wallet.BackendID]wire.Address + var err error + if peerAddr, err = ExchangeAddrsPassive(ctx, r.id, conn); err != nil { + conn.Close() + r.Log().WithField("peer", peerAddr).Error("could not authenticate peer:", err) + return err + } + + if channel.EqualWireMaps(peerAddr, wire.AddressMapfromAccountMap(r.id)) { + r.Log().Error("dialed by self") + return errors.New("dialed by self") + } + + r.addEndpoint(peerAddr, conn, false) + return nil +} diff --git a/wire/net/endpoint_registry_external_test.go b/wire/net/endpoint_registry_external_test.go index 53dd657f3..4e6b5278f 100644 --- a/wire/net/endpoint_registry_external_test.go +++ b/wire/net/endpoint_registry_external_test.go @@ -60,14 +60,14 @@ func TestEndpointRegistry_Get_Pair(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*timeout) defer cancel() p, err := dialerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(listenerID)) - assert.NoError(err) + require.NoError(err) require.NotNil(p) assert.True(channel.EqualWireMaps(p.Address, wire.AddressMapfromAccountMap(listenerID))) // should allow the listener routine to add the peer to its registry time.Sleep(timeout) p, err = listenerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(dialerID)) - assert.NoError(err) + require.NoError(err) require.NotNil(p) assert.True(channel.EqualWireMaps(p.Address, wire.AddressMapfromAccountMap(dialerID))) @@ -107,7 +107,7 @@ func TestEndpointRegistry_Get_Multiple(t *testing.T) { const N = 4 peers := make(chan *net.Endpoint, N) - for i := 0; i < N; i++ { + for range N { go func() { p, err := dialerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(listenerID)) assert.NoError(err) @@ -124,7 +124,7 @@ func TestEndpointRegistry_Get_Multiple(t *testing.T) { require := require.New(t) p := <-peers require.NotNil(p) - for i := 0; i < N-1; i++ { + for range N - 1 { p0 := <-peers require.NotNil(p0) assert.Same(p, p0) diff --git a/wire/net/endpoint_registry_internal_test.go b/wire/net/endpoint_registry_internal_test.go index 85173325e..f2e4c7575 100644 --- a/wire/net/endpoint_registry_internal_test.go +++ b/wire/net/endpoint_registry_internal_test.go @@ -55,7 +55,7 @@ func (d *mockDialer) Close() error { return nil } -func (d *mockDialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { +func (d *mockDialer) Dial(ctx context.Context, _ map[wallet.BackendID]wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { d.mutex.Lock() defer d.mutex.Unlock() @@ -132,7 +132,7 @@ func TestRegistry_Get(t *testing.T) { r.endpoints[wire.Keys(peerAddr)] = newFullEndpoint(existing) ctxtest.AssertTerminates(t, timeout, func() { p, err := r.Endpoint(context.Background(), peerAddr) - assert.NoError(t, err) + require.NoError(t, err) assert.Same(t, p, existing) }) }) @@ -166,9 +166,9 @@ func TestRegistry_Get(t *testing.T) { go ct.Stage("receiver", func(t test.ConcT) { dialer.put(a) _, err := ExchangeAddrsPassive(ctx, peerID, b) - require.NoError(t, err) + assert.NoError(t, err) _, err = b.Recv() - require.NoError(t, err) + assert.NoError(t, err) }) p, err := r.Endpoint(ctx, peerAddr) require.NoError(t, err) @@ -233,7 +233,7 @@ func TestRegistry_authenticatedDial(t *testing.T) { go ct.Stage("passive", func(rt test.ConcT) { d.put(a) _, err := ExchangeAddrsPassive(ctx, wiretest.NewRandomAccountMap(rng, channel.TestBackendID), b) - require.True(rt, IsAuthenticationError(err)) + assert.True(rt, IsAuthenticationError(err)) }) de, created := r.dialingEndpoint(remoteAddr) e, err := r.authenticatedDial(ctx, remoteAddr, de, created) @@ -257,7 +257,7 @@ func TestRegistry_authenticatedDial(t *testing.T) { defer cancel() de, created := r.dialingEndpoint(remoteAddr) e, err := r.authenticatedDial(ctx, remoteAddr, de, created) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, e) }) } @@ -300,7 +300,7 @@ func TestRegistry_setupConn(t *testing.T) { r.addEndpoint(wire.AddressMapfromAccountMap(remoteID), newMockConn(), false) ctxtest.AssertTerminates(t, timeout, func() { - assert.NoError(t, r.setupConn(a)) + require.NoError(t, r.setupConn(a)) }) }) @@ -316,7 +316,7 @@ func TestRegistry_setupConn(t *testing.T) { }() ctxtest.AssertTerminates(t, timeout, func() { - assert.NoError(t, r.setupConn(a)) + require.NoError(t, r.setupConn(a)) }) }) } diff --git a/wire/net/exchange_addr_internal_test.go b/wire/net/exchange_addr_internal_test.go index d9ddf2c3b..5d67a6a42 100644 --- a/wire/net/exchange_addr_internal_test.go +++ b/wire/net/exchange_addr_internal_test.go @@ -22,6 +22,7 @@ import ( "perun.network/go-perun/channel" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire" wiretest "perun.network/go-perun/wire/test" @@ -51,12 +52,12 @@ func TestExchangeAddrs_Success(t *testing.T) { defer conn1.Close() recvAddr0, err := ExchangeAddrsPassive(context.Background(), account1, conn1) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, channel.EqualWireMaps(recvAddr0, wire.AddressMapfromAccountMap(account0))) }() err := ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0) - assert.NoError(t, err) + require.NoError(t, err) wg.Wait() } diff --git a/wire/net/simple/account.go b/wire/net/simple/account.go index 799a59ab6..4973e7113 100644 --- a/wire/net/simple/account.go +++ b/wire/net/simple/account.go @@ -31,6 +31,23 @@ type Account struct { privateKey *rsa.PrivateKey } +// NewRandomAccount generates a new random account. +func NewRandomAccount(rng *rand.Rand) *Account { + keySize := 2048 + privateKey, err := rsa.GenerateKey(rng, keySize) + if err != nil { + panic(err) + } + + address := NewRandomAddress(rng) + address.PublicKey = &privateKey.PublicKey + + return &Account{ + addr: address, + privateKey: privateKey, + } +} + // Address returns the account's address. func (acc *Account) Address() wire.Address { return acc.addr @@ -48,20 +65,3 @@ func (acc *Account) Sign(msg []byte) ([]byte, error) { } return signature, nil } - -// NewRandomAccount generates a new random account. -func NewRandomAccount(rng *rand.Rand) *Account { - keySize := 2048 - privateKey, err := rsa.GenerateKey(rng, keySize) - if err != nil { - panic(err) - } - - address := NewRandomAddress(rng) - address.PublicKey = &privateKey.PublicKey - - return &Account{ - addr: address, - privateKey: privateKey, - } -} diff --git a/wire/net/simple/address.go b/wire/net/simple/address.go index 069d60dd8..48197f9da 100644 --- a/wire/net/simple/address.go +++ b/wire/net/simple/address.go @@ -20,9 +20,12 @@ import ( "crypto/rsa" "crypto/sha256" "encoding/binary" + "math" "math/big" "math/rand" + "strings" + "github.com/pkg/errors" "perun.network/go-perun/wallet" "perun.network/go-perun/wire" @@ -44,13 +47,32 @@ func NewAddress(host string) wire.Address { } } +// NewRandomAddress returns a new random peer address. +func NewRandomAddress(rng *rand.Rand) *Address { + const addrLen = 32 + l := rng.Intn(addrLen) + d := make([]byte, l) + if _, err := rng.Read(d); err != nil { + panic(err) + } + + a := &Address{ + Name: string(d), + } + return a +} + // MarshalBinary marshals the address to binary. func (a *Address) MarshalBinary() ([]byte, error) { // Initialize a buffer to hold the binary data var buf bytes.Buffer // Encode the length of the name string and the name itself - nameLen := uint16(len(a.Name)) + l := len(a.Name) + if l > math.MaxUint16 { + return nil, errors.New("name length out of bounds") + } + nameLen := uint16(l) if err := binary.Write(&buf, binary.BigEndian, nameLen); err != nil { return nil, err } @@ -113,7 +135,11 @@ func (a *Address) Backend() wallet.BackendID { func encodePublicKey(buf *bytes.Buffer, key *rsa.PublicKey) error { // Encode modulus length and modulus modulusBytes := key.N.Bytes() - modulusLen := uint16(len(modulusBytes)) + l := len(modulusBytes) + if l > math.MaxUint16 { + return errors.New("modulus too large") + } + modulusLen := uint16(l) if err := binary.Write(buf, binary.BigEndian, modulusLen); err != nil { return err } @@ -122,7 +148,11 @@ func encodePublicKey(buf *bytes.Buffer, key *rsa.PublicKey) error { } // Encode public exponent - if err := binary.Write(buf, binary.BigEndian, int32(key.E)); err != nil { + e := key.E + if e < math.MinInt32 || e > math.MaxInt32 { + return errors.New("public exponent too large") + } + if err := binary.Write(buf, binary.BigEndian, int32(e)); err != nil { return err } @@ -184,27 +214,12 @@ func (a *Address) Cmp(b wire.Address) int { } // Compare names - if cmp := bytes.Compare([]byte(a.Name), []byte(bTyped.Name)); cmp != 0 { + if cmp := strings.Compare(a.Name, bTyped.Name); cmp != 0 { return cmp } // Compare binary representations - return bytes.Compare([]byte(a.Name), []byte(bTyped.Name)) -} - -// NewRandomAddress returns a new random peer address. -func NewRandomAddress(rng *rand.Rand) *Address { - const addrLen = 32 - l := rng.Intn(addrLen) - d := make([]byte, l) - if _, err := rng.Read(d); err != nil { - panic(err) - } - - a := &Address{ - Name: string(d), - } - return a + return strings.Compare(a.Name, bTyped.Name) } // NewRandomAddresses returns a new random peer address. diff --git a/wire/net/simple/dialer.go b/wire/net/simple/dialer.go index 546019d70..60c7cc11f 100644 --- a/wire/net/simple/dialer.go +++ b/wire/net/simple/dialer.go @@ -70,14 +70,6 @@ func NewUnixDialer(defaultTimeout time.Duration, tlsConfig *tls.Config) *Dialer return NewNetDialer("unix", defaultTimeout, tlsConfig) } -func (d *Dialer) host(key wire.AddrKey) (string, bool) { - d.mutex.RLock() - defer d.mutex.RUnlock() - - host, ok := d.peers[key] - return host, ok -} - // Dial implements Dialer.Dial(). func (d *Dialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, ser wire.EnvelopeSerializer) (wirenet.Conn, error) { done := make(chan struct{}) @@ -115,3 +107,11 @@ func (d *Dialer) Register(addr map[wallet.BackendID]wire.Address, address string d.peers[wire.Keys(addr)] = address } + +func (d *Dialer) host(key wire.AddrKey) (string, bool) { + d.mutex.RLock() + defer d.mutex.RUnlock() + + host, ok := d.peers[key] + return host, ok +} diff --git a/wire/net/simple/dialer_internal_test.go b/wire/net/simple/dialer_internal_test.go index 1ac60e6c3..7ac8c5d09 100644 --- a/wire/net/simple/dialer_internal_test.go +++ b/wire/net/simple/dialer_internal_test.go @@ -22,7 +22,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" - "fmt" "math/big" "net" "testing" @@ -32,6 +31,7 @@ import ( "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -47,7 +47,7 @@ func TestNewTCPDialer(t *testing.T) { MinVersion: tls.VersionTLS12, // Set minimum TLS version to TLS 1.2 } d := NewTCPDialer(0, tlsConfig) - assert.Equal(t, d.network, "tcp") + assert.Equal(t, "tcp", d.network) } func TestNewUnixDialer(t *testing.T) { @@ -55,7 +55,7 @@ func TestNewUnixDialer(t *testing.T) { MinVersion: tls.VersionTLS12, // Set minimum TLS version to TLS 1.2 } d := NewUnixDialer(0, tlsConfig) - assert.Equal(t, d.network, "unix") + assert.Equal(t, "unix", d.network) } func TestDialer_Register(t *testing.T) { @@ -74,7 +74,7 @@ func TestDialer_Register(t *testing.T) { host, ok := d.host(key) assert.True(t, ok) - assert.Equal(t, host, "host") + assert.Equal(t, "host", host) } func TestDialer_Dial(t *testing.T) { @@ -108,20 +108,20 @@ func TestDialer_Dial(t *testing.T) { go ct.Stage("accept", func(rt test.ConcT) { conn, err := l.Accept(ser) assert.NoError(t, err) - require.NotNil(rt, conn) + assert.NotNil(rt, conn) re, err := conn.Recv() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, re, e) }) ct.Stage("dial", func(rt test.ConcT) { ctxtest.AssertTerminates(t, timeout, func() { conn, err := d.Dial(context.Background(), laddr, ser) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(rt, conn) - assert.NoError(t, conn.Send(e)) + require.NoError(t, conn.Send(e)) }) }) @@ -251,7 +251,7 @@ func generateSelfSignedCertConfigs(commonName string, sans []string) (*tls.Confi serverCertPool := x509.NewCertPool() ok := serverCertPool.AppendCertsFromPEM(clientCertPEM) if !ok { - return nil, nil, fmt.Errorf("failed to parse root certificate") + return nil, nil, errors.New("failed to parse root certificate") } // Create the server-side TLS configuration @@ -265,7 +265,7 @@ func generateSelfSignedCertConfigs(commonName string, sans []string) (*tls.Confi clientCertPool := x509.NewCertPool() ok = clientCertPool.AppendCertsFromPEM(serverCertPEM) if !ok { - return nil, nil, fmt.Errorf("failed to parse root certificate") + return nil, nil, errors.New("failed to parse root certificate") } // Create the client-side TLS configuration diff --git a/wire/net/simple/listener_internal_test.go b/wire/net/simple/listener_internal_test.go index b5cc7258b..f37cf0c00 100644 --- a/wire/net/simple/listener_internal_test.go +++ b/wire/net/simple/listener_internal_test.go @@ -31,6 +31,8 @@ const addr = "0.0.0.0:1337" // serverKey and serverCert are generated with the following commands: // openssl ecparam -genkey -name prime256v1 -out server.key // openssl req -new -x509 -key server.key -out server.pem -days 3650. +// +//nolint:gosec const testServerKey = `-----BEGIN EC PARAMETERS----- BggqhkjOPQMBBw== -----END EC PARAMETERS----- @@ -90,7 +92,7 @@ func TestListener_Close(t *testing.T) { t.Run("double close", func(t *testing.T) { l, err := NewTCPListener(addr, tlsConfig) require.NoError(t, err) - assert.NoError(t, l.Close(), "first close must not return error") + require.NoError(t, l.Close(), "first close must not return error") assert.Error(t, l.Close(), "second close must result in error") }) } @@ -104,7 +106,7 @@ func TestNewListener(t *testing.T) { } t.Run("happy", func(t *testing.T) { l, err := NewTCPListener(addr, tlsConfig) - assert.NoError(t, err) + require.NoError(t, err) require.NotNil(t, l) l.Close() }) diff --git a/wire/net/simple/simple_exchange_addr_test.go b/wire/net/simple/simple_exchange_addr_test.go index 885b00747..59be548a5 100644 --- a/wire/net/simple/simple_exchange_addr_test.go +++ b/wire/net/simple/simple_exchange_addr_test.go @@ -29,6 +29,7 @@ import ( "perun.network/go-perun/channel" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire" wirenet "perun.network/go-perun/wire/net" @@ -67,7 +68,7 @@ func TestExchangeAddrs_Success(t *testing.T) { }() err := wirenet.ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0) - assert.NoError(t, err) + require.NoError(t, err) wg.Wait() } diff --git a/wire/net/test/connhub_internal_test.go b/wire/net/test/connhub_internal_test.go index edd97b411..de8b1df9b 100644 --- a/wire/net/test/connhub_internal_test.go +++ b/wire/net/test/connhub_internal_test.go @@ -51,7 +51,7 @@ func TestConnHub_Create(t *testing.T) { ct.Stage("accept", func(rt pkgtest.ConcT) { conn, err := l.Accept(ser) assert.NoError(err) - require.NotNil(rt, conn) + assert.NotNil(rt, conn) assert.NoError(conn.Send(wiretest.NewRandomEnvelope(rng, wire.NewPingMsg()))) }) }) diff --git a/wire/net/test/dialer_internal_test.go b/wire/net/test/dialer_internal_test.go index e24748916..d532f1873 100644 --- a/wire/net/test/dialer_internal_test.go +++ b/wire/net/test/dialer_internal_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" perunio "perun.network/go-perun/wire/perunio/serializer" "perun.network/go-perun/wire/test" @@ -51,6 +52,6 @@ func TestDialer_Dial(t *testing.T) { func TestDialer_Close(t *testing.T) { var d Dialer - assert.NoError(t, d.Close()) + require.NoError(t, d.Close()) assert.Error(t, d.Close()) } diff --git a/wire/net/test/dialerlist_internal_test.go b/wire/net/test/dialerlist_internal_test.go index 41093a8c1..98a83b439 100644 --- a/wire/net/test/dialerlist_internal_test.go +++ b/wire/net/test/dialerlist_internal_test.go @@ -44,7 +44,7 @@ func TestDialerList_erase(t *testing.T) { d := &Dialer{} l.insert(d) assert.NoError(l.erase(d)) - assert.Len(l.entries, 0) + assert.Empty(l.entries) assert.Error(l.erase(d)) } @@ -55,5 +55,5 @@ func TestDialerList_clear(t *testing.T) { d := &Dialer{} l.insert(d) assert.Equal(l.clear(), []*Dialer{d}) - assert.Len(l.entries, 0) + assert.Empty(l.entries) } diff --git a/wire/net/test/listener_internal_test.go b/wire/net/test/listener_internal_test.go index 87b253cd3..5c2e1140d 100644 --- a/wire/net/test/listener_internal_test.go +++ b/wire/net/test/listener_internal_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire" wirenet "perun.network/go-perun/wire/net" @@ -49,7 +50,7 @@ func TestListener_Accept_Put(t *testing.T) { ctxtest.AssertTerminates(t, timeout, func() { conn, err := l.Accept(perunio.Serializer()) - assert.NoError(t, err, "Accept must not fail") + require.NoError(t, err, "Accept must not fail") assert.Same(t, connection, conn, "Accept must receive connection from Put") assert.Equal(t, 1, l.NumAccepted(), @@ -130,7 +131,7 @@ func TestListener_Put(t *testing.T) { func TestListener_Close(t *testing.T) { l := NewNetListener() assert.False(t, l.IsClosed()) - assert.NoError(t, l.Close()) + require.NoError(t, l.Close()) assert.True(t, l.IsClosed()) assert.Error(t, l.Close()) } diff --git a/wire/net/test/listenermap_internal_test.go b/wire/net/test/listenermap_internal_test.go index 05eff932e..5fbb7d865 100644 --- a/wire/net/test/listenermap_internal_test.go +++ b/wire/net/test/listenermap_internal_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire/test" pkgtest "polycry.pt/poly-go/test" @@ -54,9 +55,9 @@ func TestListenerMap_insert(t *testing.T) { t.Run("insert new", func(t *testing.T) { m := listenerMap{} - for i := 0; i < 10; i++ { + for range 10 { key := test.NewRandomAddress(rng) - assert.NoError(t, m.insert(key, NewNetListener())) + require.NoError(t, m.insert(key, NewNetListener())) _, ok := m.find(key) assert.True(t, ok) } @@ -65,7 +66,7 @@ func TestListenerMap_insert(t *testing.T) { t.Run("double insert", func(t *testing.T) { m := listenerMap{} key := test.NewRandomAddress(rng) - assert.NoError(t, m.insert(key, NewNetListener())) + require.NoError(t, m.insert(key, NewNetListener())) assert.Error(t, m.insert(key, NewNetListener())) }) } @@ -75,10 +76,10 @@ func TestListenerMap_erase(t *testing.T) { t.Run("erase existing", func(t *testing.T) { m := listenerMap{} - for i := 0; i < 10; i++ { + for range 10 { key := test.NewRandomAddress(rng) - assert.NoError(t, m.insert(key, NewNetListener())) - assert.NoError(t, m.erase(key)) + require.NoError(t, m.insert(key, NewNetListener())) + require.NoError(t, m.erase(key)) _, ok := m.find(key) assert.False(t, ok) } diff --git a/wire/perunio/bigint.go b/wire/perunio/bigint.go index 699136434..532eae66d 100644 --- a/wire/perunio/bigint.go +++ b/wire/perunio/bigint.go @@ -51,7 +51,7 @@ func (b *BigInt) Decode(reader io.Reader) error { if b.Int == nil { b.Int = new(big.Int) } - b.Int.SetBytes(bytes) + b.SetBytes(bytes) return nil } @@ -60,7 +60,7 @@ func (b BigInt) Encode(writer io.Writer) error { if b.Int == nil { panic("logic error: tried to encode nil big.Int") } - if b.Int.Sign() == -1 { + if b.Sign() == -1 { panic("encoding of negative big.Int not implemented") } diff --git a/wire/perunio/bigint_external_test.go b/wire/perunio/bigint_external_test.go index 4850cd693..6ff4ff147 100644 --- a/wire/perunio/bigint_external_test.go +++ b/wire/perunio/bigint_external_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire/perunio" peruniotest "perun.network/go-perun/wire/perunio/test" ) @@ -46,7 +47,7 @@ func TestBigInt_DecodeZeroLength(t *testing.T) { func TestBigInt_DecodeToExisting(t *testing.T) { x, buf := new(big.Int), bytes.NewBuffer([]byte{1, 42}) wx := perunio.BigInt{Int: x} - assert.NoError(t, wx.Decode(buf), "decoding {1, 42} into big.Int should work") + require.NoError(t, wx.Decode(buf), "decoding {1, 42} into big.Int should work") assert.Zero(t, big.NewInt(42).Cmp(x), "decoding {1, 42} into big.Int should result in 42") } @@ -65,24 +66,24 @@ func TestBigInt_Invalid(t *testing.T) { tooBig := perunio.BigInt{Int: big.NewInt(1)} tooBig.Lsh(tooBig.Int, pos) - a.Error(tooBig.Encode(buf), "encoding too big big.Int should fail") + require.Error(t, tooBig.Encode(buf), "encoding too big big.Int should fail") a.Zero(buf.Len(), "encoding too big big.Int should not have written anything") buf.Reset() // in case above test failed } // manually encode too big number to test failing of decoding buf.Write([]byte{perunio.MaxBigIntLength + 1}) - for i := 0; i < perunio.MaxBigIntLength+1; i++ { + for range perunio.MaxBigIntLength + 1 { buf.WriteByte(0xff) } var result perunio.BigInt - a.Error(result.Decode(buf), "decoding of an integer that is too big should fail") + require.Error(t, result.Decode(buf), "decoding of an integer that is too big should fail") buf.Reset() // Test not sending value, only length buf.WriteByte(1) - a.Error(result.Decode(buf), "decoding after sender only sent length should fail") + require.Error(t, result.Decode(buf), "decoding after sender only sent length should fail") a.Panics(func() { _ = perunio.BigInt{Int: nil}.Encode(buf) }, "encoding nil big.Int failed to panic") } diff --git a/wire/perunio/byteslice_external_test.go b/wire/perunio/byteslice_external_test.go index c90ee62c9..7796283dc 100644 --- a/wire/perunio/byteslice_external_test.go +++ b/wire/perunio/byteslice_external_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire/perunio" peruniotest "perun.network/go-perun/wire/perunio/test" @@ -46,7 +47,7 @@ func TestStutter(t *testing.T) { var decodedValue perunio.ByteSlice = make([]byte, len(values)) ctxtest.AssertTerminatesQuickly(t, func() { - assert.NoError(t, decodedValue.Decode(r)) + require.NoError(t, decodedValue.Decode(r)) }) for i, v := range values { assert.Equal(t, decodedValue[i], v) diff --git a/wire/perunio/serialize.go b/wire/perunio/serialize.go index d931ffcb0..9c6e2d335 100644 --- a/wire/perunio/serialize.go +++ b/wire/perunio/serialize.go @@ -88,6 +88,8 @@ func Encode(writer io.Writer, values ...interface{}) (err error) { //nolint: cyc // Decode decodes multiple primitive values from a reader. // All passed values must be references, not copies. +// +//nolint:cyclop func Decode(reader io.Reader, values ...interface{}) (err error) { for i, value := range values { switch v := value.(type) { diff --git a/wire/perunio/string.go b/wire/perunio/string.go index 292c0a837..69a42592d 100644 --- a/wire/perunio/string.go +++ b/wire/perunio/string.go @@ -16,19 +16,25 @@ package perunio import ( "encoding/binary" + "fmt" "io" + "math" "github.com/pkg/errors" ) // encodeString writes the length as an uint16 and then the string itself to the io.Writer. func encodeString(w io.Writer, s string) error { - l := uint16(len(s)) - if int(l) != len(s) { + l := len(s) + if l > int(math.MaxUint16) { + return fmt.Errorf("string too long: %d", l) + } + ul := uint16(l) + if int(ul) != len(s) { return errors.Errorf("string length exceeded: %d", len(s)) } - if err := binary.Write(w, byteOrder, l); err != nil { + if err := binary.Write(w, byteOrder, ul); err != nil { return errors.Wrap(err, "failed to write string length") } diff --git a/wire/perunio/string_internal_test.go b/wire/perunio/string_internal_test.go index 4310ec9a8..3016046b3 100644 --- a/wire/perunio/string_internal_test.go +++ b/wire/perunio/string_internal_test.go @@ -46,7 +46,7 @@ func TestEncodeDecodeString(t *testing.T) { }() var d string - assert.NoError(decodeString(r, &d)) + require.NoError(t, decodeString(r, &d)) r.Close() assert.Equal(s, d) <-done @@ -56,7 +56,7 @@ func TestEncodeDecodeString(t *testing.T) { t.Run("too long string", func(t *testing.T) { tooLong := string(append(uint16buf, 42)) //nolint: makezero var buf bytes.Buffer - assert.Error(encodeString(&buf, tooLong)) + require.Error(t, encodeString(&buf, tooLong)) assert.Zero(buf.Len(), "nothing should have been written to the stream") }) @@ -67,7 +67,7 @@ func TestEncodeDecodeString(t *testing.T) { buf.Write(make([]byte, 8)) // 8 bytes missing var d string - assert.Error(decodeString(&buf, &d)) + require.Error(t, decodeString(&buf, &d)) assert.Zero(buf.Len(), "buffer should be exhausted") }) } diff --git a/wire/perunio/wire_internal_test.go b/wire/perunio/wire_internal_test.go index 4d9640a46..e278f45f3 100644 --- a/wire/perunio/wire_internal_test.go +++ b/wire/perunio/wire_internal_test.go @@ -23,6 +23,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" polytest "polycry.pt/poly-go/test" ) @@ -103,7 +104,7 @@ func TestEncodeDecode(t *testing.T) { } } - a.Nil(Decode(r, d...), "failed to decode values") + require.NoError(t, Decode(r, d...), "failed to decode values") for i, v := range values { if !reflect.DeepEqual(reflect.ValueOf(d[i]).Elem().Interface(), v) { diff --git a/wire/protobuf/controlmsgs.go b/wire/protobuf/controlmsgs.go index f363b982f..5e5ca8ae2 100644 --- a/wire/protobuf/controlmsgs.go +++ b/wire/protobuf/controlmsgs.go @@ -46,24 +46,24 @@ func fromAuthResponseMsg(msg *wire.AuthResponseMsg) *Envelope_AuthResponseMsg { func toPingMsg(protoMsg *Envelope_PingMsg) (msg *wire.PingMsg) { msg = &wire.PingMsg{} - msg.Created = time.Unix(0, protoMsg.PingMsg.Created) + msg.Created = time.Unix(0, protoMsg.PingMsg.GetCreated()) return msg } func toPongMsg(protoEnvMsg *Envelope_PongMsg) (msg *wire.PongMsg) { msg = &wire.PongMsg{} - msg.Created = time.Unix(0, protoEnvMsg.PongMsg.Created) + msg.Created = time.Unix(0, protoEnvMsg.PongMsg.GetCreated()) return msg } func toShutdownMsg(protoEnvMsg *Envelope_ShutdownMsg) (msg *wire.ShutdownMsg) { msg = &wire.ShutdownMsg{} - msg.Reason = protoEnvMsg.ShutdownMsg.Reason + msg.Reason = protoEnvMsg.ShutdownMsg.GetReason() return msg } func toAuthResponseMsg(protoEnvMsg *Envelope_AuthResponseMsg) (msg *wire.AuthResponseMsg) { msg = &wire.AuthResponseMsg{} - msg.Signature = protoEnvMsg.AuthResponseMsg.Signature + msg.Signature = protoEnvMsg.AuthResponseMsg.GetSignature() return msg } diff --git a/wire/protobuf/proposalmsgs.go b/wire/protobuf/proposalmsgs.go index 980bcefdf..0dc274a1b 100644 --- a/wire/protobuf/proposalmsgs.go +++ b/wire/protobuf/proposalmsgs.go @@ -34,15 +34,15 @@ func ToLedgerChannelProposalMsg(protoEnvMsg *Envelope_LedgerChannelProposalMsg) protoMsg := protoEnvMsg.LedgerChannelProposalMsg msg = &client.LedgerChannelProposalMsg{} - msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.BaseChannelProposal) + msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.GetBaseChannelProposal()) if err != nil { return nil, err } - msg.Participant, err = ToWalletAddr(protoMsg.Participant) + msg.Participant, err = ToWalletAddr(protoMsg.GetParticipant()) if err != nil { return nil, errors.WithMessage(err, "participant address") } - msg.Peers, err = ToWireAddrs(protoMsg.Peers) + msg.Peers, err = ToWireAddrs(protoMsg.GetPeers()) return msg, errors.WithMessage(err, "peers") } @@ -51,8 +51,8 @@ func ToSubChannelProposalMsg(protoEnvMsg *Envelope_SubChannelProposalMsg) (msg * protoMsg := protoEnvMsg.SubChannelProposalMsg msg = &client.SubChannelProposalMsg{} - copy(msg.Parent[:], protoMsg.Parent) - msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.BaseChannelProposal) + copy(msg.Parent[:], protoMsg.GetParent()) + msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.GetBaseChannelProposal()) return msg, err } @@ -62,26 +62,26 @@ func ToVirtualChannelProposalMsg(protoEnvMsg *Envelope_VirtualChannelProposalMsg protoMsg := protoEnvMsg.VirtualChannelProposalMsg msg = &client.VirtualChannelProposalMsg{} - msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.BaseChannelProposal) + msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.GetBaseChannelProposal()) if err != nil { return nil, err } - msg.Proposer, err = ToWalletAddr(protoMsg.Proposer) + msg.Proposer, err = ToWalletAddr(protoMsg.GetProposer()) if err != nil { return nil, errors.WithMessage(err, "proposer") } - msg.Parents = make([]channel.ID, len(protoMsg.Parents)) - for i := range protoMsg.Parents { - copy(msg.Parents[i][:], protoMsg.Parents[i]) + msg.Parents = make([]channel.ID, len(protoMsg.GetParents())) + for i := range protoMsg.GetParents() { + copy(msg.Parents[i][:], protoMsg.GetParents()[i]) } - msg.IndexMaps = make([][]channel.Index, len(protoMsg.IndexMaps)) - for i := range protoMsg.IndexMaps { - msg.IndexMaps[i], err = ToIndexMap(protoMsg.IndexMaps[i].IndexMap) + msg.IndexMaps = make([][]channel.Index, len(protoMsg.GetIndexMaps())) + for i := range protoMsg.GetIndexMaps() { + msg.IndexMaps[i], err = ToIndexMap(protoMsg.GetIndexMaps()[i].GetIndexMap()) if err != nil { return nil, err } } - msg.Peers, err = ToWireAddrs(protoMsg.Peers) + msg.Peers, err = ToWireAddrs(protoMsg.GetPeers()) return msg, errors.WithMessage(err, "peers") } @@ -91,8 +91,8 @@ func ToLedgerChannelProposalAccMsg(protoEnvMsg *Envelope_LedgerChannelProposalAc protoMsg := protoEnvMsg.LedgerChannelProposalAccMsg msg = &client.LedgerChannelProposalAccMsg{} - msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.BaseChannelProposalAcc) - msg.Participant, err = ToWalletAddr(protoMsg.Participant) + msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.GetBaseChannelProposalAcc()) + msg.Participant, err = ToWalletAddr(protoMsg.GetParticipant()) return msg, errors.WithMessage(err, "participant") } @@ -102,7 +102,7 @@ func ToSubChannelProposalAccMsg(protoEnvMsg *Envelope_SubChannelProposalAccMsg) protoMsg := protoEnvMsg.SubChannelProposalAccMsg msg = &client.SubChannelProposalAccMsg{} - msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.BaseChannelProposalAcc) + msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.GetBaseChannelProposalAcc()) return msg } @@ -112,8 +112,8 @@ func ToVirtualChannelProposalAccMsg(protoEnvMsg *Envelope_VirtualChannelProposal protoMsg := protoEnvMsg.VirtualChannelProposalAccMsg msg = &client.VirtualChannelProposalAccMsg{} - msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.BaseChannelProposalAcc) - msg.Responder, err = ToWalletAddr(protoMsg.Responder) + msg.BaseChannelProposalAcc = ToBaseChannelProposalAcc(protoMsg.GetBaseChannelProposalAcc()) + msg.Responder, err = ToWalletAddr(protoMsg.GetResponder()) return msg, errors.WithMessage(err, "responder") } @@ -122,21 +122,21 @@ func ToChannelProposalRejMsg(protoEnvMsg *Envelope_ChannelProposalRejMsg) (msg * protoMsg := protoEnvMsg.ChannelProposalRejMsg msg = &client.ChannelProposalRejMsg{} - copy(msg.ProposalID[:], protoMsg.ProposalId) - msg.Reason = protoMsg.Reason + copy(msg.ProposalID[:], protoMsg.GetProposalId()) + msg.Reason = protoMsg.GetReason() return msg } // ToWalletAddr converts a protobuf wallet address to a wallet.Address. func ToWalletAddr(protoAddr *Address) (map[wallet.BackendID]wallet.Address, error) { addrMap := make(map[wallet.BackendID]wallet.Address) - for i := range protoAddr.AddressMapping { + for i := range protoAddr.GetAddressMapping() { var k int32 - if err := binary.Read(bytes.NewReader(protoAddr.AddressMapping[i].Key), binary.BigEndian, &k); err != nil { + if err := binary.Read(bytes.NewReader(protoAddr.GetAddressMapping()[i].GetKey()), binary.BigEndian, &k); err != nil { return nil, fmt.Errorf("failed to read key: %w", err) } addr := wallet.NewAddress(wallet.BackendID(k)) - if err := addr.UnmarshalBinary(protoAddr.AddressMapping[i].Address); err != nil { + if err := addr.UnmarshalBinary(protoAddr.GetAddressMapping()[i].GetAddress()); err != nil { return nil, fmt.Errorf("failed to unmarshal address for key %d: %w", k, err) } @@ -148,13 +148,13 @@ func ToWalletAddr(protoAddr *Address) (map[wallet.BackendID]wallet.Address, erro // ToWireAddr converts a protobuf wallet address to a wallet.Address. func ToWireAddr(protoAddr *Address) (map[wallet.BackendID]wire.Address, error) { addrMap := make(map[wallet.BackendID]wire.Address) - for i := range protoAddr.AddressMapping { + for i := range protoAddr.GetAddressMapping() { var k int32 - if err := binary.Read(bytes.NewReader(protoAddr.AddressMapping[i].Key), binary.BigEndian, &k); err != nil { + if err := binary.Read(bytes.NewReader(protoAddr.GetAddressMapping()[i].GetKey()), binary.BigEndian, &k); err != nil { return nil, fmt.Errorf("failed to read key: %w", err) } addr := wire.NewAddress() - if err := addr.UnmarshalBinary(protoAddr.AddressMapping[i].Address); err != nil { + if err := addr.UnmarshalBinary(protoAddr.GetAddressMapping()[i].GetAddress()); err != nil { return nil, fmt.Errorf("failed to unmarshal address for key %d: %w", k, err) } @@ -191,23 +191,23 @@ func ToWireAddrs(protoAddrs []*Address) ([]map[wallet.BackendID]wire.Address, er // ToBaseChannelProposal converts a protobuf BaseChannelProposal to a client BaseChannelProposal. func ToBaseChannelProposal(protoProp *BaseChannelProposal) (prop client.BaseChannelProposal, err error) { - prop.ChallengeDuration = protoProp.ChallengeDuration - copy(prop.ProposalID[:], protoProp.ProposalId) - copy(prop.NonceShare[:], protoProp.NonceShare) - prop.InitBals, err = ToAllocation(protoProp.InitBals) + prop.ChallengeDuration = protoProp.GetChallengeDuration() + copy(prop.ProposalID[:], protoProp.GetProposalId()) + copy(prop.NonceShare[:], protoProp.GetNonceShare()) + prop.InitBals, err = ToAllocation(protoProp.GetInitBals()) if err != nil { return prop, errors.WithMessage(err, "init bals") } - prop.FundingAgreement = ToBalances(protoProp.FundingAgreement) - prop.App, prop.InitData, err = ToAppAndData(protoProp.App, protoProp.InitData) - copy(prop.Aux[:], protoProp.Aux) + prop.FundingAgreement = ToBalances(protoProp.GetFundingAgreement()) + prop.App, prop.InitData, err = ToAppAndData(protoProp.GetApp(), protoProp.GetInitData()) + copy(prop.Aux[:], protoProp.GetAux()) return prop, err } // ToBaseChannelProposalAcc converts a protobuf BaseChannelProposalAcc to a client BaseChannelProposalAcc. func ToBaseChannelProposalAcc(protoPropAcc *BaseChannelProposalAcc) (propAcc client.BaseChannelProposalAcc) { - copy(propAcc.ProposalID[:], protoPropAcc.ProposalId) - copy(propAcc.NonceShare[:], protoPropAcc.NonceShare) + copy(propAcc.ProposalID[:], protoPropAcc.GetProposalId()) + copy(propAcc.NonceShare[:], protoPropAcc.GetNonceShare()) return } @@ -251,7 +251,7 @@ func ToIntSlice(backends [][]byte) ([]wallet.BackendID, error) { ints := make([]wallet.BackendID, len(backends)) for i, backend := range backends { - if len(backend) != 4 { //nolint:gomnd + if len(backend) != 4 { //nolint:mnd return nil, fmt.Errorf("backend %d length is not 4 bytes", i) } @@ -270,43 +270,43 @@ func ToIntSlice(backends [][]byte) ([]wallet.BackendID, error) { // ToAllocation converts a protobuf allocation to a channel.Allocation. func ToAllocation(protoAlloc *Allocation) (alloc *channel.Allocation, err error) { alloc = &channel.Allocation{} - alloc.Backends, err = ToIntSlice(protoAlloc.Backends) + alloc.Backends, err = ToIntSlice(protoAlloc.GetBackends()) if err != nil { return nil, errors.WithMessage(err, "backends") } - alloc.Assets = make([]channel.Asset, len(protoAlloc.Assets)) - for i := range protoAlloc.Assets { + alloc.Assets = make([]channel.Asset, len(protoAlloc.GetAssets())) + for i := range protoAlloc.GetAssets() { alloc.Assets[i] = channel.NewAsset(alloc.Backends[i]) - err = alloc.Assets[i].UnmarshalBinary(protoAlloc.Assets[i]) + err = alloc.Assets[i].UnmarshalBinary(protoAlloc.GetAssets()[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th asset", i) } } - alloc.Locked = make([]channel.SubAlloc, len(protoAlloc.Locked)) - for i := range protoAlloc.Locked { - alloc.Locked[i], err = ToSubAlloc(protoAlloc.Locked[i]) + alloc.Locked = make([]channel.SubAlloc, len(protoAlloc.GetLocked())) + for i := range protoAlloc.GetLocked() { + alloc.Locked[i], err = ToSubAlloc(protoAlloc.GetLocked()[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th sub alloc", i) } } - alloc.Balances = ToBalances(protoAlloc.Balances) + alloc.Balances = ToBalances(protoAlloc.GetBalances()) return alloc, nil } // ToBalances converts a protobuf Balances to a channel.Balances. func ToBalances(protoBalances *Balances) (balances channel.Balances) { - balances = make([][]channel.Bal, len(protoBalances.Balances)) - for i := range protoBalances.Balances { - balances[i] = ToBalance(protoBalances.Balances[i]) + balances = make([][]channel.Bal, len(protoBalances.GetBalances())) + for i := range protoBalances.GetBalances() { + balances[i] = ToBalance(protoBalances.GetBalances()[i]) } return balances } // ToBalance converts a protobuf Balance to a channel.Bal. func ToBalance(protoBalance *Balance) (balance []channel.Bal) { - balance = make([]channel.Bal, len(protoBalance.Balance)) - for j := range protoBalance.Balance { - balance[j] = new(big.Int).SetBytes(protoBalance.Balance[j]) + balance = make([]channel.Bal, len(protoBalance.GetBalance())) + for j := range protoBalance.GetBalance() { + balance[j] = new(big.Int).SetBytes(protoBalance.GetBalance()[j]) } return balance } @@ -314,12 +314,12 @@ func ToBalance(protoBalance *Balance) (balance []channel.Bal) { // ToSubAlloc converts a protobuf SubAlloc to a channel.SubAlloc. func ToSubAlloc(protoSubAlloc *SubAlloc) (subAlloc channel.SubAlloc, err error) { subAlloc = channel.SubAlloc{} - subAlloc.Bals = ToBalance(protoSubAlloc.Bals) - if len(protoSubAlloc.Id) != len(subAlloc.ID) { + subAlloc.Bals = ToBalance(protoSubAlloc.GetBals()) + if len(protoSubAlloc.GetId()) != len(subAlloc.ID) { return subAlloc, errors.New("sub alloc id has incorrect length") } - copy(subAlloc.ID[:], protoSubAlloc.Id) - subAlloc.IndexMap, err = ToIndexMap(protoSubAlloc.IndexMap.IndexMap) + copy(subAlloc.ID[:], protoSubAlloc.GetId()) + subAlloc.IndexMap, err = ToIndexMap(protoSubAlloc.GetIndexMap().GetIndexMap()) return subAlloc, err } @@ -328,9 +328,13 @@ func ToIndexMap(protoIndexMap []uint32) (indexMap []channel.Index, err error) { indexMap = make([]channel.Index, len(protoIndexMap)) for i := range protoIndexMap { if protoIndexMap[i] > math.MaxUint16 { - return nil, fmt.Errorf("%d'th index is invalid", i) //nolint:goerr113 // We do not want to define this as constant error. + return nil, fmt.Errorf("%d'th index is invalid", i) // We do not want to define this as constant error. } - indexMap[i] = channel.Index(uint16(protoIndexMap[i])) + val := protoIndexMap[i] + if val > math.MaxUint16 { + return indexMap, fmt.Errorf("index too large: %d", val) + } + indexMap[i] = channel.Index(uint16(val)) } return indexMap, nil } @@ -355,7 +359,7 @@ func FromLedgerChannelProposalMsg(msg *client.LedgerChannelProposalMsg) (_ *Enve func FromSubChannelProposalMsg(msg *client.SubChannelProposalMsg) (_ *Envelope_SubChannelProposalMsg, err error) { protoMsg := &SubChannelProposalMsg{} protoMsg.Parent = make([]byte, len(msg.Parent)) - copy(protoMsg.Parent, msg.Parent[:]) + copy(protoMsg.GetParent(), msg.Parent[:]) protoMsg.BaseChannelProposal, err = FromBaseChannelProposal(msg.BaseChannelProposal) return &Envelope_SubChannelProposalMsg{protoMsg}, err } @@ -375,7 +379,7 @@ func FromVirtualChannelProposalMsg(msg *client.VirtualChannelProposalMsg) (_ *En protoMsg.Parents = make([][]byte, len(msg.Parents)) for i := range msg.Parents { protoMsg.Parents[i] = make([]byte, len(msg.Parents[i])) - copy(protoMsg.Parents[i], msg.Parents[i][:]) + copy(protoMsg.GetParents()[i], msg.Parents[i][:]) } protoMsg.IndexMaps = make([]*IndexMap, len(msg.IndexMaps)) for i := range msg.IndexMaps { @@ -415,7 +419,7 @@ func FromVirtualChannelProposalAccMsg(msg *client.VirtualChannelProposalAccMsg) func FromChannelProposalRejMsg(msg *client.ChannelProposalRejMsg) (_ *Envelope_ChannelProposalRejMsg) { protoMsg := &ChannelProposalRejMsg{} protoMsg.ProposalId = make([]byte, len(msg.ProposalID)) - copy(protoMsg.ProposalId, msg.ProposalID[:]) + copy(protoMsg.GetProposalId(), msg.ProposalID[:]) protoMsg.Reason = msg.Reason return &Envelope_ChannelProposalRejMsg{protoMsg} } @@ -425,8 +429,12 @@ func FromWalletAddr(addr map[wallet.BackendID]wallet.Address) (*Address, error) var addressMappings []*AddressMapping //nolint:prealloc for key, address := range addr { - keyBytes := make([]byte, 4) //nolint:gomnd - binary.BigEndian.PutUint32(keyBytes, uint32(key)) + keyBytes := make([]byte, 4) //nolint:mnd + keyInt := int(key) + if keyInt < 0 || keyInt > math.MaxUint32 { + panic("Key exceeds uint32 range") + } + binary.BigEndian.PutUint32(keyBytes, uint32(keyInt)) addressBytes, err := address.MarshalBinary() if err != nil { @@ -449,8 +457,12 @@ func FromWireAddr(addr map[wallet.BackendID]wire.Address) (*Address, error) { var addressMappings []*AddressMapping //nolint:prealloc for key, address := range addr { - keyBytes := make([]byte, 4) //nolint:gomnd - binary.BigEndian.PutUint32(keyBytes, uint32(key)) + keyBytes := make([]byte, 4) //nolint:mnd + keyInt := int(key) + if keyInt < 0 || keyInt > math.MaxUint32 { + panic("Key exceeds uint32 range") + } + binary.BigEndian.PutUint32(keyBytes, uint32(keyInt)) addressBytes, err := address.MarshalBinary() if err != nil { @@ -497,13 +509,13 @@ func FromBaseChannelProposal(prop client.BaseChannelProposal) (protoProp *BaseCh protoProp = &BaseChannelProposal{} protoProp.ProposalId = make([]byte, len(prop.ProposalID)) - copy(protoProp.ProposalId, prop.ProposalID[:]) + copy(protoProp.GetProposalId(), prop.ProposalID[:]) protoProp.NonceShare = make([]byte, len(prop.NonceShare)) - copy(protoProp.NonceShare, prop.NonceShare[:]) + copy(protoProp.GetNonceShare(), prop.NonceShare[:]) protoProp.Aux = make([]byte, len(prop.Aux)) - copy(protoProp.Aux, prop.Aux[:]) + copy(protoProp.GetAux(), prop.Aux[:]) protoProp.ChallengeDuration = prop.ChallengeDuration @@ -524,8 +536,8 @@ func FromBaseChannelProposalAcc(propAcc client.BaseChannelProposalAcc) (protoPro protoPropAcc = &BaseChannelProposalAcc{} protoPropAcc.ProposalId = make([]byte, len(propAcc.ProposalID)) protoPropAcc.NonceShare = make([]byte, len(propAcc.NonceShare)) - copy(protoPropAcc.ProposalId, propAcc.ProposalID[:]) - copy(protoPropAcc.NonceShare, propAcc.NonceShare[:]) + copy(protoPropAcc.GetProposalId(), propAcc.ProposalID[:]) + copy(protoPropAcc.GetNonceShare(), propAcc.NonceShare[:]) return protoPropAcc } @@ -556,8 +568,12 @@ func FromAllocation(alloc channel.Allocation) (protoAlloc *Allocation, err error protoAlloc = &Allocation{} protoAlloc.Backends = make([][]byte, len(alloc.Backends)) for i := range alloc.Backends { - protoAlloc.Backends[i] = make([]byte, 4) //nolint:gomnd - binary.BigEndian.PutUint32(protoAlloc.Backends[i], uint32(alloc.Backends[i])) + protoAlloc.Backends[i] = make([]byte, 4) //nolint:mnd + id := int(alloc.Backends[i]) + if id < 0 || id > math.MaxUint32 { + panic("BackendID exceeds uint32 range") + } + binary.BigEndian.PutUint32(protoAlloc.GetBackends()[i], uint32(id)) } protoAlloc.Assets = make([][]byte, len(alloc.Assets)) for i := range alloc.Assets { @@ -598,10 +614,10 @@ func FromBalance(balance []channel.Bal) (protoBalance *Balance, err error) { } for i := range balance { if balance[i] == nil { - return nil, fmt.Errorf("%d'th amount is nil", i) //nolint:goerr113 // We do not want to define this as constant error. + return nil, fmt.Errorf("%d'th amount is nil", i) // We do not want to define this as constant error. } if balance[i].Sign() == -1 { - return nil, fmt.Errorf("%d'th amount is negative", i) //nolint:goerr113 // We do not want to define this as constant error. + return nil, fmt.Errorf("%d'th amount is negative", i) // We do not want to define this as constant error. } protoBalance.Balance[i] = balance[i].Bytes() } @@ -612,7 +628,7 @@ func FromBalance(balance []channel.Bal) (protoBalance *Balance, err error) { func FromSubAlloc(subAlloc channel.SubAlloc) (protoSubAlloc *SubAlloc, err error) { protoSubAlloc = &SubAlloc{} protoSubAlloc.Id = make([]byte, len(subAlloc.ID)) - copy(protoSubAlloc.Id, subAlloc.ID[:]) + copy(protoSubAlloc.GetId(), subAlloc.ID[:]) protoSubAlloc.IndexMap = &IndexMap{IndexMap: FromIndexMap(subAlloc.IndexMap)} protoSubAlloc.Bals, err = FromBalance(subAlloc.Bals) return protoSubAlloc, err diff --git a/wire/protobuf/serializer.go b/wire/protobuf/serializer.go index 9d13d2351..8d26fbf55 100644 --- a/wire/protobuf/serializer.go +++ b/wire/protobuf/serializer.go @@ -18,6 +18,7 @@ import ( "encoding/binary" "fmt" "io" + "math" "perun.network/go-perun/wallet" @@ -75,7 +76,6 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) (err error) { //nolint case *client.ChannelSyncMsg: protoEnv.Msg, err = fromChannelSyncMsg(msg) default: - //nolint: goerr113 // We do not want to define this as constant error. err = fmt.Errorf("unknown message type: %T", msg) } if err != nil { @@ -104,7 +104,11 @@ func writeEnvelope(w io.Writer, env *Envelope) error { if err != nil { return errors.Wrap(err, "marshalling envelope") } - if err := binary.Write(w, binary.BigEndian, uint16(len(data))); err != nil { + l := len(data) + if l > math.MaxUint16 { + return fmt.Errorf("envelope length out of bounds: %d", l) + } + if err := binary.Write(w, binary.BigEndian, uint16(l)); err != nil { return errors.Wrap(err, "writing length to wire") } _, err = w.Write(data) @@ -127,7 +131,7 @@ func (serializer) Decode(r io.Reader) (env *wire.Envelope, err error) { //nolint return nil, err } - switch protoMsg := protoEnv.Msg.(type) { + switch protoMsg := protoEnv.GetMsg().(type) { case *Envelope_PingMsg: env.Msg = toPingMsg(protoMsg) case *Envelope_PongMsg: @@ -163,7 +167,6 @@ func (serializer) Decode(r io.Reader) (env *wire.Envelope, err error) { //nolint case *Envelope_ChannelSyncMsg: env.Msg, err = toChannelSyncMsg(protoMsg) default: - //nolint: goerr113 // We do not want to define this as constant error. err = fmt.Errorf("unknown message type: %T", protoMsg) } @@ -184,13 +187,13 @@ func readEnvelope(r io.Reader) (*Envelope, error) { } func unmarshalSenderRecipient(protoEnv *Envelope) (map[wallet.BackendID]wire.Address, map[wallet.BackendID]wire.Address, error) { - sender, err := ToWireAddr(protoEnv.Sender) + sender, err := ToWireAddr(protoEnv.GetSender()) if err != nil { return nil, nil, errors.Wrap(err, "unmarshalling sender address") } - recipient, err1 := ToWireAddr(protoEnv.Recipient) + recipient, err1 := ToWireAddr(protoEnv.GetRecipient()) if err1 != nil { - return nil, nil, errors.Wrap(err, "unmarshalling recipient address") + return nil, nil, errors.Wrap(err1, "unmarshalling recipient address") } return sender, recipient, nil } diff --git a/wire/protobuf/syncmsgs.go b/wire/protobuf/syncmsgs.go index fb94a5b19..ee172ab8b 100644 --- a/wire/protobuf/syncmsgs.go +++ b/wire/protobuf/syncmsgs.go @@ -15,6 +15,9 @@ package protobuf import ( + "fmt" + "math" + "perun.network/go-perun/channel" "perun.network/go-perun/client" ) @@ -23,13 +26,17 @@ func toChannelSyncMsg(protoEnvMsg *Envelope_ChannelSyncMsg) (msg *client.Channel protoMsg := protoEnvMsg.ChannelSyncMsg msg = &client.ChannelSyncMsg{} - msg.Phase = channel.Phase(protoMsg.Phase) - msg.CurrentTX.Sigs = make([][]byte, len(protoMsg.CurrentTx.Sigs)) - for i := range protoMsg.CurrentTx.Sigs { - msg.CurrentTX.Sigs[i] = make([]byte, len(protoMsg.CurrentTx.Sigs[i])) - copy(msg.CurrentTX.Sigs[i], protoMsg.CurrentTx.Sigs[i]) + phase := protoMsg.GetPhase() + if phase > math.MaxUint8 { + return msg, fmt.Errorf("invalid phase: %d", phase) + } + msg.Phase = channel.Phase(phase) + msg.CurrentTX.Sigs = make([][]byte, len(protoMsg.GetCurrentTx().GetSigs())) + for i := range protoMsg.GetCurrentTx().GetSigs() { + msg.CurrentTX.Sigs[i] = make([]byte, len(protoMsg.GetCurrentTx().GetSigs()[i])) + copy(msg.CurrentTX.Sigs[i], protoMsg.GetCurrentTx().GetSigs()[i]) } - msg.CurrentTX.State, err = ToState(protoMsg.CurrentTx.State) + msg.CurrentTX.State, err = ToState(protoMsg.GetCurrentTx().GetState()) return msg, err } @@ -41,7 +48,7 @@ func fromChannelSyncMsg(msg *client.ChannelSyncMsg) (_ *Envelope_ChannelSyncMsg, protoMsg.CurrentTx.Sigs = make([][]byte, len(msg.CurrentTX.Sigs)) for i := range msg.CurrentTX.Sigs { protoMsg.CurrentTx.Sigs[i] = make([]byte, len(msg.CurrentTX.Sigs[i])) - copy(protoMsg.CurrentTx.Sigs[i], msg.CurrentTX.Sigs[i]) + copy(protoMsg.GetCurrentTx().GetSigs()[i], msg.CurrentTX.Sigs[i]) } protoMsg.CurrentTx.State, err = FromState(msg.CurrentTX.State) return &Envelope_ChannelSyncMsg{protoMsg}, err diff --git a/wire/protobuf/test/serializertest.go b/wire/protobuf/test/serializertest.go index 7488d177b..172362c12 100644 --- a/wire/protobuf/test/serializertest.go +++ b/wire/protobuf/test/serializertest.go @@ -42,7 +42,7 @@ func MsgSerializerTest(t *testing.T, msg wire.Msg) { gotEnvelope, err := ser.Decode(&buff) require.NoError(t, err) - assert.EqualValues(t, envelope, gotEnvelope) + assert.Equal(t, envelope, gotEnvelope) } func newEnvelope(rng *rand.Rand) *wire.Envelope { diff --git a/wire/protobuf/updatemsgs.go b/wire/protobuf/updatemsgs.go index 71560f0f0..62fdf8b24 100644 --- a/wire/protobuf/updatemsgs.go +++ b/wire/protobuf/updatemsgs.go @@ -15,6 +15,7 @@ package protobuf import ( + "fmt" "math" "math/big" @@ -38,15 +39,15 @@ func ToVirtualChannelFundingProposalMsg(protoEnvMsg *Envelope_VirtualChannelFund protoMsg := protoEnvMsg.VirtualChannelFundingProposalMsg msg = &client.VirtualChannelFundingProposalMsg{} - msg.Initial, err = ToSignedState(protoMsg.Initial) + msg.Initial, err = ToSignedState(protoMsg.GetInitial()) if err != nil { return nil, errors.WithMessage(err, "initial state") } - msg.IndexMap, err = ToIndexMap(protoMsg.IndexMap.IndexMap) + msg.IndexMap, err = ToIndexMap(protoMsg.GetIndexMap().GetIndexMap()) if err != nil { return nil, err } - msg.ChannelUpdateMsg, err = ToChannelUpdate(protoMsg.ChannelUpdateMsg) + msg.ChannelUpdateMsg, err = ToChannelUpdate(protoMsg.GetChannelUpdateMsg()) return msg, err } @@ -59,11 +60,11 @@ func ToVirtualChannelSettlementProposalMsg(protoEnvMsg *Envelope_VirtualChannelS protoMsg := protoEnvMsg.VirtualChannelSettlementProposalMsg msg = &client.VirtualChannelSettlementProposalMsg{} - msg.Final, err = ToSignedState(protoMsg.Final) + msg.Final, err = ToSignedState(protoMsg.GetFinal()) if err != nil { return nil, errors.WithMessage(err, "final state") } - msg.ChannelUpdateMsg, err = ToChannelUpdate(protoMsg.ChannelUpdateMsg) + msg.ChannelUpdateMsg, err = ToChannelUpdate(protoMsg.GetChannelUpdateMsg()) return msg, err } @@ -72,10 +73,10 @@ func ToChannelUpdateAccMsg(protoEnvMsg *Envelope_ChannelUpdateAccMsg) (msg *clie protoMsg := protoEnvMsg.ChannelUpdateAccMsg msg = &client.ChannelUpdateAccMsg{} - copy(msg.ChannelID[:], protoMsg.ChannelId) - msg.Version = protoMsg.Version - msg.Sig = make([]byte, len(protoMsg.Sig)) - copy(msg.Sig, protoMsg.Sig) + copy(msg.ChannelID[:], protoMsg.GetChannelId()) + msg.Version = protoMsg.GetVersion() + msg.Sig = make([]byte, len(protoMsg.GetSig())) + copy(msg.Sig, protoMsg.GetSig()) return msg } @@ -84,59 +85,64 @@ func ToChannelUpdateRejMsg(protoEnvMsg *Envelope_ChannelUpdateRejMsg) (msg *clie protoMsg := protoEnvMsg.ChannelUpdateRejMsg msg = &client.ChannelUpdateRejMsg{} - copy(msg.ChannelID[:], protoMsg.ChannelId) - msg.Version = protoMsg.Version - msg.Reason = protoMsg.Reason + copy(msg.ChannelID[:], protoMsg.GetChannelId()) + msg.Version = protoMsg.GetVersion() + msg.Reason = protoMsg.GetReason() return msg } // ToChannelUpdate parses protobuf channel updates. func ToChannelUpdate(protoUpdate *ChannelUpdateMsg) (update client.ChannelUpdateMsg, err error) { - if protoUpdate.ChannelUpdate.ActorIdx > math.MaxUint16 { + if protoUpdate.GetChannelUpdate().GetActorIdx() > math.MaxUint16 { return update, errors.New("actor index is invalid") } - update.ActorIdx = channel.Index(protoUpdate.ChannelUpdate.ActorIdx) - update.Sig = make([]byte, len(protoUpdate.Sig)) - copy(update.Sig, protoUpdate.Sig) - update.State, err = ToState(protoUpdate.ChannelUpdate.State) + idx := protoUpdate.GetChannelUpdate().GetActorIdx() + if idx > math.MaxUint16 { + return update, fmt.Errorf("invalid actor index: %d", idx) + } + update.ActorIdx = channel.Index(uint16(idx)) + + update.Sig = make([]byte, len(protoUpdate.GetSig())) + copy(update.Sig, protoUpdate.GetSig()) + update.State, err = ToState(protoUpdate.GetChannelUpdate().GetState()) return update, err } // ToSignedState converts a protobuf SignedState to a channel.SignedState. func ToSignedState(protoSignedState *SignedState) (signedState channel.SignedState, err error) { - signedState.Params, err = ToParams(protoSignedState.Params) + signedState.Params, err = ToParams(protoSignedState.GetParams()) if err != nil { return signedState, err } - signedState.Sigs = make([][]byte, len(protoSignedState.Sigs)) - for i := range protoSignedState.Sigs { - signedState.Sigs[i] = make([]byte, len(protoSignedState.Sigs[i])) - copy(signedState.Sigs[i], protoSignedState.Sigs[i]) + signedState.Sigs = make([][]byte, len(protoSignedState.GetSigs())) + for i := range protoSignedState.GetSigs() { + signedState.Sigs[i] = make([]byte, len(protoSignedState.GetSigs()[i])) + copy(signedState.Sigs[i], protoSignedState.GetSigs()[i]) } - signedState.State, err = ToState(protoSignedState.State) + signedState.State, err = ToState(protoSignedState.GetState()) return signedState, err } // ToParams converts a protobuf Params to a channel.Params. func ToParams(protoParams *Params) (*channel.Params, error) { - app, err := ToApp(protoParams.App) + app, err := ToApp(protoParams.GetApp()) if err != nil { return nil, err } - parts, err := ToWalletAddrs(protoParams.Parts) + parts, err := ToWalletAddrs(protoParams.GetParts()) if err != nil { return nil, errors.WithMessage(err, "parts") } var aux channel.Aux - copy(aux[:], protoParams.Aux) + copy(aux[:], protoParams.GetAux()) params := channel.NewParamsUnsafe( - protoParams.ChallengeDuration, + protoParams.GetChallengeDuration(), parts, app, - (new(big.Int)).SetBytes(protoParams.Nonce), - protoParams.LedgerChannel, - protoParams.VirtualChannel, + (new(big.Int)).SetBytes(protoParams.GetNonce()), + protoParams.GetLedgerChannel(), + protoParams.GetVirtualChannel(), aux, ) @@ -146,15 +152,15 @@ func ToParams(protoParams *Params) (*channel.Params, error) { // ToState converts a protobuf State to a channel.State. func ToState(protoState *State) (state *channel.State, err error) { state = &channel.State{} - copy(state.ID[:], protoState.Id) - state.Version = protoState.Version - state.IsFinal = protoState.IsFinal - allocation, err := ToAllocation(protoState.Allocation) + copy(state.ID[:], protoState.GetId()) + state.Version = protoState.GetVersion() + state.IsFinal = protoState.GetIsFinal() + allocation, err := ToAllocation(protoState.GetAllocation()) if err != nil { return nil, errors.WithMessage(err, "allocation") } state.Allocation = *allocation - state.App, state.Data, err = ToAppAndData(protoState.App, protoState.Data) + state.App, state.Data, err = ToAppAndData(protoState.GetApp(), protoState.GetData()) return state, err } @@ -202,9 +208,9 @@ func FromChannelUpdateAccMsg(msg *client.ChannelUpdateAccMsg) *Envelope_ChannelU protoMsg := &ChannelUpdateAccMsg{} protoMsg.ChannelId = make([]byte, len(msg.ChannelID)) - copy(protoMsg.ChannelId, msg.ChannelID[:]) + copy(protoMsg.GetChannelId(), msg.ChannelID[:]) protoMsg.Sig = make([]byte, len(msg.Sig)) - copy(protoMsg.Sig, msg.Sig) + copy(protoMsg.GetSig(), msg.Sig) protoMsg.Version = msg.Version return &Envelope_ChannelUpdateAccMsg{protoMsg} } @@ -213,7 +219,7 @@ func FromChannelUpdateAccMsg(msg *client.ChannelUpdateAccMsg) *Envelope_ChannelU func FromChannelUpdateRejMsg(msg *client.ChannelUpdateRejMsg) *Envelope_ChannelUpdateRejMsg { protoMsg := &ChannelUpdateRejMsg{} protoMsg.ChannelId = make([]byte, len(msg.ChannelID)) - copy(protoMsg.ChannelId, msg.ChannelID[:]) + copy(protoMsg.GetChannelId(), msg.ChannelID[:]) protoMsg.Version = msg.Version protoMsg.Reason = msg.Reason return &Envelope_ChannelUpdateRejMsg{protoMsg} @@ -224,10 +230,10 @@ func FromChannelUpdate(update *client.ChannelUpdateMsg) (protoUpdate *ChannelUpd protoUpdate = &ChannelUpdateMsg{} protoUpdate.ChannelUpdate = &ChannelUpdate{} - protoUpdate.ChannelUpdate.ActorIdx = uint32(update.ChannelUpdate.ActorIdx) + protoUpdate.ChannelUpdate.ActorIdx = uint32(update.ActorIdx) protoUpdate.Sig = make([]byte, len(update.Sig)) - copy(protoUpdate.Sig, update.Sig) - protoUpdate.ChannelUpdate.State, err = FromState(update.ChannelUpdate.State) + copy(protoUpdate.GetSig(), update.Sig) + protoUpdate.ChannelUpdate.State, err = FromState(update.State) return protoUpdate, err } @@ -237,7 +243,7 @@ func FromSignedState(signedState *channel.SignedState) (protoSignedState *Signed protoSignedState.Sigs = make([][]byte, len(signedState.Sigs)) for i := range signedState.Sigs { protoSignedState.Sigs[i] = make([]byte, len(signedState.Sigs[i])) - copy(protoSignedState.Sigs[i], signedState.Sigs[i]) + copy(protoSignedState.GetSigs()[i], signedState.Sigs[i]) } protoSignedState.Params, err = FromParams(signedState.Params) if err != nil { @@ -269,7 +275,7 @@ func FromState(state *channel.State) (protoState *State, err error) { protoState = &State{} protoState.Id = make([]byte, len(state.ID)) - copy(protoState.Id, state.ID[:]) + copy(protoState.GetId(), state.ID[:]) protoState.Version = state.Version protoState.IsFinal = state.IsFinal protoState.Allocation, err = FromAllocation(state.Allocation) diff --git a/wire/receiver.go b/wire/receiver.go index 1f4b6af7e..dac5ae904 100644 --- a/wire/receiver.go +++ b/wire/receiver.go @@ -40,6 +40,13 @@ type Receiver struct { sync.Closer } +// NewReceiver creates a new receiver. +func NewReceiver() *Receiver { + return &Receiver{ + msgs: make(chan *Envelope, receiverBufferSize), + } +} + // Next returns a channel to the next message. func (r *Receiver) Next(ctx context.Context) (*Envelope, error) { select { @@ -67,10 +74,3 @@ func (r *Receiver) Put(e *Envelope) { case <-r.Closed(): } } - -// NewReceiver creates a new receiver. -func NewReceiver() *Receiver { - return &Receiver{ - msgs: make(chan *Envelope, receiverBufferSize), - } -} diff --git a/wire/receiver_internal_test.go b/wire/receiver_internal_test.go index acf712453..b9c8c5359 100644 --- a/wire/receiver_internal_test.go +++ b/wire/receiver_internal_test.go @@ -20,6 +20,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ctxtest "polycry.pt/poly-go/context/test" ) @@ -32,7 +33,7 @@ func TestReceiver_Close(t *testing.T) { t.Parallel() r := NewReceiver() - assert.NoError(t, r.Close()) + require.NoError(t, r.Close()) assert.Error(t, r.Close()) } @@ -46,7 +47,7 @@ func TestReceiver_Next(t *testing.T) { r := NewReceiver() go r.Put(e) re, err := r.Next(context.Background()) - assert.NoError(t, err) + require.NoError(t, err) assert.Same(t, e, re) }) }) diff --git a/wire/relay.go b/wire/relay.go index 9e5afe918..881fc5711 100644 --- a/wire/relay.go +++ b/wire/relay.go @@ -124,33 +124,6 @@ func (p *Relay) Subscribe(c Consumer, predicate Predicate) error { return nil } -func (p *Relay) delete(c Consumer) { - p.mutex.Lock() - defer p.mutex.Unlock() - - if p.IsClosed() { - return - } - - for i, sub := range p.consumers { - if sub.consumer == c { - p.consumers[i] = p.consumers[len(p.consumers)-1] - p.consumers[len(p.consumers)-1] = subscription{} // For the GC. - p.consumers = p.consumers[:len(p.consumers)-1] - - return - } - } - log.Panic("deleted consumer that was not subscribed") -} - -func (p *Relay) isEmpty() bool { - p.mutex.RLock() - defer p.mutex.RUnlock() - - return len(p.consumers) == 0 -} - // Put puts an Envelope in the relay. func (p *Relay) Put(e *Envelope) { p.mutex.RLock() @@ -175,12 +148,6 @@ func (p *Relay) Put(e *Envelope) { } } -func logUnhandledMsg(e *Envelope) { - log.WithField("sender", e.Sender). - WithField("recipient", e.Recipient). - Debugf("Received %T message without subscription: %v", e.Msg, e.Msg) -} - // SetDefaultMsgHandler sets the default message handler. func (p *Relay) SetDefaultMsgHandler(handler func(*Envelope)) { if handler == nil { @@ -190,3 +157,36 @@ func (p *Relay) SetDefaultMsgHandler(handler func(*Envelope)) { defer p.mutex.Unlock() p.defaultMsgHandler = handler } + +func (p *Relay) delete(c Consumer) { + p.mutex.Lock() + defer p.mutex.Unlock() + + if p.IsClosed() { + return + } + + for i, sub := range p.consumers { + if sub.consumer == c { + p.consumers[i] = p.consumers[len(p.consumers)-1] + p.consumers[len(p.consumers)-1] = subscription{} // For the GC. + p.consumers = p.consumers[:len(p.consumers)-1] + + return + } + } + log.Panic("deleted consumer that was not subscribed") +} + +func (p *Relay) isEmpty() bool { + p.mutex.RLock() + defer p.mutex.RUnlock() + + return len(p.consumers) == 0 +} + +func logUnhandledMsg(e *Envelope) { + log.WithField("sender", e.Sender). + WithField("recipient", e.Recipient). + Debugf("Received %T message without subscription: %v", e.Msg, e.Msg) +} diff --git a/wire/relay_internal_test.go b/wire/relay_internal_test.go index 3e6fcd6ae..63fbfdd98 100644 --- a/wire/relay_internal_test.go +++ b/wire/relay_internal_test.go @@ -36,13 +36,13 @@ func TestProducer(t *testing.T) { pred := func(*Envelope) bool { return true } assert.True(t, p.isEmpty()) - assert.NoError(t, p.Subscribe(r0, pred)) + require.NoError(t, p.Subscribe(r0, pred)) assert.False(t, p.isEmpty()) - assert.NoError(t, p.Subscribe(r1, pred)) - assert.NoError(t, p.Subscribe(r2, pred)) - assert.Equal(t, len(p.consumers), 3) + require.NoError(t, p.Subscribe(r1, pred)) + require.NoError(t, p.Subscribe(r2, pred)) + assert.Len(t, p.consumers, 3) p.delete(r0) - assert.Equal(t, len(p.consumers), 2) + assert.Len(t, p.consumers, 2) assert.False(t, p.isEmpty()) assert.Panics(t, func() { p.delete(r0) }) } @@ -62,10 +62,10 @@ func TestProducer_SetDefaultMsgHandler(t *testing.T) { func TestProducer_Close(t *testing.T) { p := NewRelay() - assert.NoError(t, p.Close()) + require.NoError(t, p.Close()) err := p.Close() - assert.Error(t, err) + require.Error(t, err) assert.True(t, sync.IsAlreadyClosedError(err)) assert.NotPanics(t, func() { p.delete(nil) }, @@ -83,7 +83,7 @@ func TestProducer_Subscribe(t *testing.T) { t.Run("duplicate", func(t *testing.T) { p := NewRelay() r := NewReceiver() - assert.NoError(t, p.Subscribe(r, fn)) + require.NoError(t, p.Subscribe(r, fn)) assert.Panics(t, func() { p.Subscribe(r, fn) }) //nolint:errcheck }) @@ -120,7 +120,7 @@ func TestProducer_caching(t *testing.T) { prod.Put(ping0) assert.Equal(1, prod.cache.Size()) - assert.Len(unhandlesMsg, 0) + assert.Empty(unhandlesMsg) prod.Put(pong1) assert.Equal(1, prod.cache.Size()) @@ -135,7 +135,7 @@ func TestProducer_caching(t *testing.T) { prod.Subscribe(rec, isPing) //nolint:errcheck ctxtest.AssertTerminates(t, timeout, func() { e, err := rec.Next(ctx) - assert.NoError(err) + require.NoError(t, err) assert.Same(e, ping0) }) assert.Equal(1, prod.cache.Size()) diff --git a/wire/relay_test.go b/wire/relay_test.go index 5f33ff090..a66a52707 100644 --- a/wire/relay_test.go +++ b/wire/relay_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire" wiretest "perun.network/go-perun/wire/test" "polycry.pt/poly-go/test" @@ -27,7 +28,7 @@ func TestProducer_produce_closed(t *testing.T) { var missed *wire.Envelope p := wire.NewRelay() p.SetDefaultMsgHandler(func(e *wire.Envelope) { missed = e }) - assert.NoError(t, p.Close()) + require.NoError(t, p.Close()) rng := test.Prng(t) a := wiretest.NewRandomAddress(rng) b := wiretest.NewRandomAddress(rng) diff --git a/wire/test/address.go b/wire/test/address.go index 6df58e200..6037639de 100644 --- a/wire/test/address.go +++ b/wire/test/address.go @@ -36,8 +36,8 @@ func TestAddressImplementation(t *testing.T, newAddress wire.NewAddressFunc, new // Test Address.MarshalBinary and UnmarshalBinary. data, err := addr.MarshalBinary() - assert.NoError(err) - assert.NoError(addr.UnmarshalBinary(data), "Byte deserialization of address should work") + require.NoError(err) + require.NoError(addr.UnmarshalBinary(data), "Byte deserialization of address should work") // Test Address.Equals. null := newAddress() @@ -51,9 +51,9 @@ func TestAddressImplementation(t *testing.T, newAddress wire.NewAddressFunc, new // Test Address.Bytes. addrBytes, err := addr.MarshalBinary() - assert.NoError(err, "Marshaling address should not error") + require.NoError(err, "Marshaling address should not error") nullBytes, err := null.MarshalBinary() - assert.NoError(err, "Marshaling zero address should not error") + require.NoError(err, "Marshaling zero address should not error") assert.False(bytes.Equal(addrBytes, nullBytes), "Expected inequality of byte representations of nonzero and zero address") // a.Equal(Decode(Encode(a))) diff --git a/wire/test/bustest.go b/wire/test/bustest.go index 2f871d3f1..6403fa1de 100644 --- a/wire/test/bustest.go +++ b/wire/test/bustest.go @@ -21,6 +21,7 @@ import ( "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "perun.network/go-perun/wire" @@ -48,7 +49,7 @@ func GenericBusTest(t *testing.T, ) { t.Helper() require.Greater(t, numClients, 1) - require.Greater(t, numMsgs, 0) + require.Positive(t, numMsgs) rng := test.Prng(t) type Client struct { @@ -72,14 +73,13 @@ func GenericBusTest(t *testing.T, ctx, cancel := context.WithTimeout(context.Background(), testNoReceiveTimeout) defer cancel() for i := range clients { - i := i go ct.StageN("receive timeout", numClients, func(t test.ConcT) { r := wire.NewReceiver() defer r.Close() err := clients[i].r.Subscribe(r, func(e *wire.Envelope) bool { return true }) - require.NoError(t, err) + assert.NoError(t, err) _, err = r.Next(ctx) - require.Error(t, err) + assert.Error(t, err) }) } ct.Wait("receive timeout") @@ -98,7 +98,6 @@ func GenericBusTest(t *testing.T, if sender == recipient { continue } - sender, recipient := sender, recipient origEnv := &wire.Envelope{ Sender: wire.AddressMapfromAccountMap(clients[sender].id), Recipient: wire.AddressMapfromAccountMap(clients[recipient].id), @@ -113,16 +112,16 @@ func GenericBusTest(t *testing.T, go ct.StageN("receive", numClients*(numClients-1), func(t test.ConcT) { defer recv.Close() - for i := 0; i < numMsgs; i++ { + for range numMsgs { e, err := recv.Next(ctx) - require.NoError(t, err) - require.Equal(t, e, origEnv) + assert.NoError(t, err) + assert.Equal(t, e, origEnv) } }) go ct.StageN("publish", numClients*(numClients-1), func(t test.ConcT) { - for i := 0; i < numMsgs; i++ { + for range numMsgs { err := clients[sender].pub.Publish(ctx, origEnv) - require.NoError(t, err) + assert.NoError(t, err) } }) } From 4eb64e326ed147ca0c9872eb07e0f3acc7f94b39 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 28 Apr 2025 11:47:09 +0200 Subject: [PATCH 12/26] feat(wire/net/libp2p): add initial package libp2p Signed-off-by: Minh Huy Tran --- wire/net/libp2p/doc.go | 18 ++++++++++++++++++ wire/net/libp2p/init.go | 15 +++++++++++++++ wire/net/libp2p/init_test.go | 15 +++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 wire/net/libp2p/doc.go create mode 100644 wire/net/libp2p/init.go create mode 100644 wire/net/libp2p/init_test.go diff --git a/wire/net/libp2p/doc.go b/wire/net/libp2p/doc.go new file mode 100644 index 000000000..4372a745b --- /dev/null +++ b/wire/net/libp2p/doc.go @@ -0,0 +1,18 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package libp2p contains an implementation for the wire.Dialer and +// wire.Listener interfaces, which ultilize Libp2p technologies. +// It uses Libp2p to ensure secure peer connection. +package libp2p // import "perun.network/go-perun/wire/net/libp2p" diff --git a/wire/net/libp2p/init.go b/wire/net/libp2p/init.go new file mode 100644 index 000000000..13c739cba --- /dev/null +++ b/wire/net/libp2p/init.go @@ -0,0 +1,15 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p diff --git a/wire/net/libp2p/init_test.go b/wire/net/libp2p/init_test.go new file mode 100644 index 000000000..9633498e7 --- /dev/null +++ b/wire/net/libp2p/init_test.go @@ -0,0 +1,15 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p_test From fddc7a9c4682c70c5e617cb40bd914ec9db85418 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 28 Apr 2025 12:32:40 +0200 Subject: [PATCH 13/26] feat(go.mod): update go dependencies Signed-off-by: Minh Huy Tran --- go.mod | 47 +++++++++---- go.sum | 116 ++++++++++++++++++-------------- wire/net/libp2p/account.go | 35 ++++++++++ wire/net/libp2p/account_test.go | 15 +++++ wire/net/libp2p/address.go | 22 ++++++ wire/net/libp2p/address_test.go | 15 +++++ 6 files changed, 188 insertions(+), 62 deletions(-) create mode 100644 wire/net/libp2p/account.go create mode 100644 wire/net/libp2p/account_test.go create mode 100644 wire/net/libp2p/address.go create mode 100644 wire/net/libp2p/address_test.go diff --git a/go.mod b/go.mod index 0222f044b..bf9c06631 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,39 @@ module perun.network/go-perun -go 1.22 +go 1.23.0 + +toolchain go1.23.4 require ( + github.com/libp2p/go-libp2p v0.41.1 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 - go.uber.org/goleak v1.1.11 - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/protobuf v1.32.0 - polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.10.0 + go.uber.org/goleak v1.3.0 + golang.org/x/crypto v0.37.0 + golang.org/x/sync v0.13.0 + google.golang.org/protobuf v1.36.6 + polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 +) + +require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/libp2p/go-buffer-pool v0.1.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 v0.15.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.6.0 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect + lukechampine.com/blake3 v1.4.0 // indirect ) require ( @@ -19,12 +42,10 @@ require ( github.com/google/uuid v1.6.0 github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect - golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect - golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect + golang.org/x/sys v0.32.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 021d88d67..e7764d134 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,10 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -17,19 +21,48 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/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/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +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-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQZE= +github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= +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/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.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.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.15.0 h1:zB/HeaI/apcZiTDwhY5YqMvNVl/oQYvs3XySU+qeAVo= +github.com/multiformats/go-multiaddr v0.15.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +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.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.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA= +github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg= +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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= @@ -41,70 +74,53 @@ 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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -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-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -114,10 +130,9 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ 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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -127,7 +142,10 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b h1:BJsSrLQ3kLRNYXNqly//IYeXlVmAhpI5wYbg2WD1wR0= -polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= +lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= +polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 h1:iA5GzEa/hHfVlQpimEjPV09NATwHXxSjWNB0VVodtew= +polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU= diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go new file mode 100644 index 000000000..73a23b531 --- /dev/null +++ b/wire/net/libp2p/account.go @@ -0,0 +1,35 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/host" +) + +const ( + relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm" + + queryProtocol = "/address-book/query/1.0.0" // Protocol for querying the relay-server for a peerID. + registerProtocol = "/address-book/register/1.0.0" // Protocol for registering an on-chain address with the relay-server. + removeProtocol = "/address-book/remove/1.0.0" // Protocol for deregistering an on-chain address with the relay-server. +) + +// Account represents a libp2p wire account containting a libp2p host. +type Account struct { + host.Host + relayAddr string + privateKey crypto.PrivKey +} diff --git a/wire/net/libp2p/account_test.go b/wire/net/libp2p/account_test.go new file mode 100644 index 000000000..906ee83eb --- /dev/null +++ b/wire/net/libp2p/account_test.go @@ -0,0 +1,15 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p_test diff --git a/wire/net/libp2p/address.go b/wire/net/libp2p/address.go new file mode 100644 index 000000000..8fe0b7860 --- /dev/null +++ b/wire/net/libp2p/address.go @@ -0,0 +1,22 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import "github.com/libp2p/go-libp2p/core/peer" + +// Address is a peer address for wire discovery. +type Address struct { + peer.ID +} diff --git a/wire/net/libp2p/address_test.go b/wire/net/libp2p/address_test.go new file mode 100644 index 000000000..9633498e7 --- /dev/null +++ b/wire/net/libp2p/address_test.go @@ -0,0 +1,15 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p_test From eff95d606df5acf2b46ac2df1d5b8de05745cc36 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 28 Apr 2025 14:43:27 +0200 Subject: [PATCH 14/26] feat(libp2p): add libp2p account, address Signed-off-by: Minh Huy Tran --- .github/workflows/ci.yml | 2 +- go.mod | 81 +++++- go.sum | 448 +++++++++++++++++++++++++++++++- wire/net/libp2p/account.go | 260 ++++++++++++++++++ wire/net/libp2p/account_test.go | 234 +++++++++++++++++ wire/net/libp2p/address.go | 74 +++++- wire/net/libp2p/address_test.go | 34 +++ wire/net/libp2p/init.go | 21 ++ wire/net/libp2p/init_test.go | 6 +- 9 files changed, 1144 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6679361ff..7d2e4cacf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: release: env: - go-version: 1.22 + go-version: 1.23 jobs: check-copyright: diff --git a/go.mod b/go.mod index bf9c06631..0dea3dcef 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.23.4 require ( github.com/libp2p/go-libp2p v0.41.1 + github.com/multiformats/go-multiaddr v0.15.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 @@ -17,22 +18,97 @@ require ( ) require ( + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.3 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/ipfs/go-cid v0.5.0 // indirect + github.com/ipfs/go-log/v2 v2.5.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.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/koron/go-ssdp v0.0.5 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.2.2 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v5 v5.0.0 // 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.63 // 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/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 v0.15.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.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.6.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.22.2 // 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.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.4 // indirect + github.com/pion/ice/v4 v4.0.8 // indirect + github.com/pion/interceptor v0.1.37 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.11 // indirect + github.com/pion/sctp v1.8.37 // indirect + github.com/pion/sdp/v3 v3.0.10 // indirect + github.com/pion/srtp/v3 v3.0.4 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.0 // indirect + github.com/pion/webrtc/v4 v4.0.10 // indirect + github.com/prometheus/client_golang v1.21.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.50.1 // indirect + github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect + github.com/raulk/go-watchdog v1.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + go.uber.org/dig v1.18.0 // indirect + go.uber.org/fx v1.23.0 // indirect + go.uber.org/mock v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/tools v0.32.0 // indirect lukechampine.com/blake3 v1.4.0 // indirect ) @@ -40,12 +116,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 - github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect golang.org/x/sys v0.32.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e7764d134..750e33eb2 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,81 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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= +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/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/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.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/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/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.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +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.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= +github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +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/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/golang/protobuf v1.3.1/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= @@ -18,112 +84,466 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= +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= +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/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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/koron/go-ssdp v0.0.5 h1:E1iSMxIs4WqxTbIBLtmNBeOOC+1sCIXQeqTWVnpmwhk= +github.com/koron/go-ssdp v0.0.5/go.mod h1:Qm59B7hpKpDqfyRNWRNr00jGwLdXjDyZh6y7rH6VS0w= +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/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-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQZE= github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= +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-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8= +github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE= +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/v5 v5.0.0 h1:2djUh96d3Jiac/JpGkKs4TO49YhsfLopAoryfPmf+Po= +github.com/libp2p/go-yamux/v5 v5.0.0/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= +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.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +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.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.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.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.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.15.0 h1:zB/HeaI/apcZiTDwhY5YqMvNVl/oQYvs3XySU+qeAVo= github.com/multiformats/go-multiaddr v0.15.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +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.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.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 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.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA= github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +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/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.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= +github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= +github.com/pion/ice/v4 v4.0.8 h1:ajNx0idNG+S+v9Phu4LSn2cs8JEfTsA1/tEjkkAVpFY= +github.com/pion/ice/v4 v4.0.8/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +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.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk= +github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= +github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA= +github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= +github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= +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/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= +github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= +github.com/pion/webrtc/v4 v4.0.10 h1:Hq/JLjhqLxi+NmCtE8lnRPDr8H4LcNvwg8OxVcdv56Q= +github.com/pion/webrtc/v4 v4.0.10/go.mod h1:ViHLVaNpiuvaH8pdiuQxuA9awuE6KVzAXx3vVWilOck= +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.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q= +github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= +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.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= +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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +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/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 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 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.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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +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= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +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/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= +go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= +go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= +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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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/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-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-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-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.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +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.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +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-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.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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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-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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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.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.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/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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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.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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +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= +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-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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/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/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= @@ -133,19 +553,29 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/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.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.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 h1:iA5GzEa/hHfVlQpimEjPV09NATwHXxSjWNB0VVodtew= polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU= +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= diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go index 73a23b531..017b6b173 100644 --- a/wire/net/libp2p/account.go +++ b/wire/net/libp2p/account.go @@ -15,8 +15,25 @@ package libp2p import ( + "bufio" + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "math/rand" + "net" + + "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + swarm "github.com/libp2p/go-libp2p/p2p/net/swarm" + libp2pclient "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client" + ma "github.com/multiformats/go-multiaddr" + "github.com/pkg/errors" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" ) const ( @@ -33,3 +50,246 @@ type Account struct { relayAddr string privateKey crypto.PrivKey } + +// Address returns the account's address. +func (acc *Account) Address() wire.Address { + return &Address{acc.ID()} +} + +// Sign signs the given message with the account's private key. +func (acc *Account) Sign(data []byte) ([]byte, error) { + // Extract the private key from the account. + if acc.privateKey == nil { + return nil, errors.New("private key not set") + } + hashed := sha256.Sum256(data) + + signature, err := acc.privateKey.Sign(hashed[:]) + if err != nil { + return nil, err + } + return signature, nil + +} + +// MarshalPrivateKey marshals the account's private key to binary. +func (acc *Account) MarshalPrivateKey() ([]byte, error) { + return crypto.MarshalPrivateKey(acc.privateKey) +} + +// NewAccountFromPrivateKeyBytes creates a new account from a given private key. +func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) { + prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes) + if err != nil { + return nil, errors.WithMessage(err, "unmarshalling private key") + } + + relayInfo, relayAddr, err := getRelayServerInfo() + if err != nil { + panic(err) + } + // Construct a new libp2p client for our relay-server. + // Identity(prvKey) - Use a RSA private key to generate the ID of the host. + // EnableRelay() - Enable relay system and configures itself as a node behind a NAT + client, err := libp2p.New( + libp2p.Identity(prvKey), + libp2p.EnableRelay(), + ) + if err != nil { + return nil, errors.WithMessage(err, "creating new libp2p client") + } + + if err := client.Connect(context.Background(), *relayInfo); err != nil { + client.Close() + return nil, errors.WithMessage(err, "connecting to the relay server") + } + + // Reserve connection + // Hosts that want to have messages relayed on their behalf need to reserve a slot + // with the circuit relay service host + _, err = libp2pclient.Reserve(context.Background(), client, *relayInfo) + if err != nil { + panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server")) + } + + return &Account{client, relayAddr, prvKey}, nil +} + +// NewRandomAccount generates a new random account. +func NewRandomAccount(rng *rand.Rand) *Account { + relayInfo, relayAddr, err := getRelayServerInfo() + if err != nil { + panic(err) + } + + // Creates a new RSA key pair for this host. + prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, rng) + if err != nil { + panic(err) + } + + // Construct a new libp2p client for our relay-server. + // Identity(prvKey) - Use a RSA private key to generate the ID of the host. + // EnableRelay() - Enable relay system and configures itself as a node behind a NAT + client, err := libp2p.New( + libp2p.NoListenAddrs, + libp2p.Identity(prvKey), + libp2p.EnableRelay(), + ) + if err != nil { + client.Close() + panic(err) + } + + // Redialing hacked + client.Network().(*swarm.Swarm).Backoff().Clear(*&relayInfo.ID) + if err := client.Connect(context.Background(), *relayInfo); err != nil { + client.Close() + panic(errors.WithMessage(err, "connecting to the relay server")) + } + + // Reserve connection + // Hosts that want to have messages relayed on their behalf need to reserve a slot + // with the circuit relay service host + _, err = libp2pclient.Reserve(context.Background(), client, *relayInfo) + if err != nil { + panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server")) + } + + return &Account{client, relayAddr, prvKey} +} + +// RegisterOnChainAddress registers an on-chain address with the account to the relay-server's address book. +func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error { + id, err := peer.Decode(relayID) + if err != nil { + err = errors.WithMessage(err, "decoding peer id of relay server") + return err + } + + s, err := acc.NewStream(network.WithAllowLimitedConn(context.Background(), registerProtocol[1:]), id, registerProtocol) + if err != nil { + return errors.WithMessage(err, "creating new stream") + } + defer s.Close() + + var registerData struct { + OnChainAddress string + PeerID string + } + if onChainAddr == nil { + return errors.New("on-chain address is nil") + } + registerData.OnChainAddress = onChainAddr.String() + registerData.PeerID = acc.ID().String() + + data, err := json.Marshal(registerData) + if err != nil { + return errors.WithMessage(err, "marshalling register data") + } + + _, err = s.Write(data) + if err != nil { + return errors.WithMessage(err, "writing register data") + } + + return nil +} + +// Close closes the account. +func (acc *Account) Close() error { + return acc.Host.Close() +} + +// DeregisterOnChainAddress deregisters an on-chain address with the account from the relay-server's address book. +func (acc *Account) DeregisterOnChainAddress(onChainAddr wallet.Address) error { + relayInfo, _, err := getRelayServerInfo() + if err != nil { + return errors.WithMessage(err, "getting relay server info") + } + + s, err := acc.NewStream(network.WithAllowLimitedConn(context.Background(), removeProtocol[1:]), relayInfo.ID, removeProtocol) + if err != nil { + return errors.WithMessage(err, "creating new stream") + } + defer s.Close() + + var unregisterData struct { + OnChainAddress string + PeerID string + } + unregisterData.OnChainAddress = onChainAddr.String() + unregisterData.PeerID = acc.ID().String() + + data, err := json.Marshal(unregisterData) + if err != nil { + return errors.WithMessage(err, "marshalling register data") + } + + _, err = s.Write(data) + if err != nil { + return errors.WithMessage(err, "writing register data") + } + + return nil +} + +// QueryOnChainAddress queries the relay-server for the peerID of a peer given its on-chain address. +func (acc *Account) QueryOnChainAddress(onChainAddr wallet.Address) (*Address, error) { + id, err := peer.Decode(relayID) + if err != nil { + err = errors.WithMessage(err, "decoding peer id of relay server") + return nil, err + } + + s, err := acc.NewStream(network.WithAllowLimitedConn(context.Background(), queryProtocol[1:]), id, queryProtocol) + if err != nil { + return nil, errors.WithMessage(err, "creating new stream") + } + defer s.Close() + + rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) + rw.WriteString(fmt.Sprintf("%s\n", onChainAddr)) + rw.Flush() + + str, _ := rw.ReadString('\n') + if str == "" { + return nil, errors.New("empty response from relay server") + } + peerIDstr := str[:len(str)-1] + peerID, err := peer.Decode(peerIDstr) + if err != nil { + return nil, errors.WithMessage(err, "decoding peer id") + } + + return &Address{peerID}, nil +} + +func getRelayServerInfo() (*peer.AddrInfo, string, error) { + id, err := peer.Decode(relayID) + if err != nil { + err = errors.WithMessage(err, "decoding peer id of relay server") + return nil, "", err + } + + // Get the IP address of the relay server. + ip, err := net.LookupIP("relay.perun.network") + if err != nil { + err = errors.WithMessage(err, "looking up IP address of relay.perun.network") + return nil, "", err + } + relayAddr := "/ip4/" + ip[0].String() + "/tcp/5574" + + relayMultiaddr, err := ma.NewMultiaddr(relayAddr) + if err != nil { + err = errors.WithMessage(err, "parsing relay multiadress") + return nil, "", err + } + + relayInfo := &peer.AddrInfo{ + ID: id, + Addrs: []ma.Multiaddr{relayMultiaddr}, + } + + return relayInfo, relayAddr, nil +} diff --git a/wire/net/libp2p/account_test.go b/wire/net/libp2p/account_test.go index 906ee83eb..47a07e309 100644 --- a/wire/net/libp2p/account_test.go +++ b/wire/net/libp2p/account_test.go @@ -13,3 +13,237 @@ // limitations under the License. package libp2p_test + +import ( + "log" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "perun.network/go-perun/channel" + "perun.network/go-perun/wallet" + wtest "perun.network/go-perun/wallet/test" + "perun.network/go-perun/wire/net/libp2p" + pkgtest "polycry.pt/poly-go/test" +) + +func TestNewAccount(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() +} + +func getHost(rng *rand.Rand) *libp2p.Account { + acc := libp2p.NewRandomAccount(rng) + log.Println(acc.Host.ID()) + return acc +} + +func TestAddressBookRegister(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) +} + +func TestAddressBookRegisterEmptyAddress(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + + defer acc.Close() + + var nilAddr wallet.Address + err := acc.RegisterOnChainAddress(nilAddr) + assert.Error(t, err) +} + +func TestAddressBookDeregister(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + err = acc.DeregisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + time.Sleep(1 * time.Second) + + // Trying to query it again will fail + _, err = acc.QueryOnChainAddress(onChainAddr) + assert.Error(t, err) +} + +func TestAddressBookDeregisterPeer(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + peer := libp2p.NewRandomAccount(rng) + assert.NotNil(t, peer) + defer peer.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + peerOnChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + time.Sleep(1 * time.Millisecond) + + err = peer.RegisterOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) + + err = acc.DeregisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + // Trying to deregister the peer's address will not fail, but the server will not allow it. + err = acc.DeregisterOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) + + // Trying to query it again will be okay + peerID, err := acc.QueryOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) + + addr := peer.Address() + assert.Equal(t, peerID, addr) + + err = peer.DeregisterOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) +} + +func TestAddressBookQuery_Fail(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + _, err := acc.QueryOnChainAddress(onChainAddr) + assert.Error(t, err) +} + +func TestAddressBookQuery(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + time.Sleep(10 * time.Millisecond) + peerID, err := acc.QueryOnChainAddress(onChainAddr) + assert.NoError(t, err) + + addr := acc.Address() + assert.Equal(t, peerID, addr) + + err = acc.DeregisterOnChainAddress(onChainAddr) + assert.NoError(t, err) +} + +func TestAddressBookQueryPeer(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + peer := libp2p.NewRandomAccount(rng) + assert.NotNil(t, peer) + defer peer.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + peerOnChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + err = peer.RegisterOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) + + time.Sleep(1 * time.Second) + peerID, err := acc.QueryOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) + + addr := peer.Address() + assert.Equal(t, peerID, addr) + + err = acc.DeregisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + err = acc.DeregisterOnChainAddress(peerOnChainAddr) + assert.NoError(t, err) +} + +func TestAddressBookRegisterQueryMultiple(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) + onChainAddr2 := wtest.NewRandomAddress(rng, channel.TestBackendID) + + err := acc.RegisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + err = acc.RegisterOnChainAddress(onChainAddr2) + assert.NoError(t, err) + + time.Sleep(1 * time.Second) + + accID, err := acc.QueryOnChainAddress(onChainAddr) + assert.NoError(t, err) + + accID2, err := acc.QueryOnChainAddress(onChainAddr2) + assert.NoError(t, err) + + addr := acc.Address() + assert.Equal(t, accID, addr) + assert.Equal(t, accID2, addr) + + // Clean up + err = acc.DeregisterOnChainAddress(onChainAddr) + assert.NoError(t, err) + + err = acc.DeregisterOnChainAddress(onChainAddr2) + assert.NoError(t, err) +} + +// Test NewAccountFromPrivateKey +func TestNewAccountFromPrivateKey(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + + defer acc.Close() + + keyBytes, err := acc.MarshalPrivateKey() + assert.NoError(t, err) + + acc2, err := libp2p.NewAccountFromPrivateKeyBytes(keyBytes) + assert.NoError(t, err) + + defer acc2.Close() + + assert.NotNil(t, acc2) + assert.Equal(t, acc.ID(), acc2.ID()) + assert.Equal(t, acc.Address(), acc2.Address()) +} diff --git a/wire/net/libp2p/address.go b/wire/net/libp2p/address.go index 8fe0b7860..b429eedc0 100644 --- a/wire/net/libp2p/address.go +++ b/wire/net/libp2p/address.go @@ -14,9 +14,81 @@ package libp2p -import "github.com/libp2p/go-libp2p/core/peer" +import ( + "crypto/sha256" + "fmt" + "math/rand" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "perun.network/go-perun/wire" +) // Address is a peer address for wire discovery. type Address struct { peer.ID } + +// NewAddress returns a new address. +func NewAddress(id peer.ID) *Address { + return &Address{ID: id} +} + +// Equal returns whether the two addresses are equal. +func (a *Address) Equal(b wire.Address) bool { + bTyped, ok := b.(*Address) + if !ok { + panic("wrong type") + } + + return a.ID == bTyped.ID +} + +// Cmp compares the byte representation of two addresses. For `a.Cmp(b)` +// returns -1 if a < b, 0 if a == b, 1 if a > b. +func (a *Address) Cmp(b wire.Address) int { + bTyped, ok := b.(*Address) + if !ok { + panic("wrong type") + } + if a.ID < bTyped.ID { + return -1 + } else if a.ID == bTyped.ID { + return 0 + } + return 1 + +} + +// NewRandomAddress returns a new random peer address. +func NewRandomAddress(rng *rand.Rand) *Address { + _, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rng) + if err != nil { + panic(err) + } + + id, err := peer.IDFromPublicKey(publicKey) + if err != nil { + panic(err) + } + return &Address{id} +} + +// Verify verifies the signature of a message. +func (a *Address) Verify(msg []byte, sig []byte) error { + publicKey, err := a.ExtractPublicKey() + if err != nil { + return fmt.Errorf("extracting public key: %w", err) + } + + hashed := sha256.Sum256(msg) + + b, err := publicKey.Verify(hashed[:], sig) + if err != nil { + return fmt.Errorf("verifying signature: %w", err) + } + if b { + return nil + } + return fmt.Errorf("signature verification failed") +} diff --git a/wire/net/libp2p/address_test.go b/wire/net/libp2p/address_test.go index 9633498e7..9c498b882 100644 --- a/wire/net/libp2p/address_test.go +++ b/wire/net/libp2p/address_test.go @@ -13,3 +13,37 @@ // limitations under the License. package libp2p_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "perun.network/go-perun/wire" + "perun.network/go-perun/wire/net/libp2p" + "perun.network/go-perun/wire/test" + + pkgtest "polycry.pt/poly-go/test" +) + +func TestAddress(t *testing.T) { + test.TestAddressImplementation(t, func() wire.Address { + return libp2p.NewAddress("") + }, func(rng *rand.Rand) wire.Address { + return libp2p.NewRandomAddress(rng) + }) +} + +func TestSignature(t *testing.T) { + rng := pkgtest.Prng(t) + acc := libp2p.NewRandomAccount(rng) + assert.NotNil(t, acc) + defer acc.Close() + + msg := []byte("test message") + sig, err := acc.Sign(msg) + assert.NoError(t, err) + + addr := acc.Address() + assert.NoError(t, addr.Verify(msg, sig)) +} diff --git a/wire/net/libp2p/init.go b/wire/net/libp2p/init.go index 13c739cba..5e3f27b1b 100644 --- a/wire/net/libp2p/init.go +++ b/wire/net/libp2p/init.go @@ -13,3 +13,24 @@ // limitations under the License. package libp2p + +import ( + "math/rand" + + "perun.network/go-perun/wire" + "perun.network/go-perun/wire/test" +) + +func init() { + wire.SetNewAddressFunc(func() wire.Address { + return NewAddress("") + }) + // Setup for testing purposes. + test.SetNewRandomAddress(func(rng *rand.Rand) wire.Address { + return NewRandomAddress(rng) + }) + // Setup for testing purposes. + test.SetNewRandomAccount(func(rng *rand.Rand) wire.Account { + return NewRandomAccount(rng) + }) +} diff --git a/wire/net/libp2p/init_test.go b/wire/net/libp2p/init_test.go index 9633498e7..46e15652a 100644 --- a/wire/net/libp2p/init_test.go +++ b/wire/net/libp2p/init_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 - See NOTICE file for copyright holders. +// Copyright 2022 - See NOTICE file for copyright holders. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,3 +13,7 @@ // limitations under the License. package libp2p_test + +import ( + _ "perun.network/go-perun/backend/sim/wallet" // Initializes the randomizers. +) From 848f10703d2519556ffb81926f25e5a25b948728 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 29 Apr 2025 09:36:38 +0200 Subject: [PATCH 15/26] feat(libp2p): add dialer and listener Signed-off-by: Minh Huy Tran --- wallet/address_test.go | 2 +- wire/net/libp2p/account.go | 191 +++++++++++++++------- wire/net/libp2p/account_test.go | 11 +- wire/net/libp2p/address.go | 32 ++-- wire/net/libp2p/dialer.go | 112 +++++++++++++ wire/net/libp2p/dialer_internal_test.go | 154 +++++++++++++++++ wire/net/libp2p/listener.go | 65 ++++++++ wire/net/libp2p/listener_internal_test.go | 71 ++++++++ 8 files changed, 556 insertions(+), 82 deletions(-) create mode 100644 wire/net/libp2p/dialer.go create mode 100644 wire/net/libp2p/dialer_internal_test.go create mode 100644 wire/net/libp2p/listener.go create mode 100644 wire/net/libp2p/listener_internal_test.go diff --git a/wallet/address_test.go b/wallet/address_test.go index ccfb65495..a24f98f6a 100644 --- a/wallet/address_test.go +++ b/wallet/address_test.go @@ -119,7 +119,7 @@ func TestCloneAddresses(t *testing.T) { addrs := wallettest.NewRandomAddressArray(rng, 3, TestBackendID) addrs0 := wallet.CloneAddresses(addrs) require.Equal(t, addrs, addrs0) - require.NotSame(t, addrs, addrs0) + require.NotSame(t, &addrs, &addrs0) for i, a := range addrs { require.NotSame(t, a, addrs0[i]) } diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go index 017b6b173..a0f75065c 100644 --- a/wire/net/libp2p/account.go +++ b/wire/net/libp2p/account.go @@ -20,8 +20,10 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "log" "math/rand" "net" + "time" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/crypto" @@ -39,95 +41,85 @@ import ( const ( relayID = "QmVCPfUMr98PaaM8qbAQBgJ9jqc7XHpGp7AsyragdFDmgm" + keySize = 2048 + queryProtocol = "/address-book/query/1.0.0" // Protocol for querying the relay-server for a peerID. registerProtocol = "/address-book/register/1.0.0" // Protocol for registering an on-chain address with the relay-server. removeProtocol = "/address-book/remove/1.0.0" // Protocol for deregistering an on-chain address with the relay-server. ) -// Account represents a libp2p wire account containting a libp2p host. +// Account represents a libp2p wire account containing a libp2p host. type Account struct { host.Host - relayAddr string - privateKey crypto.PrivKey -} - -// Address returns the account's address. -func (acc *Account) Address() wire.Address { - return &Address{acc.ID()} -} - -// Sign signs the given message with the account's private key. -func (acc *Account) Sign(data []byte) ([]byte, error) { - // Extract the private key from the account. - if acc.privateKey == nil { - return nil, errors.New("private key not set") - } - hashed := sha256.Sum256(data) - - signature, err := acc.privateKey.Sign(hashed[:]) - if err != nil { - return nil, err - } - return signature, nil - -} - -// MarshalPrivateKey marshals the account's private key to binary. -func (acc *Account) MarshalPrivateKey() ([]byte, error) { - return crypto.MarshalPrivateKey(acc.privateKey) + relayAddr string + privateKey crypto.PrivKey + reservation *libp2pclient.Reservation + closer context.CancelFunc } -// NewAccountFromPrivateKeyBytes creates a new account from a given private key. -func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) { - prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes) +// NewRandomAccount generates a new random account. +func NewRandomAccount(rng *rand.Rand) *Account { + relayInfo, relayAddr, err := getRelayServerInfo() if err != nil { - return nil, errors.WithMessage(err, "unmarshalling private key") + panic(err) } - relayInfo, relayAddr, err := getRelayServerInfo() + // Creates a new RSA key pair for this host. + prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, keySize, rng) if err != nil { panic(err) } + // Construct a new libp2p client for our relay-server. // Identity(prvKey) - Use a RSA private key to generate the ID of the host. // EnableRelay() - Enable relay system and configures itself as a node behind a NAT client, err := libp2p.New( + libp2p.NoListenAddrs, libp2p.Identity(prvKey), libp2p.EnableRelay(), ) if err != nil { - return nil, errors.WithMessage(err, "creating new libp2p client") + client.Close() + panic(err) + } + + // Redialing hacked + if sw, ok := client.Network().(*swarm.Swarm); ok { + sw.Backoff().Clear(relayInfo.ID) } if err := client.Connect(context.Background(), *relayInfo); err != nil { client.Close() - return nil, errors.WithMessage(err, "connecting to the relay server") + panic(errors.WithMessage(err, "connecting to the relay server")) } // Reserve connection // Hosts that want to have messages relayed on their behalf need to reserve a slot // with the circuit relay service host - _, err = libp2pclient.Reserve(context.Background(), client, *relayInfo) + resv, err := libp2pclient.Reserve(context.Background(), client, *relayInfo) if err != nil { panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server")) } - return &Account{client, relayAddr, prvKey}, nil + ctx, cancel := context.WithCancel(context.Background()) + acc := &Account{client, relayAddr, prvKey, resv, cancel} + + go acc.keepReservationAlive(ctx, *relayInfo) + + return acc } -// NewRandomAccount generates a new random account. -func NewRandomAccount(rng *rand.Rand) *Account { - relayInfo, relayAddr, err := getRelayServerInfo() +// NewAccountFromPrivateKeyBytes creates a new account from a given private key. +func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) { + prvKey, err := crypto.UnmarshalPrivateKey(prvKeyBytes) if err != nil { - panic(err) + return nil, errors.WithMessage(err, "unmarshalling private key") } - // Creates a new RSA key pair for this host. - prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, rng) + relayInfo, relayAddr, err := getRelayServerInfo() if err != nil { panic(err) } - // Construct a new libp2p client for our relay-server. // Identity(prvKey) - Use a RSA private key to generate the ID of the host. // EnableRelay() - Enable relay system and configures itself as a node behind a NAT @@ -137,26 +129,57 @@ func NewRandomAccount(rng *rand.Rand) *Account { libp2p.EnableRelay(), ) if err != nil { - client.Close() - panic(err) + return nil, errors.WithMessage(err, "creating new libp2p client") + } + + if sw, ok := client.Network().(*swarm.Swarm); ok { + sw.Backoff().Clear(relayInfo.ID) } - // Redialing hacked - client.Network().(*swarm.Swarm).Backoff().Clear(*&relayInfo.ID) if err := client.Connect(context.Background(), *relayInfo); err != nil { client.Close() - panic(errors.WithMessage(err, "connecting to the relay server")) + return nil, errors.WithMessage(err, "connecting to the relay server") } // Reserve connection // Hosts that want to have messages relayed on their behalf need to reserve a slot // with the circuit relay service host - _, err = libp2pclient.Reserve(context.Background(), client, *relayInfo) + res, err := libp2pclient.Reserve(context.Background(), client, *relayInfo) if err != nil { panic(errors.WithMessage(err, "failed to receive a relay reservation from relay server")) } - return &Account{client, relayAddr, prvKey} + ctx, cancel := context.WithCancel(context.Background()) + acc := &Account{client, relayAddr, prvKey, res, cancel} + + go acc.keepReservationAlive(ctx, *relayInfo) + + return acc, nil +} + +// Address returns the account's address. +func (acc *Account) Address() wire.Address { + return &Address{acc.ID()} +} + +// Sign signs the given message with the account's private key. +func (acc *Account) Sign(data []byte) ([]byte, error) { + // Extract the private key from the account. + if acc.privateKey == nil { + return nil, errors.New("private key not set") + } + hashed := sha256.Sum256(data) + + signature, err := acc.privateKey.Sign(hashed[:]) + if err != nil { + return nil, err + } + return signature, nil +} + +// MarshalPrivateKey marshals the account's private key to binary. +func (acc *Account) MarshalPrivateKey() ([]byte, error) { + return crypto.MarshalPrivateKey(acc.privateKey) } // RegisterOnChainAddress registers an on-chain address with the account to the relay-server's address book. @@ -183,7 +206,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error { registerData.OnChainAddress = onChainAddr.String() registerData.PeerID = acc.ID().String() - data, err := json.Marshal(registerData) + data, err := json.Marshal(registerData) //nolint:musttag if err != nil { return errors.WithMessage(err, "marshalling register data") } @@ -198,6 +221,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error { // Close closes the account. func (acc *Account) Close() error { + acc.closer() return acc.Host.Close() } @@ -221,7 +245,7 @@ func (acc *Account) DeregisterOnChainAddress(onChainAddr wallet.Address) error { unregisterData.OnChainAddress = onChainAddr.String() unregisterData.PeerID = acc.ID().String() - data, err := json.Marshal(unregisterData) + data, err := json.Marshal(unregisterData) //nolint:musttag if err != nil { return errors.WithMessage(err, "marshalling register data") } @@ -249,7 +273,10 @@ func (acc *Account) QueryOnChainAddress(onChainAddr wallet.Address) (*Address, e defer s.Close() rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) - rw.WriteString(fmt.Sprintf("%s\n", onChainAddr)) + _, err = fmt.Fprintf(rw, "%s\n", onChainAddr) + if err != nil { + return nil, errors.WithMessage(err, "writing on-chain address") + } rw.Flush() str, _ := rw.ReadString('\n') @@ -293,3 +320,57 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) { return relayInfo, relayAddr, nil } + +// getHost returns a new random account for testing. +func getHost(rng *rand.Rand) *Account { + acc := NewRandomAccount(rng) + log.Println(acc.ID()) + return acc +} + +// keepReservationAlive keeps the reservation alive by periodically renewing it. +func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) { + const ( + reserveInterval = 50 * time.Second // slightly less than ReserveTimeout + initialBackoff = 2 * time.Second + maxBackoff = 1 * time.Minute + ) + + ticker := time.NewTicker(reserveInterval) + defer ticker.Stop() + + backoff := initialBackoff + + for { + select { + case <-ctx.Done(): + log.Println("keepReservationAlive: context cancelled") + return + + case <-ticker.C: + newReservation, err := libp2pclient.Reserve(ctx, acc.Host, ai) + if err != nil { + log.Printf("keepReservationAlive: reservation failed: %v; retrying in %s", err, backoff) + + timer := time.NewTimer(backoff) + select { + case <-ctx.Done(): + timer.Stop() + return + case <-timer.C: + if backoff < maxBackoff { + backoff *= 2 + if backoff > maxBackoff { + backoff = maxBackoff + } + } + } + continue + } + + acc.reservation = newReservation + backoff = initialBackoff + log.Println("keepReservationAlive: reservation successfully renewed") + } + } +} diff --git a/wire/net/libp2p/account_test.go b/wire/net/libp2p/account_test.go index 47a07e309..c7a1ebb4d 100644 --- a/wire/net/libp2p/account_test.go +++ b/wire/net/libp2p/account_test.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,6 @@ package libp2p_test import ( - "log" - "math/rand" "testing" "time" @@ -35,12 +33,6 @@ func TestNewAccount(t *testing.T) { defer acc.Close() } -func getHost(rng *rand.Rand) *libp2p.Account { - acc := libp2p.NewRandomAccount(rng) - log.Println(acc.Host.ID()) - return acc -} - func TestAddressBookRegister(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) @@ -227,7 +219,6 @@ func TestAddressBookRegisterQueryMultiple(t *testing.T) { assert.NoError(t, err) } -// Test NewAccountFromPrivateKey func TestNewAccountFromPrivateKey(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) diff --git a/wire/net/libp2p/address.go b/wire/net/libp2p/address.go index b429eedc0..b17047eb6 100644 --- a/wire/net/libp2p/address.go +++ b/wire/net/libp2p/address.go @@ -21,6 +21,7 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" + "github.com/pkg/errors" "perun.network/go-perun/wire" ) @@ -34,6 +35,20 @@ func NewAddress(id peer.ID) *Address { return &Address{ID: id} } +// NewRandomAddress returns a new random peer address. +func NewRandomAddress(rng *rand.Rand) *Address { + _, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, keySize, rng) + if err != nil { + panic(err) + } + + id, err := peer.IDFromPublicKey(publicKey) + if err != nil { + panic(err) + } + return &Address{id} +} + // Equal returns whether the two addresses are equal. func (a *Address) Equal(b wire.Address) bool { bTyped, ok := b.(*Address) @@ -57,21 +72,6 @@ func (a *Address) Cmp(b wire.Address) int { return 0 } return 1 - -} - -// NewRandomAddress returns a new random peer address. -func NewRandomAddress(rng *rand.Rand) *Address { - _, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rng) - if err != nil { - panic(err) - } - - id, err := peer.IDFromPublicKey(publicKey) - if err != nil { - panic(err) - } - return &Address{id} } // Verify verifies the signature of a message. @@ -90,5 +90,5 @@ func (a *Address) Verify(msg []byte, sig []byte) error { if b { return nil } - return fmt.Errorf("signature verification failed") + return errors.New("signature verification failed") } diff --git a/wire/net/libp2p/dialer.go b/wire/net/libp2p/dialer.go new file mode 100644 index 000000000..d08cbdf06 --- /dev/null +++ b/wire/net/libp2p/dialer.go @@ -0,0 +1,112 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "context" + "sync" + + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + swarm "github.com/libp2p/go-libp2p/p2p/net/swarm" + ma "github.com/multiformats/go-multiaddr" + "github.com/pkg/errors" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" + wirenet "perun.network/go-perun/wire/net" + pkgsync "polycry.pt/poly-go/sync" +) + +// Dialer is a dialer for p2p connections. +type Dialer struct { + mutex sync.RWMutex // Protects peers. + peers map[wire.AddrKey]string + host host.Host + relayID string + relayAddr string + closer pkgsync.Closer +} + +// NewP2PDialer creates a new dialer for the given account. +func NewP2PDialer(acc *Account) *Dialer { + return &Dialer{ + host: acc, + relayID: relayID, + relayAddr: acc.relayAddr, + peers: make(map[wire.AddrKey]string), + } +} + +// Dial implements Dialer.Dial(). +func (d *Dialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, serializer wire.EnvelopeSerializer) (wirenet.Conn, error) { + peerID, ok := d.get(wire.Keys(addr)) + if !ok { + return nil, errors.New("failed to dial peer: peer ID not found") + } + + _peerID, err := peer.Decode(peerID) + if err != nil { + return nil, errors.Wrap(err, "peer ID is not valid") + } + + if sw, ok := d.host.Network().(*swarm.Swarm); ok { + sw.Backoff().Clear(_peerID) + } + + fullAddr := d.relayAddr + "/p2p/" + d.relayID + "/p2p-circuit/p2p/" + _peerID.String() + peerMultiAddr, err := ma.NewMultiaddr(fullAddr) + if err != nil { + return nil, errors.Wrap(err, "failed to parse multiaddress of peer") + } + + peerAddrInfo, err := peer.AddrInfoFromP2pAddr(peerMultiAddr) + if err != nil { + return nil, errors.Wrap(err, "converting peer multiaddress to address info") + } + if err := d.host.Connect(ctx, *peerAddrInfo); err != nil { + return nil, errors.Wrap(err, "failed to dial peer: failed to connecting to peer") + } + + s, err := d.host.NewStream(network.WithAllowLimitedConn(ctx, "client"), peerAddrInfo.ID, "/client") + if err != nil { + return nil, errors.Wrap(err, "failed to dial peer: failed to creating a new stream") + } + + return wirenet.NewIoConn(s, serializer), nil +} + +// Register registers a p2p peer id for a peer wire address. +func (d *Dialer) Register(addr map[wallet.BackendID]wire.Address, peerID string) { + d.mutex.Lock() + defer d.mutex.Unlock() + d.peers[wire.Keys(addr)] = peerID +} + +// Close closes the dialer by closing the underlying libp2p host. +func (d *Dialer) Close() error { + if err := d.closer.Close(); err != nil { + return err + } + return d.host.Close() +} + +// get returns the p2p multiaddress for the given address if registered. +func (d *Dialer) get(addr wire.AddrKey) (peerID string, ok bool) { + d.mutex.RLock() + defer d.mutex.RUnlock() + peerID, ok = d.peers[addr] + return +} diff --git a/wire/net/libp2p/dialer_internal_test.go b/wire/net/libp2p/dialer_internal_test.go new file mode 100644 index 000000000..51544ff86 --- /dev/null +++ b/wire/net/libp2p/dialer_internal_test.go @@ -0,0 +1,154 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" + perunio "perun.network/go-perun/wire/perunio/serializer" + "perun.network/go-perun/wire/test" + + ctxtest "polycry.pt/poly-go/context/test" + pkgtest "polycry.pt/poly-go/test" +) + +func TestNewDialer(t *testing.T) { + rng := pkgtest.Prng(t) + h := getHost(rng) + + d := NewP2PDialer(h) + assert.NotNil(t, d) + d.Close() +} + +func TestDialer_Register(t *testing.T) { + rng := pkgtest.Prng(t) + addr := NewRandomAddress(rng) + key := wire.Key(addr) + h := getHost(rng) + d := NewP2PDialer(h) + defer d.Close() + + _, ok := d.get(key) + require.False(t, ok) + + addrs := make(map[wallet.BackendID]wire.Address) + addrs[test.TestBackendID] = addr + d.Register(addrs, "p2pAddress") + + host, ok := d.get(key) + assert.True(t, ok) + assert.Equal(t, host, "p2pAddress") +} + +func TestDialer_Dial(t *testing.T) { + timeout := 1000 * time.Millisecond + rng := pkgtest.Prng(t) + + lHost := getHost(rng) + lAddr := lHost.Address() + laddrs := make(map[wallet.BackendID]wire.Address) + laddrs[test.TestBackendID] = lAddr + lpeerID := lHost.ID() + listener := NewP2PListener(lHost) + defer listener.Close() + + dHost := getHost(rng) + dAddr := dHost.Address() + daddrs := make(map[wallet.BackendID]wire.Address) + daddrs[test.TestBackendID] = dAddr + dialer := NewP2PDialer(dHost) + dialer.Register(laddrs, lpeerID.String()) + defer dialer.Close() + + t.Run("happy", func(t *testing.T) { + e := &wire.Envelope{ + Sender: daddrs, + Recipient: laddrs, + Msg: wire.NewPingMsg(), + } + ct := pkgtest.NewConcurrent(t) + + go ct.Stage("accept", func(rt pkgtest.ConcT) { + conn, err := listener.Accept(perunio.Serializer()) + assert.NoError(t, err) + require.NotNil(rt, conn) + + re, err := conn.Recv() + assert.NoError(t, err) + assert.Equal(t, re, e) + }) + + ct.Stage("dial", func(rt pkgtest.ConcT) { + ctxtest.AssertTerminates(t, timeout, func() { + conn, err := dialer.Dial(context.Background(), laddrs, perunio.Serializer()) + assert.NoError(t, err) + require.NotNil(rt, conn) + + assert.NoError(t, conn.Send(e)) + }) + }) + + ct.Wait("dial", "accept") + }) + + t.Run("aborted context", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + ctxtest.AssertTerminates(t, timeout, func() { + conn, err := dialer.Dial(ctx, laddrs, perunio.Serializer()) + assert.Nil(t, conn) + assert.Error(t, err) + }) + }) + + t.Run("unknown host", func(t *testing.T) { + noHostAddr := map[wallet.BackendID]wire.Address{test.TestBackendID: NewRandomAddress(rng)} + dialer.Register(noHostAddr, "no such host") + + ctxtest.AssertTerminates(t, timeout, func() { + conn, err := dialer.Dial(context.Background(), noHostAddr, perunio.Serializer()) + assert.Nil(t, conn) + assert.Error(t, err) + }) + }) + + t.Run("unknown address", func(t *testing.T) { + ctxtest.AssertTerminates(t, timeout, func() { + unknownAddr := map[wallet.BackendID]wire.Address{test.TestBackendID: NewRandomAddress(rng)} + conn, err := dialer.Dial(context.Background(), unknownAddr, perunio.Serializer()) + assert.Error(t, err) + assert.Nil(t, conn) + }) + }) +} + +func TestDialer_Close(t *testing.T) { + t.Run("double close", func(t *testing.T) { + rng := pkgtest.Prng(t) + h := getHost(rng) + d := NewP2PDialer(h) + + assert.NoError(t, d.Close(), "first close must not return error") + assert.Error(t, d.Close(), "second close must result in error") + }) +} diff --git a/wire/net/libp2p/listener.go b/wire/net/libp2p/listener.go new file mode 100644 index 000000000..00b17a2d3 --- /dev/null +++ b/wire/net/libp2p/listener.go @@ -0,0 +1,65 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/pkg/errors" + "perun.network/go-perun/wire" + wirenet "perun.network/go-perun/wire/net" + pkgsync "polycry.pt/poly-go/sync" +) + +// Listener is a listener for p2p connections. +type Listener struct { + host host.Host + streams chan network.Stream + done chan struct{} + closer pkgsync.Closer +} + +// NewP2PListener creates a new listener for the given account. +func NewP2PListener(acc *Account) *Listener { + listener := Listener{ + host: acc, + streams: make(chan network.Stream), + done: make(chan struct{}), + } + acc.SetStreamHandler("/client", func(s network.Stream) { + listener.streams <- s + }) + return &listener +} + +// Accept implements Listener.Accept(). +func (l *Listener) Accept(serializer wire.EnvelopeSerializer) (wirenet.Conn, error) { + select { + case s := <-l.streams: + return wirenet.NewIoConn(s, serializer), nil + case <-l.done: + return nil, errors.New("listener is closed") + } +} + +// Close closes the Listener by closing the done channel. +func (l *Listener) Close() error { + if err := l.closer.Close(); err != nil { + return err + } + + close(l.done) + return nil +} diff --git a/wire/net/libp2p/listener_internal_test.go b/wire/net/libp2p/listener_internal_test.go new file mode 100644 index 000000000..b920569d9 --- /dev/null +++ b/wire/net/libp2p/listener_internal_test.go @@ -0,0 +1,71 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + perunio "perun.network/go-perun/wire/perunio/serializer" + + ctxtest "polycry.pt/poly-go/context/test" + pkgtest "polycry.pt/poly-go/test" +) + +func TestNewListener(t *testing.T) { + rng := pkgtest.Prng(t) + h := getHost(rng) + l := NewP2PListener(h) + defer l.Close() + assert.NotNil(t, l) +} + +func TestListener_Close(t *testing.T) { + t.Run("double close", func(t *testing.T) { + rng := pkgtest.Prng(t) + h := getHost(rng) + l := NewP2PListener(h) + assert.NoError(t, l.Close(), "first close must not return error") + assert.Error(t, l.Close(), "second close must result in error") + }) +} + +func TestListener_Accept(t *testing.T) { + // Happy case already tested in TestDialer_Dial. + rng := pkgtest.Prng(t) + h := getHost(rng) + timeout := 100 * time.Millisecond + t.Run("timeout", func(t *testing.T) { + l := NewP2PListener(h) + defer l.Close() + + ctxtest.AssertNotTerminates(t, timeout, func() { + _, err := l.Accept(perunio.Serializer()) + assert.Error(t, err) + }) + }) + + t.Run("closed", func(t *testing.T) { + l := NewP2PListener(h) + l.Close() + + ctxtest.AssertTerminates(t, timeout, func() { + conn, err := l.Accept(perunio.Serializer()) + assert.Nil(t, conn) + assert.Error(t, err) + }) + }) +} From effbd0055cd47f989669cd345c1bd3216192ca68 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 29 Apr 2025 17:31:12 +0200 Subject: [PATCH 16/26] feat(libp2p): add exchange addr tests Signed-off-by: Minh Huy Tran --- wire/net/libp2p/account.go | 6 +- wire/net/libp2p/address.go | 19 +++ wire/net/libp2p/dialer_internal_test.go | 2 +- wire/net/libp2p/init_test.go | 1 + wire/net/libp2p/mockconn_internal_test.go | 68 +++++++++++ wire/net/libp2p/simple_exchange_addr_test.go | 122 +++++++++++++++++++ 6 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 wire/net/libp2p/mockconn_internal_test.go create mode 100644 wire/net/libp2p/simple_exchange_addr_test.go diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go index a0f75065c..dcff50850 100644 --- a/wire/net/libp2p/account.go +++ b/wire/net/libp2p/account.go @@ -324,7 +324,6 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) { // getHost returns a new random account for testing. func getHost(rng *rand.Rand) *Account { acc := NewRandomAccount(rng) - log.Println(acc.ID()) return acc } @@ -345,8 +344,11 @@ func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) select { case <-ctx.Done(): log.Println("keepReservationAlive: context cancelled") + err := acc.Close() + if err != nil { + panic(err) + } return - case <-ticker.C: newReservation, err := libp2pclient.Reserve(ctx, acc.Host, ai) if err != nil { diff --git a/wire/net/libp2p/address.go b/wire/net/libp2p/address.go index b17047eb6..9b1eee2ee 100644 --- a/wire/net/libp2p/address.go +++ b/wire/net/libp2p/address.go @@ -22,9 +22,13 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" + "perun.network/go-perun/wallet" "perun.network/go-perun/wire" ) +// testBackendID is the identifier for the simulated Backend. +const testBackendID = 0 + // Address is a peer address for wire discovery. type Address struct { peer.ID @@ -49,6 +53,21 @@ func NewRandomAddress(rng *rand.Rand) *Address { return &Address{id} } +// NewRandomAddresses returns a new random peer address. +func NewRandomAddresses(rng *rand.Rand) map[wallet.BackendID]wire.Address { + _, publicKey, err := crypto.GenerateKeyPairWithReader(crypto.RSA, keySize, rng) + if err != nil { + panic(err) + } + + id, err := peer.IDFromPublicKey(publicKey) + if err != nil { + panic(err) + } + a := Address{id} + return map[wallet.BackendID]wire.Address{testBackendID: &a} +} + // Equal returns whether the two addresses are equal. func (a *Address) Equal(b wire.Address) bool { bTyped, ok := b.(*Address) diff --git a/wire/net/libp2p/dialer_internal_test.go b/wire/net/libp2p/dialer_internal_test.go index 51544ff86..e94e52837 100644 --- a/wire/net/libp2p/dialer_internal_test.go +++ b/wire/net/libp2p/dialer_internal_test.go @@ -61,7 +61,7 @@ func TestDialer_Register(t *testing.T) { } func TestDialer_Dial(t *testing.T) { - timeout := 1000 * time.Millisecond + timeout := 2 * time.Second rng := pkgtest.Prng(t) lHost := getHost(rng) diff --git a/wire/net/libp2p/init_test.go b/wire/net/libp2p/init_test.go index 46e15652a..f06847373 100644 --- a/wire/net/libp2p/init_test.go +++ b/wire/net/libp2p/init_test.go @@ -16,4 +16,5 @@ package libp2p_test import ( _ "perun.network/go-perun/backend/sim/wallet" // Initializes the randomizers. + _ "perun.network/go-perun/backend/sim/wire" // Reninit the sim wire package. ) diff --git a/wire/net/libp2p/mockconn_internal_test.go b/wire/net/libp2p/mockconn_internal_test.go new file mode 100644 index 000000000..e4704011c --- /dev/null +++ b/wire/net/libp2p/mockconn_internal_test.go @@ -0,0 +1,68 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "sync" + + "github.com/pkg/errors" + + "perun.network/go-perun/wire" + wirenet "perun.network/go-perun/wire/net" + "polycry.pt/poly-go/sync/atomic" +) + +var _ wirenet.Conn = (*MockConn)(nil) + +type MockConn struct { + mutex sync.Mutex + closed atomic.Bool + recvQueue chan *wire.Envelope + + sent func(*wire.Envelope) // observes sent messages. +} + +func newMockConn() *MockConn { + return &MockConn{ + sent: func(*wire.Envelope) {}, + recvQueue: make(chan *wire.Envelope, 1), + } +} + +func (c *MockConn) Send(e *wire.Envelope) error { + c.mutex.Lock() + defer c.mutex.Unlock() + if c.closed.IsSet() { + return errors.New("closed") + } + c.sent(e) + return nil +} + +func (c *MockConn) Recv() (*wire.Envelope, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + if c.closed.IsSet() { + return nil, errors.New("closed") + } + return <-c.recvQueue, nil +} + +func (c *MockConn) Close() error { + if !c.closed.TrySet() { + return errors.New("double close") + } + return nil +} diff --git a/wire/net/libp2p/simple_exchange_addr_test.go b/wire/net/libp2p/simple_exchange_addr_test.go new file mode 100644 index 000000000..fda659043 --- /dev/null +++ b/wire/net/libp2p/simple_exchange_addr_test.go @@ -0,0 +1,122 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This test uses the wire/net/libp2p implementation of Account and Address +// to test the default implementation of wire. +// +//nolint:testpackage +package libp2p + +import ( + "context" + "math/rand" + "net" + "sync" + "testing" + "time" + + "perun.network/go-perun/channel" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "perun.network/go-perun/wire" + wirenet "perun.network/go-perun/wire/net" + perunio "perun.network/go-perun/wire/perunio/serializer" + wiretest "perun.network/go-perun/wire/test" + ctxtest "polycry.pt/poly-go/context/test" + "polycry.pt/poly-go/test" +) + +const timeout = 100 * time.Millisecond + +func TestExchangeAddrs_ConnFail(t *testing.T) { + rng := test.Prng(t) + a, _ := newPipeConnPair() + a.Close() + acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + defer acc[channel.TestBackendID].(*Account).Close() + addr, err := wirenet.ExchangeAddrsPassive(context.Background(), acc, a) + assert.Nil(t, addr) + assert.Error(t, err) +} + +func TestExchangeAddrs_Success(t *testing.T) { + rng := test.Prng(t) + conn0, conn1 := newPipeConnPair() + defer conn0.Close() + account0, account1 := wiretest.NewRandomAccountMap(rng, channel.TestBackendID), wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + defer account0[channel.TestBackendID].(*Account).Close() + defer account1[channel.TestBackendID].(*Account).Close() + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + defer conn1.Close() + + recvAddr0, err := wirenet.ExchangeAddrsPassive(context.Background(), account1, conn1) + assert.NoError(t, err) + assert.True(t, channel.EqualWireMaps(recvAddr0, wire.AddressMapfromAccountMap(account0))) + }() + + err := wirenet.ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0) + require.NoError(t, err) + + wg.Wait() +} + +func TestExchangeAddrs_BogusMsg(t *testing.T) { + rng := test.Prng(t) + acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + defer acc[channel.TestBackendID].(*Account).Close() + conn := newMockConn() + conn.recvQueue <- newRandomEnvelope(rng, wire.NewPingMsg()) + addr, err := wirenet.ExchangeAddrsPassive(context.Background(), acc, conn) + + assert.Error(t, err, "ExchangeAddrs should error when peer sends a non-AuthResponseMsg") + assert.Nil(t, addr) +} + +func TestExchangeAddrs_Timeout(t *testing.T) { + rng := test.Prng(t) + a, _ := newPipeConnPair() + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + ctxtest.AssertTerminates(t, 20*timeout, func() { + acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + defer acc[channel.TestBackendID].(*Account).Close() + addr, err := wirenet.ExchangeAddrsPassive(ctx, acc, a) + assert.Nil(t, addr) + assert.Error(t, err) + }) +} + +// newPipeConnPair creates endpoints that are connected via pipes. +func newPipeConnPair() (a wirenet.Conn, b wirenet.Conn) { + c0, c1 := net.Pipe() + ser := perunio.Serializer() + return wirenet.NewIoConn(c0, ser), wirenet.NewIoConn(c1, ser) +} + +// NewRandomEnvelope returns an envelope around message m with random sender and +// recipient generated using randomness from rng. +func newRandomEnvelope(rng *rand.Rand, m wire.Msg) *wire.Envelope { + return &wire.Envelope{ + Sender: NewRandomAddresses(rng), + Recipient: NewRandomAddresses(rng), + Msg: m, + } +} From f5bde07784f65a097fde81a908c9f57067498268 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Wed, 30 Apr 2025 12:32:55 +0200 Subject: [PATCH 17/26] feat(wire/net): add bus test for libp2p and simple Signed-off-by: Minh Huy Tran --- wire/net/libp2p/account.go | 7 - wire/net/libp2p/account_test.go | 35 +++-- wire/net/libp2p/address_test.go | 4 + wire/net/libp2p/bus_internal_test.go | 78 ++++++++++ wire/net/libp2p/dialer_internal_test.go | 22 ++- wire/net/libp2p/listener_internal_test.go | 26 +++- wire/net/simple/bus_internal_test.go | 91 +++++++++++ wire/net/simple/dialer_internal_test.go | 177 +++++++++------------- wire/test/bustest.go | 2 +- 9 files changed, 300 insertions(+), 142 deletions(-) create mode 100644 wire/net/libp2p/bus_internal_test.go create mode 100644 wire/net/simple/bus_internal_test.go diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go index dcff50850..9f5e7f301 100644 --- a/wire/net/libp2p/account.go +++ b/wire/net/libp2p/account.go @@ -321,12 +321,6 @@ func getRelayServerInfo() (*peer.AddrInfo, string, error) { return relayInfo, relayAddr, nil } -// getHost returns a new random account for testing. -func getHost(rng *rand.Rand) *Account { - acc := NewRandomAccount(rng) - return acc -} - // keepReservationAlive keeps the reservation alive by periodically renewing it. func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) { const ( @@ -343,7 +337,6 @@ func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) for { select { case <-ctx.Done(): - log.Println("keepReservationAlive: context cancelled") err := acc.Close() if err != nil { panic(err) diff --git a/wire/net/libp2p/account_test.go b/wire/net/libp2p/account_test.go index c7a1ebb4d..78aa5f844 100644 --- a/wire/net/libp2p/account_test.go +++ b/wire/net/libp2p/account_test.go @@ -19,6 +19,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" "perun.network/go-perun/wallet" wtest "perun.network/go-perun/wallet/test" @@ -30,19 +31,19 @@ func TestNewAccount(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() + require.NoError(t, acc.Close()) } func TestAddressBookRegister(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) err := acc.RegisterOnChainAddress(onChainAddr) assert.NoError(t, err) + assert.NoError(t, acc.Close()) } func TestAddressBookRegisterEmptyAddress(t *testing.T) { @@ -50,18 +51,16 @@ func TestAddressBookRegisterEmptyAddress(t *testing.T) { acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() - var nilAddr wallet.Address err := acc.RegisterOnChainAddress(nilAddr) assert.Error(t, err) + assert.NoError(t, acc.Close()) } func TestAddressBookDeregister(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) @@ -76,6 +75,7 @@ func TestAddressBookDeregister(t *testing.T) { // Trying to query it again will fail _, err = acc.QueryOnChainAddress(onChainAddr) assert.Error(t, err) + assert.NoError(t, acc.Close()) } func TestAddressBookDeregisterPeer(t *testing.T) { @@ -121,7 +121,9 @@ func TestAddressBookQuery_Fail(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() + defer func() { + assert.NoError(t, acc.Close()) + }() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) @@ -133,7 +135,9 @@ func TestAddressBookQuery(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() + defer func() { + assert.NoError(t, acc.Close()) + }() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) @@ -155,11 +159,15 @@ func TestAddressBookQueryPeer(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() + defer func() { + assert.NoError(t, acc.Close()) + }() peer := libp2p.NewRandomAccount(rng) assert.NotNil(t, peer) - defer peer.Close() + defer func() { + assert.NoError(t, peer.Close()) + }() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) peerOnChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) @@ -188,7 +196,9 @@ func TestAddressBookRegisterQueryMultiple(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - defer acc.Close() + defer func() { + assert.NoError(t, acc.Close()) + }() onChainAddr := wtest.NewRandomAddress(rng, channel.TestBackendID) onChainAddr2 := wtest.NewRandomAddress(rng, channel.TestBackendID) @@ -223,8 +233,9 @@ func TestNewAccountFromPrivateKey(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) assert.NotNil(t, acc) - - defer acc.Close() + defer func() { + assert.NoError(t, acc.Close()) + }() keyBytes, err := acc.MarshalPrivateKey() assert.NoError(t, err) diff --git a/wire/net/libp2p/address_test.go b/wire/net/libp2p/address_test.go index 9c498b882..eb366f93d 100644 --- a/wire/net/libp2p/address_test.go +++ b/wire/net/libp2p/address_test.go @@ -37,6 +37,10 @@ func TestAddress(t *testing.T) { func TestSignature(t *testing.T) { rng := pkgtest.Prng(t) acc := libp2p.NewRandomAccount(rng) + defer func() { + assert.NoError(t, acc.Close()) + }() + assert.NotNil(t, acc) defer acc.Close() diff --git a/wire/net/libp2p/bus_internal_test.go b/wire/net/libp2p/bus_internal_test.go new file mode 100644 index 000000000..f1838a0db --- /dev/null +++ b/wire/net/libp2p/bus_internal_test.go @@ -0,0 +1,78 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libp2p + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "perun.network/go-perun/channel" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" + perunnet "perun.network/go-perun/wire/net" + perunio "perun.network/go-perun/wire/perunio/serializer" + wiretest "perun.network/go-perun/wire/test" +) + +func TestBus(t *testing.T) { + const numClients = 4 + const numMsgs = 4 + + i := 0 // Keep track of the current client ID + dialers := make([]*Dialer, numClients) + accs := make([]*Account, numClients) + defer func() { + // Close all accounts + for _, acc := range accs { + if acc != nil { + acc.Close() + } + } + }() + + wiretest.GenericBusTest(t, func(acc map[wallet.BackendID]wire.Account) (wire.Bus, wire.Bus) { + libp2pAcc, ok := acc[channel.TestBackendID].(*Account) + assert.True(t, ok) + + accs[i] = libp2pAcc + dialer := NewP2PDialer(libp2pAcc) + dialers[i] = dialer + + if i == numClients-1 { + // Register all peers + for j := range numClients { + for k := range numClients { + if j != k { + peerAcc := accs[k] + dialers[j].Register( + map[wallet.BackendID]wire.Address{ + channel.TestBackendID: peerAcc.Address(), + }, + peerAcc.ID().String()) + } + } + } + } + + listener := NewP2PListener(libp2pAcc) + + bus := perunnet.NewBus(acc, dialer, perunio.Serializer()) + go bus.Listen(listener) + + i++ + + return bus, bus + }, numClients, numMsgs) +} diff --git a/wire/net/libp2p/dialer_internal_test.go b/wire/net/libp2p/dialer_internal_test.go index e94e52837..2cacebeb9 100644 --- a/wire/net/libp2p/dialer_internal_test.go +++ b/wire/net/libp2p/dialer_internal_test.go @@ -33,9 +33,10 @@ import ( func TestNewDialer(t *testing.T) { rng := pkgtest.Prng(t) - h := getHost(rng) + acc := NewRandomAccount(rng) + defer acc.Close() - d := NewP2PDialer(h) + d := NewP2PDialer(acc) assert.NotNil(t, d) d.Close() } @@ -44,8 +45,9 @@ func TestDialer_Register(t *testing.T) { rng := pkgtest.Prng(t) addr := NewRandomAddress(rng) key := wire.Key(addr) - h := getHost(rng) - d := NewP2PDialer(h) + acc := NewRandomAccount(rng) + defer acc.Close() + d := NewP2PDialer(acc) defer d.Close() _, ok := d.get(key) @@ -64,7 +66,7 @@ func TestDialer_Dial(t *testing.T) { timeout := 2 * time.Second rng := pkgtest.Prng(t) - lHost := getHost(rng) + lHost := NewRandomAccount(rng) lAddr := lHost.Address() laddrs := make(map[wallet.BackendID]wire.Address) laddrs[test.TestBackendID] = lAddr @@ -72,7 +74,7 @@ func TestDialer_Dial(t *testing.T) { listener := NewP2PListener(lHost) defer listener.Close() - dHost := getHost(rng) + dHost := NewRandomAccount(rng) dAddr := dHost.Address() daddrs := make(map[wallet.BackendID]wire.Address) daddrs[test.TestBackendID] = dAddr @@ -140,13 +142,17 @@ func TestDialer_Dial(t *testing.T) { assert.Nil(t, conn) }) }) + + assert.NoError(t, lHost.Close()) + assert.NoError(t, dHost.Close()) } func TestDialer_Close(t *testing.T) { t.Run("double close", func(t *testing.T) { rng := pkgtest.Prng(t) - h := getHost(rng) - d := NewP2PDialer(h) + acc := NewRandomAccount(rng) + defer acc.Close() + d := NewP2PDialer(acc) assert.NoError(t, d.Close(), "first close must not return error") assert.Error(t, d.Close(), "second close must result in error") diff --git a/wire/net/libp2p/listener_internal_test.go b/wire/net/libp2p/listener_internal_test.go index b920569d9..153e4f4e2 100644 --- a/wire/net/libp2p/listener_internal_test.go +++ b/wire/net/libp2p/listener_internal_test.go @@ -27,8 +27,12 @@ import ( func TestNewListener(t *testing.T) { rng := pkgtest.Prng(t) - h := getHost(rng) - l := NewP2PListener(h) + acc := NewRandomAccount(rng) + defer func() { + assert.NoError(t, acc.Close()) + }() + + l := NewP2PListener(acc) defer l.Close() assert.NotNil(t, l) } @@ -36,8 +40,12 @@ func TestNewListener(t *testing.T) { func TestListener_Close(t *testing.T) { t.Run("double close", func(t *testing.T) { rng := pkgtest.Prng(t) - h := getHost(rng) - l := NewP2PListener(h) + acc := NewRandomAccount(rng) + defer func() { + assert.NoError(t, acc.Close()) + }() + + l := NewP2PListener(acc) assert.NoError(t, l.Close(), "first close must not return error") assert.Error(t, l.Close(), "second close must result in error") }) @@ -46,10 +54,14 @@ func TestListener_Close(t *testing.T) { func TestListener_Accept(t *testing.T) { // Happy case already tested in TestDialer_Dial. rng := pkgtest.Prng(t) - h := getHost(rng) + acc := NewRandomAccount(rng) + defer func() { + assert.NoError(t, acc.Close()) + }() + timeout := 100 * time.Millisecond t.Run("timeout", func(t *testing.T) { - l := NewP2PListener(h) + l := NewP2PListener(acc) defer l.Close() ctxtest.AssertNotTerminates(t, timeout, func() { @@ -59,7 +71,7 @@ func TestListener_Accept(t *testing.T) { }) t.Run("closed", func(t *testing.T) { - l := NewP2PListener(h) + l := NewP2PListener(acc) l.Close() ctxtest.AssertTerminates(t, timeout, func() { diff --git a/wire/net/simple/bus_internal_test.go b/wire/net/simple/bus_internal_test.go new file mode 100644 index 000000000..82146c097 --- /dev/null +++ b/wire/net/simple/bus_internal_test.go @@ -0,0 +1,91 @@ +// Copyright 2025 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package simple + +import ( + "fmt" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" + perunnet "perun.network/go-perun/wire/net" + perunio "perun.network/go-perun/wire/perunio/serializer" + wiretest "perun.network/go-perun/wire/test" +) + +func TestBus(t *testing.T) { + const numClients = 5 + const numMsgs = 5 + const defaultTimeout = 100 * time.Millisecond + + commonName := "127.0.0.1" + sans := []string{"127.0.0.1", "localhost"} + tlsConfigs, err := generateSelfSignedCertConfigs(commonName, sans, numClients) + assert.NoError(t, err) + + hosts := make([]string, numClients) + for i := range numClients { + port, err := findFreePort() + assert.NoError(t, err) + hosts[i] = fmt.Sprintf("127.0.0.1:%d", port) + } + + dialers := make([]*Dialer, numClients) + for j := range numClients { + dialers[j] = NewTCPDialer(defaultTimeout, tlsConfigs[j]) + defer dialers[j].Close() + } + + i := 0 + + listeners := make([]*Listener, numClients) + for j := range numClients { + listener, err := NewTCPListener(hosts[j], tlsConfigs[j]) + assert.NoError(t, err) + listeners[j] = listener + defer listeners[j].Close() + } + + wiretest.GenericBusTest(t, func(acc map[wallet.BackendID]wire.Account) (wire.Bus, wire.Bus) { + for j := range numClients { + dialers[j].Register( + wire.AddressMapfromAccountMap(acc), + hosts[i], + ) + } + + bus := perunnet.NewBus(acc, dialers[i], perunio.Serializer()) + go bus.Listen(listeners[i]) + i++ + return bus, bus + }, numClients, numMsgs) +} + +func findFreePort() (int, error) { + // Create a listener on a random port to get an available port. + l, err := net.Listen("tcp", ":0") //nolint:gosec // Use ":0" to bind to a random free port + if err != nil { + return 0, err + } + defer l.Close() + + // Get the port from the listener's address + addr := l.Addr().(*net.TCPAddr) + return addr.Port, nil +} diff --git a/wire/net/simple/dialer_internal_test.go b/wire/net/simple/dialer_internal_test.go index 7ac8c5d09..5b25455fc 100644 --- a/wire/net/simple/dialer_internal_test.go +++ b/wire/net/simple/dialer_internal_test.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "net" "testing" @@ -85,15 +86,15 @@ func TestDialer_Dial(t *testing.T) { commonName := "127.0.0.1" sans := []string{"127.0.0.1", "localhost"} - lConfig, dConfig, err := generateSelfSignedCertConfigs(commonName, sans) + tlsConfigs, err := generateSelfSignedCertConfigs(commonName, sans, 2) require.NoError(t, err, "failed to generate self-signed certificate configs") - l, err := NewTCPListener(lhost, lConfig) + l, err := NewTCPListener(lhost, tlsConfigs[0]) require.NoError(t, err) defer l.Close() ser := perunio.Serializer() - d := NewTCPDialer(timeout, dConfig) + d := NewTCPDialer(timeout, tlsConfigs[1]) d.Register(laddr, lhost) daddr := wire.AddressMapfromAccountMap(wiretest.NewRandomAccountMap(rng, channel.TestBackendID)) defer d.Close() @@ -159,121 +160,83 @@ func TestDialer_Dial(t *testing.T) { }) } -// generateSelfSignedCertConfigs generates a self-signed certificate and returns -// the server and client TLS configurations. -func generateSelfSignedCertConfigs(commonName string, sans []string) (*tls.Config, *tls.Config, error) { - keySize := 2048 - // Generate a new RSA private key for the server - serverPrivateKey, err := rsa.GenerateKey(rand.Reader, keySize) - if err != nil { - return nil, nil, err - } - - // Generate a new RSA private key for the client - clientPrivateKey, err := rsa.GenerateKey(rand.Reader, keySize) - if err != nil { - return nil, nil, err - } +const certificateTimeout = time.Hour - // Create a certificate template for the server - serverTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Perun Network"}, - CommonName: commonName, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - // Add SANs to the server certificate template - for _, san := range sans { - if ip := net.ParseIP(san); ip != nil { - serverTemplate.IPAddresses = append(serverTemplate.IPAddresses, ip) - } else { - serverTemplate.DNSNames = append(serverTemplate.DNSNames, san) +// generateSelfSignedCertConfigs generates self-signed certificates and returns +// a list of TLS configurations for n clients. +func generateSelfSignedCertConfigs(commonName string, sans []string, numClients int) ([]*tls.Config, error) { + keySize := 2048 + configs := make([]*tls.Config, numClients) + certPEMs := make([][]byte, numClients) + tlsCerts := make([]tls.Certificate, numClients) + + for i := range numClients { + // Private key for the client + privateKey, err := rsa.GenerateKey(rand.Reader, keySize) + if err != nil { + return nil, err } - } - - // Generate a self-signed server certificate - serverCertDER, err := x509.CreateCertificate(rand.Reader, &serverTemplate, &serverTemplate, &serverPrivateKey.PublicKey, serverPrivateKey) - if err != nil { - return nil, nil, err - } - - // Encode the server certificate to PEM format - serverCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCertDER}) - - // Encode the server private key to PEM format - serverKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(serverPrivateKey)}) - - // Create a tls.Certificate object for the server - serverCert, err := tls.X509KeyPair(serverCertPEM, serverKeyPEM) - if err != nil { - return nil, nil, err - } - // Create a certificate template for the client - clientTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Perun Network"}, - CommonName: commonName, // Change this to the client's common name - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(24 * time.Hour), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, // Set the client authentication usage - BasicConstraintsValid: true, - } + // Create a certificate template + template := x509.Certificate{ + SerialNumber: big.NewInt(int64(i) + 1), + Subject: pkix.Name{ + Organization: []string{"Perun Network"}, + CommonName: fmt.Sprintf("%s-client-%d", commonName, i+1), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(certificateTimeout), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } - // Generate a self-signed client certificate - clientCertDER, err := x509.CreateCertificate(rand.Reader, &clientTemplate, &clientTemplate, &clientPrivateKey.PublicKey, serverPrivateKey) - if err != nil { - return nil, nil, err - } + // Add SANs to the server certificate template + for _, san := range sans { + if ip := net.ParseIP(san); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, san) + } + } - // Encode the client certificate to PEM format - clientCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: clientCertDER}) + // Generate a self-signed server certificate + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) + if err != nil { + return nil, err + } - // Encode the client private key to PEM format - clientKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(clientPrivateKey)}) + // Encode the server certificate to PEM format + certPEMs[i] = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) - // Create a tls.Certificate object for the client - clientCert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM) - if err != nil { - return nil, nil, err - } + // Encode the server private key to PEM format + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) - serverCertPool := x509.NewCertPool() - ok := serverCertPool.AppendCertsFromPEM(clientCertPEM) - if !ok { - return nil, nil, errors.New("failed to parse root certificate") - } - - // Create the server-side TLS configuration - serverConfig := &tls.Config{ - ClientCAs: serverCertPool, - Certificates: []tls.Certificate{serverCert}, - ClientAuth: tls.RequireAndVerifyClientCert, - MinVersion: tls.VersionTLS12, // Set minimum TLS version to TLS 1.2 + // Create a tls.Certificate object for the server + tlsCerts[i], err = tls.X509KeyPair(certPEMs[i], keyPEM) + if err != nil { + return nil, err + } } - clientCertPool := x509.NewCertPool() - ok = clientCertPool.AppendCertsFromPEM(serverCertPEM) - if !ok { - return nil, nil, errors.New("failed to parse root certificate") - } + for i := range numClients { + certPool := x509.NewCertPool() + for j := range numClients { + ok := certPool.AppendCertsFromPEM(certPEMs[j]) + if !ok { + return nil, errors.New("failed to parse root certificate") + } + } - // Create the client-side TLS configuration - clientConfig := &tls.Config{ - RootCAs: clientCertPool, - Certificates: []tls.Certificate{clientCert}, - MinVersion: tls.VersionTLS12, // Set minimum TLS version to TLS 1.2 + // Create the server-side TLS configuration + configs[i] = &tls.Config{ + RootCAs: certPool, + ClientCAs: certPool, + Certificates: []tls.Certificate{tlsCerts[i]}, + ClientAuth: tls.RequireAndVerifyClientCert, + MinVersion: tls.VersionTLS12, // Set minimum TLS version to TLS 1.2 + } } - return serverConfig, clientConfig, nil + return configs, nil } diff --git a/wire/test/bustest.go b/wire/test/bustest.go index 6403fa1de..44e17e21c 100644 --- a/wire/test/bustest.go +++ b/wire/test/bustest.go @@ -90,7 +90,7 @@ func GenericBusTest(t *testing.T, ct := test.NewConcurrent(t) ctx, cancel := context.WithTimeout( context.Background(), - time.Duration((numClients)*(numClients-1)*numMsgs)*10*time.Millisecond) + time.Duration((numClients)*(numClients-1)*numMsgs)*100*time.Millisecond) defer cancel() waiting() for sender := range clients { From 8f6050cb4409c34fc4e1e2e07869cd59b0b6e676 Mon Sep 17 00:00:00 2001 From: Sophia Koehler Date: Wed, 21 May 2025 11:29:16 +0200 Subject: [PATCH 18/26] fix(channel/backend.go): Dynamic use of backends instead of hardcoded index feat(client/): Allow multiple Assets in Test Signed-off-by: Sophia Koehler --- channel/backend.go | 4 ++-- client/appchannel_test.go | 6 ++--- client/client_role_test.go | 6 ++--- client/payment_test.go | 6 ++--- client/subchannel_test.go | 12 +++++----- client/test/channel.go | 4 ++-- client/test/role.go | 38 ++++++++++++++++++------------- client/test/subchannel.go | 2 +- client/test/subchannel_dispute.go | 2 +- 9 files changed, 43 insertions(+), 37 deletions(-) diff --git a/channel/backend.go b/channel/backend.go index 7ca3633b5..189e2999d 100644 --- a/channel/backend.go +++ b/channel/backend.go @@ -66,8 +66,8 @@ func SetBackend(b Backend, id int) { // CalcID calculates the CalcID. func CalcID(p *Params) (ID, error) { var lastErr error - for _, b := range backend { - id, err := b.CalcID(p) + for i := range p.Parts[0] { + id, err := backend[i].CalcID(p) if err == nil { return id, nil } diff --git a/client/appchannel_test.go b/client/appchannel_test.go index e21127b68..d5e97ed85 100644 --- a/client/appchannel_test.go +++ b/client/appchannel_test.go @@ -45,9 +45,9 @@ func TestProgression(t *testing.T) { execConfig := &clienttest.ProgressionExecConfig{ BaseExecConfig: clienttest.MakeBaseExecConfig( [2]map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(setups[0].Identity), wire.AddressMapfromAccountMap(setups[1].Identity)}, - chtest.NewRandomAsset(rng, channel.TestBackendID), - channel.TestBackendID, - [2]*big.Int{big.NewInt(99), big.NewInt(1)}, + []channel.Asset{chtest.NewRandomAsset(rng, channel.TestBackendID)}, + []wallet.BackendID{channel.TestBackendID}, + [][2]*big.Int{{big.NewInt(99), big.NewInt(1)}}, client.WithApp(app, channel.NewMockOp(channel.OpValid)), ), } diff --git a/client/client_role_test.go b/client/client_role_test.go index 0de26193b..980b41440 100644 --- a/client/client_role_test.go +++ b/client/client_role_test.go @@ -89,9 +89,9 @@ func runAliceBobTest(ctx context.Context, t *testing.T, setup func(*rand.Rand) ( cfg := &ctest.AliceBobExecConfig{ BaseExecConfig: ctest.MakeBaseExecConfig( [2]map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(setups[0].Identity), wire.AddressMapfromAccountMap(setups[1].Identity)}, - chtest.NewRandomAsset(rng, channel.TestBackendID), - channel.TestBackendID, - [2]*big.Int{big.NewInt(100), big.NewInt(100)}, + []channel.Asset{chtest.NewRandomAsset(rng, channel.TestBackendID)}, + []wallet.BackendID{channel.TestBackendID}, + [][2]*big.Int{{big.NewInt(100), big.NewInt(100)}}, app, ), NumPayments: [2]int{2, 2}, diff --git a/client/payment_test.go b/client/payment_test.go index 5865eec4b..c5d0d6a48 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -61,9 +61,9 @@ func TestPaymentDispute(t *testing.T) { cfg := &ctest.MalloryCarolExecConfig{ BaseExecConfig: ctest.MakeBaseExecConfig( [2]map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(setups[mallory].Identity), wire.AddressMapfromAccountMap(setups[carol].Identity)}, - chtest.NewRandomAsset(rng, channel.TestBackendID), - channel.TestBackendID, - [2]*big.Int{big.NewInt(100), big.NewInt(1)}, + []channel.Asset{chtest.NewRandomAsset(rng, channel.TestBackendID)}, + []wallet.BackendID{channel.TestBackendID}, + [][2]*big.Int{{big.NewInt(100), big.NewInt(1)}}, client.WithoutApp(), ), NumPayments: [2]int{5, 0}, diff --git a/client/subchannel_test.go b/client/subchannel_test.go index 50b4c3a1f..a4bbc0536 100644 --- a/client/subchannel_test.go +++ b/client/subchannel_test.go @@ -43,9 +43,9 @@ func TestSubChannelHappy(t *testing.T) { cfg := ctest.NewSusieTimExecConfig( ctest.MakeBaseExecConfig( [2]map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(setups[0].Identity), wire.AddressMapfromAccountMap(setups[1].Identity)}, - chtest.NewRandomAsset(rng, channel.TestBackendID), - channel.TestBackendID, - [2]*big.Int{big.NewInt(100), big.NewInt(100)}, + []channel.Asset{chtest.NewRandomAsset(rng, channel.TestBackendID)}, + []wallet.BackendID{channel.TestBackendID}, + [][2]*big.Int{{big.NewInt(100), big.NewInt(100)}}, client.WithoutApp(), ), 2, @@ -81,9 +81,9 @@ func TestSubChannelDispute(t *testing.T) { baseCfg := ctest.MakeBaseExecConfig( [2]map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(setups[0].Identity), wire.AddressMapfromAccountMap(setups[1].Identity)}, - chtest.NewRandomAsset(rng, channel.TestBackendID), - channel.TestBackendID, - [2]*big.Int{big.NewInt(100), big.NewInt(100)}, + []channel.Asset{chtest.NewRandomAsset(rng, channel.TestBackendID)}, + []wallet.BackendID{channel.TestBackendID}, + [][2]*big.Int{{big.NewInt(100), big.NewInt(100)}}, client.WithoutApp(), ) cfg := &ctest.DisputeSusieTimExecConfig{ diff --git a/client/test/channel.go b/client/test/channel.go index 28b7c6c9e..ee1b02092 100644 --- a/client/test/channel.go +++ b/client/test/channel.go @@ -85,8 +85,8 @@ func (ch *paymentChannel) openSubChannel( bID wallet.BackendID, ) *paymentChannel { initAlloc := channel.Allocation{ - Assets: []channel.Asset{cfg.Asset()}, - Backends: []wallet.BackendID{bID}, + Assets: cfg.Asset(), + Backends: cfg.Backend(), Balances: [][]channel.Bal{{initBals[0], initBals[1]}}, } diff --git a/client/test/role.go b/client/test/role.go index 9f74a92c6..c4af9cefa 100644 --- a/client/test/role.go +++ b/client/test/role.go @@ -86,18 +86,18 @@ type ( // ExecConfig contains additional config parameters for the tests. ExecConfig interface { Peers() [2]map[wallet.BackendID]wire.Address // must match the RoleSetup.Identity's - Asset() channel.Asset // single Asset to use in this channel - Backend() wallet.BackendID // backend corresponding to asset - InitBals() [2]*big.Int // channel deposit of each role + Asset() []channel.Asset // single Asset to use in this channel + Backend() []wallet.BackendID // backend corresponding to asset + InitBals() [][2]*big.Int // channel deposit of each role App() client.ProposalOpts // must be either WithApp or WithoutApp } // BaseExecConfig contains base config parameters. BaseExecConfig struct { peers [2]map[wallet.BackendID]wire.Address // must match the RoleSetup.Identity's - asset channel.Asset // single Asset to use in this channel - backend wallet.BackendID // backend corresponding to asset - initBals [2]*big.Int // channel deposit of each role + asset []channel.Asset // single Asset to use in this channel + backend []wallet.BackendID // backend corresponding to asset + initBals [][2]*big.Int // channel deposit of each role app client.ProposalOpts // must be either WithApp or WithoutApp } @@ -185,9 +185,9 @@ func ExecuteTwoPartyTest(ctx context.Context, t *testing.T, role [2]Executer, cf // MakeBaseExecConfig creates a new BaseExecConfig. func MakeBaseExecConfig( peers [2]map[wallet.BackendID]wire.Address, - asset channel.Asset, - backend wallet.BackendID, - initBals [2]*big.Int, + asset []channel.Asset, + backend []wallet.BackendID, + initBals [][2]*big.Int, app client.ProposalOpts, ) BaseExecConfig { return BaseExecConfig{ @@ -205,17 +205,17 @@ func (c *BaseExecConfig) Peers() [2]map[wallet.BackendID]wire.Address { } // Asset returns the asset. -func (c *BaseExecConfig) Asset() channel.Asset { +func (c *BaseExecConfig) Asset() []channel.Asset { return c.asset } // Backend returns the asset. -func (c *BaseExecConfig) Backend() wallet.BackendID { +func (c *BaseExecConfig) Backend() []wallet.BackendID { return c.backend } // InitBals returns the initial balances. -func (c *BaseExecConfig) InitBals() [2]*big.Int { +func (c *BaseExecConfig) InitBals() [][2]*big.Int { return c.initBals } @@ -355,13 +355,19 @@ func (r *role) LedgerChannelProposal(rng *rand.Rand, cfg ExecConfig) *client.Led r.log.Panic("Invalid ExecConfig: App does not specify an app.") } - peers, asset, bals := cfg.Peers(), cfg.Asset(), cfg.InitBals() - alloc := channel.NewAllocation(len(peers), []wallet.BackendID{cfg.Backend()}, asset) - alloc.SetAssetBalances(asset, bals[:]) + peers, assets, bals, backend := cfg.Peers(), cfg.Asset(), cfg.InitBals(), cfg.Backend() + alloc := channel.NewAllocation(len(peers), cfg.Backend(), assets...) + for i := range assets { + alloc.SetAssetBalances(assets[i], bals[i][:]) + } + peersList := make(map[wallet.BackendID]wallet.Address) + for i := range backend { + peersList[backend[i]] = r.setup.Wallet[backend[i]].NewRandomAccount(rng).Address() + } prop, err := client.NewLedgerChannelProposal( r.challengeDuration, - map[wallet.BackendID]wallet.Address{cfg.Backend(): r.setup.Wallet[cfg.Backend()].NewRandomAccount(rng).Address()}, + peersList, alloc, peers[:], client.WithNonceFrom(rng), diff --git a/client/test/subchannel.go b/client/test/subchannel.go index 782489a4c..cedf209ce 100644 --- a/client/test/subchannel.go +++ b/client/test/subchannel.go @@ -77,7 +77,7 @@ func (r *Susie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { // stage 2 - open subchannels openSubChannel := func(parentChannel *paymentChannel, funds []*big.Int, app client.ProposalOpts) *paymentChannel { - return parentChannel.openSubChannel(rng, cfg, funds, app, cfg.backend) + return parentChannel.openSubChannel(rng, cfg, funds, app, cfg.backend[0]) } var subChannels []*paymentChannel diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index 45692d66a..e897b94b2 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -58,7 +58,7 @@ func (r *DisputeSusie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { r.waitStage() // Stage 2 - Open sub-channel. - subChannel := ledgerChannel.openSubChannel(rng, cfg, cfg.SubChannelFunds[:], client.WithoutApp(), _cfg.Backend()) + subChannel := ledgerChannel.openSubChannel(rng, cfg, cfg.SubChannelFunds[:], client.WithoutApp(), _cfg.Backend()[0]) subReq0 := client.NewTestChannel(subChannel.Channel).AdjudicatorReq() // Store AdjudicatorReq for version 0 r.waitStage() From 93a45591b742ee5cd60fd7fdcb18d6076ade2935 Mon Sep 17 00:00:00 2001 From: Sophia Koehler Date: Wed, 28 May 2025 11:29:38 +0200 Subject: [PATCH 19/26] fix(client/test): Remove unused argument in openSubChannel Signed-off-by: Sophia Koehler --- client/test/channel.go | 3 --- client/test/subchannel.go | 2 +- client/test/subchannel_dispute.go | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client/test/channel.go b/client/test/channel.go index ee1b02092..264d4bba9 100644 --- a/client/test/channel.go +++ b/client/test/channel.go @@ -21,8 +21,6 @@ import ( "math/big" "time" - "perun.network/go-perun/wallet" - "perun.network/go-perun/channel" "perun.network/go-perun/client" "perun.network/go-perun/log" @@ -82,7 +80,6 @@ func (ch *paymentChannel) openSubChannel( cfg ExecConfig, initBals []*big.Int, app client.ProposalOpts, - bID wallet.BackendID, ) *paymentChannel { initAlloc := channel.Allocation{ Assets: cfg.Asset(), diff --git a/client/test/subchannel.go b/client/test/subchannel.go index cedf209ce..0d0c14893 100644 --- a/client/test/subchannel.go +++ b/client/test/subchannel.go @@ -77,7 +77,7 @@ func (r *Susie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { // stage 2 - open subchannels openSubChannel := func(parentChannel *paymentChannel, funds []*big.Int, app client.ProposalOpts) *paymentChannel { - return parentChannel.openSubChannel(rng, cfg, funds, app, cfg.backend[0]) + return parentChannel.openSubChannel(rng, cfg, funds, app) } var subChannels []*paymentChannel diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index e897b94b2..2b1b97952 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -58,7 +58,7 @@ func (r *DisputeSusie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { r.waitStage() // Stage 2 - Open sub-channel. - subChannel := ledgerChannel.openSubChannel(rng, cfg, cfg.SubChannelFunds[:], client.WithoutApp(), _cfg.Backend()[0]) + subChannel := ledgerChannel.openSubChannel(rng, cfg, cfg.SubChannelFunds[:], client.WithoutApp()) subReq0 := client.NewTestChannel(subChannel.Channel).AdjudicatorReq() // Store AdjudicatorReq for version 0 r.waitStage() From 8cd8f575cd77e67ca3b1258a7d51da4338de528d Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 14 Jul 2025 18:56:49 +0200 Subject: [PATCH 20/26] fix(all): changes according to PR's review Signed-off-by: Minh Huy Tran --- .golangci.json | 4 +- apps/payment/app.go | 1 + apps/payment/app_internal_test.go | 4 ++ backend/sim/channel/asset.go | 17 ++----- backend/sim/channel/asset_test.go | 2 +- backend/sim/wallet/address.go | 11 ++--- backend/sim/wire/address.go | 3 ++ channel/adjudicator.go | 19 ++++---- channel/allocation.go | 28 +++++------- channel/allocation_test.go | 8 +++- channel/appregistry.go | 1 + channel/errors.go | 3 +- channel/machine.go | 13 ++++-- channel/multi/adjudicator.go | 2 + channel/multi/asset.go | 4 ++ channel/multi/funder.go | 2 + channel/multi/subscription.go | 1 + .../persistence/keyvalue/persistedstate.go | 3 +- .../keyvalue/persistrestorer_internal_test.go | 1 + channel/persistence/persistence.go | 1 + channel/persistence/statemachine.go | 1 + channel/persistence/test/channel.go | 14 +++--- channel/persistence/test/peerchans.go | 1 + .../persistence/test/persistrestorertest.go | 2 + channel/state.go | 8 ++-- channel/test/app_randomizer.go | 1 + channel/test/app_randomizer_internal_test.go | 2 + channel/test/randomizer.go | 1 + channel/transaction.go | 2 + client/adjudicate.go | 11 +++-- client/channel.go | 1 + client/channelconn.go | 9 ++-- client/client.go | 4 +- client/client_test.go | 2 + client/clientconn.go | 10 +++-- client/failing_funding_test.go | 1 + client/multiledger_test.go | 4 ++ client/payment_test.go | 2 + client/proposal.go | 1 + client/proposalmsgs.go | 5 +++ client/restore.go | 1 + client/restore_internal_test.go | 1 + client/subchannel_test.go | 2 - client/sync.go | 2 + client/test/alice.go | 1 + client/test/backend.go | 1 + client/test/carol.go | 1 + client/test/channel.go | 4 ++ client/test/fund.go | 1 + client/test/mallory.go | 1 + client/test/multiledger.go | 1 + client/test/paymentchannel.go | 4 +- client/test/persistence.go | 1 + client/test/progression.go | 2 + client/test/proposalmsgs.go | 1 + client/test/responder.go | 1 + client/test/role.go | 5 ++- client/test/subchannel.go | 3 +- client/test/subchannel_dispute.go | 2 + client/test/updatemsgs.go | 9 ++-- client/test/virtualchannel.go | 4 +- client/update.go | 1 + client/updateinterception.go | 4 +- client/updatemsgs.go | 5 +++ client/virtual_channel.go | 3 +- client/virtual_channel_settlement.go | 1 + client/virtual_channel_util.go | 4 ++ log/levellified.go | 1 + log/levellified_internal_test.go | 2 + log/log.go | 3 ++ log/none_internal_test.go | 1 + wallet/address.go | 44 ++++++++++++++----- wallet/address_test.go | 1 + wallet/backend.go | 1 + wallet/sig.go | 1 + wallet/test/address.go | 1 + wallet/test/randomizer.go | 2 + wallet/test/wallet.go | 5 +++ wallet/test/walletbench.go | 2 + watcher/local/registry.go | 1 + watcher/local/watcher.go | 20 ++++++++- watcher/local/watcher_test.go | 5 ++- wire/account.go | 7 ++- wire/address.go | 14 ++++-- wire/cache_internal_test.go | 1 + wire/hybridbus.go | 1 + wire/net/bus.go | 3 ++ wire/net/endpoint.go | 1 + wire/net/endpoint_internal_test.go | 3 +- wire/net/endpoint_registry.go | 9 ++-- wire/net/endpoint_registry_internal_test.go | 3 ++ wire/net/exchange_addr_internal_test.go | 1 + wire/net/libp2p/account.go | 10 ++++- wire/net/libp2p/bus_internal_test.go | 1 + wire/net/libp2p/dialer.go | 4 +- wire/net/libp2p/dialer_internal_test.go | 2 + wire/net/libp2p/listener_internal_test.go | 3 ++ wire/net/simple/dialer.go | 4 +- wire/net/test/connhub.go | 18 +++++--- wire/net/test/connhub_internal_test.go | 2 +- wire/net/test/dialer.go | 4 +- wire/net/test/listener.go | 4 +- wire/net/test/pipeconn.go | 3 +- wire/perunio/bigint.go | 13 ++++-- wire/perunio/bigint_external_test.go | 2 + wire/perunio/byteslice.go | 2 + wire/perunio/byteslice_external_test.go | 1 + wire/perunio/equal_binary.go | 1 + wire/perunio/equal_encoding.go | 7 ++- wire/perunio/serialize.go | 4 ++ wire/perunio/serializer/serializer.go | 2 + wire/perunio/string.go | 12 +++-- wire/perunio/string_internal_test.go | 3 ++ wire/perunio/test/serializertest.go | 4 ++ wire/perunio/wire_internal_test.go | 1 + wire/protobuf/controlmsgs.go | 3 ++ wire/protobuf/proposalmsgs.go | 16 +++++++ wire/protobuf/serializer.go | 4 ++ wire/protobuf/syncmsgs.go | 2 + wire/protobuf/updatemsgs.go | 1 + wire/receiver.go | 4 +- wire/receiver_internal_test.go | 2 + wire/relay.go | 2 + wire/relay_internal_test.go | 1 + wire/test/bustest.go | 10 +++++ wire/test/marshalertest.go | 1 + wire/test/msgstest.go | 5 +++ wire/test/serializinglocalbus.go | 1 + 128 files changed, 425 insertions(+), 143 deletions(-) diff --git a/.golangci.json b/.golangci.json index 493d210a8..4ddb238e2 100644 --- a/.golangci.json +++ b/.golangci.json @@ -37,7 +37,9 @@ "depguard", "nonamedreturns", "inamedparam", - "exhaustruct" + "exhaustruct", + "wsl_v5", + "noinlineerr" ], "exclusions": { "generated": "lax", diff --git a/apps/payment/app.go b/apps/payment/app.go index 4619656db..6290f7df0 100644 --- a/apps/payment/app.go +++ b/apps/payment/app.go @@ -45,6 +45,7 @@ func (a *App) NewData() channel.Data { // participants. func (a *App) ValidTransition(_ *channel.Params, from, to *channel.State, actor channel.Index) error { assertNoData(to) + for i, asset := range from.Balances { for j, bal := range asset { if int(actor) == j && bal.Cmp(to.Balances[i][j]) == -1 { diff --git a/apps/payment/app_internal_test.go b/apps/payment/app_internal_test.go index 7bcac626a..867513c68 100644 --- a/apps/payment/app_internal_test.go +++ b/apps/payment/app_internal_test.go @@ -37,8 +37,10 @@ func TestApp_ValidInit(t *testing.T) { app := new(App) nildata := &channel.State{Data: nil} + assert.Panics(func() { app.ValidInit(nil, nildata) }) //nolint:errcheck wrongdata := &channel.State{Data: new(channel.MockOp)} + assert.Panics(func() { app.ValidInit(nil, wrongdata) }) //nolint:errcheck data := &channel.State{Data: Data()} @@ -50,6 +52,7 @@ func TestApp_ValidTransition(t *testing.T) { alloc = [][]int64 to struct { alloc + valid int // the valid actor index, or -1 if there's no valid actor } ) @@ -120,6 +123,7 @@ func TestApp_ValidTransition(t *testing.T) { from := test.NewRandomState(rng, test.WithApp(app), test.WithBalances(asBalances(tests[0].from...)...), test.WithNumAssets(len(tests[0].from))) to := from.Clone() to.Data = nil + assert.Panics(t, func() { app.ValidTransition(nil, from, to, 0) }) //nolint:errcheck }) diff --git a/backend/sim/channel/asset.go b/backend/sim/channel/asset.go index 403b07379..4fe0fcd7f 100644 --- a/backend/sim/channel/asset.go +++ b/backend/sim/channel/asset.go @@ -17,10 +17,8 @@ package channel import ( "encoding/binary" "fmt" - "math" "math/rand" - "github.com/pkg/errors" "perun.network/go-perun/channel" ) @@ -33,23 +31,20 @@ var byteOrder = binary.BigEndian // Asset simulates a `channel.Asset` by only containing an `ID`. type Asset struct { - ID int64 + ID uint64 } var _ channel.Asset = new(Asset) // NewRandomAsset returns a new random sim Asset. func NewRandomAsset(rng *rand.Rand) *Asset { - return &Asset{ID: rng.Int63()} + return &Asset{ID: rng.Uint64()} } // MarshalBinary marshals the address into its binary representation. func (a Asset) MarshalBinary() ([]byte, error) { data := make([]byte, assetLen) - if a.ID < 0 { - return nil, errors.New("asset ID must be non-negative") - } - byteOrder.PutUint64(data, uint64(a.ID)) + byteOrder.PutUint64(data, a.ID) return data, nil } @@ -58,11 +53,7 @@ func (a *Asset) UnmarshalBinary(data []byte) error { if len(data) != assetLen { return fmt.Errorf("unexpected length %d, want %d", len(data), assetLen) // We do not want to define this as constant error. } - id := byteOrder.Uint64(data) - if id > math.MaxInt64 { - return fmt.Errorf("asset ID %d is too large", id) - } - a.ID = int64(id) + a.ID = byteOrder.Uint64(data) return nil } diff --git a/backend/sim/channel/asset_test.go b/backend/sim/channel/asset_test.go index b710b2e96..ba86f3811 100644 --- a/backend/sim/channel/asset_test.go +++ b/backend/sim/channel/asset_test.go @@ -25,6 +25,6 @@ import ( func Test_Asset_GenericMarshaler(t *testing.T) { rng := pkgtest.Prng(t) for range 10 { - test.GenericMarshalerTest(t, &channel.Asset{ID: rng.Int63()}) + test.GenericMarshalerTest(t, &channel.Asset{ID: rng.Uint64()}) } } diff --git a/backend/sim/wallet/address.go b/backend/sim/wallet/address.go index 9642181d2..1b8b8af9f 100644 --- a/backend/sim/wallet/address.go +++ b/backend/sim/wallet/address.go @@ -32,16 +32,11 @@ type Address ecdsa.PublicKey // NewRandomAddress creates a new address using the randomness // provided by rng. func NewRandomAddress(rng io.Reader) *Address { - privateKey, err := ecdsa.GenerateKey(curve, rng) + key, err := ecdsa.GenerateKey(curve, rng) if err != nil { - log.Panicf("Creation of account failed with error", err) - } - - return &Address{ - Curve: privateKey.Curve, - X: privateKey.X, - Y: privateKey.Y, + log.Panicf("account creation failed: %v", err) } + return (*Address)(&key.PublicKey) } // BackendID returns the backend id of the address. diff --git a/backend/sim/wire/address.go b/backend/sim/wire/address.go index d43d50c61..349696424 100644 --- a/backend/sim/wire/address.go +++ b/backend/sim/wire/address.go @@ -36,10 +36,12 @@ func NewAddress() *Address { // NewRandomAddress returns a new random peer address. func NewRandomAddress(rng *rand.Rand) *Address { addr := Address{} + _, err := rng.Read(addr[:]) if err != nil { panic(err) } + return &addr } @@ -51,6 +53,7 @@ func (a Address) MarshalBinary() (data []byte, err error) { // UnmarshalBinary unmarshals an address from binary. func (a *Address) UnmarshalBinary(data []byte) error { copy(a[:], data) + return nil } diff --git a/channel/adjudicator.go b/channel/adjudicator.go index dc020af39..43452a5bf 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -126,9 +126,10 @@ type ( // A ProgressReq collects all necessary information to do a progress call to // the adjudicator. ProgressReq struct { - AdjudicatorReq // Tx should refer to the currently registered state - NewState *State // New state to progress into - Sig wallet.Sig // Own signature on the new state + AdjudicatorReq // Tx should refer to the currently registered state + + NewState *State // New state to progress into + Sig wallet.Sig // Own signature on the new state } // An AdjudicatorSubscription is a subscription to AdjudicatorEvents for a @@ -173,17 +174,19 @@ type ( // ProgressedEvent is the abstract event that signals an on-chain progression. ProgressedEvent struct { - AdjudicatorEventBase // Channel ID and ForceExec phase timeout - State *State // State that was progressed into - Idx Index // Index of the participant who progressed + AdjudicatorEventBase // Channel ID and ForceExec phase timeout + + State *State // State that was progressed into + Idx Index // Index of the participant who progressed } // RegisteredEvent is the abstract event that signals a successful state // registration on the blockchain. RegisteredEvent struct { AdjudicatorEventBase // Channel ID and Refutation phase timeout - State *State - Sigs []wallet.Sig + + State *State + Sigs []wallet.Sig } // ConcludedEvent signals channel conclusion. diff --git a/channel/allocation.go b/channel/allocation.go index 4b21c4273..5bfb80552 100644 --- a/channel/allocation.go +++ b/channel/allocation.go @@ -65,12 +65,13 @@ type ( // // Locked holds the locked allocations to sub-app-channels. Allocation struct { + // Balances is the allocation of assets to the Params.Parts + Balances + // Backends is the indexes to which backend the assets belong to Backends []wallet.BackendID // Assets are the asset types held in this channel Assets []Asset - // Balances is the allocation of assets to the Params.Parts - Balances // Locked describes the funds locked in subchannels. There is one entry // per subchannel. Locked []SubAlloc @@ -353,18 +354,23 @@ func (b Balances) Encode(w io.Writer) error { if numAssets > 0 { numParts = len(b[0]) } + if numAssets > MaxNumAssets { return errors.Errorf("expected maximum number of assets %d, got %d", MaxNumAssets, numAssets) } + if numParts > MaxNumParts { return errors.Errorf("expected maximum number of parts %d, got %d", MaxNumParts, numParts) } + if numParts < 0 || numParts > math.MaxUint16 { return errors.New("too many participants") } + if err := perunio.Encode(w, Index(numAssets), Index(numParts)); err != nil { return errors.WithMessage(err, "encoding dimensions") } + for i := range b { for j := range b[i] { if err := perunio.Encode(w, b[i][j]); err != nil { @@ -373,6 +379,7 @@ func (b Balances) Encode(w io.Writer) error { } } } + return nil } @@ -403,20 +410,7 @@ func (a Allocation) Encode(w io.Writer) error { err, "invalid allocations cannot be encoded, got %v", a) } // encode dimensions - assetLen := len(a.Assets) - if assetLen > math.MaxUint16 { - return errors.New("too many assets") - } - balanceLen := len(a.Balances[0]) - if balanceLen > math.MaxUint16 { - return errors.New("too many participants") - } - lockedLen := len(a.Locked) - if lockedLen > math.MaxUint16 { - return errors.New("too many sub-allocations") - } - - if err := perunio.Encode(w, Index(assetLen), Index(balanceLen), Index(lockedLen)); err != nil { + if err := perunio.Encode(w, Index(len(a.Assets)), Index(len(a.Balances[0])), Index(len(a.Locked))); err != nil { //nolint:gosec return err } // encode assets @@ -519,6 +513,7 @@ func (a Allocation) Valid() error { if len(a.Assets) == 0 || len(a.Balances) == 0 { return errors.New("assets and participant balances must not be of length zero (or nil)") } + if len(a.Assets) > MaxNumAssets || len(a.Locked) > MaxNumSubAllocations { return errors.New("too many assets or sub-allocations") } @@ -779,6 +774,7 @@ func (s *SubAlloc) BalancesEqual(b []Bal) bool { if len(s.Bals) != len(b) { return false } + for i, bal := range s.Bals { if bal.Cmp(b[i]) != 0 { return false diff --git a/channel/allocation_test.go b/channel/allocation_test.go index d9904a86a..33753a2c4 100644 --- a/channel/allocation_test.go +++ b/channel/allocation_test.go @@ -73,6 +73,7 @@ func randomBalancesWithMismatchingNumAssets(rng *rand.Rand, rngBase int) (b1, b2 return 1 + rng.Intn(rngBase) } numAssets1 := randomNumAssets() + numAssets2 := randomNumAssets() for numAssets2 == numAssets1 { numAssets2 = randomNumAssets() @@ -91,6 +92,7 @@ func randomBalancesWithMismatchingNumParts(rng *rand.Rand, rngBase int) (b1, b2 return 2 + rng.Intn(rngBase) } numParts1 := randomNumParts() + numParts2 := randomNumParts() for numParts2 == numParts1 { numParts2 = randomNumParts() @@ -338,7 +340,8 @@ func TestAllocation_Clone(t *testing.T) { for _, _tt := range tests { tt := _tt t.Run(tt.name, func(t *testing.T) { - if err := tt.alloc.Valid(); err != nil { + err := tt.alloc.Valid() + if err != nil { t.Fatal(err.Error()) } pkgtest.VerifyClone(t, tt.alloc) @@ -554,7 +557,8 @@ func TestAllocation_Valid(t *testing.T) { for _, _tt := range tests { tt := _tt t.Run(tt.name, func(t *testing.T) { - if got := tt.alloc.Valid(); (got == nil) != tt.valid { + got := tt.alloc.Valid() + if (got == nil) != tt.valid { t.Errorf("Allocation.valid() = %v, want valid = %v", got, tt.valid) } }) diff --git a/channel/appregistry.go b/channel/appregistry.go index 5200081fe..a59353a00 100644 --- a/channel/appregistry.go +++ b/channel/appregistry.go @@ -29,6 +29,7 @@ type AppIDKey string type appReg struct { sync.RWMutex + resolvers []appRegEntry singles map[AppIDKey]App defaultRes AppResolver diff --git a/channel/errors.go b/channel/errors.go index fef8bf4ae..20d5952e3 100644 --- a/channel/errors.go +++ b/channel/errors.go @@ -34,9 +34,10 @@ type ( // PhaseTransitionError happens in case of an invalid channel machine phase // transition. PhaseTransitionError struct { + PhaseTransition + ID ID current Phase - PhaseTransition } ) diff --git a/channel/machine.go b/channel/machine.go index 2d41ff728..fdf4b19d3 100644 --- a/channel/machine.go +++ b/channel/machine.go @@ -43,6 +43,13 @@ func (i *Index) Decode(r stdio.Reader) error { return err } +func FromInt(idx int) (Index, error) { + if idx < 0 || idx > math.MaxUint16 { + return 0, errors.Errorf("index out of bounds: %d", idx) + } + return Index(idx), nil +} + type ( // Phase is a phase of the channel pushdown automaton. Phase uint8 @@ -130,6 +137,9 @@ var signingPhases = []Phase{InitSigning, Signing, Progressing} // The other transitions are specific to the type of machine and are implemented // individually. type machine struct { + // logger embedding + log.Embedding + phase Phase acc map[wallet.BackendID]wallet.Account `cloneable:"shallow"` idx Index @@ -137,9 +147,6 @@ type machine struct { stagingTX Transaction currentTX Transaction prevTXs []Transaction - - // logger embedding - log.Embedding } // newMachine returns a new uninitialized machine for the given parameters. diff --git a/channel/multi/adjudicator.go b/channel/multi/adjudicator.go index 80c9467bc..ab394e2e3 100644 --- a/channel/multi/adjudicator.go +++ b/channel/multi/adjudicator.go @@ -95,10 +95,12 @@ func (a *Adjudicator) Withdraw(ctx context.Context, req channel.AdjudicatorReq, func (a *Adjudicator) dispatch(assetIds []LedgerBackendID, f func(channel.Adjudicator) error) error { n := len(assetIds) errs := make(chan error, n) + for _, l := range assetIds { go func(l LedgerBackendID) { err := func() error { key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + adjs, ok := a.adjudicators[key] if !ok { return fmt.Errorf("adjudicator not found for id %v", l) diff --git a/channel/multi/asset.go b/channel/multi/asset.go index d162eb0c7..c7af186cb 100644 --- a/channel/multi/asset.go +++ b/channel/multi/asset.go @@ -49,6 +49,7 @@ type ( // LedgerIDs returns the identifiers of the associated ledgers. func (a assets) LedgerIDs() ([]LedgerBackendID, error) { ids := make(map[LedgerBackendKey]LedgerBackendID) + for _, asset := range a { ma, ok := asset.(Asset) if !ok { @@ -60,6 +61,7 @@ func (a assets) LedgerIDs() ([]LedgerBackendID, error) { ids[LedgerBackendKey{BackendID: assetID.BackendID(), LedgerID: string(assetID.LedgerID().MapKey())}] = assetID } idsArray := make([]LedgerBackendID, len(ids)) + i := 0 for _, v := range ids { idsArray[i] = v @@ -72,7 +74,9 @@ func (a assets) LedgerIDs() ([]LedgerBackendID, error) { // IsMultiLedgerAssets returns whether the assets are from multiple ledgers. func IsMultiLedgerAssets(assets []channel.Asset) bool { hasMulti := false + var id LedgerBackendID + for _, asset := range assets { multiAsset, ok := asset.(Asset) switch { diff --git a/channel/multi/funder.go b/channel/multi/funder.go index e18048c6a..bdac3cf82 100644 --- a/channel/multi/funder.go +++ b/channel/multi/funder.go @@ -69,6 +69,7 @@ func (f *Funder) Fund(ctx context.Context, request channel.FundingReq) error { return fmt.Errorf("challenge duration %d is too large", duration) } d := time.Duration(duration) * time.Second + ctx, cancel := context.WithTimeout(ctx, d) defer cancel() @@ -78,6 +79,7 @@ func (f *Funder) Fund(ctx context.Context, request channel.FundingReq) error { } var egoisticLedgers []LedgerBackendID + var nonEgoisticLedgers []LedgerBackendID for i, l := range ledgerIDs { diff --git a/channel/multi/subscription.go b/channel/multi/subscription.go index 2e90df0c9..f737a9c33 100644 --- a/channel/multi/subscription.go +++ b/channel/multi/subscription.go @@ -89,6 +89,7 @@ func (s *AdjudicatorSubscription) Close() error { for _, sub := range s.subs { sub.Close() } + close(s.done) return nil } diff --git a/channel/persistence/keyvalue/persistedstate.go b/channel/persistence/keyvalue/persistedstate.go index bb79ff9ac..42cde28a9 100644 --- a/channel/persistence/keyvalue/persistedstate.go +++ b/channel/persistence/keyvalue/persistedstate.go @@ -65,7 +65,8 @@ func (id optChannelIDEnc) Encode(w io.Writer) error { func (id optChannelIDDec) Decode(r io.Reader) error { var exists bool - if err := perunio.Decode(r, &exists); err != nil { + err := perunio.Decode(r, &exists) + if err != nil { return err } if exists { diff --git a/channel/persistence/keyvalue/persistrestorer_internal_test.go b/channel/persistence/keyvalue/persistrestorer_internal_test.go index 604ebec76..9d412bac4 100644 --- a/channel/persistence/keyvalue/persistrestorer_internal_test.go +++ b/channel/persistence/keyvalue/persistrestorer_internal_test.go @@ -58,6 +58,7 @@ func TestPersistRestorer_Generic(t *testing.T) { func TestChannelIterator_Next_Empty(t *testing.T) { var it ChannelIterator var success bool + assert.NotPanics(t, func() { success = it.Next(context.Background()) }) assert.False(t, success) require.NoError(t, it.err) diff --git a/channel/persistence/persistence.go b/channel/persistence/persistence.go index 6fa8446a2..19f6245ba 100644 --- a/channel/persistence/persistence.go +++ b/channel/persistence/persistence.go @@ -127,6 +127,7 @@ type ( // sub-channel, also holds the parent channel's ID. Channel struct { chSource + PeersV []map[wallet.BackendID]wire.Address Parent *channel.ID } diff --git a/channel/persistence/statemachine.go b/channel/persistence/statemachine.go index 148e9140b..6a079bfba 100644 --- a/channel/persistence/statemachine.go +++ b/channel/persistence/statemachine.go @@ -27,6 +27,7 @@ import ( // to it and, if successful, persists changed data using a Persister. type StateMachine struct { *channel.StateMachine + pr Persister } diff --git a/channel/persistence/test/channel.go b/channel/persistence/test/channel.go index 26c8e3f79..48621c194 100644 --- a/channel/persistence/test/channel.go +++ b/channel/persistence/test/channel.go @@ -32,10 +32,11 @@ import ( // Channel is a wrapper around a persisted channel and its participants, as well // as the associated persister and restorer. type Channel struct { + *persistence.StateMachine + accounts []map[wallet.BackendID]wallet.Account peers []map[wallet.BackendID]wire.Address parent *channel.ID - *persistence.StateMachine pr persistence.PersistRestorer ctx context.Context //nolint:containedctx // This is just done for testing. Could be revised. @@ -58,6 +59,7 @@ func NewRandomChannel( rng *rand.Rand, ) (c *Channel) { bID := wallet.BackendID(channel.TestBackendID) + if len(peers) > 0 { for i := range peers[0] { bID = i @@ -90,7 +92,7 @@ func NewRandomChannel( } func requireEqualPeers(t require.TestingT, expected, actual []map[wallet.BackendID]wire.Address) { - assert.Equal(t, len(expected), len(actual)) + require.Equal(t, len(expected), len(actual)) for i, p := range expected { if !channel.EqualWireMaps(p, actual[i]) { t.Errorf("restored peers for channel do not match\nexpected: %v\nactual: %v", @@ -107,10 +109,10 @@ func requireEqualPeers(t require.TestingT, expected, actual []map[wallet.Backend func (c *Channel) AssertPersisted(ctx context.Context, t require.TestingT) { ch, err := c.pr.RestoreChannel(ctx, c.ID()) require.NoError(t, err) - assert.NotNil(t, ch) + require.NotNil(t, ch) c.RequireEqual(t, ch) requireEqualPeers(t, c.peers, ch.PeersV) - assert.Equal(t, c.parent, ch.Parent) + require.Equal(t, c.parent, ch.Parent) } // RequireEqual asserts that the channel is equal to the provided channel state. @@ -233,13 +235,13 @@ func (c *Channel) SetRegistered(t require.TestingT) { // SetWithdrawing calls SetWithdrawing on the state machine and then checks the persistence. func (c *Channel) SetWithdrawing(t require.TestingT) { - assert.NoError(t, c.StateMachine.SetWithdrawing(c.ctx)) + require.NoError(t, c.StateMachine.SetWithdrawing(c.ctx)) c.AssertPersisted(c.ctx, t) } // SetWithdrawn calls SetWithdrawn on the state machine and then checks the persistence. func (c *Channel) SetWithdrawn(t require.TestingT) { - assert.NoError(t, c.StateMachine.SetWithdrawn(c.ctx)) + require.NoError(t, c.StateMachine.SetWithdrawn(c.ctx)) rc, err := c.pr.RestoreChannel(c.ctx, c.ID()) assert.Error(t, err, "restoring of a non-existing channel") assert.Nil(t, rc, "restoring of a non-existing channel") diff --git a/channel/persistence/test/peerchans.go b/channel/persistence/test/peerchans.go index 6377f4d2c..80818fac0 100644 --- a/channel/persistence/test/peerchans.go +++ b/channel/persistence/test/peerchans.go @@ -65,6 +65,7 @@ func (pc peerChans) Delete(id channel.ID) { ids[i] = ids[lim] pc[pk] = ids[:lim] + break // next peer, no double channel ids } } diff --git a/channel/persistence/test/persistrestorertest.go b/channel/persistence/test/persistrestorertest.go index 01e4ccd15..82d7a684b 100644 --- a/channel/persistence/test/persistrestorertest.go +++ b/channel/persistence/test/persistrestorertest.go @@ -121,6 +121,7 @@ func GenericPersistRestorerTest( for _, ch := range channels[idx] { iterIdx++ iterIdx := iterIdx + go ct.StageN("testing", numChans*numPeers, func(t pkgtest.ConcT) { chIndex := iterIdx log.Error(subSeed) @@ -182,6 +183,7 @@ func GenericPersistRestorerTest( persistedPeers, err := pr.ActivePeers(ctx) require.NoError(t, err) require.Len(t, persistedPeers, numPeers+1) // + local client + peerLoop: for idx, addr := range peers { for _, paddr := range persistedPeers { diff --git a/channel/state.go b/channel/state.go index ec5397303..7f03559df 100644 --- a/channel/state.go +++ b/channel/state.go @@ -33,6 +33,10 @@ type ( // The state is the piece of data that is signed and sent to the adjudicator // during disputes. State struct { + // Allocation is the current allocation of channel assets to + // the channel participants and apps running inside this channel. + Allocation + // id is the immutable id of the channel this state belongs to ID ID // version counter @@ -41,9 +45,7 @@ type ( // We do not want a deep copy here, since the Apps are just an immutable reference. // They are only included in the State to support serialization of the `Data` field. App App `cloneable:"shallow"` - // Allocation is the current allocation of channel assets to - // the channel participants and apps running inside this channel. - Allocation + // Data is the app-specific data. Data Data // IsFinal indicates that the channel is in its final state. Such a state diff --git a/channel/test/app_randomizer.go b/channel/test/app_randomizer.go index d1eda5330..f53ca7e96 100644 --- a/channel/test/app_randomizer.go +++ b/channel/test/app_randomizer.go @@ -55,6 +55,7 @@ func NewRandomApp(rng *rand.Rand, opts ...RandomOpt) channel.App { app, _ := channel.Resolve(def) return app } + var bID wallet.BackendID bID, err := opt.Backend() if err != nil { diff --git a/channel/test/app_randomizer_internal_test.go b/channel/test/app_randomizer_internal_test.go index 67e28d253..ba7666ebb 100644 --- a/channel/test/app_randomizer_internal_test.go +++ b/channel/test/app_randomizer_internal_test.go @@ -29,12 +29,14 @@ func TestAppRandomizerSet(t *testing.T) { assert.False(t, isAppRandomizerSet, "isAppRandomizerSet should be defaulted to false") old := appRandomizer + assert.NotPanics(t, func() { SetAppRandomizer(NewMockAppRandomizer()) }, "first SetAppRandomizer() should work") assert.True(t, isAppRandomizerSet, "isAppRandomizerSet should be true") assert.NotNil(t, appRandomizer, "appRandomizer should not be nil") assert.NotEqual(t, old, appRandomizer, "appRandomizer should have changed") old = appRandomizer + assert.Panics(t, func() { SetAppRandomizer(NewMockAppRandomizer()) }, "second SetAppRandomizer() should panic") assert.True(t, isAppRandomizerSet, "isAppRandomizerSet should be true") assert.NotNil(t, appRandomizer, "appRandomizer should not be nil") diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index 510c61af2..0f9a8b438 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -383,6 +383,7 @@ func NewRandomTransaction(rng *rand.Rand, sigMask []bool, opts ...RandomOpt) *ch sigs := make([]wallet.Sig, numParts) err = nil + for i, choice := range sigMask { if !choice { sigs[i] = nil diff --git a/channel/transaction.go b/channel/transaction.go index e616ac859..ab7013501 100644 --- a/channel/transaction.go +++ b/channel/transaction.go @@ -27,6 +27,7 @@ import ( // channel participants. type Transaction struct { *State + Sigs []wallet.Sig } @@ -61,6 +62,7 @@ func (t *Transaction) Decode(r io.Reader) error { if err := perunio.Decode(r, &stateSet); err != nil { return errors.WithMessage(err, "decoding stateSet bit") } + switch stateSet { case 0: t.State = nil diff --git a/client/adjudicate.go b/client/adjudicate.go index 9179db73c..901f9692d 100644 --- a/client/adjudicate.go +++ b/client/adjudicate.go @@ -54,7 +54,8 @@ func (c *Channel) Watch(h AdjudicatorEventHandler) error { c.machMtx.Lock() c.statesPub = statesPub c.machMtx.Unlock() - if err := c.handleEvents(eventsSub, h); err != nil { + err = c.handleEvents(eventsSub, h) + if err != nil { return errors.WithMessage(err, "handling events from watcher") } @@ -103,7 +104,8 @@ func (c *Channel) handleEvents(eventsSub watcher.AdjudicatorSub, h AdjudicatorEv return nil } log.WithField("channel", c.Params().ID()).WithField("participant", c.Idx()).Infof("event %T: %v", e, e) - if err := c.setMachinePhase(c.Ctx(), e); err != nil { + err := c.setMachinePhase(c.Ctx(), e) + if err != nil { return errors.WithMessage(err, "setting machine phase") } @@ -213,7 +215,8 @@ func (c *Channel) ForceUpdate(ctx context.Context, updater func(*channel.State)) state.Version++ // Check state transition. - if err := c.machine.ValidTransition(state); err != nil { + err = c.machine.ValidTransition(state) + if err != nil { return errors.WithMessage(err, "validating state transition") } @@ -462,6 +465,7 @@ func (c *Channel) ensureRegistered(ctx context.Context) error { } registered := make(chan error) + go func() { registered <- c.awaitRegistered(ctx) }() @@ -489,6 +493,7 @@ func (c *Channel) awaitRegistered(ctx context.Context) error { if err != nil { return errors.WithMessage(err, "subscribing to adjudicator events") } + defer func() { if err := sub.Close(); err != nil { c.Log().Warn("Subscription closed with error:", err) diff --git a/client/channel.go b/client/channel.go index ada79e101..5139dd61c 100644 --- a/client/channel.go +++ b/client/channel.go @@ -201,6 +201,7 @@ func (c *Channel) initExchangeSigsAndEnable(ctx context.Context) error { defer resRecv.Close() send := make(chan error) + go func() { send <- c.conn.Send(ctx, &ChannelUpdateAccMsg{ ChannelID: c.ID(), diff --git a/client/channelconn.go b/client/channelconn.go index ffc099b25..9902b7b3c 100644 --- a/client/channelconn.go +++ b/client/channelconn.go @@ -152,6 +152,7 @@ type ( // with Next(), which returns the peer's channel index and the message. channelMsgRecv struct { *wire.Receiver + peers []map[wallet.BackendID]wire.Address log log.Logger } @@ -172,8 +173,10 @@ func (r *channelMsgRecv) Next(ctx context.Context) (channel.Index, ChannelMsg, e if !ok { return 0, nil, errors.Errorf("unexpected message type: expected ChannelMsg, got %T", env.Msg) } - if idx < 0 || idx > math.MaxUint16 { - return 0, nil, errors.Errorf("channel connection received message from unexpected peer %v", env.Sender) + index, err := channel.FromInt(idx) + if err != nil { + return 0, nil, errors.WithMessagef(err, "converting index %d to channel.Index", idx) } - return channel.Index(idx), msg, nil + + return index, msg, nil } diff --git a/client/client.go b/client/client.go index efe54ce26..71d895c70 100644 --- a/client/client.go +++ b/client/client.go @@ -35,6 +35,8 @@ import ( // // Currently, only the two-party protocol is fully implemented. type Client struct { + sync.Closer + address map[wallet.BackendID]wire.Address conn clientConn channels chanRegistry @@ -47,8 +49,6 @@ type Client struct { fundingWatcher *stateWatcher settlementWatcher *stateWatcher watcher watcher.Watcher - - sync.Closer } // New creates a new State Channel Client. diff --git a/client/client_test.go b/client/client_test.go index 93bf7b18c..24eb3aec9 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -75,6 +75,7 @@ func TestClient_Handle_NilArgs(t *testing.T) { dummyUH := client.UpdateHandlerFunc(func(*channel.State, client.ChannelUpdate, *client.UpdateResponder) {}) assert.Panics(t, func() { c.Handle(nil, dummyUH) }) + dummyPH := client.ProposalHandlerFunc(func(client.ChannelProposal, *client.ProposalResponder) {}) assert.Panics(t, func() { c.Handle(dummyPH, nil) }) } @@ -92,6 +93,7 @@ func TestClient_New(t *testing.T) { func TestChannelRejection(t *testing.T) { rng := test.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() diff --git a/client/clientconn.go b/client/clientconn.go index 02bd47880..31b71000d 100644 --- a/client/clientconn.go +++ b/client/clientconn.go @@ -27,11 +27,12 @@ import ( // A clientConn bundles all the messaging infrastructure for a Client. type clientConn struct { - *wire.Relay // Client relay, subscribed to the bus. Embedded for methods Subscribe and Cache. - bus wire.Bus - reqRecv *wire.Receiver // subscription to incoming requests - sender map[wallet.BackendID]wire.Address log.Embedding + *wire.Relay // Client relay, subscribed to the bus. Embedded for methods Subscribe and Cache. + + bus wire.Bus + reqRecv *wire.Receiver // subscription to incoming requests + sender map[wallet.BackendID]wire.Address } // Publish publishes the message on the bus. Makes clientConn implement the @@ -53,6 +54,7 @@ func makeClientConn(address map[wallet.BackendID]wire.Address, bus wire.Bus) (c c.sender = address c.bus = bus c.Relay = wire.NewRelay() + defer func() { if err != nil { if cerr := c.Relay.Close(); cerr != nil { diff --git a/client/failing_funding_test.go b/client/failing_funding_test.go index 50fd8ec87..5a1ef85c5 100644 --- a/client/failing_funding_test.go +++ b/client/failing_funding_test.go @@ -44,6 +44,7 @@ func TestFailingFunding(t *testing.T) { func(r *rand.Rand) ([2]ctest.RoleSetup, channel.Asset) { roles := NewSetups(rng, []string{"Frida", "Fred"}, channel.TestBackendID) asset := chtest.NewRandomAsset(rng, channel.TestBackendID) + return [2]ctest.RoleSetup{roles[0], roles[1]}, asset }, ) diff --git a/client/multiledger_test.go b/client/multiledger_test.go index fc7929925..1e8cddeef 100644 --- a/client/multiledger_test.go +++ b/client/multiledger_test.go @@ -23,14 +23,18 @@ import ( func TestMultiLedgerHappy(t *testing.T) { mlt := ctest.SetupMultiLedgerTest(t) + ctx, cancel := context.WithTimeout(context.Background(), twoPartyTestTimeout) defer cancel() + ctest.TestMultiLedgerHappy(ctx, t, mlt, challengeDuration) } func TestMultiLedgerDispute(t *testing.T) { mlt := ctest.SetupMultiLedgerTest(t) + ctx, cancel := context.WithTimeout(context.Background(), twoPartyTestTimeout) defer cancel() + ctest.TestMultiLedgerDispute(ctx, t, mlt, challengeDuration) } diff --git a/client/payment_test.go b/client/payment_test.go index 5865eec4b..4b8ee19d6 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -74,6 +74,7 @@ func TestPaymentDispute(t *testing.T) { func TestPaymentChannelsOptimistic(t *testing.T) { rng := pkgtest.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), testDuration) defer cancel() @@ -83,6 +84,7 @@ func TestPaymentChannelsOptimistic(t *testing.T) { func TestPaymentChannelsDispute(t *testing.T) { rng := pkgtest.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), testDuration) defer cancel() diff --git a/client/proposal.go b/client/proposal.go index ed6ba1d92..595a846ec 100644 --- a/client/proposal.go +++ b/client/proposal.go @@ -346,6 +346,7 @@ func (c *Client) proposeTwoPartyChannel( return false } } + receiver := wire.NewReceiver() defer receiver.Close() diff --git a/client/proposalmsgs.go b/client/proposalmsgs.go index 8c7c07b1c..455395118 100644 --- a/client/proposalmsgs.go +++ b/client/proposalmsgs.go @@ -115,6 +115,7 @@ type ( // LedgerChannelProposalMsg is a channel proposal for ledger channels. LedgerChannelProposalMsg struct { BaseChannelProposal + Participant map[wallet.BackendID]wallet.Address // Proposer's address in the channel. Peers []map[wallet.BackendID]wire.Address // Participants' wire addresses. } @@ -122,6 +123,7 @@ type ( // SubChannelProposalMsg is a channel proposal for subchannels. SubChannelProposalMsg struct { BaseChannelProposal + Parent channel.ID } ) @@ -406,6 +408,7 @@ type ( // each channel instantiation. LedgerChannelProposalAccMsg struct { BaseChannelProposalAcc + Participant map[wallet.BackendID]wallet.Address // Responder's participant address. } @@ -519,6 +522,7 @@ type ( // VirtualChannelProposalMsg is a channel proposal for virtual channels. VirtualChannelProposalMsg struct { BaseChannelProposal + Proposer map[wallet.BackendID]wallet.Address // Proposer's address in the channel. Peers []map[wallet.BackendID]wire.Address // Participants' wire addresses. Parents []channel.ID // Parent channels for each participant. @@ -529,6 +533,7 @@ type ( // virtual channel proposals. VirtualChannelProposalAccMsg struct { BaseChannelProposalAcc + Responder map[wallet.BackendID]wallet.Address // Responder's participant address. } ) diff --git a/client/restore.go b/client/restore.go index c6e95e05a..1cf18a2a8 100644 --- a/client/restore.go +++ b/client/restore.go @@ -73,6 +73,7 @@ func (c *Client) restorePeerChannels(ctx context.Context, p map[wallet.BackendID if err != nil { return errors.WithMessagef(err, "restoring channels for peer: %v", err) } + defer func() { if cerr := it.Close(); cerr != nil { err = errors.WithMessagef(err, "(error closing iterator: %v)", cerr) diff --git a/client/restore_internal_test.go b/client/restore_internal_test.go index c31917804..7451b1046 100644 --- a/client/restore_internal_test.go +++ b/client/restore_internal_test.go @@ -39,6 +39,7 @@ func patchChFromSource( peers ...map[wallet.BackendID]wire.Address, ) (*Channel, error) { bID := wallet.BackendID(channel.TestBackendID) + if len(peers) > 0 { for i := range peers[0] { bID = i diff --git a/client/subchannel_test.go b/client/subchannel_test.go index 50b4c3a1f..d3352b6d9 100644 --- a/client/subchannel_test.go +++ b/client/subchannel_test.go @@ -48,8 +48,6 @@ func TestSubChannelHappy(t *testing.T) { [2]*big.Int{big.NewInt(100), big.NewInt(100)}, client.WithoutApp(), ), - 2, - 3, [][2]*big.Int{ {big.NewInt(10), big.NewInt(10)}, {big.NewInt(5), big.NewInt(5)}, diff --git a/client/sync.go b/client/sync.go index 7ec29e784..ee7532573 100644 --- a/client/sync.go +++ b/client/sync.go @@ -87,7 +87,9 @@ func (c *Client) syncChannel(ctx context.Context, ch *persistence.Channel, p map // syncMsg needs to be a clone so that there's no data race when updating the // own channel data later. syncMsg := newChannelSyncMsg(persistence.CloneSource(ch)) + go func() { sendError <- c.conn.pubMsg(ctx, syncMsg, p) }() + defer func() { // When returning, either log the send error, or return it. sendErr := <-sendError diff --git a/client/test/alice.go b/client/test/alice.go index 69e41271a..ef7d8f827 100644 --- a/client/test/alice.go +++ b/client/test/alice.go @@ -23,6 +23,7 @@ import ( // AliceBobExecConfig contains config parameters for Alice and Bob test. type AliceBobExecConfig struct { BaseExecConfig + NumPayments [2]int // how many payments each role sends TxAmounts [2]*big.Int // amounts that are to be sent/requested by each role } diff --git a/client/test/backend.go b/client/test/backend.go index 1825c3d49..e798c30ca 100644 --- a/client/test/backend.go +++ b/client/test/backend.go @@ -144,6 +144,7 @@ func (b *MockBackend) Fund(ctx context.Context, req channel.FundingReq, acc wall // MockAdjudicator is an adjudicator used for testing. type MockAdjudicator struct { *MockBackend + acc wallet.Address } diff --git a/client/test/carol.go b/client/test/carol.go index 2fef85195..b1d9760b9 100644 --- a/client/test/carol.go +++ b/client/test/carol.go @@ -24,6 +24,7 @@ import ( // Carol is a Responder. She accepts an incoming channel proposal. type Carol struct { Responder + registered chan *channel.RegisteredEvent } diff --git a/client/test/channel.go b/client/test/channel.go index 28b7c6c9e..99f976150 100644 --- a/client/test/channel.go +++ b/client/test/channel.go @@ -34,6 +34,7 @@ type ( // interface so it can be its own update handler. paymentChannel struct { *client.Channel + r *role // Reuse of timeout and testing obj log log.Logger @@ -70,9 +71,11 @@ func (ch *paymentChannel) Handle(up client.ChannelUpdate, res *client.UpdateResp accept := <-ch.handler if accept { ch.log.Debug("Accepting...") + ch.res <- handlerRes{up, res.Accept(ctx)} } else { ch.log.Debug("Rejecting...") + ch.res <- handlerRes{up, res.Reject(ctx, "Rejection")} } } @@ -141,6 +144,7 @@ func (ch *paymentChannel) sendTransfer(amount channel.Bal, desc string) { func (ch *paymentChannel) recvUpdate(accept bool, desc string) *channel.State { ch.log.Debugf("Receiving update: %s, accept: %t", desc, accept) + ch.handler <- accept select { diff --git a/client/test/fund.go b/client/test/fund.go index 4f61669a8..7b2cc1d22 100644 --- a/client/test/fund.go +++ b/client/test/fund.go @@ -138,6 +138,7 @@ func runFredFridaTest( // Fred gets the channel and settles it afterwards. chFred := <-chsFred require.NotNil(t, chFred) + select { case err := <-errsFred: require.IsType(t, &client.ChannelFundingError{}, err) diff --git a/client/test/mallory.go b/client/test/mallory.go index 0075ebac8..1d58bbdfc 100644 --- a/client/test/mallory.go +++ b/client/test/mallory.go @@ -27,6 +27,7 @@ import ( // MalloryCarolExecConfig contains config parameters for Mallory and Carol test. type MalloryCarolExecConfig struct { BaseExecConfig + NumPayments [2]int // how many payments each role sends TxAmounts [2]*big.Int // amounts that are to be sent/requested by each role } diff --git a/client/test/multiledger.go b/client/test/multiledger.go index f991ea894..48f6d9dc7 100644 --- a/client/test/multiledger.go +++ b/client/test/multiledger.go @@ -149,6 +149,7 @@ func (a *MultiLedgerAsset) UnmarshalBinary(data []byte) error { // MultiLedgerClient represents a test client. type MultiLedgerClient struct { *client.Client + WireAddress map[wallet.BackendID]wire.Address WalletAddress map[wallet.BackendID]wallet.Address Events chan channel.AdjudicatorEvent diff --git a/client/test/paymentchannel.go b/client/test/paymentchannel.go index 72897439c..1595edef7 100644 --- a/client/test/paymentchannel.go +++ b/client/test/paymentchannel.go @@ -67,7 +67,7 @@ type paymentChannelTest struct { } // TestPaymentChannelOptimistic tests payment channel functionality in the happy case. -func TestPaymentChannelOptimistic( //nolint:revive // test.Test... stutters but OK here. +func TestPaymentChannelOptimistic( // test.Test... stutters but OK here. ctx context.Context, t *testing.T, setup PaymentChannelSetup, @@ -218,6 +218,7 @@ func setupPaymentChannelTest( pct.errs <- errors.WithMessage(err, "Bob: accepting channel update") } } + go bob.Handle(openingProposalHandlerBob, updateProposalHandlerBob) //nolint:contextcheck // Establish ledger channel between Alice and Ingrid. @@ -239,6 +240,7 @@ func setupPaymentChannelTest( pct.chAliceBob, err = alice.ProposeChannel(ctx, lcpAlice) require.NoError(t, err, "opening channel between Alice and Ingrid") + select { case pct.chBobAlice = <-channelsBob: case err := <-pct.errs: diff --git a/client/test/persistence.go b/client/test/persistence.go index 4ecd00b98..82c39739e 100644 --- a/client/test/persistence.go +++ b/client/test/persistence.go @@ -178,6 +178,7 @@ func (r *Robert) Execute(cfg ExecConfig) { ctx, cancel := context.WithTimeout(context.Background(), r.timeout) defer cancel() r.RequireNoError(r.Restore(ctx)) // should restore channels + select { case ch = <-newCh: // expected r.RequireTrue(ch != nil) diff --git a/client/test/progression.go b/client/test/progression.go index bfe728868..30407c946 100644 --- a/client/test/progression.go +++ b/client/test/progression.go @@ -30,6 +30,7 @@ type ProgressionExecConfig struct { // Watcher is a client that handles adjudicator events. type Watcher struct { log.Logger + registered chan *channel.RegisteredEvent progressed chan *channel.ProgressedEvent } @@ -45,6 +46,7 @@ func makeWatcher(log log.Logger) Watcher { // HandleAdjudicatorEvent is the callback for adjudicator event handling. func (w *Watcher) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { w.Infof("HandleAdjudicatorEvent %T: %v", e, e) + switch e := e.(type) { case *channel.RegisteredEvent: w.registered <- e diff --git a/client/test/proposalmsgs.go b/client/test/proposalmsgs.go index 39efd718b..9de484388 100644 --- a/client/test/proposalmsgs.go +++ b/client/test/proposalmsgs.go @@ -49,6 +49,7 @@ func channelProposalReqSerializationTest(t *testing.T, serializerTest func(t *te if i&1 == 0 { app = client.WithApp(test.NewRandomAppAndData(rng)) } + switch i % 3 { case 0: m = NewRandomLedgerChannelProposal(rng, client.WithNonceFrom(rng), app) diff --git a/client/test/responder.go b/client/test/responder.go index a45860262..59de4009c 100644 --- a/client/test/responder.go +++ b/client/test/responder.go @@ -34,6 +34,7 @@ func (r *Responder) Execute(cfg ExecConfig, exec func(ExecConfig, *paymentChanne rng := r.NewRng() propHandler, waitHandler := r.GoHandle(rng) + defer func() { r.RequireNoError(r.Close()) waitHandler() diff --git a/client/test/role.go b/client/test/role.go index 9f74a92c6..1c419c81b 100644 --- a/client/test/role.go +++ b/client/test/role.go @@ -45,6 +45,7 @@ type ( // A Role is a client.Client together with a protocol execution path. role struct { *client.Client + chans *channelMap newChan func(*paymentChannel) // new channel callback setup RoleSetup @@ -58,8 +59,9 @@ type ( } channelMap struct { - entries map[channel.ID]*paymentChannel sync.RWMutex + + entries map[channel.ID]*paymentChannel } // BalanceReader can be used to read state from a ledger. @@ -120,6 +122,7 @@ type ( Client struct { *client.Client RoleSetup + WalletAddress map[wallet.BackendID]wallet.Address } ) diff --git a/client/test/subchannel.go b/client/test/subchannel.go index 782489a4c..1dc134437 100644 --- a/client/test/subchannel.go +++ b/client/test/subchannel.go @@ -25,6 +25,7 @@ import ( // SusieTimExecConfig contains config parameters for Susie and Tim test. type SusieTimExecConfig struct { BaseExecConfig + SubChannelFunds [][2]*big.Int // sub-channel funding amounts, also determines number of sub-channels, must be at least 1 SubSubChannelFunds [][2]*big.Int // sub-sub-channel funding amounts, also determines number of sub-sub-channels LeafChannelApp client.ProposalOpts // app used in the leaf channels @@ -34,8 +35,6 @@ type SusieTimExecConfig struct { // NewSusieTimExecConfig creates a new object from the given parameters. func NewSusieTimExecConfig( base BaseExecConfig, - _ int, - _ int, subChannelFunds [][2]*big.Int, subSubChannelFunds [][2]*big.Int, leafChannelApp client.ProposalOpts, diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index 45692d66a..35c78c395 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -29,6 +29,7 @@ const nStagesDisputeSusieTime = 4 // DisputeSusieTimExecConfig contains config parameters for sub-channel dispute test. type DisputeSusieTimExecConfig struct { BaseExecConfig + SubChannelFunds [2]*big.Int // sub-channel funding amounts TxAmount *big.Int // transaction amount } @@ -120,6 +121,7 @@ func (r *DisputeSusie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { // DisputeTim is a Responder. He accepts incoming channel proposals and updates. type DisputeTim struct { Responder + registered chan *channel.RegisteredEvent subCh channel.ID } diff --git a/client/test/updatemsgs.go b/client/test/updatemsgs.go index deeaa8bd0..9977241a8 100644 --- a/client/test/updatemsgs.go +++ b/client/test/updatemsgs.go @@ -15,7 +15,6 @@ package test import ( - "math" "math/rand" "testing" @@ -123,14 +122,14 @@ func channelUpdateRejSerializationTest(t *testing.T, serializerTest func(t *test func newRandomMsgChannelUpdate(rng *rand.Rand) *client.ChannelUpdateMsg { state := test.NewRandomState(rng) sig := newRandomSig(rng, channel.TestBackendID) - idx := rng.Intn(state.NumParts()) - if idx < 0 || idx > math.MaxUint16 { - panic("index out of bounds") + idx, err := channel.FromInt(rng.Intn(state.NumParts())) + if err != nil { + panic(err) } return &client.ChannelUpdateMsg{ ChannelUpdate: client.ChannelUpdate{ State: state, - ActorIdx: channel.Index(idx), + ActorIdx: idx, }, Sig: sig, } diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index a9751e0b8..711e11a5a 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -222,7 +222,7 @@ type VirtualChannelBalances struct { FinalBalsBob []*big.Int } -func setupVirtualChannelTest( //nolint +func setupVirtualChannelTest( //nolint:cyclop, maintidx t *testing.T, ctx context.Context, setup VirtualChannelSetup, @@ -274,7 +274,7 @@ func setupVirtualChannelTest( //nolint } } var updateProposalHandlerIngrid client.UpdateHandlerFunc = func( - _ *channel.State, cu client.ChannelUpdate, ur *client.UpdateResponder, + _ *channel.State, _ client.ChannelUpdate, _ *client.UpdateResponder, ) { } go ingrid.Handle(openingProposalHandlerIngrid, updateProposalHandlerIngrid) //nolint:contextcheck diff --git a/client/update.go b/client/update.go index 2087d97f6..b398933e8 100644 --- a/client/update.go +++ b/client/update.go @@ -255,6 +255,7 @@ func (c *Channel) update(ctx context.Context, updater func(*channel.State) error if err := updater(state); err != nil { return err } + state.Version++ return c.updateGeneric(ctx, state, func(mcu *ChannelUpdateMsg) wire.Msg { return mcu }) diff --git a/client/updateinterception.go b/client/updateinterception.go index 0ddf05203..8352cd145 100644 --- a/client/updateinterception.go +++ b/client/updateinterception.go @@ -36,8 +36,9 @@ type ( } updateInterceptors struct { - entries map[channel.ID]*updateInterceptor sync.RWMutex + + entries map[channel.ID]*updateInterceptor } ) @@ -56,6 +57,7 @@ func (ui *updateInterceptor) Accept(ctx context.Context) error { if err := ur.responder.Accept(ctx); err != nil { return err } + ui.response <- struct{}{} case <-ctx.Done(): return ctx.Err() diff --git a/client/updatemsgs.go b/client/updatemsgs.go index d151b52a9..5f167bc25 100644 --- a/client/updatemsgs.go +++ b/client/updatemsgs.go @@ -56,11 +56,13 @@ type ( // controller. ChannelMsg interface { wire.Msg + ID() channel.ID } channelUpdateResMsg interface { ChannelMsg + Ver() uint64 } @@ -68,6 +70,7 @@ type ( // additionally holds the signature on the proposed state. ChannelUpdateMsg struct { ChannelUpdate + // Sig is the signature on the proposed state by the peer sending the // ChannelUpdate. Sig wallet.Sig @@ -207,6 +210,7 @@ type ( // VirtualChannelFundingProposalMsg is a channel update that proposes the funding of a virtual channel. VirtualChannelFundingProposalMsg struct { ChannelUpdateMsg + Initial channel.SignedState IndexMap []channel.Index } @@ -214,6 +218,7 @@ type ( // VirtualChannelSettlementProposalMsg is a channel update that proposes the settlement of a virtual channel. VirtualChannelSettlementProposalMsg struct { ChannelUpdateMsg + Final channel.SignedState } ) diff --git a/client/virtual_channel.go b/client/virtual_channel.go index 2826cbe71..bce4db8eb 100644 --- a/client/virtual_channel.go +++ b/client/virtual_channel.go @@ -55,7 +55,6 @@ func (c *Client) fundVirtualChannel(ctx context.Context, virtual *Channel, prop func (c *Channel) proposeVirtualChannelFunding(ctx context.Context, virtual *Channel, indexMap []channel.Index) error { // We assume that the channel is locked. - state := c.state().Clone() state.Version++ @@ -115,6 +114,7 @@ func (c *Channel) watchVirtual() error { if err != nil { return errors.WithMessage(err, "subscribing to adjudicator state changes") } + defer func() { if err := sub.Close(); err != nil { log.Warn(err) @@ -315,6 +315,7 @@ func (c *Client) validateVirtualChannelFundingProposal( func (c *Client) matchFundingProposal(ctx context.Context, a, b interface{}) bool { var err error + defer func() { if err != nil { c.log.Debug("matching funding proposal:", err) diff --git a/client/virtual_channel_settlement.go b/client/virtual_channel_settlement.go index 02254a122..ee242e003 100644 --- a/client/virtual_channel_settlement.go +++ b/client/virtual_channel_settlement.go @@ -151,6 +151,7 @@ func (c *Client) validateVirtualChannelSettlementProposal( func (c *Client) matchSettlementProposal(ctx context.Context, a, b interface{}) bool { var err error + defer func() { if err != nil { c.log.Debug("matching settlement proposal:", err) diff --git a/client/virtual_channel_util.go b/client/virtual_channel_util.go index 7c3ebe479..7a5ee5beb 100644 --- a/client/virtual_channel_util.go +++ b/client/virtual_channel_util.go @@ -68,6 +68,7 @@ type watcherEntry struct { type stateWatcher struct { sync.Mutex + entries map[interface{}]watcherEntry condition func(ctx context.Context, a, b interface{}) bool } @@ -86,8 +87,10 @@ func (w *stateWatcher) Await( state interface{}, ) (err error) { match := make(chan struct{}, 1) + w.register(ctx, state, match) defer w.deregister(state) + select { case <-match: case <-ctx.Done(): @@ -107,6 +110,7 @@ func (w *stateWatcher) register( for k, e := range w.entries { if w.condition(ctx, state, e.state) { done <- struct{}{} + e.done <- struct{}{} delete(w.entries, k) return diff --git a/log/levellified.go b/log/levellified.go index 2364fb69e..d571e3624 100644 --- a/log/levellified.go +++ b/log/levellified.go @@ -28,6 +28,7 @@ var _ LevelLogger = &Levellified{StdLogger: &log.Logger{}} type Levellified struct { // wrapped logger StdLogger + // Lvl is the current logging level Lvl Level } diff --git a/log/levellified_internal_test.go b/log/levellified_internal_test.go index 43a734174..51d9aea3f 100644 --- a/log/levellified_internal_test.go +++ b/log/levellified_internal_test.go @@ -33,6 +33,7 @@ func TestLevellified(t *testing.T) { requirePrefix := func(logFn func(...interface{}), lvl Level) { buf.Reset() logFn(logStr) + golden := "[" + lvl.String() + "] " + logStr + "\n" if buf.String() != golden { t.Errorf("Want: %q, have: %q", golden, buf.String()) @@ -43,6 +44,7 @@ func TestLevellified(t *testing.T) { requireSilent := func(logFn func(...interface{})) { buf.Reset() logFn(logStr) + if buf.Len() > 0 { t.Errorf("Want no logging, have: %q", buf.String()) } diff --git a/log/log.go b/log/log.go index c58c75631..5f9dedf8b 100644 --- a/log/log.go +++ b/log/log.go @@ -36,6 +36,7 @@ func Set(l Logger) { logger = new(none) return } + logger = l } @@ -228,6 +229,7 @@ type ( func AppendField(owner Owner, key string, value interface{}) Logger { l := owner.Log().WithField(key, value) owner.SetLog(l) + return l } @@ -236,6 +238,7 @@ func AppendField(owner Owner, key string, value interface{}) Logger { func AppendFields(owner Owner, fs Fields) Logger { l := owner.Log().WithFields(fs) owner.SetLog(l) + return l } diff --git a/log/none_internal_test.go b/log/none_internal_test.go index 48d2fac97..ca58d1b67 100644 --- a/log/none_internal_test.go +++ b/log/none_internal_test.go @@ -47,6 +47,7 @@ func TestNone(t *testing.T) { // Test that fatal functions call exit et := test.NewExit(t, &exit) + funs := []func(){func() { None.Fatalf("") }, func() { None.Fatal() }, func() { None.Fatalln() }} for _, fn := range funs { et.AssertExit(fn, 1) diff --git a/wallet/address.go b/wallet/address.go index 047db6887..03f18fe21 100644 --- a/wallet/address.go +++ b/wallet/address.go @@ -92,7 +92,8 @@ func CloneAddress(a Address) Address { } clone := NewAddress(a.BackendID()) - if err := clone.UnmarshalBinary(data); err != nil { + err = clone.UnmarshalBinary(data) + if err != nil { log.WithError(err).Panic("error binary-unmarshaling Address") } return clone @@ -140,18 +141,24 @@ func (a AddressDecMap) Encode(w stdio.Writer) error { return errors.New("map length out of bounds") } length := int32(l) - if err := perunio.Encode(w, length); err != nil { + err := perunio.Encode(w, length) + if err != nil { return errors.WithMessage(err, "encoding map length") } + for i, addr := range a { id := int(i) if id < math.MinInt32 || id > math.MaxInt32 { return errors.New("map index out of bounds") } - if err := perunio.Encode(w, int32(id)); err != nil { + + err := perunio.Encode(w, int32(id)) + if err != nil { return errors.WithMessage(err, "encoding map index") } - if err := perunio.Encode(w, addr); err != nil { + + err = perunio.Encode(w, addr) + if err != nil { return errors.WithMessagef(err, "encoding %d-th address map entry", i) } } @@ -166,12 +173,16 @@ func (a AddressMapArray) Encode(w stdio.Writer) error { return errors.New("array length out of bounds") } length := int32(l) - if err := perunio.Encode(w, length); err != nil { + + err := perunio.Encode(w, length) + if err != nil { return errors.WithMessage(err, "encoding array length") } + for i, addr := range a.Addr { addressCopy := addr - if err := perunio.Encode(w, (*AddressDecMap)(&addressCopy)); err != nil { + err := perunio.Encode(w, (*AddressDecMap)(&addressCopy)) + if err != nil { return errors.WithMessagef(err, "encoding %d-th address array entry", i) } } @@ -181,17 +192,21 @@ func (a AddressMapArray) Encode(w stdio.Writer) error { // Decode decodes the map length first, then all Addresses and their key in the map. func (a *AddressDecMap) Decode(r stdio.Reader) (err error) { var mapLen int32 - if err := perunio.Decode(r, &mapLen); err != nil { + err = perunio.Decode(r, &mapLen) + if err != nil { return errors.WithMessage(err, "decoding map length") } + *a = make(map[BackendID]Address, mapLen) for i := range mapLen { var idx int32 - if err := perunio.Decode(r, &idx); err != nil { + err := perunio.Decode(r, &idx) + if err != nil { return errors.WithMessage(err, "decoding map index") } addr := NewAddress(BackendID(idx)) - if err := perunio.Decode(r, addr); err != nil { + err = perunio.Decode(r, addr) + if err != nil { return errors.WithMessagef(err, "decoding %d-th address map entry", i) } (*a)[BackendID(idx)] = addr @@ -202,12 +217,15 @@ func (a *AddressDecMap) Decode(r stdio.Reader) (err error) { // Decode decodes the array length first, then all AddressDecMaps in the array. func (a *AddressMapArray) Decode(r stdio.Reader) (err error) { var mapLen int32 - if err := perunio.Decode(r, &mapLen); err != nil { + err = perunio.Decode(r, &mapLen) + if err != nil { return errors.WithMessage(err, "decoding array length") } + a.Addr = make([]map[BackendID]Address, mapLen) for i := range mapLen { - if err := perunio.Decode(r, (*AddressDecMap)(&a.Addr[i])); err != nil { + err := perunio.Decode(r, (*AddressDecMap)(&a.Addr[i])) + if err != nil { return errors.WithMessagef(err, "decoding %d-th address map entry", i) } } @@ -229,6 +247,7 @@ func Key(a Address) AddrKey { if err := perunio.Encode(&buff, a); err != nil { panic("Could not encode address in AddrKey: " + err.Error()) } + return AddrKey(buff.String()) } @@ -237,16 +256,19 @@ func Key(a Address) AddrKey { // Panics when the `Address` can't be decoded. func FromKey(k AddrKey) Address { buff := bytes.NewBuffer([]byte(k)) + var id uint32 err := perunio.Decode(buff, &id) if err != nil { panic("Could not decode id: " + err.Error()) } a := NewAddress(BackendID(int(id))) + err = perunio.Decode(buff, a) if err != nil { panic("Could not decode address: " + err.Error()) } + return a } diff --git a/wallet/address_test.go b/wallet/address_test.go index a24f98f6a..f91c56690 100644 --- a/wallet/address_test.go +++ b/wallet/address_test.go @@ -120,6 +120,7 @@ func TestCloneAddresses(t *testing.T) { addrs0 := wallet.CloneAddresses(addrs) require.Equal(t, addrs, addrs0) require.NotSame(t, &addrs, &addrs0) + for i, a := range addrs { require.NotSame(t, a, addrs0[i]) } diff --git a/wallet/backend.go b/wallet/backend.go index 2ef0b7b71..cd96eb6a2 100644 --- a/wallet/backend.go +++ b/wallet/backend.go @@ -45,6 +45,7 @@ func SetBackend(b Backend, id int) { if backend == nil { backend = make(map[BackendID]Backend) } + if backend[BackendID(id)] != nil { panic("wallet backend already set") } diff --git a/wallet/sig.go b/wallet/sig.go index 27beb51b0..930688524 100644 --- a/wallet/sig.go +++ b/wallet/sig.go @@ -63,6 +63,7 @@ func EncodeSparseSigs(w io.Writer, sigs []Sig) error { // Encode mask mask := make([]uint8, int(math.Ceil(float64(n)/float64(bitsPerByte)))) + for i, sig := range sigs { if sig != nil { mask[i/bitsPerByte] |= 1 << (i % bitsPerByte) diff --git a/wallet/test/address.go b/wallet/test/address.go index 8986dc150..381daf116 100644 --- a/wallet/test/address.go +++ b/wallet/test/address.go @@ -34,6 +34,7 @@ func TestAddress(t *testing.T, s *Setup) { //nolint:revive // `test.Test...` stu // Test Address.String. nullString := null.String() addrString := addr.String() + assert.NotEmpty(t, nullString) assert.NotEmpty(t, addrString) assert.NotEqual(t, addrString, nullString) diff --git a/wallet/test/randomizer.go b/wallet/test/randomizer.go index 35aab64b8..449a05258 100644 --- a/wallet/test/randomizer.go +++ b/wallet/test/randomizer.go @@ -58,6 +58,7 @@ func SetRandomizer(b Randomizer, bID wallet.BackendID) { if randomizer == nil { randomizer = make(map[wallet.BackendID]Randomizer) } + if randomizer[bID] != nil { panic("wallet/test randomizer already set") } @@ -100,6 +101,7 @@ func NewWallet(bID wallet.BackendID) Wallet { // by calling NewRandomAccount. func NewRandomAccounts(rng *rand.Rand, n int, bID wallet.BackendID) ([]map[wallet.BackendID]wallet.Account, []map[wallet.BackendID]wallet.Address) { accs := make([]map[wallet.BackendID]wallet.Account, n) + addrs := make([]map[wallet.BackendID]wallet.Address, n) for i := range accs { accs[i] = map[wallet.BackendID]wallet.Account{bID: NewRandomAccount(rng, bID)} diff --git a/wallet/test/wallet.go b/wallet/test/wallet.go index 663d8059f..4860a6a02 100644 --- a/wallet/test/wallet.go +++ b/wallet/test/wallet.go @@ -64,12 +64,14 @@ func TestAccountWithWalletAndBackend(t *testing.T, s *Setup) { //nolint:revive / copy(tampered, sig) // Invalidate the signature and check for error tampered[0] = ^sig[0] + valid, err = s.Backend.VerifySignature(s.DataToSign, tampered, s.AddressInWallet) if valid && err == nil { t.Error("Verification of invalid signature should produce error or return false") } // Truncate the signature and check for error tampered = sig[:len(sig)-1] + valid, err = s.Backend.VerifySignature(s.DataToSign, tampered, s.AddressInWallet) if valid && err != nil { t.Error("Verification of invalid signature should produce error or return false") @@ -77,6 +79,7 @@ func TestAccountWithWalletAndBackend(t *testing.T, s *Setup) { //nolint:revive / // Expand the signature and check for error //nolint:gocritic tampered = append(sig, 0) + valid, err = s.Backend.VerifySignature(s.DataToSign, tampered, s.AddressInWallet) if valid && err != nil { t.Error("Verification of invalid signature should produce error or return false") @@ -113,9 +116,11 @@ func GenericSignatureSizeTest(t *testing.T, s *Setup) { // Test that signatures have constant length l := len(sign) + for range 8 { t.Run("parallel signing", func(t *testing.T) { t.Parallel() + for range 256 { sign, err := acc.SignData(s.DataToSign) require.NoError(t, err, "Sign with unlocked account should succeed") diff --git a/wallet/test/walletbench.go b/wallet/test/walletbench.go index 563a1ffa9..b660262a3 100644 --- a/wallet/test/walletbench.go +++ b/wallet/test/walletbench.go @@ -72,8 +72,10 @@ func benchBackendVerifySig(b *testing.B, s *Setup) { func benchUnmarshalAddress(b *testing.B, s *Setup) { b.Helper() + for range b.N { addr := s.Backend.NewAddress() + err := addr.UnmarshalBinary(s.AddressMarshalled) if err != nil { b.Fatal(err) diff --git a/watcher/local/registry.go b/watcher/local/registry.go index d6fa5e1e3..e754eda31 100644 --- a/watcher/local/registry.go +++ b/watcher/local/registry.go @@ -40,6 +40,7 @@ func newRegistry() *registry { func (r *registry) addIfSucceeds(id channel.ID, chInitializer chInitializer) (*ch, error) { r.mtx.Lock() defer r.mtx.Unlock() + if _, ok := r.chs[id]; ok { return nil, errors.New("already watching for this channel") } diff --git a/watcher/local/watcher.go b/watcher/local/watcher.go index 53d1c3a08..1083ba4b9 100644 --- a/watcher/local/watcher.go +++ b/watcher/local/watcher.go @@ -45,8 +45,9 @@ const ( type ( // Watcher implements a local watcher. Watcher struct { - rs channel.RegisterSubscriber *registry + + rs channel.RegisterSubscriber } txRetriever struct { @@ -124,8 +125,10 @@ func IsErrSubChannelsPresent(err error) bool { // before and after execution. func (ch *ch) Go(fn func()) { ch.wg.Add(1) + go func() { defer ch.wg.Done() + fn() }() } @@ -164,9 +167,11 @@ func (w *Watcher) StartWatchingSubChannel( if !ok { return nil, nil, errors.New("parent channel not registered with the watcher") } + if parentCh.isSubChannel() { return nil, nil, errors.New("parent must be a ledger channel") } + parentCh.subChsAccess.Lock() defer parentCh.subChsAccess.Unlock() statesPub, eventsSub, err := w.startWatching(ctx, parentCh, signedState) @@ -200,8 +205,10 @@ func (w *Watcher) StopWatching(_ context.Context, id channel.ID) error { if ch.isSubChannel() { parent = ch.parent } + parent.subChsAccess.Lock() defer parent.subChsAccess.Unlock() + if ch.isClosed { // Channel could have been closed while were waiting for the mutex locked. return errors.New("channel not registered with the watcher") @@ -213,6 +220,7 @@ func (w *Watcher) StopWatching(_ context.Context, id channel.ID) error { if _, ok := latestParentTx.SubAlloc(id); ok { parent.archivedSubChStates[id] = makeSignedState(ch.params, ch.txRetriever.retrieve()) } + delete(parent.subChs, id) } else if len(ch.subChs) > 0 { return errors.WithMessagef(ErrSubChannelsPresent, "cannot de-register: %d %v", len(ch.subChs), ch.id) @@ -232,6 +240,7 @@ func (w *Watcher) startWatching( id := signedState.State.ID var statesPubSub *statesPubSub + var eventsToClientPubSub *adjudicatorPubSub chInitializer1 := func() (*ch, error) { eventsFromChainSub, err := w.rs.Subscribe(ctx, id) @@ -252,6 +261,7 @@ func (w *Watcher) startWatching( State: signedState.State, Sigs: signedState.Sigs, } + ch.Go(func() { ch.handleStatesFromClient(initialTx) }) ch.Go(func() { ch.handleEventsFromChain(w.rs, w.registry) }) //nolint:contextcheck @@ -307,7 +317,9 @@ func (lt txRetriever) retrieve() channel.Transaction { func (ch *ch) handleStatesFromClient(initialTx channel.Transaction) { currentTx := initialTx var _tx channel.Transaction + var ok bool + for { select { case _tx, ok = <-ch.statesSub.statesStream(): @@ -323,6 +335,7 @@ func (ch *ch) handleStatesFromClient(initialTx channel.Transaction) { if found { currentTx = pendingTx } + ch.txRetriever.response <- currentTx } } @@ -332,6 +345,7 @@ func (ch *ch) handleStatesFromClient(initialTx channel.Transaction) { // returns the last read transaction. func readPendingTxs(statesSub statesSub, timeout time.Duration) (channel.Transaction, bool) { var currentTx, temp channel.Transaction + var ok bool found := false @@ -359,6 +373,7 @@ func readPendingTxs(statesSub statesSub, timeout time.Duration) (channel.Transac func (ch *ch) handleEventsFromChain(registerer channel.Registerer, chRegistry *registry) { // Create a context that is canceled when the watcher is stopped. ctx, cancel := context.WithCancel(context.Background()) + go func() { <-ch.done cancel() @@ -427,6 +442,7 @@ func (ch *ch) handleRegisteredEvent( log.Error("Error registering dispute: ", err) return } + log.Debug("Registered successfully") ch.registered = true } @@ -462,6 +478,7 @@ func registerDispute(ctx context.Context, r *registry, registerer channel.Regist func retrieveLatestSubStates(r *registry, parent *ch) (channel.Transaction, []channel.SignedState) { parentTx := parent.txRetriever.retrieve() + subStates := make([]channel.SignedState, len(parentTx.Locked)) for i := range parentTx.Locked { // Can be done concurrently. @@ -497,6 +514,7 @@ func closePubSubs(ch *ch) { err := errors.WithMessage(err, "closing events from chain sub") log.WithField("id", ch.id).Error(err.Error()) } + ch.statesSub.close() ch.wg.Wait() diff --git a/watcher/local/watcher_test.go b/watcher/local/watcher_test.go index abeec70b4..18ab1a66c 100644 --- a/watcher/local/watcher_test.go +++ b/watcher/local/watcher_test.go @@ -504,8 +504,7 @@ func Test_Watcher_StopWatching(t *testing.T) { t.Run("happy/adjSub_noError", func(t *testing.T) { f(t, nil) }) t.Run("happy/adjSub_error", func(t *testing.T) { f(t, assert.AnError) }) t.Run("happy/concurrency", func(t *testing.T) { - // defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) - + // defer goleak.VerifyNone(t, goleak.IgnoreCurrent() params, txs := randomTxsForSingleCh(rng, 1) adjSub := &mocks.AdjudicatorSubscription{} trigger := setExpectationNextCall(adjSub) @@ -519,6 +518,7 @@ func Test_Watcher_StopWatching(t *testing.T) { wg := sync.WaitGroup{} for range 2 { wg.Add(1) + go func() { w.StopWatching(context.Background(), txs[0].ID) //nolint:errcheck wg.Done() @@ -804,6 +804,7 @@ func setExpectationNextCall( for i := range adjEvents { handle := make(chan time.Time) triggers.handles <- handle + triggers.adjEvents <- adjEvents[i] adjSub.On("Next").Return(adjEvents[i]).WaitUntil(handle).Once() diff --git a/wire/account.go b/wire/account.go index f864fc6fb..17c5a22d0 100644 --- a/wire/account.go +++ b/wire/account.go @@ -77,12 +77,15 @@ func (m *AuthResponseMsg) Encode(w io.Writer) error { func (m *AuthResponseMsg) Decode(r io.Reader) (err error) { // Read the length of the signature var signatureLen uint32 - if err := binary.Read(r, binary.BigEndian, &signatureLen); err != nil { + err = binary.Read(r, binary.BigEndian, &signatureLen) + if err != nil { return fmt.Errorf("failed to read signature length: %w", err) } + // Read the signature bytes m.Signature = make([]byte, signatureLen) - if _, err := io.ReadFull(r, m.Signature); err != nil { + _, err = io.ReadFull(r, m.Signature) + if err != nil { return fmt.Errorf("failed to read signature: %w", err) } return nil diff --git a/wire/address.go b/wire/address.go index 5d58bc1b4..bd240deff 100644 --- a/wire/address.go +++ b/wire/address.go @@ -64,18 +64,22 @@ func (a AddressDecMap) Encode(w stdio.Writer) error { return errors.New("map length out of bounds") } length := int32(l) - if err := perunio.Encode(w, length); err != nil { + err := perunio.Encode(w, length) + if err != nil { return errors.WithMessage(err, "encoding map length") } + for i, addr := range a { id := int(i) if id < math.MinInt32 || id > math.MaxInt32 { return errors.New("map index out of bounds") } - if err := perunio.Encode(w, int32(id)); err != nil { + err := perunio.Encode(w, int32(id)) + if err != nil { return errors.WithMessage(err, "encoding map index") } - if err := perunio.Encode(w, addr); err != nil { + err = perunio.Encode(w, addr) + if err != nil { return errors.WithMessagef(err, "encoding %d-th address map entry", i) } } @@ -90,9 +94,11 @@ func (a AddressMapArray) Encode(w stdio.Writer) error { return errors.New("array length out of bounds") } length := int32(l) - if err := perunio.Encode(w, length); err != nil { + err := perunio.Encode(w, length) + if err != nil { return errors.WithMessage(err, "encoding array length") } + for i, addr := range a { if err := perunio.Encode(w, AddressDecMap(addr)); err != nil { return errors.WithMessagef(err, "encoding %d-th address array entry", i) diff --git a/wire/cache_internal_test.go b/wire/cache_internal_test.go index 10cb3c3d5..4e1545f59 100644 --- a/wire/cache_internal_test.go +++ b/wire/cache_internal_test.go @@ -37,6 +37,7 @@ func TestCache(t *testing.T) { ping0 := newEnvelope(NewPingMsg()) pong := newEnvelope(NewPongMsg()) + time.Sleep(1 * time.Millisecond) // Sleep to ensure unique timestamps. ping1 := newEnvelope(NewPingMsg()) ping2 := newEnvelope(NewPingMsg()) diff --git a/wire/hybridbus.go b/wire/hybridbus.go index 49bab8803..7dfb19806 100644 --- a/wire/hybridbus.go +++ b/wire/hybridbus.go @@ -81,6 +81,7 @@ func (b *hybridBus) Publish(ctx context.Context, e *Envelope) error { // Wait until all sending attempts terminated. This is when either the // context expires, all buses fail, or at least one bus succeeds. err := errg.Wait() + if success.IsSet() { return nil } diff --git a/wire/net/bus.go b/wire/net/bus.go index 9847f868c..a334de84e 100644 --- a/wire/net/bus.go +++ b/wire/net/bus.go @@ -53,6 +53,7 @@ func NewBus(id map[wallet.BackendID]wire.Account, d Dialer, s wire.EnvelopeSeria } onNewEndpoint := func(map[wallet.BackendID]wire.Address) wire.Consumer { return b.mainRecv } + b.reg = NewEndpointRegistry(id, onNewEndpoint, d, s) go b.dispatchMsgs() @@ -79,6 +80,7 @@ func (b *Bus) SubscribeClient(c wire.Consumer, addr map[wallet.BackendID]wire.Ad func (b *Bus) Publish(ctx context.Context, e *wire.Envelope) (err error) { for attempt := 1; attempt <= PublishAttempts; attempt++ { log.Tracef("Bus.Publish attempt: %d/%d", attempt, PublishAttempts) + var ep *Endpoint if ep, err = b.reg.Endpoint(ctx, e.Recipient); err == nil { if err = ep.Send(ctx, e); err == nil { @@ -144,6 +146,7 @@ func (b *Bus) dispatchMsgs() { b.mutex.Lock() r, ok := b.recvs[wire.Keys(e.Recipient)] b.mutex.Unlock() + if !ok { log.WithField("sender", e.Sender). WithField("recipient", e.Recipient). diff --git a/wire/net/endpoint.go b/wire/net/endpoint.go index 28bc1a225..264c80401 100644 --- a/wire/net/endpoint.go +++ b/wire/net/endpoint.go @@ -57,6 +57,7 @@ func (p *Endpoint) Send(ctx context.Context, e *wire.Envelope) error { // Asynchronously send, because we cannot abort Conn.Send(). go func() { defer p.sending.Unlock() + sent <- p.conn.Send(e) }() diff --git a/wire/net/endpoint_internal_test.go b/wire/net/endpoint_internal_test.go index f37ab09c9..d71f15fbb 100644 --- a/wire/net/endpoint_internal_test.go +++ b/wire/net/endpoint_internal_test.go @@ -91,9 +91,10 @@ func (s *setup) Close() error { // client is a simulated client in the test setup. // All of the client's incoming messages can be read from its receiver. type client struct { + *wire.Receiver + endpoint *Endpoint Registry *EndpointRegistry - *wire.Receiver } // makeClient creates a simulated test client. diff --git a/wire/net/endpoint_registry.go b/wire/net/endpoint_registry.go index 6539ca528..8a78781b2 100644 --- a/wire/net/endpoint_registry.go +++ b/wire/net/endpoint_registry.go @@ -67,6 +67,9 @@ func newDialingEndpoint(addr map[wallet.BackendID]wire.Address) *dialingEndpoint // connections. It should not be used manually, but only internally by a // wire.Bus. type EndpointRegistry struct { + log.Embedding + perunsync.Closer + id map[wallet.BackendID]wire.Account // The identity of the node. dialer Dialer // Used for dialing peers. onNewEndpoint func(map[wallet.BackendID]wire.Address) wire.Consumer // Selects Consumer for new Endpoints' receive loop. @@ -75,9 +78,6 @@ type EndpointRegistry struct { endpoints map[wire.AddrKey]*fullEndpoint // The list of all of all established Endpoints. dialing map[wire.AddrKey]*dialingEndpoint mutex sync.RWMutex // protects peers and dialing. - - log.Embedding - perunsync.Closer } const exchangeAddrsTimeout = 10 * time.Second @@ -300,6 +300,7 @@ func (r *EndpointRegistry) addEndpoint(addr map[wallet.BackendID]wire.Address, c // fullEndpoint retrieves or creates a fullEndpoint for the passed address. func (r *EndpointRegistry) fullEndpoint(addr map[wallet.BackendID]wire.Address, e *Endpoint) (_ *fullEndpoint, created bool) { key := wire.Keys(addr) + r.mutex.Lock() defer r.mutex.Unlock() entry, ok := r.endpoints[key] @@ -366,6 +367,7 @@ func (p *fullEndpoint) delete(expectedOldValue *Endpoint) { func (r *EndpointRegistry) find(addr map[wallet.BackendID]wire.Address) *Endpoint { r.mutex.RLock() defer r.mutex.RUnlock() + if e, ok := r.endpoints[wire.Keys(addr)]; ok { return e.Endpoint() } @@ -379,6 +381,7 @@ func (r *EndpointRegistry) setupConn(conn Conn) error { defer cancel() var peerAddr map[wallet.BackendID]wire.Address + var err error if peerAddr, err = ExchangeAddrsPassive(ctx, r.id, conn); err != nil { conn.Close() diff --git a/wire/net/endpoint_registry_internal_test.go b/wire/net/endpoint_registry_internal_test.go index f2e4c7575..55504e7bd 100644 --- a/wire/net/endpoint_registry_internal_test.go +++ b/wire/net/endpoint_registry_internal_test.go @@ -163,6 +163,7 @@ func TestRegistry_Get(t *testing.T) { a, b := newPipeConnPair() ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() + go ct.Stage("receiver", func(t test.ConcT) { dialer.put(a) _, err := ExchangeAddrsPassive(ctx, peerID, b) @@ -203,6 +204,7 @@ func TestRegistry_authenticatedDial(t *testing.T) { t.Run("dial success, ExchangeAddrs fail", func(t *testing.T) { a, b := newPipeConnPair() + go func() { d.put(a) if _, err := b.Recv(); err != nil { @@ -308,6 +310,7 @@ func TestRegistry_setupConn(t *testing.T) { d := &mockDialer{dial: make(chan Conn)} r := NewEndpointRegistry(id, nilConsumer, d, perunio.Serializer()) a, b := newPipeConnPair() + go func() { err := ExchangeAddrsActive(context.Background(), remoteID, wire.AddressMapfromAccountMap(id), b) if err != nil { diff --git a/wire/net/exchange_addr_internal_test.go b/wire/net/exchange_addr_internal_test.go index 5d67a6a42..36d877bba 100644 --- a/wire/net/exchange_addr_internal_test.go +++ b/wire/net/exchange_addr_internal_test.go @@ -78,6 +78,7 @@ func TestExchangeAddrs_Timeout(t *testing.T) { func TestExchangeAddrs_BogusMsg(t *testing.T) { rng := test.Prng(t) acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + conn := newMockConn() conn.recvQueue <- wiretest.NewRandomEnvelope(rng, wire.NewPingMsg()) addr, err := ExchangeAddrsPassive(context.Background(), acc, conn) diff --git a/wire/net/libp2p/account.go b/wire/net/libp2p/account.go index 9f5e7f301..e8470d3c3 100644 --- a/wire/net/libp2p/account.go +++ b/wire/net/libp2p/account.go @@ -51,6 +51,7 @@ const ( // Account represents a libp2p wire account containing a libp2p host. type Account struct { host.Host + relayAddr string privateKey crypto.PrivKey reservation *libp2pclient.Reservation @@ -88,7 +89,8 @@ func NewRandomAccount(rng *rand.Rand) *Account { sw.Backoff().Clear(relayInfo.ID) } - if err := client.Connect(context.Background(), *relayInfo); err != nil { + err = client.Connect(context.Background(), *relayInfo) + if err != nil { client.Close() panic(errors.WithMessage(err, "connecting to the relay server")) } @@ -136,7 +138,8 @@ func NewAccountFromPrivateKeyBytes(prvKeyBytes []byte) (*Account, error) { sw.Backoff().Clear(relayInfo.ID) } - if err := client.Connect(context.Background(), *relayInfo); err != nil { + err = client.Connect(context.Background(), *relayInfo) + if err != nil { client.Close() return nil, errors.WithMessage(err, "connecting to the relay server") } @@ -200,6 +203,7 @@ func (acc *Account) RegisterOnChainAddress(onChainAddr wallet.Address) error { OnChainAddress string PeerID string } + if onChainAddr == nil { return errors.New("on-chain address is nil") } @@ -360,11 +364,13 @@ func (acc *Account) keepReservationAlive(ctx context.Context, ai peer.AddrInfo) } } } + continue } acc.reservation = newReservation backoff = initialBackoff + log.Println("keepReservationAlive: reservation successfully renewed") } } diff --git a/wire/net/libp2p/bus_internal_test.go b/wire/net/libp2p/bus_internal_test.go index f1838a0db..4d432fc57 100644 --- a/wire/net/libp2p/bus_internal_test.go +++ b/wire/net/libp2p/bus_internal_test.go @@ -33,6 +33,7 @@ func TestBus(t *testing.T) { i := 0 // Keep track of the current client ID dialers := make([]*Dialer, numClients) accs := make([]*Account, numClients) + defer func() { // Close all accounts for _, acc := range accs { diff --git a/wire/net/libp2p/dialer.go b/wire/net/libp2p/dialer.go index d08cbdf06..8199a9e4a 100644 --- a/wire/net/libp2p/dialer.go +++ b/wire/net/libp2p/dialer.go @@ -76,7 +76,9 @@ func (d *Dialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Addres if err != nil { return nil, errors.Wrap(err, "converting peer multiaddress to address info") } - if err := d.host.Connect(ctx, *peerAddrInfo); err != nil { + + err = d.host.Connect(ctx, *peerAddrInfo) + if err != nil { return nil, errors.Wrap(err, "failed to dial peer: failed to connecting to peer") } diff --git a/wire/net/libp2p/dialer_internal_test.go b/wire/net/libp2p/dialer_internal_test.go index 2cacebeb9..0a8dfaa73 100644 --- a/wire/net/libp2p/dialer_internal_test.go +++ b/wire/net/libp2p/dialer_internal_test.go @@ -71,6 +71,7 @@ func TestDialer_Dial(t *testing.T) { laddrs := make(map[wallet.BackendID]wire.Address) laddrs[test.TestBackendID] = lAddr lpeerID := lHost.ID() + listener := NewP2PListener(lHost) defer listener.Close() @@ -79,6 +80,7 @@ func TestDialer_Dial(t *testing.T) { daddrs := make(map[wallet.BackendID]wire.Address) daddrs[test.TestBackendID] = dAddr dialer := NewP2PDialer(dHost) + dialer.Register(laddrs, lpeerID.String()) defer dialer.Close() diff --git a/wire/net/libp2p/listener_internal_test.go b/wire/net/libp2p/listener_internal_test.go index 153e4f4e2..81747fa1a 100644 --- a/wire/net/libp2p/listener_internal_test.go +++ b/wire/net/libp2p/listener_internal_test.go @@ -41,6 +41,7 @@ func TestListener_Close(t *testing.T) { t.Run("double close", func(t *testing.T) { rng := pkgtest.Prng(t) acc := NewRandomAccount(rng) + defer func() { assert.NoError(t, acc.Close()) }() @@ -55,11 +56,13 @@ func TestListener_Accept(t *testing.T) { // Happy case already tested in TestDialer_Dial. rng := pkgtest.Prng(t) acc := NewRandomAccount(rng) + defer func() { assert.NoError(t, acc.Close()) }() timeout := 100 * time.Millisecond + t.Run("timeout", func(t *testing.T) { l := NewP2PListener(acc) defer l.Close() diff --git a/wire/net/simple/dialer.go b/wire/net/simple/dialer.go index 60c7cc11f..15812e6d8 100644 --- a/wire/net/simple/dialer.go +++ b/wire/net/simple/dialer.go @@ -32,12 +32,12 @@ import ( // Dialer is a simple lookup-table based dialer that can dial known peers. // New peer addresses can be added via Register(). type Dialer struct { + pkgsync.Closer + mutex sync.RWMutex // Protects peers. peers map[wire.AddrKey]string // Known peer addresses. dialer tls.Dialer // Used to dial connections. network string // The socket type. - - pkgsync.Closer } var _ wirenet.Dialer = (*Dialer)(nil) diff --git a/wire/net/test/connhub.go b/wire/net/test/connhub.go index 9ae13467b..29c9298c6 100644 --- a/wire/net/test/connhub.go +++ b/wire/net/test/connhub.go @@ -27,11 +27,11 @@ import ( // ConnHub is a factory for creating and connecting test dialers and listeners. type ConnHub struct { - mutex gosync.RWMutex listenerMap - dialers dialerList - sync.Closer + + mutex gosync.RWMutex + dialers dialerList } // NewNetListener creates a new test listener for the given address. @@ -46,7 +46,8 @@ func (h *ConnHub) NewNetListener(addr map[wallet.BackendID]wire.Address) *Listen } listener := NewNetListener() - if err := h.insert(addr, listener); err != nil { + err := h.insert(addr, listener) + if err != nil { panic("double registration") } @@ -81,18 +82,21 @@ func (h *ConnHub) Close() (err error) { h.mutex.Lock() defer h.mutex.Unlock() - if err := h.Closer.Close(); err != nil { + err = h.Closer.Close() + if err != nil { return errors.WithMessage(err, "ConnHub already closed") } for _, l := range h.clear() { - if cerr := l.value.Close(); cerr != nil && err == nil { + cerr := l.value.Close() + if cerr != nil && err == nil { err = cerr } } for _, d := range h.dialers.clear() { - if cerr := d.Close(); cerr != nil && err == nil { + cerr := d.Close() + if cerr != nil && err == nil { err = cerr } } diff --git a/wire/net/test/connhub_internal_test.go b/wire/net/test/connhub_internal_test.go index de8b1df9b..edd97b411 100644 --- a/wire/net/test/connhub_internal_test.go +++ b/wire/net/test/connhub_internal_test.go @@ -51,7 +51,7 @@ func TestConnHub_Create(t *testing.T) { ct.Stage("accept", func(rt pkgtest.ConcT) { conn, err := l.Accept(ser) assert.NoError(err) - assert.NotNil(rt, conn) + require.NotNil(rt, conn) assert.NoError(conn.Send(wiretest.NewRandomEnvelope(rng, wire.NewPingMsg()))) }) }) diff --git a/wire/net/test/dialer.go b/wire/net/test/dialer.go index c9658bc1a..fe53c4ca4 100644 --- a/wire/net/test/dialer.go +++ b/wire/net/test/dialer.go @@ -30,10 +30,10 @@ import ( // Dialer is a test dialer that can dial connections to Listeners via a ConnHub. type Dialer struct { + sync.Closer + hub *ConnHub dialed int32 - - sync.Closer } var _ wirenet.Dialer = (*Dialer)(nil) diff --git a/wire/net/test/listener.go b/wire/net/test/listener.go index 0ebc201bf..92e895515 100644 --- a/wire/net/test/listener.go +++ b/wire/net/test/listener.go @@ -33,9 +33,9 @@ var _ wirenet.Listener = &Listener{} // whether a Listener is still open. type Listener struct { sync.Closer - queue chan wirenet.Conn // The connection queue (unbuffered). - accepted int32 // The number of connections that have been accepted. + queue chan wirenet.Conn // The connection queue (unbuffered). + accepted int32 // The number of connections that have been accepted. } // NewNetListener creates a new test listener. diff --git a/wire/net/test/pipeconn.go b/wire/net/test/pipeconn.go index bd95f36d1..e3d2a8ed0 100644 --- a/wire/net/test/pipeconn.go +++ b/wire/net/test/pipeconn.go @@ -33,7 +33,8 @@ type Conn struct { // Send sends an envelope. func (c *Conn) Send(e *wire.Envelope) (err error) { - if err = c.conn.Send(e); err != nil { + err = c.conn.Send(e) + if err != nil { c.Close() } return diff --git a/wire/perunio/bigint.go b/wire/perunio/bigint.go index 532eae66d..94c086c86 100644 --- a/wire/perunio/bigint.go +++ b/wire/perunio/bigint.go @@ -34,7 +34,8 @@ type BigInt struct { func (b *BigInt) Decode(reader io.Reader) error { // Read length lengthData := make([]byte, 1) - if _, err := reader.Read(lengthData); err != nil { + _, err := reader.Read(lengthData) + if err != nil { return errors.Wrap(err, "failed to decode length of big.Int") } @@ -44,14 +45,17 @@ func (b *BigInt) Decode(reader io.Reader) error { } bytes := make([]byte, length) - if n, err := io.ReadFull(reader, bytes); err != nil { + n, err := io.ReadFull(reader, bytes) + if err != nil { return errors.Wrapf(err, "failed to read bytes for big.Int, read %d/%d", n, length) } if b.Int == nil { b.Int = new(big.Int) } + b.SetBytes(bytes) + return nil } @@ -60,6 +64,7 @@ func (b BigInt) Encode(writer io.Writer) error { if b.Int == nil { panic("logic error: tried to encode nil big.Int") } + if b.Sign() == -1 { panic("encoding of negative big.Int not implemented") } @@ -72,7 +77,8 @@ func (b BigInt) Encode(writer io.Writer) error { } // Write length - if _, err := writer.Write([]byte{uint8(length)}); err != nil { + _, err := writer.Write([]byte{uint8(length)}) + if err != nil { return errors.Wrap(err, "failed to write length") } @@ -82,5 +88,6 @@ func (b BigInt) Encode(writer io.Writer) error { // Write bytes n, err := writer.Write(bytes) + return errors.Wrapf(err, "failed to write big.Int, wrote %d bytes of %d", n, length) } diff --git a/wire/perunio/bigint_external_test.go b/wire/perunio/bigint_external_test.go index 6ff4ff147..45e595fa4 100644 --- a/wire/perunio/bigint_external_test.go +++ b/wire/perunio/bigint_external_test.go @@ -53,6 +53,7 @@ func TestBigInt_DecodeToExisting(t *testing.T) { func TestBigInt_Negative(t *testing.T) { neg, buf := perunio.BigInt{Int: big.NewInt(-1)}, new(bytes.Buffer) + assert.Panics(t, func() { _ = neg.Encode(buf) }, "encoding negative big.Int should panic") assert.Zero(t, buf.Len(), "encoding negative big.Int should not write anything") } @@ -73,6 +74,7 @@ func TestBigInt_Invalid(t *testing.T) { // manually encode too big number to test failing of decoding buf.Write([]byte{perunio.MaxBigIntLength + 1}) + for range perunio.MaxBigIntLength + 1 { buf.WriteByte(0xff) } diff --git a/wire/perunio/byteslice.go b/wire/perunio/byteslice.go index 1c3f0f145..e2d914177 100644 --- a/wire/perunio/byteslice.go +++ b/wire/perunio/byteslice.go @@ -41,8 +41,10 @@ func (b *ByteSlice) Decode(r io.Reader) error { n, err := r.Read(*b) for n < len(*b) && err == nil { var nn int + nn, err = r.Read((*b)[n:]) n += nn } + return errors.Wrap(err, "failed to read []byte") } diff --git a/wire/perunio/byteslice_external_test.go b/wire/perunio/byteslice_external_test.go index 7796283dc..97d020a94 100644 --- a/wire/perunio/byteslice_external_test.go +++ b/wire/perunio/byteslice_external_test.go @@ -49,6 +49,7 @@ func TestStutter(t *testing.T) { ctxtest.AssertTerminatesQuickly(t, func() { require.NoError(t, decodedValue.Decode(r)) }) + for i, v := range values { assert.Equal(t, decodedValue[i], v) } diff --git a/wire/perunio/equal_binary.go b/wire/perunio/equal_binary.go index 348c96f06..ed96a3f77 100644 --- a/wire/perunio/equal_binary.go +++ b/wire/perunio/equal_binary.go @@ -37,6 +37,7 @@ func EqualBinary(a, b encoding.BinaryMarshaler) (bool, error) { if err != nil { return false, errors.Wrap(err, "EqualBinary: marshaling a") } + binaryB, err := b.MarshalBinary() if err != nil { return false, errors.Wrap(err, "EqualBinary: marshaling b") diff --git a/wire/perunio/equal_encoding.go b/wire/perunio/equal_encoding.go index 3afd5ad77..683011a36 100644 --- a/wire/perunio/equal_encoding.go +++ b/wire/perunio/equal_encoding.go @@ -35,10 +35,13 @@ func EqualEncoding(a, b Encoder) (bool, error) { return true, nil } - if err := a.Encode(buffA); err != nil { + err := a.Encode(buffA) + if err != nil { return false, errors.Wrap(err, "EqualEncoding encode error") } - if err := b.Encode(buffB); err != nil { + + err = b.Encode(buffB) + if err != nil { return false, errors.Wrap(err, "EqualEncoding encode error") } diff --git a/wire/perunio/serialize.go b/wire/perunio/serialize.go index 9c6e2d335..7913c43f4 100644 --- a/wire/perunio/serialize.go +++ b/wire/perunio/serialize.go @@ -51,6 +51,7 @@ func Encode(writer io.Writer, values ...interface{}) (err error) { //nolint: cyc err = encodeString(writer, v) case encoding.BinaryMarshaler: var data []byte + data, err = v.MarshalBinary() if err != nil { return errors.WithMessage(err, "marshaling to byte array") @@ -60,6 +61,7 @@ func Encode(writer io.Writer, values ...interface{}) (err error) { //nolint: cyc if length > uint16MaxValue { panic(fmt.Sprintf("length of marshaled data is %d, should be <= %d", len(data), uint16MaxValue)) } + err = binary.Write(writer, byteOrder, uint16(length)) if err != nil { return errors.WithMessage(err, "writing length of marshalled data") @@ -69,6 +71,7 @@ func Encode(writer io.Writer, values ...interface{}) (err error) { //nolint: cyc if length == 0 { break } + err = ByteSlice(data).Encode(writer) default: if enc, ok := value.(Encoder); ok { @@ -123,6 +126,7 @@ func Decode(reader io.Reader, values ...interface{}) (err error) { if length == 0 { break } + var data ByteSlice = make([]byte, length) err = data.Decode(reader) if err != nil { diff --git a/wire/perunio/serializer/serializer.go b/wire/perunio/serializer/serializer.go index 6d38e1590..384f278ea 100644 --- a/wire/perunio/serializer/serializer.go +++ b/wire/perunio/serializer/serializer.go @@ -34,12 +34,14 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) error { if err := perunio.Encode(w, wire.AddressDecMap(env.Sender), wire.AddressDecMap(env.Recipient)); err != nil { return err } + return wire.EncodeMsg(env.Msg, w) } // Decode decodes an envelope from the wire using perunio encoding format. func (serializer) Decode(r io.Reader) (env *wire.Envelope, err error) { env = &wire.Envelope{} + err = perunio.Decode(r, (*wire.AddressDecMap)(&env.Sender)) if err != nil { return env, errors.WithMessage(err, "decoding sender addresses") diff --git a/wire/perunio/string.go b/wire/perunio/string.go index 69a42592d..cb730e094 100644 --- a/wire/perunio/string.go +++ b/wire/perunio/string.go @@ -34,7 +34,8 @@ func encodeString(w io.Writer, s string) error { return errors.Errorf("string length exceeded: %d", len(s)) } - if err := binary.Write(w, byteOrder, ul); err != nil { + err := binary.Write(w, byteOrder, ul) + if err != nil { return errors.Wrap(err, "failed to write string length") } @@ -44,19 +45,22 @@ func encodeString(w io.Writer, s string) error { return nil } - _, err := io.WriteString(w, s) + _, err = io.WriteString(w, s) + return errors.Wrap(err, "failed to write string") } // decodeString reads the length as uint16 and the string itself from the io.Reader. func decodeString(r io.Reader, s *string) error { var l uint16 - if err := binary.Read(r, byteOrder, &l); err != nil { + err := binary.Read(r, byteOrder, &l) + if err != nil { return errors.Wrap(err, "failed to read string length") } buf := make([]byte, l) - if _, err := io.ReadFull(r, buf); err != nil { + _, err = io.ReadFull(r, buf) + if err != nil { return errors.Wrap(err, "failed to read string") } *s = string(buf) diff --git a/wire/perunio/string_internal_test.go b/wire/perunio/string_internal_test.go index 3016046b3..942b274b0 100644 --- a/wire/perunio/string_internal_test.go +++ b/wire/perunio/string_internal_test.go @@ -39,9 +39,11 @@ func TestEncodeDecodeString(t *testing.T) { for _, s := range ss { r, w := io.Pipe() done := make(chan struct{}) + go func() { defer close(done) defer w.Close() + assert.NoError(encodeString(w, s)) }() @@ -55,6 +57,7 @@ func TestEncodeDecodeString(t *testing.T) { t.Run("too long string", func(t *testing.T) { tooLong := string(append(uint16buf, 42)) //nolint: makezero + var buf bytes.Buffer require.Error(t, encodeString(&buf, tooLong)) assert.Zero(buf.Len(), "nothing should have been written to the stream") diff --git a/wire/perunio/test/serializertest.go b/wire/perunio/test/serializertest.go index 01899d2a1..151b5d9dc 100644 --- a/wire/perunio/test/serializertest.go +++ b/wire/perunio/test/serializertest.go @@ -40,6 +40,7 @@ func genericDecodeEncodeTest(t *testing.T, serializers ...perunio.Serializer) { for i, v := range serializers { r, w := io.Pipe() br := iotest.OneByteReader(r) + go func() { if err := perunio.Encode(w, v); err != nil { t.Errorf("failed to encode %dth element (%T): %+v", i, v, err) @@ -50,7 +51,9 @@ func genericDecodeEncodeTest(t *testing.T, serializers ...perunio.Serializer) { dest := reflect.New(reflect.TypeOf(v).Elem()) err := perunio.Decode(br, dest.Interface().(perunio.Serializer)) + r.Close() + if err != nil { t.Errorf("failed to decode %dth element (%T): %+v", i, v, err) } else { @@ -67,6 +70,7 @@ func genericDecodeEncodeTest(t *testing.T, serializers ...perunio.Serializer) { func GenericBrokenPipeTest(t *testing.T, serializers ...perunio.Serializer) { for i, v := range serializers { r, w := io.Pipe() + _ = w.Close() if err := v.Encode(w); err == nil { t.Errorf("encoding on closed writer should fail, but does not. %dth element (%T)", i, v) diff --git a/wire/perunio/wire_internal_test.go b/wire/perunio/wire_internal_test.go index e278f45f3..54c861958 100644 --- a/wire/perunio/wire_internal_test.go +++ b/wire/perunio/wire_internal_test.go @@ -59,6 +59,7 @@ func TestEncodeDecode(t *testing.T) { r, w := io.Pipe() longInt, _ := new(big.Int).SetString("12345671823897123798901234561234567890", 16) + var byte32 [32]byte for i := byte(0); i < 32; i++ { byte32[i] = i + 1 diff --git a/wire/protobuf/controlmsgs.go b/wire/protobuf/controlmsgs.go index 5e5ca8ae2..66f65fbd0 100644 --- a/wire/protobuf/controlmsgs.go +++ b/wire/protobuf/controlmsgs.go @@ -41,18 +41,21 @@ func fromShutdownMsg(msg *wire.ShutdownMsg) *Envelope_ShutdownMsg { func fromAuthResponseMsg(msg *wire.AuthResponseMsg) *Envelope_AuthResponseMsg { protoMsg := &AuthResponseMsg{} protoMsg.Signature = msg.Signature + return &Envelope_AuthResponseMsg{protoMsg} } func toPingMsg(protoMsg *Envelope_PingMsg) (msg *wire.PingMsg) { msg = &wire.PingMsg{} msg.Created = time.Unix(0, protoMsg.PingMsg.GetCreated()) + return msg } func toPongMsg(protoEnvMsg *Envelope_PongMsg) (msg *wire.PongMsg) { msg = &wire.PongMsg{} msg.Created = time.Unix(0, protoEnvMsg.PongMsg.GetCreated()) + return msg } diff --git a/wire/protobuf/proposalmsgs.go b/wire/protobuf/proposalmsgs.go index 0dc274a1b..606c89528 100644 --- a/wire/protobuf/proposalmsgs.go +++ b/wire/protobuf/proposalmsgs.go @@ -34,10 +34,12 @@ func ToLedgerChannelProposalMsg(protoEnvMsg *Envelope_LedgerChannelProposalMsg) protoMsg := protoEnvMsg.LedgerChannelProposalMsg msg = &client.LedgerChannelProposalMsg{} + msg.BaseChannelProposal, err = ToBaseChannelProposal(protoMsg.GetBaseChannelProposal()) if err != nil { return nil, err } + msg.Participant, err = ToWalletAddr(protoMsg.GetParticipant()) if err != nil { return nil, errors.WithMessage(err, "participant address") @@ -130,6 +132,7 @@ func ToChannelProposalRejMsg(protoEnvMsg *Envelope_ChannelProposalRejMsg) (msg * // ToWalletAddr converts a protobuf wallet address to a wallet.Address. func ToWalletAddr(protoAddr *Address) (map[wallet.BackendID]wallet.Address, error) { addrMap := make(map[wallet.BackendID]wallet.Address) + for i := range protoAddr.GetAddressMapping() { var k int32 if err := binary.Read(bytes.NewReader(protoAddr.GetAddressMapping()[i].GetKey()), binary.BigEndian, &k); err != nil { @@ -148,6 +151,7 @@ func ToWalletAddr(protoAddr *Address) (map[wallet.BackendID]wallet.Address, erro // ToWireAddr converts a protobuf wallet address to a wallet.Address. func ToWireAddr(protoAddr *Address) (map[wallet.BackendID]wire.Address, error) { addrMap := make(map[wallet.BackendID]wire.Address) + for i := range protoAddr.GetAddressMapping() { var k int32 if err := binary.Read(bytes.NewReader(protoAddr.GetAddressMapping()[i].GetKey()), binary.BigEndian, &k); err != nil { @@ -179,6 +183,7 @@ func ToWalletAddrs(protoAddrs []*Address) ([]map[wallet.BackendID]wallet.Address // ToWireAddrs converts protobuf wire addresses to a slice of wire.Address. func ToWireAddrs(protoAddrs []*Address) ([]map[wallet.BackendID]wire.Address, error) { addrMap := make([]map[wallet.BackendID]wire.Address, len(protoAddrs)) + var err error for i, addMap := range protoAddrs { addrMap[i], err = ToWireAddr(addMap) @@ -218,6 +223,7 @@ func ToApp(protoApp []byte) (app channel.App, err error) { return app, nil } appDef, _ := channel.NewAppID() + err = appDef.UnmarshalBinary(protoApp) if err != nil { return app, err @@ -270,6 +276,7 @@ func ToIntSlice(backends [][]byte) ([]wallet.BackendID, error) { // ToAllocation converts a protobuf allocation to a channel.Allocation. func ToAllocation(protoAlloc *Allocation) (alloc *channel.Allocation, err error) { alloc = &channel.Allocation{} + alloc.Backends, err = ToIntSlice(protoAlloc.GetBackends()) if err != nil { return nil, errors.WithMessage(err, "backends") @@ -314,10 +321,12 @@ func ToBalance(protoBalance *Balance) (balance []channel.Bal) { // ToSubAlloc converts a protobuf SubAlloc to a channel.SubAlloc. func ToSubAlloc(protoSubAlloc *SubAlloc) (subAlloc channel.SubAlloc, err error) { subAlloc = channel.SubAlloc{} + subAlloc.Bals = ToBalance(protoSubAlloc.GetBals()) if len(protoSubAlloc.GetId()) != len(subAlloc.ID) { return subAlloc, errors.New("sub alloc id has incorrect length") } + copy(subAlloc.ID[:], protoSubAlloc.GetId()) subAlloc.IndexMap, err = ToIndexMap(protoSubAlloc.GetIndexMap().GetIndexMap()) return subAlloc, err @@ -343,6 +352,7 @@ func ToIndexMap(protoIndexMap []uint32) (indexMap []channel.Index, err error) { // Envelope_LedgerChannelProposalMsg. func FromLedgerChannelProposalMsg(msg *client.LedgerChannelProposalMsg) (_ *Envelope_LedgerChannelProposalMsg, err error) { protoMsg := &LedgerChannelProposalMsg{} + protoMsg.BaseChannelProposal, err = FromBaseChannelProposal(msg.BaseChannelProposal) if err != nil { return nil, err @@ -385,6 +395,7 @@ func FromVirtualChannelProposalMsg(msg *client.VirtualChannelProposalMsg) (_ *En for i := range msg.IndexMaps { protoMsg.IndexMaps[i] = &IndexMap{IndexMap: FromIndexMap(msg.IndexMaps[i])} } + protoMsg.Peers, err = FromWireAddrs(msg.Peers) return &Envelope_VirtualChannelProposalMsg{protoMsg}, errors.WithMessage(err, "peers") } @@ -462,6 +473,7 @@ func FromWireAddr(addr map[wallet.BackendID]wire.Address) (*Address, error) { if keyInt < 0 || keyInt > math.MaxUint32 { panic("Key exceeds uint32 range") } + binary.BigEndian.PutUint32(keyBytes, uint32(keyInt)) addressBytes, err := address.MarshalBinary() @@ -555,6 +567,7 @@ func FromAppAndData(app channel.App, data channel.Data) (protoApp, protoData []b if channel.IsNoApp(app) { return []byte{}, []byte{}, nil } + protoApp, err = app.Def().MarshalBinary() if err != nil { return []byte{}, []byte{}, err @@ -566,6 +579,7 @@ func FromAppAndData(app channel.App, data channel.Data) (protoApp, protoData []b // FromAllocation converts a channel.Allocation to a protobuf Allocation. func FromAllocation(alloc channel.Allocation) (protoAlloc *Allocation, err error) { protoAlloc = &Allocation{} + protoAlloc.Backends = make([][]byte, len(alloc.Backends)) for i := range alloc.Backends { protoAlloc.Backends[i] = make([]byte, 4) //nolint:mnd @@ -573,6 +587,7 @@ func FromAllocation(alloc channel.Allocation) (protoAlloc *Allocation, err error if id < 0 || id > math.MaxUint32 { panic("BackendID exceeds uint32 range") } + binary.BigEndian.PutUint32(protoAlloc.GetBackends()[i], uint32(id)) } protoAlloc.Assets = make([][]byte, len(alloc.Assets)) @@ -616,6 +631,7 @@ func FromBalance(balance []channel.Bal) (protoBalance *Balance, err error) { if balance[i] == nil { return nil, fmt.Errorf("%d'th amount is nil", i) // We do not want to define this as constant error. } + if balance[i].Sign() == -1 { return nil, fmt.Errorf("%d'th amount is negative", i) // We do not want to define this as constant error. } diff --git a/wire/protobuf/serializer.go b/wire/protobuf/serializer.go index 8d26fbf55..2f0fc9e5e 100644 --- a/wire/protobuf/serializer.go +++ b/wire/protobuf/serializer.go @@ -78,11 +78,13 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) (err error) { //nolint default: err = fmt.Errorf("unknown message type: %T", msg) } + if err != nil { return err } sender, recipient, err := marshalSenderRecipient(env) protoEnv.Sender, protoEnv.Recipient = sender, recipient + if err != nil { return err } @@ -127,6 +129,7 @@ func (serializer) Decode(r io.Reader) (env *wire.Envelope, err error) { //nolint sender, recipient, err := unmarshalSenderRecipient(protoEnv) env.Sender, env.Recipient = sender, recipient + if err != nil { return nil, err } @@ -182,6 +185,7 @@ func readEnvelope(r io.Reader) (*Envelope, error) { if _, err := r.Read(data); err != nil { return nil, errors.Wrap(err, "reading data from wire") } + var protoEnv Envelope return &protoEnv, errors.Wrap(proto.Unmarshal(data, &protoEnv), "unmarshalling envelope") } diff --git a/wire/protobuf/syncmsgs.go b/wire/protobuf/syncmsgs.go index ee172ab8b..66d195c6e 100644 --- a/wire/protobuf/syncmsgs.go +++ b/wire/protobuf/syncmsgs.go @@ -31,6 +31,7 @@ func toChannelSyncMsg(protoEnvMsg *Envelope_ChannelSyncMsg) (msg *client.Channel return msg, fmt.Errorf("invalid phase: %d", phase) } msg.Phase = channel.Phase(phase) + msg.CurrentTX.Sigs = make([][]byte, len(protoMsg.GetCurrentTx().GetSigs())) for i := range protoMsg.GetCurrentTx().GetSigs() { msg.CurrentTX.Sigs[i] = make([]byte, len(protoMsg.GetCurrentTx().GetSigs()[i])) @@ -45,6 +46,7 @@ func fromChannelSyncMsg(msg *client.ChannelSyncMsg) (_ *Envelope_ChannelSyncMsg, protoMsg.CurrentTx = &Transaction{} protoMsg.Phase = uint32(msg.Phase) + protoMsg.CurrentTx.Sigs = make([][]byte, len(msg.CurrentTX.Sigs)) for i := range msg.CurrentTX.Sigs { protoMsg.CurrentTx.Sigs[i] = make([]byte, len(msg.CurrentTX.Sigs[i])) diff --git a/wire/protobuf/updatemsgs.go b/wire/protobuf/updatemsgs.go index 62fdf8b24..3a217c076 100644 --- a/wire/protobuf/updatemsgs.go +++ b/wire/protobuf/updatemsgs.go @@ -240,6 +240,7 @@ func FromChannelUpdate(update *client.ChannelUpdateMsg) (protoUpdate *ChannelUpd // FromSignedState converts a channel.SignedState to a protobuf SignedState. func FromSignedState(signedState *channel.SignedState) (protoSignedState *SignedState, err error) { protoSignedState = &SignedState{} + protoSignedState.Sigs = make([][]byte, len(signedState.Sigs)) for i := range signedState.Sigs { protoSignedState.Sigs[i] = make([]byte, len(signedState.Sigs[i])) diff --git a/wire/receiver.go b/wire/receiver.go index dac5ae904..1d6ceed7b 100644 --- a/wire/receiver.go +++ b/wire/receiver.go @@ -35,9 +35,9 @@ var _ Consumer = (*Receiver)(nil) // execution context at a time. If multiple contexts need to access a peer's // messages, then multiple receivers have to be created. type Receiver struct { - msgs chan *Envelope - sync.Closer + + msgs chan *Envelope } // NewReceiver creates a new receiver. diff --git a/wire/receiver_internal_test.go b/wire/receiver_internal_test.go index b9c8c5359..66c44a40b 100644 --- a/wire/receiver_internal_test.go +++ b/wire/receiver_internal_test.go @@ -67,6 +67,7 @@ func TestReceiver_Next(t *testing.T) { t.Parallel() ctxtest.AssertTerminates(t, timeout*2, func() { r := NewReceiver() + go func() { time.Sleep(timeout) r.Close() @@ -93,6 +94,7 @@ func TestReceiver_Next(t *testing.T) { t.Parallel() ctxtest.AssertTerminates(t, timeout*2, func() { r := NewReceiver() + ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() re, err := r.Next(ctx) diff --git a/wire/relay.go b/wire/relay.go index 881fc5711..1fff21d3b 100644 --- a/wire/relay.go +++ b/wire/relay.go @@ -26,6 +26,7 @@ import ( // Relay handles (un)registering Consumers for a message Relay's messages. type Relay struct { sync.Closer + mutex stdsync.RWMutex consumers []subscription @@ -115,6 +116,7 @@ func (p *Relay) Subscribe(c Consumer, predicate Predicate) error { // Put cached messages into consumer in a go routine because receiving on it // probably starts after subscription. cached := p.cache.Messages(predicate) + go func() { for _, m := range cached { c.Put(m) diff --git a/wire/relay_internal_test.go b/wire/relay_internal_test.go index 63fbfdd98..df299216c 100644 --- a/wire/relay_internal_test.go +++ b/wire/relay_internal_test.go @@ -74,6 +74,7 @@ func TestProducer_Close(t *testing.T) { func TestProducer_Subscribe(t *testing.T) { fn := func(*Envelope) bool { return true } + t.Run("closed", func(t *testing.T) { p := NewRelay() p.Close() diff --git a/wire/test/bustest.go b/wire/test/bustest.go index 44e17e21c..3be72d955 100644 --- a/wire/test/bustest.go +++ b/wire/test/bustest.go @@ -52,6 +52,7 @@ func GenericBusTest(t *testing.T, require.Positive(t, numMsgs) rng := test.Prng(t) + type Client struct { r *wire.Relay pub, sub wire.Bus @@ -70,8 +71,10 @@ func GenericBusTest(t *testing.T, testNoReceive := func(t *testing.T) { t.Helper() ct := test.NewConcurrent(t) + ctx, cancel := context.WithTimeout(context.Background(), testNoReceiveTimeout) defer cancel() + for i := range clients { go ct.StageN("receive timeout", numClients, func(t test.ConcT) { r := wire.NewReceiver() @@ -82,17 +85,21 @@ func GenericBusTest(t *testing.T, assert.Error(t, err) }) } + ct.Wait("receive timeout") } testPublishAndReceive := func(t *testing.T, waiting func()) { t.Helper() ct := test.NewConcurrent(t) + ctx, cancel := context.WithTimeout( context.Background(), time.Duration((numClients)*(numClients-1)*numMsgs)*100*time.Millisecond) defer cancel() + waiting() + for sender := range clients { for recipient := range clients { if sender == recipient { @@ -112,6 +119,7 @@ func GenericBusTest(t *testing.T, go ct.StageN("receive", numClients*(numClients-1), func(t test.ConcT) { defer recv.Close() + for range numMsgs { e, err := recv.Next(ctx) assert.NoError(t, err) @@ -126,6 +134,7 @@ func GenericBusTest(t *testing.T, }) } } + ct.Wait("publish", "receive") // There must be no additional messages received. @@ -158,6 +167,7 @@ func equalMaps(a, b map[wallet.BackendID]wire.Address) bool { if len(a) != len(b) { return false } + for k, v := range a { if !v.Equal(b[k]) { return false diff --git a/wire/test/marshalertest.go b/wire/test/marshalertest.go index 48c936cce..687d9378d 100644 --- a/wire/test/marshalertest.go +++ b/wire/test/marshalertest.go @@ -32,6 +32,7 @@ type binary interface { // and decoding of serializer values works. func GenericMarshalerTest(t *testing.T, serializers ...binary) { t.Helper() + for i, v := range serializers { data, err := v.MarshalBinary() require.NoError(t, err, "failed to encode %dth element (%T)", i, v) diff --git a/wire/test/msgstest.go b/wire/test/msgstest.go index 29e8d3a68..98c84d453 100644 --- a/wire/test/msgstest.go +++ b/wire/test/msgstest.go @@ -40,10 +40,12 @@ func AuthMsgsSerializationTest(t *testing.T, serializerTest func(t *testing.T, m t.Helper() rng := pkgtest.Prng(t) + testMsg, err := wire.NewAuthResponseMsg(NewRandomAccountMap(rng, TestBackendID)) if err != nil { t.Fatal(err) } + serializerTest(t, testMsg) } @@ -51,8 +53,11 @@ func AuthMsgsSerializationTest(t *testing.T, serializerTest func(t *testing.T, m // minLen+maxLenDiff. func newRandomASCIIString(rng *rand.Rand, minLen, maxLenDiff int) string { str := make([]byte, minLen+rng.Intn(maxLenDiff)) + const firstPrintableASCII = 32 + const lastPrintableASCII = 126 + for i := range str { str[i] = byte(firstPrintableASCII + rng.Intn(lastPrintableASCII-firstPrintableASCII)) } diff --git a/wire/test/serializinglocalbus.go b/wire/test/serializinglocalbus.go index 0a2246174..81651b568 100644 --- a/wire/test/serializinglocalbus.go +++ b/wire/test/serializinglocalbus.go @@ -25,6 +25,7 @@ import ( // SerializingLocalBus is a local bus that also serializes messages for testing. type SerializingLocalBus struct { *wire.LocalBus + ser wire.EnvelopeSerializer } From d541e25ad509ce843d39813fb49dd59b43f67113 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Mon, 14 Jul 2025 19:10:28 +0200 Subject: [PATCH 21/26] fix: clear linting problem Signed-off-by: Minh Huy Tran --- .golangci.json | 4 +--- client/payment_test.go | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.golangci.json b/.golangci.json index 4ddb238e2..493d210a8 100644 --- a/.golangci.json +++ b/.golangci.json @@ -37,9 +37,7 @@ "depguard", "nonamedreturns", "inamedparam", - "exhaustruct", - "wsl_v5", - "noinlineerr" + "exhaustruct" ], "exclusions": { "generated": "lax", diff --git a/client/payment_test.go b/client/payment_test.go index 4b8ee19d6..d59ce03e7 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -48,6 +48,7 @@ func TestPaymentHappy(t *testing.T) { func TestPaymentDispute(t *testing.T) { rng := pkgtest.Prng(t) + ctx, cancel := context.WithTimeout(context.Background(), twoPartyTestTimeout) defer cancel() From a5c5eb3728b7be8350a8f245acfbe4a91a0b05c2 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 15 Jul 2025 08:46:22 +0200 Subject: [PATCH 22/26] chore: fix ci problem Signed-off-by: Minh Huy Tran --- client/payment_test.go | 1 - wire/net/simple/bus_internal_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/payment_test.go b/client/payment_test.go index d59ce03e7..4b8ee19d6 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -48,7 +48,6 @@ func TestPaymentHappy(t *testing.T) { func TestPaymentDispute(t *testing.T) { rng := pkgtest.Prng(t) - ctx, cancel := context.WithTimeout(context.Background(), twoPartyTestTimeout) defer cancel() diff --git a/wire/net/simple/bus_internal_test.go b/wire/net/simple/bus_internal_test.go index 82146c097..7874c1f88 100644 --- a/wire/net/simple/bus_internal_test.go +++ b/wire/net/simple/bus_internal_test.go @@ -30,7 +30,7 @@ import ( ) func TestBus(t *testing.T) { - const numClients = 5 + const numClients = 4 const numMsgs = 5 const defaultTimeout = 100 * time.Millisecond From 83be060e615fe711e785abd17374891b71901aca Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 29 Jul 2025 09:47:48 +0200 Subject: [PATCH 23/26] Revert: feat(adjudicate): stateMap to signedState: This reverts commit 2bb1a55. Signed-off-by: Minh Huy Tran --- channel/adjudicator.go | 8 ++++---- client/adjudicate.go | 6 +----- client/test/backend.go | 4 ++-- client/test/subchannel_dispute.go | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/channel/adjudicator.go b/channel/adjudicator.go index 43452a5bf..73c6eb066 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -207,7 +207,7 @@ type ( } // StateMap represents a channel state tree. - StateMap map[ID]*SignedState + StateMap map[ID]*State ) // NewProgressReq creates a new ProgressReq object. @@ -305,12 +305,12 @@ func (t *TimeTimeout) String() string { // MakeStateMap creates a new StateMap object. func MakeStateMap() StateMap { - return make(map[ID]*SignedState) + return make(map[ID]*State) } // Add adds the given states to the state map. -func (m StateMap) Add(states ...*SignedState) { +func (m StateMap) Add(states ...*State) { for _, s := range states { - m[s.State.ID] = s + m[s.ID] = s } } diff --git a/client/adjudicate.go b/client/adjudicate.go index 901f9692d..3aa8fb7d4 100644 --- a/client/adjudicate.go +++ b/client/adjudicate.go @@ -445,11 +445,7 @@ func (c *Channel) gatherSubChannelStates() (states []channel.SignedState, err er func (c *Channel) subChannelStateMap() (states channel.StateMap, err error) { states = channel.MakeStateMap() err = c.applyToSubChannelsRecursive(func(c *Channel) error { - states[c.ID()] = &channel.SignedState{ - Params: c.Params(), - State: c.machine.CurrentTX().State, - Sigs: c.machine.CurrentTX().Sigs, - } + states[c.ID()] = c.state() return nil }) return diff --git a/client/test/backend.go b/client/test/backend.go index e798c30ca..28c349818 100644 --- a/client/test/backend.go +++ b/client/test/backend.go @@ -265,7 +265,7 @@ func (b *MockBackend) Withdraw(_ context.Context, req channel.AdjudicatorReq, su states[0] = req.Tx.State i := 1 for _, s := range subStates { - states[i] = s.State + states[i] = s i++ } if err := b.checkStates(states, checkWithdraw); err != nil { @@ -361,7 +361,7 @@ func (b *MockBackend) setLatestEvent(ch channel.ID, e channel.AdjudicatorEvent) func outcomeRecursive(state *channel.State, subStates channel.StateMap) (outcome channel.Balances) { outcome = state.Balances.Clone() for _, subAlloc := range state.Locked { - subOutcome := outcomeRecursive(subStates[subAlloc.ID].State, subStates) + subOutcome := outcomeRecursive(subStates[subAlloc.ID], subStates) for a, bals := range subOutcome { for p, bal := range bals { _p := p diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index 35c78c395..8c5605c8f 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -106,7 +106,7 @@ func (r *DisputeSusie) exec(_cfg ExecConfig, ledgerChannel *paymentChannel) { r.log.Debug("Attempt withdrawing refuted state.") m := channel.MakeStateMap() - m.Add(&subState0) + m.Add(subState0.State) err = r.setup.Adjudicator.Withdraw(ctx, reqLedger, m) r.RequireTruef(err != nil, "withdraw should fail because other party should have refuted.") From b6499375785df63049b09c5a11412ce3bfc83989 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Tue, 29 Jul 2025 11:41:48 +0200 Subject: [PATCH 24/26] chore(CHANGELOG): update description for v0.14.0 Signed-off-by: Minh Huy Tran --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58eb579dc..409d2ceaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,38 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.14.0] Narvi - 2025-07-29 [:boom:] +Added [Libp2p](https://libp2p.io/) wire for go-perun. This enables seamless and secure P2P connection between clients. + +### Added :boom: + +* Wire implementation with libp2p [#420] + +### Fixed +* Removed test backend signature from `authMsg` of exchange address protocol [#422] + +### Changed + +* Updade action cache [#420] + +* Update workflow go version to 1.23 [#420] + +* Update workflow go linter to 2.1 [#420] + +[#420]: https://github.com/hyperledger-labs/go-perun/pull/420 +[#422]: https://github.com/hyperledger-labs/go-perun/pull/422 + + ## [0.13.0] Metis - 2025-01-19 [:boom:] Support for multiple backends, allowing multiple address implementations per client. This enables the simultaneous use of several smaller backends, enhancing modularization. -## Added :boom: +### Added :boom: * Backend field in Allocation [#410] * Added interface restrictions to ensure cross-contract compatibility, including new functions and fields in interfaces such as Asset and Address [#410] -## Changed +### Changed * Updade action cache [#409] @@ -31,7 +53,7 @@ Support for multiple backends, allowing multiple address implementations per cli [#410]: https://github.com/hyperledger-labs/go-perun/pull/410 [#413]: https://github.com/hyperledger-labs/go-perun/pull/413 -## Legend +### Legend - :boom: This is a breaking change, e.g., it changes the external API. [:boom:]: #breaking @@ -39,7 +61,7 @@ Support for multiple backends, allowing multiple address implementations per cli ## [0.12.0] Leda - 2024-11-19 [:boom:] Flexibility in funding for payment channels and basic Layer-2 security. -## Added :boom: +### Added :boom: * Egoistic funding allows users to wait for their peers to fund before they fund themselves. This change has to be adopted by the Perun backends to be usable ([in case of Ethereum](https://github.com/hyperledger-labs/perun-eth-backend/pull/45)): [#397 ] @@ -47,7 +69,7 @@ Flexibility in funding for payment channels and basic Layer-2 security. * Support for Stellar backend in README: [#408] -## Changed +### Changed * Update go to 1.22: [#406 ] [#397]: https://github.com/hyperledger-labs/go-perun/pull/397 @@ -55,7 +77,7 @@ Flexibility in funding for payment channels and basic Layer-2 security. [#402]: https://github.com/hyperledger-labs/go-perun/pull/402 [#408]: https://github.com/hyperledger-labs/go-perun/pull/408 -## Legend +### Legend - :boom: This is a breaking change, e.g., it changes the external API. [:boom:]: #breaking @@ -63,13 +85,13 @@ Flexibility in funding for payment channels and basic Layer-2 security. ## [0.11.0] Kiviuq - 2024-02-21 [:boom:] Exposure of protobuf converters & `SignedState`, abstraction of tests and bug fixes. -## Added +### Added - Add Fabric to backend list in README: [#377] - Create new type `TransparentChannel` to expose `SignedState`: [#389] - Update backend compatibility list in README: [#392] - Add MAINTAINERS.md file, Update NOTICE: [#394] -## Fixed +### Fixed - Fix sub-channel test: [#359] - Fix Multi-Adjudicator Subscription: [#366] - Use correct identity for client tests: [#376] @@ -77,7 +99,7 @@ Exposure of protobuf converters & `SignedState`, abstraction of tests and bug fi - Fix linter copyright year checking in CI: [#389] - Fix failing unit tests: [#399] -## Changed [:boom:] +### Changed [:boom:] - Abstract multiledger test, making it usable by backends: [#355] - Abstract fund recovery test, making it usable by backends: [#370] - Abstract virtual channel test, making it usable by backends: [#375] @@ -488,6 +510,9 @@ Initial release. [:boom:]: #breaking [Unreleased]: https://github.com/hyperledger-labs/go-perun/compare/v0.11.0...HEAD +[0.14.0]: https://github.com/hyperledger-labs/go-perun/compare/v0.13.0...v0.14.0 +[0.13.0]: https://github.com/hyperledger-labs/go-perun/compare/v0.12.0...v0.13.0 +[0.12.0]: https://github.com/hyperledger-labs/go-perun/compare/v0.11.0...v0.12.0 [0.11.0]: https://github.com/hyperledger-labs/go-perun/compare/v0.10.0...v0.11.0 [0.10.0]: https://github.com/hyperledger-labs/go-perun/compare/v0.9.1...v0.10.0 [0.9.1]: https://github.com/hyperledger-labs/go-perun/compare/v0.9.0...v0.9.1 From 9ceb0607b2d827f998c4306dc9019a65af0c07a8 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Thu, 2 Oct 2025 13:52:37 +1000 Subject: [PATCH 25/26] chore(doc): update README and CHANGELOG for v0.14.1 release Signed-off-by: Minh Huy Tran --- CHANGELOG.md | 15 +++++++++++++++ README.md | 29 +++++++++++++++++++---------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409d2ceaf..9d347efb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.14.1] Narvi - 2025-10-02 +Extended the testing framework with more robust tests for payment and virtual channels. +Added documentation for cross-chain backend integration and backend ID mapping. + + +### Added + +* Documentation section describing supported backends and their IDs. + +### Changed + +* Extended Perun testing framework with improved payment and virtual channel tests. [#423] + +[#423]: https://github.com/hyperledger-labs/go-perun/pull/423 + ## [0.14.0] Narvi - 2025-07-29 [:boom:] Added [Libp2p](https://libp2p.io/) wire for go-perun. This enables seamless and secure P2P connection between clients. diff --git a/README.md b/README.md index f37aa1809..39778a826 100644 --- a/README.md +++ b/README.md @@ -70,16 +70,25 @@ The following features are planned for future releases: ### Backends -There are multiple **blockchain backends** available. A backend is automatically initialized when its top-level package `backend/` is imported. -- **Ethereum.** The Ethereum backend is available at [perun-eth-backend](https://github.com/perun-network/perun-eth-backend/). -- **Polkadot.** The Polkadot backend is available at [perun-polkadot-backend](https://github.com/perun-network/perun-polkadot-backend). -- **Cosmos.** The Cosmos backend is available at [perun-cosmwasm-backend](https://github.com/hyperledger-labs/perun-cosmwasm-backend). -- **Cardano.** The Cardano backend is available at [perun-cardano-backend](https://github.com/perun-network/perun-cardano-backend). -- **NERVOS.** The NERVOS backend is available at [perun-ckb-backend](https://github.com/perun-network/perun-ckb-backend). -- **Dfinity.** The Dfinity Internet Computer backend is available at [perun-icp-backend](https://github.com/perun-network/perun-icp-backend). -- **Stellar.** The Stellar backend is available at [perun-stellar-backend](https://github.com/perun-network/perun-stellar-backend). -- **Fabric.** The Hyperledger Fabric backend is available at [perun-fabric](https://github.com/perun-network/perun-fabric). -- **SimBackend.** The SimBackend represents an ideal blockchain backend (`backend/sim`) implementation that can be used for testing. + _go-perun_ supports multiple **blockchain backends**. A backend is automatically initialized when its top-level package `backend/` is imported. +| ID | Backend | Status | Repository | +| --- | ---------- | ----------------------- | ------------------------------------------------------------------------------------ | +| 0 | SimBackend | ๐Ÿงช Testing only | Built-in (`backend/sim`) โ€“ represents an ideal blockchain backend for simulation | +| 1 | Ethereum | โœ… Cross-chain supported | [perun-eth-backend](https://github.com/perun-network/perun-eth-backend/) | +| 2 | Stellar | โœ… Cross-chain supported | [perun-stellar-backend](https://github.com/perun-network/perun-stellar-backend) | +| 3 | Nervos | ๐Ÿ›  In development | [perun-ckb-backend](https://github.com/perun-network/perun-ckb-backend) | +| 4 | Polkadot | ๐Ÿ›  In development | [perun-polkadot-backend](https://github.com/perun-network/perun-polkadot-backend) | +| 5 | Dfinity | ๐Ÿ›  In development | [perun-icp-backend](https://github.com/perun-network/perun-icp-backend) | +| 6 | Solana | ๐Ÿ›  In development | [perun-solana-backend](https://github.com/perun-network/perun-solana-backend) | +| - | Cosmos | โŒ Not supported | [perun-cosmwasm-backend](https://github.com/hyperledger-labs/perun-cosmwasm-backend) | +| - | Cardano | โŒ Not supported | [perun-cardano-backend](https://github.com/perun-network/perun-cardano-backend) | +| - | Fabric | โŒ Not supported | [perun-fabric](https://github.com/perun-network/perun-fabric) | + +#### Legend +- โœ… **Cross-chain supported** โ€“ stable, production-ready backends. +- ๐Ÿ›  **In development / upgrade** โ€“ experimental or actively being updated. +- โŒ **Not cross-chain supported** โ€“ backend is chain-specific, not compatible for cross-chain mode. +- ๐Ÿงช **Testing only** โ€“ simulation backend, no real blockchain. **Logging and networking** capabilities can also be injected by the user. A default [logrus](https://github.com/sirupsen/logrus) implementation of the `log.Logger` interface can be set using `log/logrus.Set`. From f91bad2e99bf11df96eb8f2a01cdad54723728a8 Mon Sep 17 00:00:00 2001 From: Minh Huy Tran Date: Thu, 2 Oct 2025 20:26:04 +1000 Subject: [PATCH 26/26] doc(README): update backend map to better visualise development status Signed-off-by: Minh Huy Tran --- README.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 39778a826..7fbc8dcaf 100644 --- a/README.md +++ b/README.md @@ -70,25 +70,26 @@ The following features are planned for future releases: ### Backends - _go-perun_ supports multiple **blockchain backends**. A backend is automatically initialized when its top-level package `backend/` is imported. -| ID | Backend | Status | Repository | -| --- | ---------- | ----------------------- | ------------------------------------------------------------------------------------ | -| 0 | SimBackend | ๐Ÿงช Testing only | Built-in (`backend/sim`) โ€“ represents an ideal blockchain backend for simulation | -| 1 | Ethereum | โœ… Cross-chain supported | [perun-eth-backend](https://github.com/perun-network/perun-eth-backend/) | -| 2 | Stellar | โœ… Cross-chain supported | [perun-stellar-backend](https://github.com/perun-network/perun-stellar-backend) | -| 3 | Nervos | ๐Ÿ›  In development | [perun-ckb-backend](https://github.com/perun-network/perun-ckb-backend) | -| 4 | Polkadot | ๐Ÿ›  In development | [perun-polkadot-backend](https://github.com/perun-network/perun-polkadot-backend) | -| 5 | Dfinity | ๐Ÿ›  In development | [perun-icp-backend](https://github.com/perun-network/perun-icp-backend) | -| 6 | Solana | ๐Ÿ›  In development | [perun-solana-backend](https://github.com/perun-network/perun-solana-backend) | -| - | Cosmos | โŒ Not supported | [perun-cosmwasm-backend](https://github.com/hyperledger-labs/perun-cosmwasm-backend) | -| - | Cardano | โŒ Not supported | [perun-cardano-backend](https://github.com/perun-network/perun-cardano-backend) | -| - | Fabric | โŒ Not supported | [perun-fabric](https://github.com/perun-network/perun-fabric) | + _go-perun_ supports multiple **blockchain backends**. A backend is automatically initialized when its top-level package `backend/` is imported.#### Backend Map + +| ID | Backend | Payment Channel Status | Cross-Chain Status | Repository | +| --- | ---------- | ---------------------- | ------------------ | ------------------------------------------------------------------------------------ | +| 0 | SimBackend | ๐Ÿงช Testing only | ๐Ÿงช Testing only | Built-in (`backend/sim`) โ€“ represents an ideal blockchain backend for simulation | +| 1 | Ethereum | โœ… Supported | โœ… Supported | [perun-eth-backend](https://github.com/perun-network/perun-eth-backend/) | +| 2 | Stellar | โœ… Supported | โœ… Supported | [perun-stellar-backend](https://github.com/perun-network/perun-stellar-backend) | +| 3 | Nervos | โœ… Supported | ๐Ÿšง In development | [perun-ckb-backend](https://github.com/perun-network/perun-ckb-backend) | +| 4 | Polkadot | โœ… Supported | ๐Ÿšง In development | [perun-polkadot-backend](https://github.com/perun-network/perun-polkadot-backend) | +| 5 | Dfinity | โœ… Supported | ๐Ÿšง In development | [perun-icp-backend](https://github.com/perun-network/perun-icp-backend) | +| 6 | Solana | ๐Ÿšง In development | ๐Ÿšง In development | [perun-solana-backend](https://github.com/perun-network/perun-solana-backend) | +| - | Cosmos | โœ… Supported | ๐ŸŸก Single-chain | [perun-cosmwasm-backend](https://github.com/hyperledger-labs/perun-cosmwasm-backend) | +| - | Cardano | โœ… Supported | ๐ŸŸก Single-chain | [perun-cardano-backend](https://github.com/perun-network/perun-cardano-backend) | +| - | Fabric | โœ… Supported | ๐ŸŸก Single-chain | [perun-fabric](https://github.com/perun-network/perun-fabric) | #### Legend -- โœ… **Cross-chain supported** โ€“ stable, production-ready backends. -- ๐Ÿ›  **In development / upgrade** โ€“ experimental or actively being updated. -- โŒ **Not cross-chain supported** โ€“ backend is chain-specific, not compatible for cross-chain mode. -- ๐Ÿงช **Testing only** โ€“ simulation backend, no real blockchain. +- โœ… **Supported** โ€“ stable and available. +- ๐Ÿšง **In development** โ€“ actively worked on, not fully stable. +- ๐ŸŸก **Single-chain** โ€“ supports only local (non-cross-chain) payment/state channels. +- ๐Ÿงช **Testing only** โ€“ simulation backend, no real blockchain. **Logging and networking** capabilities can also be injected by the user. A default [logrus](https://github.com/sirupsen/logrus) implementation of the `log.Logger` interface can be set using `log/logrus.Set`.