diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e62105574..483c054a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: release: env: - go-version: 1.17 + go-version: 1.18 jobs: check-copyright: @@ -39,7 +39,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.43 + version: v1.45 - name: Lint proto files uses: plexsystems/protolint-action@v0.6.0 diff --git a/.golangci.yml b/.golangci.yml index b25ce4581..668e8353e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -54,7 +54,8 @@ linters-settings: # Default values: - ^print.*$ - 'fmt\.Print.*' - + exclude-rules: + - path: ".*\\.pb\\.go$" # Exclude protobuf generated files. issues: # Re-enable default excludes. include: diff --git a/apps/payment/app_internal_test.go b/apps/payment/app_internal_test.go index 7834afc65..417e34e83 100644 --- a/apps/payment/app_internal_test.go +++ b/apps/payment/app_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -27,7 +27,7 @@ import ( func TestApp_Def(t *testing.T) { rng := pkgtest.Prng(t) - def := test.NewRandomAppID(rng) + def := test.NewRandomAppID(rng, channel.TestBackendID) app := &App{def} assert.True(t, app.Def().Equal(app.Def())) } diff --git a/apps/payment/randomizer.go b/apps/payment/randomizer.go index 52c0fb649..0e5f26268 100644 --- a/apps/payment/randomizer.go +++ b/apps/payment/randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package payment import ( "math/rand" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" ) @@ -27,8 +29,8 @@ type Randomizer struct{} var _ test.AppRandomizer = (*Randomizer)(nil) // NewRandomApp always returns a payment app with a different address. -func (*Randomizer) NewRandomApp(rng *rand.Rand) channel.App { - return &App{test.NewRandomAppID(rng)} +func (*Randomizer) NewRandomApp(rng *rand.Rand, bID wallet.BackendID) channel.App { + return &App{test.NewRandomAppID(rng, bID)} } // NewRandomData returns NoData because a PaymentApp does not have data. diff --git a/apps/payment/randomizer_internal_test.go b/apps/payment/randomizer_internal_test.go index 9a9dea688..cccc1c42e 100644 --- a/apps/payment/randomizer_internal_test.go +++ b/apps/payment/randomizer_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -28,7 +28,7 @@ func TestRandomizer(t *testing.T) { rng := pkgtest.Prng(t) r := new(Randomizer) - app := r.NewRandomApp(rng) + app := r.NewRandomApp(rng, channel.TestBackendID) channel.RegisterApp(app) regApp, err := channel.Resolve(app.Def()) assert.NoError(t, err) diff --git a/apps/payment/resolver_internal_test.go b/apps/payment/resolver_internal_test.go index 6c7de90b3..1b516d186 100644 --- a/apps/payment/resolver_internal_test.go +++ b/apps/payment/resolver_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -32,7 +32,7 @@ func TestResolver(t *testing.T) { rng := pkgtest.Prng(t) assert, require := assert.New(t), require.New(t) - def := ctest.NewRandomAppID(rng) + def := ctest.NewRandomAppID(rng, channel.TestBackendID) channel.RegisterAppResolver(def.Equal, &Resolver{}) app, err := channel.Resolve(def) diff --git a/backend/sim/channel/app.go b/backend/sim/channel/app.go index c7502b3da..9efb2b298 100644 --- a/backend/sim/channel/app.go +++ b/backend/sim/channel/app.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -48,5 +48,5 @@ func (id AppID) Key() channel.AppIDKey { // NewRandomAppID generates a new random app identifier. func NewRandomAppID(rng *rand.Rand) AppID { addr := wallet.NewRandomAddress(rng) - return AppID{addr} + return AppID{Address: addr} } diff --git a/backend/sim/channel/asset.go b/backend/sim/channel/asset.go index 52913e322..4cf494490 100644 --- a/backend/sim/channel/asset.go +++ b/backend/sim/channel/asset.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -65,3 +65,9 @@ func (a Asset) Equal(b channel.Asset) bool { } return a.ID == simAsset.ID } + +// Address returns the address of the asset. +func (a Asset) Address() []byte { + data, _ := a.MarshalBinary() + return data +} diff --git a/backend/sim/channel/backend.go b/backend/sim/channel/backend.go index 57e53ad7b..62e011c18 100644 --- a/backend/sim/channel/backend.go +++ b/backend/sim/channel/backend.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -33,23 +33,22 @@ type backend struct{} var _ channel.Backend = new(backend) // CalcID calculates a channel's ID by hashing all fields of its parameters. -func (*backend) CalcID(p *channel.Params) (id channel.ID) { +func (*backend) CalcID(p *channel.Params) (id channel.ID, err error) { w := sha256.New() // Write Parts - for _, addr := range p.Parts { - if err := perunio.Encode(w, addr); err != nil { - log.Panic("Could not write to sha256 hasher") - } + if err := perunio.Encode(w, wallet.AddressMapArray{Addr: p.Parts}); err != nil { + log.Panic("Could not write to sha256 hasher") } - err := perunio.Encode(w, p.Nonce, p.ChallengeDuration, channel.OptAppEnc{App: p.App}, p.LedgerChannel, p.VirtualChannel) + err = perunio.Encode(w, p.Nonce, p.ChallengeDuration, channel.OptAppEnc{App: p.App}, p.LedgerChannel, p.VirtualChannel) if err != nil { - log.Panic("Could not write to sha256 hasher") + return } if copy(id[:], w.Sum(nil)) != channel.IDLen { - log.Panic("Could not copy id") + err = errors.New("Could not copy id") + return } return } @@ -83,7 +82,7 @@ func (b *backend) NewAsset() channel.Asset { // NewAppID returns an object of type AppID, which can be used for // unmarshalling an app identifier from its binary representation. -func (b *backend) NewAppID() channel.AppID { +func (b *backend) NewAppID() (channel.AppID, error) { addr := &simwallet.Address{} - return AppID{addr} + return AppID{addr}, nil } diff --git a/backend/sim/channel/channel_internal_test.go b/backend/sim/channel/channel_internal_test.go index 2980df34d..bf21bf2d4 100644 --- a/backend/sim/channel/channel_internal_test.go +++ b/backend/sim/channel/channel_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package channel import ( "testing" + "perun.network/go-perun/channel" + chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/wallet" wtest "perun.network/go-perun/wallet/test" @@ -36,11 +38,13 @@ func newChannelSetup(t *testing.T) *chtest.Setup { params2, state2 := chtest.NewRandomParamsAndState(rng, chtest.WithIsFinal(!state.IsFinal), chtest.WithNumLocked(int(rng.Int31n(4)+1))) return &chtest.Setup{ - Params: params, - Params2: params2, - State: state, - State2: state2, - Account: wtest.NewRandomAccount(rng), - RandomAddress: func() wallet.Address { return wtest.NewRandomAddress(rng) }, + Params: params, + Params2: params2, + State: state, + State2: state2, + Account: wtest.NewRandomAccount(rng, channel.TestBackendID), + RandomAddress: func() map[wallet.BackendID]wallet.Address { + return wtest.NewRandomAddresses(rng, channel.TestBackendID) + }, } } diff --git a/backend/sim/channel/init.go b/backend/sim/channel/init.go index b8dac016b..734a654bd 100644 --- a/backend/sim/channel/init.go +++ b/backend/sim/channel/init.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -22,9 +22,9 @@ import ( ) func init() { - channel.SetBackend(new(backend)) - test.SetRandomizer(new(randomizer)) + channel.SetBackend(new(backend), channel.TestBackendID) + test.SetRandomizer(new(randomizer), channel.TestBackendID) test.SetNewRandomAppID(func(r *rand.Rand) channel.AppID { return NewRandomAppID(r) - }) + }, channel.TestBackendID) } diff --git a/backend/sim/wallet/account.go b/backend/sim/wallet/account.go index 4dec64423..3f8d01a30 100644 --- a/backend/sim/wallet/account.go +++ b/backend/sim/wallet/account.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -57,7 +57,7 @@ func NewRandomAccount(rng io.Reader) *Account { // Address returns the address of this account. func (a *Account) Address() wallet.Address { - return wallet.Address((*Address)(&a.privKey.PublicKey)) + return (*Address)(&a.privKey.PublicKey) } // SignData is used to sign data with this account. If the account is locked, diff --git a/backend/sim/wallet/address.go b/backend/sim/wallet/address.go index 6b4d4a49f..4d0b0e5aa 100644 --- a/backend/sim/wallet/address.go +++ b/backend/sim/wallet/address.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -20,6 +20,8 @@ import ( "io" "math/big" + "perun.network/go-perun/channel" + "perun.network/go-perun/log" "perun.network/go-perun/wallet" ) @@ -27,6 +29,11 @@ import ( // Address represents a simulated address. type Address ecdsa.PublicKey +// BackendID returns the backend id of the address. +func (a Address) BackendID() wallet.BackendID { + return channel.TestBackendID +} + const ( // elemLen is the length of the binary representation of a single element // of the address in bytes. @@ -54,6 +61,21 @@ func NewRandomAddress(rng io.Reader) *Address { } } +// NewRandomAddresses creates a new address using the randomness +// provided by rng. +func NewRandomAddresses(rng io.Reader) map[wallet.BackendID]wallet.Address { + privateKey, err := ecdsa.GenerateKey(curve, rng) + if err != nil { + log.Panicf("Creation of account failed with error", err) + } + + return map[wallet.BackendID]wallet.Address{channel.TestBackendID: &Address{ + Curve: privateKey.Curve, + X: privateKey.X, + Y: privateKey.Y, + }} +} + // Bytes converts this address to bytes. func (a *Address) Bytes() []byte { data := a.byteArray() @@ -90,9 +112,11 @@ func (a *Address) Equal(addr wallet.Address) bool { } // Cmp checks the ordering of two addresses according to following definition: -// -1 if (a.X < addr.X) || ((a.X == addr.X) && (a.Y < addr.Y)) -// 0 if (a.X == addr.X) && (a.Y == addr.Y) -// +1 if (a.X > addr.X) || ((a.X == addr.X) && (a.Y > addr.Y)) +// +// -1 if (a.X < addr.X) || ((a.X == addr.X) && (a.Y < addr.Y)) +// 0 if (a.X == addr.X) && (a.Y == addr.Y) +// +1 if (a.X > addr.X) || ((a.X == addr.X) && (a.Y > addr.Y)) +// // So the X coordinate is weighted higher. // Pancis if the passed address is of the wrong type. func (a *Address) Cmp(addr wallet.Address) int { diff --git a/backend/sim/wallet/init.go b/backend/sim/wallet/init.go index 8a5d5c68a..681b2f157 100644 --- a/backend/sim/wallet/init.go +++ b/backend/sim/wallet/init.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -15,11 +15,12 @@ package wallet import ( + "perun.network/go-perun/channel" "perun.network/go-perun/wallet" "perun.network/go-perun/wallet/test" ) func init() { - wallet.SetBackend(new(Backend)) - test.SetRandomizer(newRandomizer()) + wallet.SetBackend(new(Backend), channel.TestBackendID) + test.SetRandomizer(newRandomizer(), channel.TestBackendID) } diff --git a/backend/sim/wallet/wallet.go b/backend/sim/wallet/wallet.go index 0f660b2fa..f3d3cc5b4 100644 --- a/backend/sim/wallet/wallet.go +++ b/backend/sim/wallet/wallet.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -64,7 +64,6 @@ type Wallet struct { func (w *Wallet) Unlock(a wallet.Address) (wallet.Account, error) { w.accMutex.RLock() defer w.accMutex.RUnlock() - acc, ok := w.accs[wallet.Key(a)] if !ok { return nil, errors.Errorf("unlock unknown address: %v", a) @@ -90,7 +89,6 @@ func (w *Wallet) LockAll() { func (w *Wallet) IncrementUsage(a wallet.Address) { w.accMutex.RLock() defer w.accMutex.RUnlock() - acc, ok := w.accs[wallet.Key(a)] if !ok { panic("invalid address") @@ -106,7 +104,6 @@ func (w *Wallet) IncrementUsage(a wallet.Address) { func (w *Wallet) DecrementUsage(a wallet.Address) { w.accMutex.Lock() defer w.accMutex.Unlock() - acc, ok := w.accs[wallet.Key(a)] if !ok { panic("invalid address") @@ -129,7 +126,6 @@ func (w *Wallet) DecrementUsage(a wallet.Address) { func (w *Wallet) UsageCount(a wallet.Address) int { w.accMutex.RLock() defer w.accMutex.RUnlock() - acc, ok := w.accs[wallet.Key(a)] if !ok { panic("invalid address") @@ -153,15 +149,13 @@ func (w *Wallet) NewRandomAccount(rng *rand.Rand) wallet.Account { // account was already registered beforehand, an error is returned. Does not // lock or unlock the account. func (w *Wallet) AddAccount(acc *Account) error { - key := wallet.Key(acc.Address()) - w.accMutex.Lock() defer w.accMutex.Unlock() - if _, ok := w.accs[key]; ok { + if _, ok := w.accs[wallet.Key(acc.Address())]; ok { return errors.New("duplicate insertion") } - w.accs[key] = acc + w.accs[wallet.Key(acc.Address())] = acc return nil } @@ -171,7 +165,6 @@ func (w *Wallet) AddAccount(acc *Account) error { func (w *Wallet) HasAccount(acc *Account) bool { w.accMutex.RLock() defer w.accMutex.RUnlock() - _, ok := w.accs[wallet.Key(acc.Address())] return ok } diff --git a/channel/actionmachine.go b/channel/actionmachine.go index 6507e5ee5..dd032dba0 100644 --- a/channel/actionmachine.go +++ b/channel/actionmachine.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -33,7 +33,7 @@ type ActionMachine struct { } // NewActionMachine creates a new ActionMachine. -func NewActionMachine(acc wallet.Account, params Params) (*ActionMachine, error) { +func NewActionMachine(acc map[wallet.BackendID]wallet.Account, params Params) (*ActionMachine, error) { app, ok := params.App.(ActionApp) if !ok { return nil, errors.New("app must be ActionApp") diff --git a/channel/adjudicator.go b/channel/adjudicator.go index ddfd1a7b0..34bc32b84 100644 --- a/channel/adjudicator.go +++ b/channel/adjudicator.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -110,7 +110,7 @@ type ( // protocol, possibly saving unnecessary double sending of transactions. AdjudicatorReq struct { Params *Params - Acc wallet.Account + Acc map[wallet.BackendID]wallet.Account Tx Transaction Idx Index // Always the own index Secondary bool // Optimized secondary call protocol diff --git a/channel/allocation.go b/channel/allocation.go index d0f04038e..4cb9b9ccc 100644 --- a/channel/allocation.go +++ b/channel/allocation.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -20,6 +20,8 @@ import ( "log" "math/big" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire/perunio" perunbig "polycry.pt/poly-go/math/big" @@ -62,6 +64,8 @@ type ( // // Locked holds the locked allocations to sub-app-channels. Allocation struct { + // 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 @@ -98,6 +102,8 @@ type ( encoding.BinaryUnmarshaler // Equal returns true iff this asset is equal to the given asset. Equal(Asset) bool + // Address returns the address in string representation. + Address() []byte } ) @@ -109,9 +115,10 @@ var ( ) // NewAllocation returns a new allocation for the given number of participants and assets. -func NewAllocation(numParts int, assets ...Asset) *Allocation { +func NewAllocation(numParts int, backends []wallet.BackendID, assets ...Asset) *Allocation { return &Allocation{ Assets: assets, + Backends: backends, Balances: MakeBalances(len(assets), numParts), } } @@ -185,11 +192,13 @@ func (a *Allocation) NumParts() int { // Clone returns a deep copy of the Allocation object. // If it is nil, it returns nil. func (a Allocation) Clone() (clone Allocation) { + if a.Backends != nil { + clone.Backends = make([]wallet.BackendID, len(a.Backends)) + copy(clone.Backends, a.Backends) + } if a.Assets != nil { clone.Assets = make([]Asset, len(a.Assets)) - for i, asset := range a.Assets { - clone.Assets[i] = asset - } + copy(clone.Assets, a.Assets) } clone.Balances = a.Balances.Clone() @@ -320,8 +329,11 @@ func (a Allocation) Encode(w io.Writer) error { return err } // encode assets - for i, a := range a.Assets { - if err := perunio.Encode(w, a); err != nil { + for i, asset := range a.Assets { + if err := perunio.Encode(w, uint32(a.Backends[i])); err != nil { + return errors.WithMessagef(err, "encoding backends %d", i) + } + if err := perunio.Encode(w, asset); err != nil { return errors.WithMessagef(err, "encoding asset %d", i) } } @@ -352,8 +364,15 @@ func (a *Allocation) Decode(r io.Reader) error { } // decode assets a.Assets = make([]Asset, numAssets) + a.Backends = make([]wallet.BackendID, numAssets) for i := range a.Assets { - asset := NewAsset() + // decode backend index + var id uint32 + if err := perunio.Decode(r, &id); err != nil { + return errors.WithMessagef(err, "decoding backend index for asset %d", i) + } + a.Backends[i] = wallet.BackendID(id) + asset := NewAsset(a.Backends[i]) if err := perunio.Decode(r, asset); err != nil { return errors.WithMessagef(err, "decoding asset %d", i) } @@ -563,10 +582,7 @@ func (a *Allocation) RemoveSubAlloc(subAlloc SubAlloc) error { for i := range a.Locked { if subAlloc.Equal(&a.Locked[i]) == nil { // remove element at index i - b := a.Locked - copy(b[i:], b[i+1:]) - b[len(b)-1] = SubAlloc{} - a.Locked = b[:len(b)-1] + a.Locked = append(a.Locked[:i], a.Locked[i+1:]...) return nil } } @@ -579,6 +595,12 @@ func (a *Allocation) Equal(b *Allocation) error { if a == b { return nil } + + // Compare Backends + if err := AssertBackendsEqual(a.Backends, b.Backends); err != nil { + return errors.WithMessage(err, "comparing backends") + } + // Compare Assets if err := AssertAssetsEqual(a.Assets, b.Assets); err != nil { return errors.WithMessage(err, "comparing assets") @@ -608,6 +630,21 @@ func AssertAssetsEqual(a []Asset, b []Asset) error { return nil } +// AssertBackendsEqual returns an error if the given assets are not equal. +func AssertBackendsEqual(a []wallet.BackendID, b []wallet.BackendID) error { + if len(a) != len(b) { + return errors.New("length mismatch") + } + + for i, bID := range a { + if !bID.Equal(b[i]) { + return errors.Errorf("value mismatch at index %d", i) + } + } + + return nil +} + var _ perunio.Serializer = new(SubAlloc) // Valid checks if this suballocation is valid. diff --git a/channel/allocation_test.go b/channel/allocation_test.go index 0751fbe5f..7d19cfd38 100644 --- a/channel/allocation_test.go +++ b/channel/allocation_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -270,11 +272,15 @@ func TestAllocationValidLimits(t *testing.T) { for ti, x := range inputs { allocation := &channel.Allocation{ Assets: make([]channel.Asset, x.numAssets), + Backends: make([]wallet.BackendID, x.numAssets), Balances: make(channel.Balances, x.numAssets), Locked: make([]channel.SubAlloc, x.numSuballocations), } allocation.Assets = test.NewRandomAssets(rng, test.WithNumAssets(x.numAssets)) + for i := range allocation.Assets { + allocation.Backends[i] = channel.TestBackendID + } for i := range allocation.Balances { for j := range allocation.Balances[i] { diff --git a/channel/app.go b/channel/app.go index 417bd41f1..e98c282dd 100644 --- a/channel/app.go +++ b/channel/app.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -151,6 +151,11 @@ type OptAppEnc struct { App } +// OptAppEncMap makes an optional App value encodable. +type OptAppEncMap struct { + App map[int]App +} + // Encode encodes an optional App value. func (e OptAppEnc) Encode(w io.Writer) error { if IsNoApp(e.App) { @@ -159,11 +164,37 @@ func (e OptAppEnc) Encode(w io.Writer) error { return perunio.Encode(w, true, e.App.Def()) } +// Encode encodes an optional App value. +func (e OptAppEncMap) Encode(w io.Writer) error { + if e.App == nil { + return perunio.Encode(w, false) + } + // Encode the map length first + if err := perunio.Encode(w, len(e.App)); err != nil { + return err + } + // Encode each map entry + for key, app := range e.App { + if IsNoApp(app) { + return errors.New("app in map is nil") + } + if err := perunio.Encode(w, key, app.Def()); err != nil { + return err + } + } + return nil +} + // OptAppDec makes an optional App value decodable. type OptAppDec struct { App *App } +// OptAppDecMap makes an optional App value decodable. +type OptAppDecMap struct { + App *map[int]App +} + // Decode decodes an optional App value. func (d OptAppDec) Decode(r io.Reader) (err error) { var hasApp bool @@ -174,7 +205,7 @@ func (d OptAppDec) Decode(r io.Reader) (err error) { *d.App = NoApp() return nil } - appDef := backend.NewAppID() + appDef, _ := NewAppID() err = perunio.Decode(r, appDef) if err != nil { return errors.WithMessage(err, "decode app address") @@ -183,6 +214,39 @@ func (d OptAppDec) Decode(r io.Reader) (err error) { return errors.WithMessage(err, "resolve app") } +// Decode decodes an optional App value. +func (d OptAppDecMap) Decode(r io.Reader) (err error) { + var mapLen int + if err := perunio.Decode(r, &mapLen); err != nil { + return err + } + *d.App = make(map[int]App, mapLen) + for i := 0; i < mapLen; i++ { + var key int + if err := perunio.Decode(r, &key); err != nil { + return err + } + var hasApp bool + if err := perunio.Decode(r, &hasApp); err != nil { + return err + } + if !hasApp { + (*d.App)[key] = nil + continue + } + var appID AppID + if err := perunio.Decode(r, &appID); err != nil { + return err + } + app, err := Resolve(appID) + if err != nil { + return errors.WithMessage(err, "resolve app") + } + (*d.App)[key] = app + } + return nil +} + // OptAppAndDataEnc makes an optional pair of App definition and Data encodable. type OptAppAndDataEnc struct { App App diff --git a/channel/backend.go b/channel/backend.go index be57819de..39f17a796 100644 --- a/channel/backend.go +++ b/channel/backend.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -15,12 +15,14 @@ package channel import ( + "errors" + "perun.network/go-perun/wallet" ) // backend is set to the global channel backend. Must not be set directly but // through importing the needed backend. -var backend Backend +var backend map[wallet.BackendID]Backend // Backend is an interface that needs to be implemented for every blockchain. // It provides basic functionalities to the framework. @@ -30,7 +32,7 @@ type Backend interface { // In order to guarantee non-malleability of States, any parameters omitted // from the CalcID digest need to be signed together with the State in // Sign(). - CalcID(*Params) ID + CalcID(*Params) (ID, error) // Sign signs a channel's State with the given Account. // Returns the signature or an error. @@ -46,41 +48,63 @@ type Backend interface { // NewAppID returns an object of type AppID, which can be used for // unmarshalling an app identifier from its binary representation. - NewAppID() AppID + NewAppID() (AppID, error) } // SetBackend sets the global channel backend. Must not be called directly but // through importing the needed backend. -func SetBackend(b Backend) { - if backend != nil { +func SetBackend(b Backend, id int) { + if backend == nil { + backend = make(map[wallet.BackendID]Backend) + } + if backend[wallet.BackendID(id)] != nil { panic("channel backend already set") } - backend = b + backend[wallet.BackendID(id)] = b } // CalcID calculates the CalcID. -func CalcID(p *Params) ID { - return backend.CalcID(p) +func CalcID(p *Params) (ID, error) { + var lastErr error + for _, b := range backend { + id, err := b.CalcID(p) + if err == nil { + return id, nil + } + lastErr = err + } + + if lastErr != nil { + return ID{}, lastErr + } + + return ID{}, errors.New("no valid ID found") } // Sign creates a signature from the account a on state s. -func Sign(a wallet.Account, s *State) (wallet.Sig, error) { - return backend.Sign(a, s) +func Sign(a wallet.Account, s *State, bID wallet.BackendID) (wallet.Sig, error) { + return backend[bID].Sign(a, s) } // Verify verifies that a signature was a valid signature from addr on a state. -func Verify(addr wallet.Address, state *State, sig wallet.Sig) (bool, error) { - return backend.Verify(addr, state, sig) +func Verify(a wallet.Address, state *State, sig wallet.Sig) (bool, error) { + return backend[a.BackendID()].Verify(a, state, sig) } // NewAsset returns a variable of type Asset, which can be used // for unmarshalling an asset from its binary representation. -func NewAsset() Asset { - return backend.NewAsset() +func NewAsset(id wallet.BackendID) Asset { + return backend[id].NewAsset() } // NewAppID returns an object of type AppID, which can be used for // unmarshalling an app identifier from its binary representation. -func NewAppID() AppID { - return backend.NewAppID() +func NewAppID() (AppID, error) { + for i := range backend { + id, err := backend[i].NewAppID() + if err == nil { + return id, nil + } + } + return nil, errors.New("no backend found") } diff --git a/channel/backendtest.go b/channel/backendtest.go index 087f04ed8..6914fe301 100644 --- a/channel/backendtest.go +++ b/channel/backendtest.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,10 +21,13 @@ import ( "github.com/stretchr/testify/require" ) +// TestBackendID is the identifier for the simulated Backend. +const TestBackendID = 0 + // SetBackendTest is a generic backend test. func SetBackendTest(t *testing.T) { t.Helper() - assert.Panics(t, func() { SetBackend(nil) }, "nil backend set should panic") + assert.Panics(t, func() { SetBackend(nil, TestBackendID) }, "nil backend set should panic") require.NotNil(t, backend, "backend should be already set by init()") - assert.Panics(t, func() { SetBackend(backend) }, "setting a backend twice should panic") + assert.Panics(t, func() { SetBackend(backend[TestBackendID], TestBackendID) }, "setting a backend twice should panic") } diff --git a/channel/errors.go b/channel/errors.go index 6d6809e0e..9e5646193 100644 --- a/channel/errors.go +++ b/channel/errors.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/channel/machine.go b/channel/machine.go index 0175a2067..7563b7d81 100644 --- a/channel/machine.go +++ b/channel/machine.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -130,7 +130,7 @@ var signingPhases = []Phase{InitSigning, Signing, Progressing} // individually. type machine struct { phase Phase - acc wallet.Account `cloneable:"shallow"` + acc map[wallet.BackendID]wallet.Account `cloneable:"shallow"` idx Index params Params stagingTX Transaction @@ -142,8 +142,8 @@ type machine struct { } // newMachine returns a new uninitialized machine for the given parameters. -func newMachine(acc wallet.Account, params Params) (*machine, error) { - idx := wallet.IndexOfAddr(params.Parts, acc.Address()) +func newMachine(acc map[wallet.BackendID]wallet.Account, params Params) (*machine, error) { + idx := wallet.IndexOfAddrs(params.Parts, AddressMapfromAccountMap(acc)) if idx < 0 { return nil, errors.New("account not part of participant set") } @@ -157,7 +157,7 @@ func newMachine(acc wallet.Account, params Params) (*machine, error) { }, nil } -func restoreMachine(acc wallet.Account, source Source) (*machine, error) { +func restoreMachine(acc map[wallet.BackendID]wallet.Account, source Source) (*machine, error) { m, err := newMachine(acc, *source.Params()) if err != nil { return nil, err @@ -174,7 +174,7 @@ func (m *machine) ID() ID { } // Account returns the account this channel is using for signing state updates. -func (m *machine) Account() wallet.Account { +func (m *machine) Account() map[wallet.BackendID]wallet.Account { return m.acc } @@ -224,11 +224,13 @@ func (m *machine) Sig() (sig wallet.Sig, err error) { } if m.stagingTX.Sigs[m.idx] == nil { - sig, err = Sign(m.acc, m.stagingTX.State) - if err != nil { - return + for b, acc := range m.acc { + sig, err = Sign(acc, m.stagingTX.State, b) + if err == nil { + m.stagingTX.Sigs[m.idx] = sig + return sig, nil + } } - m.stagingTX.Sigs[m.idx] = sig } else { sig = m.stagingTX.Sigs[m.idx] } @@ -289,11 +291,12 @@ func (m *machine) AddSig(idx Index, sig wallet.Sig) error { if m.stagingTX.Sigs[idx] != nil { return errors.Errorf("signature for idx %d already present (ID: %x)", idx, m.params.id) } - - if ok, err := Verify(m.params.Parts[idx], m.stagingTX.State, sig); err != nil { - return err - } else if !ok { - return errors.Errorf("invalid signature for idx %d (ID: %x)", idx, m.params.id) + for _, add := range m.params.Parts[idx] { + if ok, err := Verify(add, m.stagingTX.State, sig); err != nil { + return err + } else if !ok { + return errors.Errorf("invalid signature for idx %d (ID: %x)", idx, m.params.id) + } } m.stagingTX.Sigs[idx] = sig @@ -572,3 +575,12 @@ func (m *machine) forceState(p Phase, s *State) { m.setPhase(p) m.addTx(m.newTransaction(s)) } + +// AddressMapfromAccountMap returns a map of addresses from a map of accounts. +func AddressMapfromAccountMap(accs map[wallet.BackendID]wallet.Account) map[wallet.BackendID]wallet.Address { + addresses := make(map[wallet.BackendID]wallet.Address) + for id, a := range accs { + addresses[id] = a.Address() + } + return addresses +} diff --git a/channel/machine_test.go b/channel/machine_test.go index f381163c2..848a30aa7 100644 --- a/channel/machine_test.go +++ b/channel/machine_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package channel_test import ( "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" @@ -27,14 +29,16 @@ import ( func TestMachineClone(t *testing.T) { rng := pkgtest.Prng(t) - acc := wtest.NewRandomAccount(rng) - params := *test.NewRandomParams(rng, test.WithFirstPart(acc.Address())) + acc := wtest.NewRandomAccountMapSlice(rng, channel.TestBackendID, 1) + params := *test.NewRandomParams(rng, test.WithFirstPart(map[wallet.BackendID]wallet.Address{channel.TestBackendID: acc[0][channel.TestBackendID].Address()})) - sm, err := channel.NewStateMachine(acc, params) + sm, err := channel.NewStateMachine(acc[0], params) require.NoError(t, err) - pkgtest.VerifyClone(t, sm) + cloneSM := sm.Clone() + require.Equal(t, sm, cloneSM) - am, err := channel.NewActionMachine(acc, params) + am, err := channel.NewActionMachine(acc[0], params) require.NoError(t, err) - pkgtest.VerifyClone(t, am) + cloneAM := am.Clone() + require.Equal(t, am, cloneAM) } diff --git a/channel/mock_app.go b/channel/mock_app.go index b8dc4f6be..936d65af0 100644 --- a/channel/mock_app.go +++ b/channel/mock_app.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/channel/mock_app_internal_test.go b/channel/mock_app_internal_test.go index c0f4b1c00..335ad2231 100644 --- a/channel/mock_app_internal_test.go +++ b/channel/mock_app_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/channel/multi/adjudicator.go b/channel/multi/adjudicator.go index 5fa5d9db2..b21d22386 100644 --- a/channel/multi/adjudicator.go +++ b/channel/multi/adjudicator.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -23,24 +23,26 @@ import ( // Adjudicator is a multi-ledger adjudicator. type Adjudicator struct { - adjudicators map[LedgerIDMapKey]channel.Adjudicator + adjudicators map[LedgerBackendKey]channel.Adjudicator } // NewAdjudicator creates a new adjudicator. func NewAdjudicator() *Adjudicator { return &Adjudicator{ - adjudicators: make(map[LedgerIDMapKey]channel.Adjudicator), + adjudicators: make(map[LedgerBackendKey]channel.Adjudicator), } } // RegisterAdjudicator registers an adjudicator for a given ledger. -func (a *Adjudicator) RegisterAdjudicator(l LedgerID, la channel.Adjudicator) { - a.adjudicators[l.MapKey()] = la +func (a *Adjudicator) RegisterAdjudicator(l LedgerBackendID, la channel.Adjudicator) { + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + a.adjudicators[key] = la } // LedgerAdjudicator returns the adjudicator for a given ledger. -func (a *Adjudicator) LedgerAdjudicator(l LedgerID) (channel.Adjudicator, bool) { - adj, ok := a.adjudicators[l.MapKey()] +func (a *Adjudicator) LedgerAdjudicator(l LedgerBackendID) (channel.Adjudicator, bool) { + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + adj, ok := a.adjudicators[key] return adj, ok } @@ -48,12 +50,12 @@ func (a *Adjudicator) LedgerAdjudicator(l LedgerID) (channel.Adjudicator, bool) // all relevant adjudicators. If any of the calls fails, the method returns an // error. func (a *Adjudicator) Register(ctx context.Context, req channel.AdjudicatorReq, subStates []channel.SignedState) error { - ledgers, err := assets(req.Tx.Assets).LedgerIDs() + ledgerIDs, err := assets(req.Tx.Assets).LedgerIDs() if err != nil { return err } - err = a.dispatch(ledgers, func(la channel.Adjudicator) error { + err = a.dispatch(ledgerIDs, func(la channel.Adjudicator) error { return la.Register(ctx, req, subStates) }) return err @@ -63,12 +65,12 @@ func (a *Adjudicator) Register(ctx context.Context, req channel.AdjudicatorReq, // Progress calls to all relevant adjudicators. If any of the calls fails, the // method returns an error. func (a *Adjudicator) Progress(ctx context.Context, req channel.ProgressReq) error { - ledgers, err := assets(req.Tx.Assets).LedgerIDs() + ledgerIDs, err := assets(req.Tx.Assets).LedgerIDs() if err != nil { return err } - err = a.dispatch(ledgers, func(la channel.Adjudicator) error { + err = a.dispatch(ledgerIDs, func(la channel.Adjudicator) error { return la.Progress(ctx, req) }) return err @@ -78,31 +80,32 @@ func (a *Adjudicator) Progress(ctx context.Context, req channel.ProgressReq) err // Withdraw calls to all relevant adjudicators. If any of the calls fails, the // method returns an error. func (a *Adjudicator) Withdraw(ctx context.Context, req channel.AdjudicatorReq, subStates channel.StateMap) error { - ledgers, err := assets(req.Tx.Assets).LedgerIDs() + ledgerIDs, err := assets(req.Tx.Assets).LedgerIDs() if err != nil { return err } - err = a.dispatch(ledgers, func(la channel.Adjudicator) error { + err = a.dispatch(ledgerIDs, func(la channel.Adjudicator) error { return la.Withdraw(ctx, req, subStates) }) return err } // dispatch dispatches an adjudicator call on all given ledgers. -func (a *Adjudicator) dispatch(ledgers []LedgerID, f func(channel.Adjudicator) error) error { - n := len(ledgers) +func (a *Adjudicator) dispatch(assetIds []LedgerBackendID, f func(channel.Adjudicator) error) error { + n := len(assetIds) errs := make(chan error, n) - for _, l := range ledgers { - go func(l LedgerID) { + for _, l := range assetIds { + go func(l LedgerBackendID) { err := func() error { - id := l.MapKey() - la, ok := a.adjudicators[id] + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + adjs, ok := a.adjudicators[key] if !ok { - return fmt.Errorf("Adjudicator not found for ledger %v", id) + return fmt.Errorf("adjudicator not found for id %v", l) } - err := f(la) + // Call the provided function f with the Adjudicator + err := f(adjs) return err }() errs <- err diff --git a/channel/multi/asset.go b/channel/multi/asset.go index 50507b5c3..d6df73bf0 100644 --- a/channel/multi/asset.go +++ b/channel/multi/asset.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,9 +21,17 @@ import ( ) type ( - // Asset defines a multi-ledger asset. + // Asset defines a multi-ledger asset, extending channel.asset by a method LedgerBackendID() which returns the LedgerID and BackendID. Asset interface { channel.Asset + LedgerBackendID() LedgerBackendID + } + + // LedgerBackendID represents an asset identifier. + // BackendID returns the identifier of the backend the asset belongs to. + // LedgerID returns the identifier of the ledger the asset belongs to. + LedgerBackendID interface { + BackendID() uint32 LedgerID() LedgerID } @@ -39,19 +47,19 @@ type ( ) // LedgerIDs returns the identifiers of the associated ledgers. -func (a assets) LedgerIDs() ([]LedgerID, error) { - ids := make(map[LedgerIDMapKey]LedgerID) +func (a assets) LedgerIDs() ([]LedgerBackendID, error) { + ids := make(map[LedgerBackendKey]LedgerBackendID) for _, asset := range a { ma, ok := asset.(Asset) if !ok { - return nil, fmt.Errorf("wrong asset type: expected multi.Asset, got %T", a) + return nil, fmt.Errorf("wrong asset type: expected Asset, got %T", asset) } - id := ma.LedgerID() - ids[id.MapKey()] = id - } + assetID := ma.LedgerBackendID() - idsArray := make([]LedgerID, len(ids)) + 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 @@ -64,7 +72,7 @@ func (a assets) LedgerIDs() ([]LedgerID, error) { // IsMultiLedgerAssets returns whether the assets are from multiple ledgers. func IsMultiLedgerAssets(assets []channel.Asset) bool { hasMulti := false - var id LedgerID + var id LedgerBackendID for _, asset := range assets { multiAsset, ok := asset.(Asset) switch { @@ -72,8 +80,8 @@ func IsMultiLedgerAssets(assets []channel.Asset) bool { continue case !hasMulti: hasMulti = true - id = multiAsset.LedgerID() - case id.MapKey() != multiAsset.LedgerID().MapKey(): + id = multiAsset.LedgerBackendID() + case id.LedgerID().MapKey() != multiAsset.LedgerBackendID().LedgerID().MapKey(): return true } } diff --git a/channel/multi/funder.go b/channel/multi/funder.go index 95c904596..e7ed8d659 100644 --- a/channel/multi/funder.go +++ b/channel/multi/funder.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -22,31 +22,39 @@ import ( "perun.network/go-perun/channel" ) +// LedgerBackendKey is a representation of LedgerBackendID that kan be used in map lookups. +type LedgerBackendKey struct { + BackendID uint32 + LedgerID string +} + // Funder is a multi-ledger funder. // funders is a map of LedgerIDs corresponding to a funder on some chain. // egoisticChains is a map of LedgerIDs corresponding to a boolean indicating whether the chain should be funded last. type Funder struct { - funders map[LedgerIDMapKey]channel.Funder - egoisticChains map[LedgerIDMapKey]bool + funders map[LedgerBackendKey]channel.Funder + egoisticChains map[LedgerBackendKey]bool } // NewFunder creates a new funder. func NewFunder() *Funder { return &Funder{ - funders: make(map[LedgerIDMapKey]channel.Funder), - egoisticChains: make(map[LedgerIDMapKey]bool), + funders: make(map[LedgerBackendKey]channel.Funder), + egoisticChains: make(map[LedgerBackendKey]bool), } } // RegisterFunder registers a funder for a given ledger. -func (f *Funder) RegisterFunder(l LedgerID, lf channel.Funder) { - f.funders[l.MapKey()] = lf - f.egoisticChains[l.MapKey()] = false +func (f *Funder) RegisterFunder(l LedgerBackendID, lf channel.Funder) { + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + f.funders[key] = lf + f.egoisticChains[key] = false } // SetEgoisticChain sets the egoistic chain flag for a given ledger. -func (f *Funder) SetEgoisticChain(l LedgerID, egoistic bool) { - f.egoisticChains[l.MapKey()] = egoistic +func (f *Funder) SetEgoisticChain(l LedgerBackendID, id int, egoistic bool) { + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + f.egoisticChains[key] = egoistic } // Fund funds a multi-ledger channel. It dispatches funding calls to all @@ -58,16 +66,17 @@ func (f *Funder) Fund(ctx context.Context, request channel.FundingReq) error { ctx, cancel := context.WithTimeout(ctx, d) defer cancel() - ledgers, err := assets(request.State.Assets).LedgerIDs() + ledgerIDs, err := assets(request.State.Assets).LedgerIDs() if err != nil { return err } - var egoisticLedgers []LedgerID - var nonEgoisticLedgers []LedgerID + var egoisticLedgers []LedgerBackendID + var nonEgoisticLedgers []LedgerBackendID - for _, l := range ledgers { - if f.egoisticChains[l.MapKey()] { + for _, l := range ledgerIDs { + key := LedgerBackendKey{BackendID: l.BackendID(), LedgerID: string(l.LedgerID().MapKey())} + if f.egoisticChains[key] { egoisticLedgers = append(egoisticLedgers, l) } else { nonEgoisticLedgers = append(nonEgoisticLedgers, l) @@ -89,29 +98,36 @@ func (f *Funder) Fund(ctx context.Context, request channel.FundingReq) error { return nil } -func fundLedgers(ctx context.Context, request channel.FundingReq, ledgers []LedgerID, funders map[LedgerIDMapKey]channel.Funder) error { - n := len(ledgers) +func fundLedgers(ctx context.Context, request channel.FundingReq, assetIDs []LedgerBackendID, funders map[LedgerBackendKey]channel.Funder) error { + // Calculate the total number of funders + n := len(assetIDs) + errs := make(chan error, n) - for _, le := range ledgers { - go func(le LedgerID) { - errs <- func() error { - id := le.MapKey() - lf, ok := funders[id] - if !ok { - return fmt.Errorf("Funder not found for ledger %v", id) - } - - err := lf.Fund(ctx, request) - return err - }() - }(le) + + // Iterate over blockchains to get the LedgerIDs + for _, assetID := range assetIDs { + go func(assetID LedgerBackendID) { + key := LedgerBackendKey{BackendID: assetID.BackendID(), LedgerID: string(assetID.LedgerID().MapKey())} + // Get the Funder from the funders map + funder, ok := funders[key] + if !ok { + errs <- fmt.Errorf("funder map not found for blockchain %d and ledger %d", assetID.BackendID(), assetID.LedgerID()) + return + } + + // Call the Fund method + err := funder.Fund(ctx, request) + errs <- err + }(assetID) } + // Collect errors for i := 0; i < n; i++ { err := <-errs if err != nil { return err } } + return nil } diff --git a/channel/multi/subscription.go b/channel/multi/subscription.go index ddcca7912..0ccf139bf 100644 --- a/channel/multi/subscription.go +++ b/channel/multi/subscription.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/channel/params.go b/channel/params.go index d872b8ea6..1b31e754d 100644 --- a/channel/params.go +++ b/channel/params.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,7 +19,6 @@ import ( "math/big" "github.com/pkg/errors" - "perun.network/go-perun/log" "perun.network/go-perun/wallet" "perun.network/go-perun/wire/perunio" @@ -63,7 +62,7 @@ type Params struct { // ChallengeDuration in seconds during disputes ChallengeDuration uint64 // Parts are the channel participants - Parts []wallet.Address + Parts []map[wallet.BackendID]wallet.Address // App identifies the application that this channel is running. It is // optional, and if nil, signifies that a channel is a payment channel. App App `cloneable:"shallow"` @@ -84,10 +83,20 @@ 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 []map[wallet.BackendID]wallet.Address, app App, nonce Nonce, ledger bool, virtual bool) (*Params, error) { if err := ValidateParameters(challengeDuration, len(parts), app, nonce); err != nil { return nil, errors.WithMessage(err, "invalid parameter for NewParams") } + for _, ps := range parts { + for id, p := range ps { + if backend[p.BackendID()] == nil { + return nil, errors.Errorf("no backend with id %d", p.BackendID()) + } + if id != p.BackendID() { + return nil, errors.Errorf("participant %v has wrong backend id %d", p, p.BackendID()) + } + } + } return NewParamsUnsafe(challengeDuration, parts, app, nonce, ledger, virtual), nil } @@ -130,7 +139,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 []map[wallet.BackendID]wallet.Address, app App, nonce Nonce, ledger bool, virtual bool) *Params { p := &Params{ ChallengeDuration: challengeDuration, Parts: parts, @@ -139,17 +148,32 @@ func NewParamsUnsafe(challengeDuration uint64, parts []wallet.Address, app App, LedgerChannel: ledger, VirtualChannel: virtual, } + // probably an expensive hash operation, do it only once during creation. - p.id = CalcID(p) + 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 { + cloneMap := make([]map[wallet.BackendID]wallet.Address, len(as)) + for i, a := range as { + cloneMap[i] = wallet.CloneAddressesMap(a) + } + return cloneMap +} + // Clone returns a deep copy of Params. func (p *Params) Clone() *Params { return &Params{ id: p.ID(), ChallengeDuration: p.ChallengeDuration, - Parts: wallet.CloneAddresses(p.Parts), + Parts: CloneAddresses(p.Parts), App: p.App, Nonce: new(big.Int).Set(p.Nonce), LedgerChannel: p.LedgerChannel, @@ -161,8 +185,8 @@ func (p *Params) Clone() *Params { func (p *Params) Encode(w stdio.Writer) error { return perunio.Encode(w, p.ChallengeDuration, - wallet.AddressesWithLen(p.Parts), - OptAppEnc{p.App}, + wallet.AddressMapArray{Addr: p.Parts}, + OptAppEnc{App: p.App}, p.Nonce, p.LedgerChannel, p.VirtualChannel, @@ -173,7 +197,7 @@ func (p *Params) Encode(w stdio.Writer) error { func (p *Params) Decode(r stdio.Reader) error { var ( challengeDuration uint64 - parts wallet.AddressesWithLen + parts wallet.AddressMapArray app App nonce Nonce ledger bool @@ -192,7 +216,7 @@ func (p *Params) Decode(r stdio.Reader) error { return err } - _p, err := NewParams(challengeDuration, parts, app, nonce, ledger, virtual) + _p, err := NewParams(challengeDuration, parts.Addr, app, nonce, ledger, virtual) if err != nil { return err } diff --git a/channel/params_test.go b/channel/params_test.go index 44981b787..321cfd722 100644 --- a/channel/params_test.go +++ b/channel/params_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package channel_test import ( "testing" + "github.com/stretchr/testify/require" + "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" "perun.network/go-perun/wire/perunio" @@ -27,7 +29,12 @@ import ( func TestParams_Clone(t *testing.T) { rng := pkgtest.Prng(t) params := test.NewRandomParams(rng) - pkgtest.VerifyClone(t, params) + clone := params.Clone() + + require.Equalf(t, params.Parts, clone.Parts, "Clone() = %v, want %v", clone, params) + require.Equalf(t, params.App, clone.App, "Clone() = %v, want %v", clone, params) + require.Equalf(t, params.ChallengeDuration, clone.ChallengeDuration, "Clone() = %v, want %v", clone, params) + require.Equalf(t, params.Nonce, clone.Nonce, "Clone() = %v, want %v", clone, params) } func TestParams_Serializer(t *testing.T) { diff --git a/channel/persistence/keyvalue/cache.go b/channel/persistence/keyvalue/cache.go index af667f4a4..7b97bf49f 100644 --- a/channel/persistence/keyvalue/cache.go +++ b/channel/persistence/keyvalue/cache.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -22,7 +22,7 @@ import ( "perun.network/go-perun/wire" ) -//nolint:deadcode,unused +//nolint:deadcode func newChannelCache() *channelCache { return &channelCache{ peers: make(map[channel.ID][]wire.Address), @@ -31,14 +31,12 @@ 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() @@ -58,7 +56,6 @@ 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() @@ -81,7 +78,6 @@ 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/persistedstate.go b/channel/persistence/keyvalue/persistedstate.go index dd625f434..277b13f37 100644 --- a/channel/persistence/keyvalue/persistedstate.go +++ b/channel/persistence/keyvalue/persistedstate.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/channel/persistence/keyvalue/persister.go b/channel/persistence/keyvalue/persister.go index 6f606cce6..672145ba3 100644 --- a/channel/persistence/keyvalue/persister.go +++ b/channel/persistence/keyvalue/persister.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -23,6 +23,8 @@ import ( "strconv" "strings" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -32,7 +34,7 @@ import ( ) // ChannelCreated inserts a channel into the database. -func (pr *PersistRestorer) ChannelCreated(_ context.Context, s channel.Source, peers []wire.Address, parent *channel.ID) error { +func (pr *PersistRestorer) ChannelCreated(_ context.Context, s channel.Source, peers []map[wallet.BackendID]wire.Address, parent *channel.ID) error { db := pr.channelDB(s.ID()).NewBatch() // Write the channel data in the "Channel" table. numParts := len(s.Params().Parts) @@ -47,7 +49,7 @@ func (pr *PersistRestorer) ChannelCreated(_ context.Context, s channel.Source, p } // Write peers in the "Channel" table. - if err := dbPut(db, prefix.Peers, wire.AddressesWithLen(peers)); err != nil { + if err := dbPut(db, prefix.Peers, (*wire.AddressMapArray)(&peers)); err != nil { return errors.WithMessage(err, "putting peers into channel table") } @@ -244,9 +246,9 @@ func decodeIdxFromDBKey(key string) (int, error) { return strconv.Atoi(vals[len(vals)-1]) } -func peerChannelKey(p wire.Address, ch channel.ID) (string, error) { +func peerChannelKey(p map[wallet.BackendID]wire.Address, ch channel.ID) (string, error) { var key bytes.Buffer - if err := perunio.Encode(&key, p); err != nil { + if err := perunio.Encode(&key, wire.AddressDecMap(p)); err != nil { return "", errors.WithMessage(err, "encoding peer address") } key.WriteString(":channel:") diff --git a/channel/persistence/keyvalue/persistrestorer_internal_test.go b/channel/persistence/keyvalue/persistrestorer_internal_test.go index bafd808c0..36a42d69e 100644 --- a/channel/persistence/keyvalue/persistrestorer_internal_test.go +++ b/channel/persistence/keyvalue/persistrestorer_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -16,7 +16,7 @@ package keyvalue import ( "context" - "io/ioutil" + "os" "testing" "github.com/stretchr/testify/assert" @@ -31,7 +31,7 @@ import ( ) func TestPersistRestorer_Generic(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "perun-test-kvpersistrestorer-db-*") + tmpdir, err := os.MkdirTemp("", "perun-test-kvpersistrestorer-db-*") require.NoError(t, err) lvldb, err := leveldb.LoadDatabase(tmpdir) require.NoError(t, err) diff --git a/channel/persistence/keyvalue/restorer.go b/channel/persistence/keyvalue/restorer.go index 6dfea22cd..2acd8bfbd 100644 --- a/channel/persistence/keyvalue/restorer.go +++ b/channel/persistence/keyvalue/restorer.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -41,20 +41,20 @@ type ChannelIterator struct { } // ActivePeers returns a list of all peers with which a channel is persisted. -func (pr *PersistRestorer) ActivePeers(ctx context.Context) ([]wire.Address, error) { +func (pr *PersistRestorer) ActivePeers(ctx context.Context) ([]map[wallet.BackendID]wire.Address, error) { it := sortedkv.NewTable(pr.db, prefix.PeerDB).NewIterator() - peermap := make(map[wire.AddrKey]wire.Address) + peermap := make(map[wire.AddrKey]map[wallet.BackendID]wire.Address) for it.Next() { - addr := wire.NewAddress() - err := perunio.Decode(bytes.NewBufferString(it.Key()), addr) + var addr map[wallet.BackendID]wire.Address + err := perunio.Decode(bytes.NewBufferString(it.Key()), (*wire.AddressDecMap)(&addr)) if err != nil { return nil, errors.WithMessagef(err, "decoding peer key (%x)", it.Key()) } - peermap[wire.Key(addr)] = addr + peermap[wire.Keys(addr)] = addr } - peers := make([]wire.Address, 0, len(peermap)) + peers := make([]map[wallet.BackendID]wire.Address, 0, len(peermap)) for _, peer := range peermap { peers = append(peers, peer) } @@ -63,13 +63,13 @@ func (pr *PersistRestorer) ActivePeers(ctx context.Context) ([]wire.Address, err // channelPeers returns a slice of peer addresses for a given channel id from // the db of PersistRestorer. -func (pr *PersistRestorer) channelPeers(id channel.ID) ([]wire.Address, error) { - var ps wire.AddressesWithLen +func (pr *PersistRestorer) channelPeers(id channel.ID) ([]map[wallet.BackendID]wire.Address, error) { + var ps wire.AddressMapArray peers, err := pr.channelDB(id).Get(prefix.Peers) if err != nil { return nil, errors.WithMessage(err, "unable to get peerlist from db") } - return []wire.Address(ps), errors.WithMessage(perunio.Decode(bytes.NewBuffer([]byte(peers)), &ps), + return ps, errors.WithMessage(perunio.Decode(bytes.NewBuffer([]byte(peers)), &ps), "decoding peerlist") } @@ -83,7 +83,7 @@ func (pr *PersistRestorer) RestoreAll() (persistence.ChannelIterator, error) { // RestorePeer should return an iterator over all persisted channels which // the given peer is a part of. -func (pr *PersistRestorer) RestorePeer(addr wire.Address) (persistence.ChannelIterator, error) { +func (pr *PersistRestorer) RestorePeer(addr map[wallet.BackendID]wire.Address) (persistence.ChannelIterator, error) { it := &ChannelIterator{restorer: pr} chandb := sortedkv.NewTable(pr.db, prefix.ChannelDB) @@ -106,9 +106,9 @@ func (pr *PersistRestorer) RestorePeer(addr wire.Address) (persistence.ChannelIt } // peerChannelsKey creates a db-key-string for a given wire.Address. -func peerChannelsKey(addr wire.Address) (string, error) { +func peerChannelsKey(addr map[wallet.BackendID]wire.Address) (string, error) { var key strings.Builder - if err := perunio.Encode(&key, addr); err != nil { + if err := perunio.Encode(&key, wire.AddressDecMap(addr)); err != nil { return "", errors.WithMessage(err, "encoding peer address") } key.WriteString(":channel:") @@ -157,7 +157,7 @@ func (i *ChannelIterator) Next(context.Context) bool { !i.decodeNext("index", &i.ch.IdxV, noOpts) || !i.decodeNext("params", i.ch.ParamsV, noOpts) || !i.decodeNext("parent", optChannelIDDec{&i.ch.Parent}, noOpts) || - !i.decodeNext("peers", (*wire.AddressesWithLen)(&i.ch.PeersV), noOpts) || + !i.decodeNext("peers", (*wire.AddressMapArray)(&i.ch.PeersV), noOpts) || !i.decodeNext("phase", &i.ch.PhaseV, noOpts) { return false } diff --git a/channel/persistence/nonpersister.go b/channel/persistence/nonpersister.go index 8efab461d..7a3f37f27 100644 --- a/channel/persistence/nonpersister.go +++ b/channel/persistence/nonpersister.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package persistence import ( "context" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -32,10 +34,13 @@ type nonPersistRestorer struct{} // Persister implementation -func (nonPersistRestorer) ChannelCreated(context.Context, channel.Source, []wire.Address, *channel.ID) error { +func (nonPersistRestorer) ChannelCreated(context.Context, channel.Source, []map[wallet.BackendID]wire.Address, *channel.ID) error { + return nil +} + +func (nonPersistRestorer) ChannelRemoved(context.Context, channel.ID) error { return nil } -func (nonPersistRestorer) ChannelRemoved(context.Context, channel.ID) error { return nil } func (nonPersistRestorer) Staged(context.Context, channel.Source) error { return nil } func (nonPersistRestorer) SigAdded(context.Context, channel.Source, channel.Index) error { return nil } func (nonPersistRestorer) Enabled(context.Context, channel.Source) error { return nil } @@ -44,13 +49,15 @@ func (nonPersistRestorer) Close() error // Restorer implementation -func (nonPersistRestorer) ActivePeers(context.Context) ([]wire.Address, error) { return nil, nil } +func (nonPersistRestorer) ActivePeers(context.Context) ([]map[wallet.BackendID]wire.Address, error) { + return nil, nil +} func (nonPersistRestorer) RestoreAll() (ChannelIterator, error) { return emptyChanIterator{}, nil } -func (nonPersistRestorer) RestorePeer(wire.Address) (ChannelIterator, error) { +func (nonPersistRestorer) RestorePeer(map[wallet.BackendID]wire.Address) (ChannelIterator, error) { return emptyChanIterator{}, nil } diff --git a/channel/persistence/persistence.go b/channel/persistence/persistence.go index 30705dfe2..15a9ae2ea 100644 --- a/channel/persistence/persistence.go +++ b/channel/persistence/persistence.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "context" "io" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" "perun.network/go-perun/wire" ) @@ -35,7 +37,7 @@ type ( // state will be empty. The passed peers are the channel network peers, // which should also be persisted. The parent field is the parent // channel's ID, or nil, if it is a ledger channel. - ChannelCreated(ctx context.Context, source channel.Source, peers []wire.Address, parent *channel.ID) error + ChannelCreated(ctx context.Context, source channel.Source, peers []map[wallet.BackendID]wire.Address, parent *channel.ID) error // ChannelRemoved is called by the client when a channel is removed because // it has been successfully settled and its data is no longer needed. All @@ -72,11 +74,11 @@ type ( Restorer interface { // ActivePeers should return a list of all peers with which any channel is // persisted. - ActivePeers(context.Context) ([]wire.Address, error) + ActivePeers(context.Context) ([]map[wallet.BackendID]wire.Address, error) // RestorePeer should return an iterator over all persisted channels which // the given peer is a part of. - RestorePeer(wire.Address) (ChannelIterator, error) + RestorePeer(map[wallet.BackendID]wire.Address) (ChannelIterator, error) // RestoreChannel should return the channel with the requested ID. RestoreChannel(context.Context, channel.ID) (*Channel, error) @@ -125,7 +127,7 @@ type ( // sub-channel, also holds the parent channel's ID. Channel struct { chSource - PeersV []wire.Address + PeersV []map[wallet.BackendID]wire.Address Parent *channel.ID } ) @@ -156,7 +158,7 @@ func NewChannel() *Channel { // FromSource creates a new Channel object from given `channel.Source`, the // channel's network peers, and the parent channel ID, if it exists. -func FromSource(s channel.Source, ps []wire.Address, parent *channel.ID) *Channel { +func FromSource(s channel.Source, ps []map[wallet.BackendID]wire.Address, parent *channel.ID) *Channel { return &Channel{ chSource{ IdxV: s.Idx(), diff --git a/channel/persistence/statemachine_test.go b/channel/persistence/statemachine_test.go index c5e731ebf..a65c7da90 100644 --- a/channel/persistence/statemachine_test.go +++ b/channel/persistence/statemachine_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" @@ -38,9 +40,11 @@ func TestStateMachine(t *testing.T) { require := require.New(t) rng := pkgtest.Prng(t) - const n = 5 // number of participants - accs, parts := wtest.NewRandomAccounts(rng, n) // local participant idx 0 - params := ctest.NewRandomParams(rng, ctest.WithParts(parts...)) + const n = 5 // number of participants + accs, parts := wtest.NewRandomAccounts(rng, n, 0) // local participant idx 0 + params := ctest.NewRandomParams(rng, ctest.WithParts(parts)) + t.Log("Participants:", parts, "accs:", accs) + t.Log("Params:", params) csm, err := channel.NewStateMachine(accs[0], *params) require.NoError(err) @@ -67,8 +71,11 @@ func TestStateMachine(t *testing.T) { tpr.AssertEqual(csm) // remote signers for i := 1; i < n; i++ { - sig, err := channel.Sign(accs[i], csm.StagingState()) - require.NoError(err) + var sig wallet.Sig + for b, acc := range accs[i] { + sig, err = channel.Sign(acc, csm.StagingState(), b) + require.NoError(err) + } err = sm.AddSig(ctx, channel.Index(i), sig) require.NoError(err) tpr.AssertEqual(csm) diff --git a/channel/persistence/test/channel.go b/channel/persistence/test/channel.go index 9cd8857dd..534ee914d 100644 --- a/channel/persistence/test/channel.go +++ b/channel/persistence/test/channel.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -31,8 +31,8 @@ import ( // Channel is a wrapper around a persisted channel and its participants, as well // as the associated persister and restorer. type Channel struct { - accounts []wallet.Account - peers []wire.Address + accounts []map[wallet.BackendID]wallet.Account + peers []map[wallet.BackendID]wire.Address parent *channel.ID *persistence.StateMachine @@ -52,12 +52,18 @@ func NewRandomChannel( t require.TestingT, pr persistence.PersistRestorer, user channel.Index, - peers []wire.Address, + peers []map[wallet.BackendID]wire.Address, parent *Channel, rng *rand.Rand, ) (c *Channel) { - accs, parts := wtest.NewRandomAccounts(rng, len(peers)) - params := ctest.NewRandomParams(rng, ctest.WithParts(parts...)) + bID := wallet.BackendID(channel.TestBackendID) + if len(peers) > 0 { + for i := range peers[0] { + bID = i + } + } + accs, parts := wtest.NewRandomAccounts(rng, len(peers), bID) + params := ctest.NewRandomParams(rng, ctest.WithParts(parts)) csm, err := channel.NewStateMachine(accs[0], *params) require.NoError(t, err) @@ -82,10 +88,10 @@ func NewRandomChannel( return c } -func requireEqualPeers(t require.TestingT, expected, actual []wire.Address) { +func requireEqualPeers(t require.TestingT, expected, actual []map[wallet.BackendID]wire.Address) { require.Equal(t, len(expected), len(actual)) for i, p := range expected { - if !p.Equal(actual[i]) { + if !channel.EqualWireMaps(p, actual[i]) { t.Errorf("restored peers for channel do not match\nexpected: %v\nactual: %v", actual, expected) t.FailNow() @@ -174,7 +180,7 @@ func (c *Channel) SignAll(ctx context.Context, t require.TestingT) { c.AssertPersisted(ctx, t) // remote signers for i := range c.accounts { - sig, err := channel.Sign(c.accounts[i], c.StagingState()) + 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.AssertPersisted(ctx, t) diff --git a/channel/persistence/test/peerchans.go b/channel/persistence/test/peerchans.go index 5861340ff..408d36c5c 100644 --- a/channel/persistence/test/peerchans.go +++ b/channel/persistence/test/peerchans.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -16,6 +16,9 @@ package test import ( "bytes" + "fmt" + + "perun.network/go-perun/wallet" "perun.network/go-perun/channel" "perun.network/go-perun/wire" @@ -24,7 +27,7 @@ import ( type peerChans map[string][]channel.ID -func (pc peerChans) ID(p wire.Address) []channel.ID { +func (pc peerChans) ID(p map[wallet.BackendID]wire.Address) []channel.ID { ids, ok := pc[peerKey(p)] if !ok { return nil @@ -32,23 +35,24 @@ func (pc peerChans) ID(p wire.Address) []channel.ID { return ids } -func (pc peerChans) Peers() []wire.Address { - ps := make([]wire.Address, 0, len(pc)) +func (pc peerChans) Peers() []map[wallet.BackendID]wire.Address { + ps := make([]map[wallet.BackendID]wire.Address, 0, len(pc)) for k := range pc { - ps = append(ps, peerFromKey(k)) + pk, _ := peerFromKey(k) + ps = append(ps, pk) } return ps } // Add adds the given channel id to each peer's id list. -func (pc peerChans) Add(id channel.ID, ps ...wire.Address) { +func (pc peerChans) Add(id channel.ID, ps ...map[wallet.BackendID]wire.Address) { for _, p := range ps { pc.add(id, p) } } // Don't use add, use Add. -func (pc peerChans) add(id channel.ID, p wire.Address) { +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) @@ -74,20 +78,21 @@ func (pc peerChans) Delete(id channel.ID) { } } -func peerKey(a wire.Address) string { +func peerKey(a map[wallet.BackendID]wire.Address) string { key := new(bytes.Buffer) - err := perunio.Encode(key, a) + err := perunio.Encode(key, wire.AddressDecMap(a)) if err != nil { panic("error encoding peer key: " + err.Error()) } return key.String() } -func peerFromKey(s string) wire.Address { - p := wire.NewAddress() - err := perunio.Decode(bytes.NewBuffer([]byte(s)), p) +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) if err != nil { - panic("error decoding peer key: " + err.Error()) + return nil, fmt.Errorf("error decoding peer key: %w", err) } - return p + return decMap, nil } diff --git a/channel/persistence/test/peerchans_internal_test.go b/channel/persistence/test/peerchans_internal_test.go index 33c59d4fb..9436d2531 100644 --- a/channel/persistence/test/peerchans_internal_test.go +++ b/channel/persistence/test/peerchans_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package test import ( "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "perun.network/go-perun/channel" @@ -30,7 +32,7 @@ func TestEndpointChans(t *testing.T) { assert := assert.New(t) rng := pkgtest.Prng(t) id := []channel.ID{ctest.NewRandomChannelID(rng), ctest.NewRandomChannelID(rng)} - ps := wiretest.NewRandomAddresses(rng, 3) + ps := wiretest.NewRandomAddressesMap(rng, 3) pc := make(peerChans) pc.Add(id[0], ps...) @@ -43,7 +45,7 @@ func TestEndpointChans(t *testing.T) { pc.Delete(id[0]) // p[1] should be deleted as id[0] was their only channel assert.ElementsMatch(id[1:], pc.ID(ps[0])) assert.ElementsMatch(id[1:], pc.ID(ps[2])) - assert.ElementsMatch([]wire.Address{ps[0], ps[2]}, pc.Peers()) + assert.ElementsMatch([]map[wallet.BackendID]wire.Address{ps[0], ps[2]}, pc.Peers()) assert.Nil(pc.ID(ps[1])) pc.Delete(id[1]) // now all peers should have been deleted diff --git a/channel/persistence/test/persistrestorer.go b/channel/persistence/test/persistrestorer.go index e1b9879ea..4d5356130 100644 --- a/channel/persistence/test/persistrestorer.go +++ b/channel/persistence/test/persistrestorer.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -22,6 +22,8 @@ import ( "sync" "testing" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -57,7 +59,7 @@ func NewPersistRestorer(t *testing.T) *PersistRestorer { // ChannelCreated fully persists all of the source's data. func (pr *PersistRestorer) ChannelCreated( - _ context.Context, source channel.Source, peers []wire.Address, parent *channel.ID, + _ context.Context, source channel.Source, peers []map[wallet.BackendID]wire.Address, parent *channel.ID, ) error { pr.mu.Lock() defer pr.mu.Unlock() @@ -182,7 +184,7 @@ func (pr *PersistRestorer) channel(id channel.ID) (*persistence.Channel, bool) { // Restorer implementation // ActivePeers returns all peers that channels are persisted for. -func (pr *PersistRestorer) ActivePeers(context.Context) ([]wire.Address, error) { +func (pr *PersistRestorer) ActivePeers(context.Context) ([]map[wallet.BackendID]wire.Address, error) { pr.mu.RLock() defer pr.mu.RUnlock() @@ -191,7 +193,7 @@ func (pr *PersistRestorer) ActivePeers(context.Context) ([]wire.Address, error) // RestorePeer returns an iterator over all persisted channels which // the given peer is a part of. -func (pr *PersistRestorer) RestorePeer(peer wire.Address) (persistence.ChannelIterator, error) { +func (pr *PersistRestorer) RestorePeer(peer map[wallet.BackendID]wire.Address) (persistence.ChannelIterator, error) { pr.mu.RLock() defer pr.mu.RUnlock() diff --git a/channel/persistence/test/persistrestorertest.go b/channel/persistence/test/persistrestorertest.go index bc5552930..07a58b893 100644 --- a/channel/persistence/test/persistrestorertest.go +++ b/channel/persistence/test/persistrestorertest.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,7 +34,7 @@ import ( // Client is a mock client that can be used to create channels. type Client struct { - addr wire.Address + addr map[wallet.BackendID]wire.Address rng *rand.Rand pr persistence.PersistRestorer @@ -55,9 +57,9 @@ func NewClient(ctx context.Context, t *testing.T, rng *rand.Rand, pr persistence // NewChannel creates a new channel with the supplied peer as the other // participant. The client's participant index is randomly chosen. -func (c *Client) NewChannel(t require.TestingT, p wire.Address, parent *Channel) *Channel { +func (c *Client) NewChannel(t require.TestingT, p map[wallet.BackendID]wire.Address, parent *Channel) *Channel { idx := c.rng.Intn(channelNumPeers) - peers := make([]wire.Address, channelNumPeers) + peers := make([]map[wallet.BackendID]wire.Address, channelNumPeers) peers[idx] = c.addr peers[idx^1] = p @@ -94,7 +96,7 @@ func GenericPersistRestorerTest( ct := pkgtest.NewConcurrent(t) c := NewClient(ctx, t, rng, pr) - peers := test.NewRandomAddresses(rng, numPeers) + peers := test.NewRandomAddressesMap(rng, numPeers) channels := make([]map[channel.ID]*Channel, numPeers) var prevCh *Channel @@ -185,7 +187,7 @@ func GenericPersistRestorerTest( peerLoop: for idx, addr := range peers { for _, paddr := range persistedPeers { - if addr.Equal(paddr) { + if channel.EqualWireMaps(addr, paddr) { continue peerLoop // found, next address } } diff --git a/channel/state.go b/channel/state.go index 758728a70..dfb9da6dd 100644 --- a/channel/state.go +++ b/channel/state.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,9 @@ import ( "encoding" "io" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" + "github.com/pkg/errors" "perun.network/go-perun/wire/perunio" @@ -78,7 +81,7 @@ func newState(params *Params, initBals Allocation, initData Data) (*State, error n := len(params.Parts) for _, asset := range initBals.Balances { if n != len(asset) { - return nil, errors.New("number of participants in parameters and initial balances don't match") + return nil, errors.Errorf("number of participants in parameters and initial balances don't match: parts: %d, balances: %d", n, len(asset)) } } if err := initBals.Valid(); err != nil { @@ -158,3 +161,16 @@ func (s *State) Equal(t *State) error { func (s *State) ToSubAlloc() *SubAlloc { return NewSubAlloc(s.ID, s.Allocation.Sum(), nil) } + +// EqualWireMaps compares two wire.Address maps for equality. +func EqualWireMaps(a, b map[wallet.BackendID]wire.Address) bool { + if len(a) != len(b) { + return false + } + for i, addr := range a { + if !addr.Equal(b[i]) { + return false + } + } + return true +} diff --git a/channel/statemachine.go b/channel/statemachine.go index 80a94c605..fb0ecee35 100644 --- a/channel/statemachine.go +++ b/channel/statemachine.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -29,7 +29,7 @@ type StateMachine struct { } // NewStateMachine creates a new StateMachine. -func NewStateMachine(acc wallet.Account, params Params) (*StateMachine, error) { +func NewStateMachine(acc map[wallet.BackendID]wallet.Account, params Params) (*StateMachine, error) { app, ok := params.App.(StateApp) if !ok { return nil, errors.New("app must be StateApp") @@ -47,7 +47,7 @@ func NewStateMachine(acc wallet.Account, params Params) (*StateMachine, error) { } // RestoreStateMachine restores a state machine to the data given by Source. -func RestoreStateMachine(acc wallet.Account, source Source) (*StateMachine, error) { +func RestoreStateMachine(acc map[wallet.BackendID]wallet.Account, source Source) (*StateMachine, error) { app, ok := source.Params().App.(StateApp) if !ok { return nil, errors.New("app must be StateApp") @@ -115,11 +115,12 @@ func (m *StateMachine) CheckUpdate( if err := m.validTransition(state, actor); err != nil { return err } - - if ok, err := Verify(m.params.Parts[sigIdx], state, sig); err != nil { - return errors.WithMessagef(err, "verifying signature[%d]", sigIdx) - } else if !ok { - return errors.Errorf("invalid signature[%d]", sigIdx) + for _, add := range m.params.Parts[sigIdx] { + if ok, err := Verify(add, state, sig); err != nil { + return errors.WithMessagef(err, "verifying signature[%d]", sigIdx) + } else if !ok { + return errors.Errorf("invalid signature[%d]", sigIdx) + } } return nil } diff --git a/channel/test/app_randomizer.go b/channel/test/app_randomizer.go index ae7442b20..c536684a7 100644 --- a/channel/test/app_randomizer.go +++ b/channel/test/app_randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,13 +17,15 @@ package test import ( "math/rand" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" ) // The AppRandomizer interface provides functionality for creating random // data and apps which is useful for testing. type AppRandomizer interface { - NewRandomApp(*rand.Rand) channel.App + NewRandomApp(*rand.Rand, wallet.BackendID) channel.App NewRandomData(*rand.Rand) channel.Data } @@ -53,8 +55,13 @@ 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 { + bID = wallet.BackendID(channel.TestBackendID) + } // WithAppDef does not set the app in the options - app := opt.AppRandomizer().NewRandomApp(rng) + app := opt.AppRandomizer().NewRandomApp(rng, bID) channel.RegisterApp(app) updateOpts(opts, WithApp(app)) return app @@ -82,14 +89,17 @@ func NewRandomAppAndData(rng *rand.Rand, opts ...RandomOpt) (channel.App, channe // NewRandomAppIDFunc is an app identifier randomizer function. type NewRandomAppIDFunc = func(*rand.Rand) channel.AppID -var newRandomAppID NewRandomAppIDFunc +var newRandomAppID map[wallet.BackendID]NewRandomAppIDFunc // SetNewRandomAppID sets the function generating a new app identifier. -func SetNewRandomAppID(f NewRandomAppIDFunc) { - newRandomAppID = f +func SetNewRandomAppID(f NewRandomAppIDFunc, bID wallet.BackendID) { + if newRandomAppID == nil { + newRandomAppID = make(map[wallet.BackendID]NewRandomAppIDFunc) + } + newRandomAppID[bID] = f } // NewRandomAppID creates a new random channel.AppID. -func NewRandomAppID(rng *rand.Rand) channel.AppID { - return newRandomAppID(rng) +func NewRandomAppID(rng *rand.Rand, bID wallet.BackendID) channel.AppID { + return newRandomAppID[bID](rng) } diff --git a/channel/test/backend.go b/channel/test/backend.go index e109df926..ff1c2a164 100644 --- a/channel/test/backend.go +++ b/channel/test/backend.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -25,7 +25,7 @@ import ( "perun.network/go-perun/wallet" ) -type addressCreator = func() wallet.Address +type addressCreator = func() map[wallet.BackendID]wallet.Address // Setup provides all objects needed for the generic channel tests. type ( @@ -73,7 +73,8 @@ func mergeTestOpts(opts ...GenericTestOption) GenericTestOptions { func GenericBackendTest(t *testing.T, s *Setup, opts ...GenericTestOption) { t.Helper() require := require.New(t) - ID := channel.CalcID(s.Params) + ID, err := channel.CalcID(s.Params) + require.NoError(err, "CalcID should not return an error") require.Equal(ID, s.State.ID, "ChannelID(params) should match the States ID") require.Equal(ID, s.Params.ID(), "ChannelID(params) should match the Params ID") require.NotNil(s.State.Data, "State data can not be nil") @@ -95,27 +96,30 @@ func GenericBackendTest(t *testing.T, s *Setup, opts ...GenericTestOption) { func genericChannelIDTest(t *testing.T, s *Setup) { t.Helper() require.NotNil(t, s.Params.Parts, "params.Parts can not be nil") - assert.Panics(t, func() { channel.CalcID(nil) }, "ChannelID(nil) should panic") + _, err := channel.CalcID(s.Params) + assert.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) { params := modParams - ID := channel.CalcID(¶ms) + ID, _ := channel.CalcID(¶ms) assert.NotEqual(t, ID, s.State.ID, "Channel ids should differ") } } func genericSignTest(t *testing.T, s *Setup) { t.Helper() - _, err := channel.Sign(s.Account, s.State) + _, err := channel.Sign(s.Account, s.State, s.State.Backends[0]) assert.NoError(t, err, "Sign should not return an error") } func genericVerifyTest(t *testing.T, s *Setup, opts ...GenericTestOption) { t.Helper() addr := s.Account.Address() - require.Equal(t, channel.CalcID(s.Params), s.Params.ID(), "Invalid test params") - sig, err := channel.Sign(s.Account, s.State) + id, err := channel.CalcID(s.Params) + require.NoError(t, err, "CalcID should not return an error") + require.Equal(t, s.Params.ID(), id, "Invalid test params") + sig, err := channel.Sign(s.Account, s.State, s.State.Backends[0]) require.NoError(t, err, "Sign should not return an error") ok, err := channel.Verify(addr, s.State, sig) @@ -131,9 +135,12 @@ func genericVerifyTest(t *testing.T, s *Setup, opts ...GenericTestOption) { // Different address and same state and params for i := 0; i < 10; i++ { - ok, err := channel.Verify(s.RandomAddress(), s.State, sig) - assert.NoError(t, err, "Verify should not return an error") - assert.False(t, ok, "Verify should return false") + 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") + assert.False(t, ok, "Verify should return false") + } } } @@ -164,7 +171,7 @@ func buildModifiedParams(p1, p2 *channel.Params, s *Setup) (ret []channel.Params // Modify Parts[0] { modParams := *p1 - modParams.Parts = make([]wallet.Address, len(p1.Parts)) + modParams.Parts = make([]map[wallet.BackendID]wallet.Address, len(p1.Parts)) copy(modParams.Parts, p1.Parts) modParams.Parts[0] = s.RandomAddress() ret = appendModParams(ret, modParams) @@ -238,6 +245,7 @@ func buildModifiedStates(s1, s2 *channel.State, _opts ...GenericTestOption) (ret { modState := s1.Clone() modState.Assets = s2.Assets + modState.Backends = s2.Backends modState = ensureConsistentBalances(modState) ret = append(ret, *modState) } @@ -245,6 +253,7 @@ func buildModifiedStates(s1, s2 *channel.State, _opts ...GenericTestOption) (ret { modState := s1.Clone() modState.Allocation.Assets[0] = s2.Allocation.Assets[0] + modState.Allocation.Backends[0] = s2.Allocation.Backends[0] ret = append(ret, *modState) } } diff --git a/channel/test/mock_app_randomizer.go b/channel/test/mock_app_randomizer.go index 0e20c2493..d3814eae6 100644 --- a/channel/test/mock_app_randomizer.go +++ b/channel/test/mock_app_randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package test import ( "math/rand" + "perun.network/go-perun/wallet" + "github.com/google/uuid" "perun.network/go-perun/channel" ) @@ -32,8 +34,8 @@ func NewMockAppRandomizer() *MockAppRandomizer { } // NewRandomApp creates a new MockApp with a random address. -func (MockAppRandomizer) NewRandomApp(rng *rand.Rand) channel.App { - return channel.NewMockApp(NewRandomAppID(rng)) +func (MockAppRandomizer) NewRandomApp(rng *rand.Rand, bID wallet.BackendID) channel.App { + return channel.NewMockApp(NewRandomAppID(rng, bID)) } // NewRandomData creates a new MockOp with a random operation. diff --git a/channel/test/randomizer.go b/channel/test/randomizer.go index f1303f23e..3f2f7f5d2 100644 --- a/channel/test/randomizer.go +++ b/channel/test/randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -33,14 +33,17 @@ type Randomizer interface { NewRandomAsset(*rand.Rand) channel.Asset } -var randomizer Randomizer +var randomizer map[wallet.BackendID]Randomizer // SetRandomizer sets the global Randomizer variable. -func SetRandomizer(r Randomizer) { - if randomizer != nil { +func SetRandomizer(r Randomizer, bID wallet.BackendID) { + if randomizer == nil { + randomizer = make(map[wallet.BackendID]Randomizer) + } + if randomizer[bID] != nil { panic("channel/test randomizer already set") } - randomizer = r + randomizer[bID] = r } // NewRandomPhase generates a random channel machine phase. @@ -49,8 +52,8 @@ func NewRandomPhase(rng *rand.Rand) channel.Phase { } // NewRandomAsset generates a new random `channel.Asset`. -func NewRandomAsset(rng *rand.Rand) channel.Asset { - return randomizer.NewRandomAsset(rng) +func NewRandomAsset(rng *rand.Rand, bID wallet.BackendID) channel.Asset { + return randomizer[bID].NewRandomAsset(rng) } // NewRandomAssets generates new random `channel.Asset`s. @@ -61,9 +64,25 @@ func NewRandomAssets(rng *rand.Rand, opts ...RandomOpt) []channel.Asset { return assets } numAssets := opt.NumAssets(rng) + if backend, err := opt.Backend(); err == nil { + assets := make([]channel.Asset, opt.NumAssets(rng)) + for i := range assets { + assets[i] = NewRandomAsset(rng, backend) + } + updateOpts(opts, WithAssets(assets...)) + return assets + } + if backends, err := opt.BackendID(); err == nil { + assets := make([]channel.Asset, numAssets) + for i := range assets { + assets[i] = NewRandomAsset(rng, backends[i]) + } + updateOpts(opts, WithAssets(assets...)) + return assets + } as := make([]channel.Asset, numAssets) for i := range as { - as[i] = NewRandomAsset(rng) + as[i] = NewRandomAsset(rng, channel.TestBackendID) } updateOpts(opts, WithAssets(as...)) @@ -82,8 +101,9 @@ func NewRandomAllocation(rng *rand.Rand, opts ...RandomOpt) *channel.Allocation assets := NewRandomAssets(rng, opt) bals := NewRandomBalances(rng, opt) locked := NewRandomLocked(rng, opt) + backends := NewRandomBackends(rng, len(assets), opt) - alloc := &channel.Allocation{Assets: assets, Balances: bals, Locked: locked} + alloc := &channel.Allocation{Backends: backends, Assets: assets, Balances: bals, Locked: locked} updateOpts(opts, WithAllocation(alloc)) return alloc } @@ -157,11 +177,17 @@ func NewRandomParams(rng *rand.Rand, opts ...RandomOpt) *channel.Params { return params } numParts := opt.NumParts(rng) - var parts []wallet.Address + var parts []map[wallet.BackendID]wallet.Address if parts = opt.Parts(); parts == nil { - parts = make([]wallet.Address, numParts) + parts = make([]map[wallet.BackendID]wallet.Address, numParts) + for i := range parts { - parts[i] = test.NewRandomAddress(rng) + var backend wallet.BackendID + if backend, _ = opt.Backend(); backend != 0 { + parts[i] = map[wallet.BackendID]wallet.Address{backend: test.NewRandomAddress(rng, backend)} + } else { + parts[i] = map[wallet.BackendID]wallet.Address{channel.TestBackendID: test.NewRandomAddress(rng, channel.TestBackendID)} + } } } if firstPart := opt.FirstPart(); firstPart != nil { @@ -304,6 +330,30 @@ func NewRandomBalances(rng *rand.Rand, opts ...RandomOpt) channel.Balances { return balances } +// NewRandomBackends generates new random backend IDs. +// Options: `WithNumAssets` and `WithBackendIDs`. +func NewRandomBackends(rng *rand.Rand, num int, opts ...RandomOpt) []wallet.BackendID { + opt := mergeRandomOpts(opts...) + if backends, err := opt.BackendID(); err == nil { + return backends + } + if backend, err := opt.Backend(); err == nil { + backends := make([]wallet.BackendID, num) + for i := range backends { + backends[i] = backend + } + updateOpts(opts, WithBackendIDs(backends)) + return backends + } + backends := make([]wallet.BackendID, num) + for i := range backends { + backends[i] = 0 + } + + updateOpts(opts, WithBackendIDs(backends)) + return backends +} + // NewRandomTransaction generates a new random `channel.Transaction`. // `sigMask` defines which signatures are generated. `len(sigmask)` is // assumed to be the number of participants. @@ -311,18 +361,22 @@ func NewRandomBalances(rng *rand.Rand, opts ...RandomOpt) channel.Balances { // Options: all from `NewRandomParamsAndState`. func NewRandomTransaction(rng *rand.Rand, sigMask []bool, opts ...RandomOpt) *channel.Transaction { opt := mergeRandomOpts(opts...) + bID, err := opt.Backend() + if err != nil { + bID = channel.TestBackendID + } numParts := len(sigMask) - accs, addrs := test.NewRandomAccounts(rng, numParts) - params := NewRandomParams(rng, WithParts(addrs...), opt) + accs, addrs := test.NewRandomAccounts(rng, numParts, bID) + params := NewRandomParams(rng, WithParts(addrs), opt) state := NewRandomState(rng, WithID(params.ID()), WithNumParts(numParts), opt) sigs := make([]wallet.Sig, numParts) - var err error + err = nil for i, choice := range sigMask { if !choice { sigs[i] = nil } else { - sigs[i], err = channel.Sign(accs[i], state) + sigs[i], err = channel.Sign(accs[i][bID], state, bID) } if err != nil { panic(err) diff --git a/channel/test/randomopts.go b/channel/test/randomopts.go index ba4d348e3..527d73549 100644 --- a/channel/test/randomopts.go +++ b/channel/test/randomopts.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -65,6 +65,16 @@ func WithApp(app channel.App) RandomOpt { return RandomOpt{"app": app, "appDef": appDef} } +// WithBackend sets the `backend` that should be used. +func WithBackend(id wallet.BackendID) RandomOpt { + return RandomOpt{"backend": id} +} + +// WithBackendIDs sets the `backend` that should be used. +func WithBackendIDs(id []wallet.BackendID) RandomOpt { + return RandomOpt{"backendIDs": id} +} + // WithoutApp configures a NoApp and NoData. func WithoutApp() RandomOpt { return RandomOpt{"app": channel.NoApp(), "appDef": nil, "appData": channel.NoData()} @@ -112,7 +122,7 @@ func WithChallengeDuration(d uint64) RandomOpt { } // WithFirstPart sets the first participant that should be used in randomly generated Params. Overrides `WithParts`. -func WithFirstPart(part wallet.Address) RandomOpt { +func WithFirstPart(part map[wallet.BackendID]wallet.Address) RandomOpt { return RandomOpt{"firstPart": part} } @@ -182,12 +192,12 @@ func WithState(state *channel.State) RandomOpt { // WithParams sets the `Params` that should be used. // Also sets `WithID`, `WithChallengeDuration`, `WithParts`, `WithApp` and `WithNonce`. func WithParams(params *channel.Params) RandomOpt { - return RandomOpt{"params": params}.Append(WithID(params.ID()), WithChallengeDuration(params.ChallengeDuration), WithParts(params.Parts...), WithApp(params.App), WithNonce(params.Nonce)) + return RandomOpt{"params": params}.Append(WithID(params.ID()), WithChallengeDuration(params.ChallengeDuration), WithParts(params.Parts), WithApp(params.App), WithNonce(params.Nonce)) } // WithParts sets the `Parts` that should be used when generating Params. // Also sets `WithNumParts`. -func WithParts(parts ...wallet.Address) RandomOpt { +func WithParts(parts []map[wallet.BackendID]wallet.Address) RandomOpt { return RandomOpt{"parts": parts, "numParts": len(parts)} } @@ -291,6 +301,22 @@ func (o RandomOpt) Assets() []channel.Asset { return o["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 o["backend"].(wallet.BackendID), nil +} + +// 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 o["backendIDs"].([]wallet.BackendID), nil +} + // Balances returns the `Balances` value of the `RandomOpt`. // If not present, returns nil. func (o RandomOpt) Balances() channel.Balances { @@ -330,11 +356,11 @@ func (o RandomOpt) ID() (id channel.ID, valid bool) { // FirstPart returns the `FirstPart` value of the `RandomOpt`. // If not present, returns nil. -func (o RandomOpt) FirstPart() wallet.Address { +func (o RandomOpt) FirstPart() map[wallet.BackendID]wallet.Address { if _, ok := o["firstPart"]; !ok { return nil } - return o["firstPart"].(wallet.Address) + return o["firstPart"].(map[wallet.BackendID]wallet.Address) } // IsFinal returns the `IsFinal` value of the `RandomOpt`. @@ -472,11 +498,11 @@ func (o RandomOpt) Params() *channel.Params { // Parts returns the `Parts` value of the `RandomOpt`. // If not present, returns nil. -func (o RandomOpt) Parts() []wallet.Address { +func (o RandomOpt) Parts() []map[wallet.BackendID]wallet.Address { if _, ok := o["parts"]; !ok { return nil } - return o["parts"].([]wallet.Address) + return o["parts"].([]map[wallet.BackendID]wallet.Address) } // Version returns the `Version` value of the `RandomOpt`. diff --git a/channel/transaction_test.go b/channel/transaction_test.go index 4b8702594..57fb7a705 100644 --- a/channel/transaction_test.go +++ b/channel/transaction_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/adjudicate.go b/client/adjudicate.go index 362a8f294..ea8cd2f2e 100644 --- a/client/adjudicate.go +++ b/client/adjudicate.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package client import ( "context" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -293,7 +295,9 @@ func (c *Channel) Settle(ctx context.Context, secondary bool) (err error) { return } } - c.wallet.DecrementUsage(c.machine.Account().Address()) + for i, wall := range c.wallet { + wall.DecrementUsage(c.machine.Account()[i].Address()) + } return }); err != nil { return errors.WithMessage(err, "decrementing account usage") @@ -339,9 +343,9 @@ func (c *Channel) withdraw(ctx context.Context, secondary bool) error { } // hasParticipant returns we are participating in the channel. -func (c *Channel) hasParticipant(id wire.Address) bool { +func (c *Channel) hasParticipant(id map[wallet.BackendID]wire.Address) bool { for _, p := range c.Peers() { - if id.Equal(p) { + if channel.EqualWireMaps(id, p) { return true } } diff --git a/client/appchannel_test.go b/client/appchannel_test.go index fbbeae345..47edee968 100644 --- a/client/appchannel_test.go +++ b/client/appchannel_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "math/big" "testing" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" @@ -30,20 +32,21 @@ import ( func TestProgression(t *testing.T) { rng := pkgtest.Prng(t) - setups := NewSetups(rng, []string{"Paul", "Paula"}) + setups := NewSetups(rng, []string{"Paul", "Paula"}, channel.TestBackendID) roles := [2]clienttest.Executer{ clienttest.NewPaul(t, setups[0]), clienttest.NewPaula(t, setups[1]), } - appAddress := chtest.NewRandomAppID(rng) + appAddress := chtest.NewRandomAppID(rng, channel.TestBackendID) app := channel.NewMockApp(appAddress) channel.RegisterApp(app) execConfig := &clienttest.ProgressionExecConfig{ BaseExecConfig: clienttest.MakeBaseExecConfig( - [2]wire.Address{setups[0].Identity.Address(), setups[1].Identity.Address()}, - chtest.NewRandomAsset(rng), + [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)}, client.WithApp(app, channel.NewMockOp(channel.OpValid)), ), diff --git a/client/channel.go b/client/channel.go index b17335562..f2f56014e 100644 --- a/client/channel.go +++ b/client/channel.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -43,7 +43,7 @@ type Channel struct { statesPub watcher.StatesPub onUpdate func(from, to *channel.State) adjudicator channel.Adjudicator - wallet wallet.Wallet + wallet map[wallet.BackendID]wallet.Wallet parent *Channel // must be nil for ledger channel subChannelFundings *updateInterceptors // awaited subchannel funding updates @@ -53,35 +53,39 @@ type Channel struct { // newChannel is internally used by the Client to create a new channel // controller after the channel proposal protocol ran successfully. func (c *Client) newChannel( - acc wallet.Account, + acc map[wallet.BackendID]wallet.Account, parent *Channel, - peers []wire.Address, + peers []map[wallet.BackendID]wire.Address, // peerIdx, BackendID -> Address params channel.Params, ) (*Channel, error) { machine, err := channel.NewStateMachine(acc, params) if err != nil { return nil, errors.WithMessage(err, "creating state machine") } - return c.channelFromMachine(machine, parent, peers...) + return c.channelFromMachine(machine, parent, peers) } // channelFromSource is used to create a channel controller from restored data. -func (c *Client) channelFromSource(s channel.Source, parent *Channel, peers ...wire.Address) (*Channel, error) { - acc, err := c.wallet.Unlock(s.Params().Parts[s.Idx()]) - if err != nil { - return nil, errors.WithMessage(err, "unlocking account for channel") +func (c *Client) channelFromSource(s channel.Source, parent *Channel, peers []map[wallet.BackendID]wire.Address) (*Channel, error) { + accs := make(map[wallet.BackendID]wallet.Account) + var err error + for i, wall := range c.wallet { + accs[i], err = wall.Unlock(s.Params().Parts[s.Idx()][i]) + if err != nil { + return nil, errors.WithMessage(err, "unlocking account for channel") + } } - machine, err := channel.RestoreStateMachine(acc, s) + machine, err := channel.RestoreStateMachine(accs, s) if err != nil { return nil, errors.WithMessage(err, "restoring state machine") } - return c.channelFromMachine(machine, parent, peers...) + return c.channelFromMachine(machine, parent, peers) } // channelFromMachine creates a channel controller around the passed state machine. -func (c *Client) channelFromMachine(machine *channel.StateMachine, parent *Channel, peers ...wire.Address) (*Channel, error) { +func (c *Client) channelFromMachine(machine *channel.StateMachine, parent *Channel, peers []map[wallet.BackendID]wire.Address) (*Channel, error) { logger := c.logChan(machine.ID()) machine.SetLog(logger) // client logger has more fields pmachine := persistence.FromStateMachine(machine, c.pr) @@ -170,7 +174,7 @@ func (c *Channel) Phase() channel.Phase { // Peers returns the Perun network addresses of all peers, in the order // of the channel participants. -func (c *Channel) Peers() []wire.Address { +func (c *Channel) Peers() []map[wallet.BackendID]wire.Address { return c.conn.Peers() } diff --git a/client/channelconn.go b/client/channelconn.go index ea1e9d5e8..2b1ea5ed2 100644 --- a/client/channelconn.go +++ b/client/channelconn.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,7 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" + "perun.network/go-perun/wallet" "perun.network/go-perun/channel" "perun.network/go-perun/log" @@ -34,7 +35,7 @@ type channelConn struct { pub wire.Publisher // outgoing message publisher r *wire.Relay // update response relay/incoming messages - peers []wire.Address + peers []map[wallet.BackendID]wire.Address idx channel.Index // our index log log.Logger @@ -42,7 +43,7 @@ type channelConn struct { // 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 []wire.Address, idx channel.Index, sub wire.Subscriber, pub wire.Publisher) (_ *channelConn, err error) { +func newChannelConn(id channel.ID, peers []map[wallet.BackendID]wire.Address, idx channel.Index, sub wire.Subscriber, pub wire.Publisher) (_ *channelConn, err error) { // relay to receive all update responses relay := wire.NewRelay() // we cache all responses for the lifetime of the relay @@ -84,7 +85,7 @@ func newChannelConn(id channel.ID, peers []wire.Address, idx channel.Index, sub }, nil } -func (c *channelConn) sender() wire.Address { +func (c *channelConn) sender() map[wallet.BackendID]wire.Address { return c.peers[c.idx] } @@ -119,7 +120,7 @@ func (c *channelConn) Send(ctx context.Context, msg wire.Msg) error { // Peers returns the ordered list of peer addresses. Note that the own peer is // included in the list. -func (c *channelConn) Peers() []wire.Address { +func (c *channelConn) Peers() []map[wallet.BackendID]wire.Address { return c.peers } @@ -147,7 +148,7 @@ type ( // with Next(), which returns the peer's channel index and the message. channelMsgRecv struct { *wire.Receiver - peers []wire.Address + peers []map[wallet.BackendID]wire.Address log log.Logger } ) @@ -159,7 +160,7 @@ func (r *channelMsgRecv) Next(ctx context.Context) (channel.Index, ChannelMsg, e if err != nil { return 0, nil, err } - idx := wire.IndexOfAddr(r.peers, env.Sender) + idx := wire.IndexOfAddrs(r.peers, env.Sender) if idx == -1 { return 0, nil, errors.Errorf("channel connection received message from unexpected peer %v", env.Sender) } @@ -167,5 +168,5 @@ 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) } - return channel.Index(idx), msg, nil // predicate must guarantee that the conversion is safe + return channel.Index(idx), msg, nil } diff --git a/client/chanregistry.go b/client/chanregistry.go index 8ffa4d985..0250c5203 100644 --- a/client/chanregistry.go +++ b/client/chanregistry.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/client.go b/client/client.go index 0fc28ee82..e6503a7bd 100644 --- a/client/client.go +++ b/client/client.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -35,12 +35,12 @@ import ( // // Currently, only the two-party protocol is fully implemented. type Client struct { - address wire.Address + address map[wallet.BackendID]wire.Address conn clientConn channels chanRegistry funder channel.Funder adjudicator channel.Adjudicator - wallet wallet.Wallet + wallet map[wallet.BackendID]wallet.Wallet pr persistence.PersistRestorer log log.Logger // structured logger for this client version1Cache version1Cache @@ -68,11 +68,11 @@ type Client struct { // // If any argument is nil, New panics. func New( - address wire.Address, + address map[wallet.BackendID]wire.Address, bus wire.Bus, funder channel.Funder, adjudicator channel.Adjudicator, - wallet wallet.Wallet, + wallet map[wallet.BackendID]wallet.Wallet, watcher watcher.Watcher, ) (c *Client, err error) { if address == nil { @@ -201,7 +201,7 @@ func (c *Client) SetLog(l log.Logger) { c.log = l } -func (c *Client) logPeer(p wire.Address) log.Logger { +func (c *Client) logPeer(p map[wallet.BackendID]wire.Address) log.Logger { return c.log.WithField("peer", p) } @@ -220,7 +220,7 @@ func (c *Client) Restore(ctx context.Context) error { var eg errgroup.Group for _, p := range ps { - if p.Equal(c.address) { + if channel.EqualWireMaps(p, c.address) { continue // skip own peer } p := p diff --git a/client/client_internal_test.go b/client/client_internal_test.go index fee1f27a8..2c398baf4 100644 --- a/client/client_internal_test.go +++ b/client/client_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -28,7 +28,7 @@ func TestClient_Channel(t *testing.T) { rng := test.Prng(t) // dummy client that only has an id and a registry c := &Client{ - address: wiretest.NewRandomAddress(rng), + address: wiretest.NewRandomAddressesMap(rng, 1)[0], channels: makeChanRegistry(), } diff --git a/client/client_persistence_test.go b/client/client_persistence_test.go index d04bc373b..9a1592d9f 100644 --- a/client/client_persistence_test.go +++ b/client/client_persistence_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/channel" + chprtest "perun.network/go-perun/channel/persistence/test" ctest "perun.network/go-perun/client/test" ) @@ -39,7 +41,7 @@ func TestPersistencePetraRobert(t *testing.T) { func NewSetupsPersistence(t *testing.T, rng *rand.Rand, names []string) []ctest.RoleSetup { t.Helper() - setups := NewSetups(rng, names) + setups := NewSetups(rng, names, channel.TestBackendID) for i := range names { setups[i].PR = chprtest.NewPersistRestorer(t) } diff --git a/client/client_role_test.go b/client/client_role_test.go index 0af84af4f..19277af87 100644 --- a/client/client_role_test.go +++ b/client/client_role_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,10 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "perun.network/go-perun/apps/payment" chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" @@ -34,10 +38,10 @@ import ( const ( roleOperationTimeout = 1 * time.Second - twoPartyTestTimeout = 10 * time.Second + twoPartyTestTimeout = 20 * time.Second ) -func NewSetups(rng *rand.Rand, names []string) []ctest.RoleSetup { +func NewSetups(rng *rand.Rand, names []string, bID wallet.BackendID) []ctest.RoleSetup { var ( bus = wiretest.NewSerializingLocalBus() n = len(names) @@ -50,11 +54,11 @@ func NewSetups(rng *rand.Rand, names []string) []ctest.RoleSetup { if err != nil { panic("Error initializing watcher: " + err.Error()) } - w := wtest.NewWallet() - acc := w.NewRandomAccount(rng) + w := map[wallet.BackendID]wtest.Wallet{bID: wtest.NewWallet(bID)} + acc := w[0].NewRandomAccount(rng) setup[i] = ctest.RoleSetup{ Name: names[i], - Identity: wiretest.NewRandomAccount(rng), + Identity: wiretest.NewRandomAccountMap(rng, bID), Bus: bus, Funder: backend.NewFunder(acc.Address()), Adjudicator: backend.NewAdjudicator(acc.Address()), @@ -84,8 +88,9 @@ func runAliceBobTest(ctx context.Context, t *testing.T, setup func(*rand.Rand) ( cfg := &ctest.AliceBobExecConfig{ BaseExecConfig: ctest.MakeBaseExecConfig( - [2]wire.Address{setups[0].Identity.Address(), setups[1].Identity.Address()}, - chtest.NewRandomAsset(rng), + [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)}, app, ), diff --git a/client/client_test.go b/client/client_test.go index 1e89d0153..602706f55 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,15 +45,15 @@ func (d DummyBus) Publish(context.Context, *wire.Envelope) error { return errors.New("DummyBus.Publish called") } -func (d DummyBus) SubscribeClient(wire.Consumer, wire.Address) error { +func (d DummyBus) SubscribeClient(wire.Consumer, map[wallet.BackendID]wire.Address) error { return nil } func TestClient_New_NilArgs(t *testing.T) { rng := test.Prng(t) - id := wiretest.NewRandomAddress(rng) + id := wiretest.NewRandomAddressesMap(rng, 1)[0] backend := &ctest.MockBackend{} - b, f, a, w := &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, wtest.RandomWallet() + b, f, a, w := &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, map[wallet.BackendID]wallet.Wallet{channel.TestBackendID: wtest.RandomWallet(channel.TestBackendID)} watcher, err := local.NewWatcher(backend) require.NoError(t, err, "initializing the watcher should not error") assert.Panics(t, func() { client.New(nil, b, f, a, w, watcher) }) //nolint:errcheck @@ -68,7 +70,7 @@ func TestClient_Handle_NilArgs(t *testing.T) { watcher, err := local.NewWatcher(backend) require.NoError(t, err, "initializing the watcher should not error") c, err := client.New(wiretest.NewRandomAddress(rng), - &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, wtest.RandomWallet(), watcher) + &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, map[wallet.BackendID]wallet.Wallet{channel.TestBackendID: wtest.RandomWallet(channel.TestBackendID)}, watcher) require.NoError(t, err) dummyUH := client.UpdateHandlerFunc(func(*channel.State, client.ChannelUpdate, *client.UpdateResponder) {}) @@ -82,8 +84,8 @@ func TestClient_New(t *testing.T) { backend := &ctest.MockBackend{} watcher, err := local.NewWatcher(backend) require.NoError(t, err, "initializing the watcher should not error") - c, err := client.New(wiretest.NewRandomAddress(rng), - &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, wtest.RandomWallet(), watcher) + c, err := client.New(wiretest.NewRandomAddressesMap(rng, 1)[0], + &DummyBus{t}, &ctest.MockFunder{}, &ctest.MockAdjudicator{}, map[wallet.BackendID]wallet.Wallet{channel.TestBackendID: wtest.RandomWallet(channel.TestBackendID)}, watcher) assert.NoError(t, err) require.NotNil(t, c) } @@ -93,8 +95,8 @@ func TestChannelRejection(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - roles := NewSetups(rng, []string{"Alice", "Bob"}) - asset := chtest.NewRandomAsset(rng) + roles := NewSetups(rng, []string{"Alice", "Bob"}, channel.TestBackendID) + asset := chtest.NewRandomAsset(rng, channel.TestBackendID) clients := ctest.NewClients(t, rng, roles) require := require.New(t) alice, bob := clients[0], clients[1] @@ -111,8 +113,13 @@ func TestChannelRejection(t *testing.T) { ) // Create channel proposal. - parts := []wire.Address{alice.Identity.Address(), bob.Identity.Address()} - initAlloc := channel.NewAllocation(len(parts), asset) + parts := []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(alice.Identity), wire.AddressMapfromAccountMap(bob.Identity)} + var bID wallet.BackendID + for i := range parts[0] { + bID = i + break + } + initAlloc := channel.NewAllocation(len(parts), []wallet.BackendID{bID}, asset) prop, err := client.NewLedgerChannelProposal( challengeDuration, alice.WalletAddress, diff --git a/client/clientconn.go b/client/clientconn.go index 660320e14..513194848 100644 --- a/client/clientconn.go +++ b/client/clientconn.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package client import ( "context" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/log" @@ -28,11 +30,11 @@ 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 wire.Address + sender map[wallet.BackendID]wire.Address log.Embedding } -func makeClientConn(address wire.Address, bus wire.Bus) (c clientConn, err error) { +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 c.bus = bus @@ -76,7 +78,7 @@ func (c clientConn) nextReq(ctx context.Context) (*wire.Envelope, error) { // pubMsg publishes the given message on the wire bus, setting the own client as // the sender. -func (c *clientConn) pubMsg(ctx context.Context, msg wire.Msg, rec wire.Address) error { +func (c *clientConn) pubMsg(ctx context.Context, msg wire.Msg, rec map[wallet.BackendID]wire.Address) error { c.Log().WithField("peer", rec).Debugf("Publishing message: %v: %+v", msg.Type(), msg) return c.bus.Publish(ctx, &wire.Envelope{ Sender: c.sender, diff --git a/client/failing_funding_test.go b/client/failing_funding_test.go index 543cfd094..bebce149d 100644 --- a/client/failing_funding_test.go +++ b/client/failing_funding_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -42,8 +42,8 @@ func TestFailingFunding(t *testing.T) { BalanceDelta: big.NewInt(0), }, func(r *rand.Rand) ([2]ctest.RoleSetup, channel.Asset) { - roles := NewSetups(rng, []string{"Frida", "Fred"}) - asset := chtest.NewRandomAsset(rng) + 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/payment_test.go b/client/payment_test.go index 027693200..dfe892fa1 100644 --- a/client/payment_test.go +++ b/client/payment_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -20,6 +20,10 @@ import ( "math/rand" "testing" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" ctest "perun.network/go-perun/client/test" @@ -32,7 +36,7 @@ func TestPaymentHappy(t *testing.T) { defer cancel() runAliceBobTest(ctx, t, func(rng *rand.Rand) ([]ctest.RoleSetup, [2]ctest.Executer) { - setups := NewSetups(rng, []string{"Alice", "Bob"}) + setups := NewSetups(rng, []string{"Alice", "Bob"}, channel.TestBackendID) roles := [2]ctest.Executer{ ctest.NewAlice(t, setups[0]), ctest.NewBob(t, setups[1]), @@ -47,7 +51,7 @@ func TestPaymentDispute(t *testing.T) { defer cancel() const mallory, carol = 0, 1 // Indices of Mallory and Carol - setups := NewSetups(rng, []string{"Mallory", "Carol"}) + setups := NewSetups(rng, []string{"Mallory", "Carol"}, channel.TestBackendID) roles := [2]ctest.Executer{ ctest.NewMallory(t, setups[0]), ctest.NewCarol(t, setups[1]), @@ -55,8 +59,9 @@ func TestPaymentDispute(t *testing.T) { cfg := &ctest.MalloryCarolExecConfig{ BaseExecConfig: ctest.MakeBaseExecConfig( - [2]wire.Address{setups[mallory].Identity.Address(), setups[carol].Identity.Address()}, - chtest.NewRandomAsset(rng), + [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)}, client.WithoutApp(), ), diff --git a/client/proposal.go b/client/proposal.go index 39c524a2c..70c11bf63 100644 --- a/client/proposal.go +++ b/client/proposal.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -62,7 +62,7 @@ type ( // panic. ProposalResponder struct { client *Client - peer wire.Address + peer map[wallet.BackendID]wire.Address req ChannelProposal called atomic.Bool } @@ -128,21 +128,21 @@ func (r *ProposalResponder) Accept(ctx context.Context, acc ChannelProposalAccep } // SetEgoisticChain sets the egoistic chain flag for a given ledger. -func (r *ProposalResponder) SetEgoisticChain(egoistic multi.LedgerID) { +func (r *ProposalResponder) SetEgoisticChain(egoistic multi.LedgerBackendID, id int) { mf, ok := r.client.funder.(*multi.Funder) if !ok { log.Panic("unexpected type for funder") } - mf.SetEgoisticChain(egoistic, true) + mf.SetEgoisticChain(egoistic, id, true) } // RemoveEgoisticChain removes the egoistic chain flag for a given ledger. -func (r *ProposalResponder) RemoveEgoisticChain(egoistic multi.LedgerID) { +func (r *ProposalResponder) RemoveEgoisticChain(egoistic multi.LedgerBackendID, id int) { mf, ok := r.client.funder.(*multi.Funder) if !ok { log.Panic("unexpected type for funder") } - mf.SetEgoisticChain(egoistic, false) + mf.SetEgoisticChain(egoistic, id, false) } // Reject lets the user signal that they reject the channel proposal. @@ -208,7 +208,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() //nolint:contextcheck + defer c.releaseVer1Cache() ch, err := c.proposeTwoPartyChannel(ctx, prop) if err != nil { return nil, errors.WithMessage(err, "channel proposal") @@ -252,7 +252,7 @@ func (c *Client) cleanupChannelOpening(prop ChannelProposal, ourIdx channel.Inde // The proposer is expected to be the first peer in the participant list. // // This handler is dispatched from the Client.Handle routine. -func (c *Client) handleChannelProposal(handler ProposalHandler, p wire.Address, req ChannelProposal) { +func (c *Client) handleChannelProposal(handler ProposalHandler, p map[wallet.BackendID]wire.Address, req ChannelProposal) { ourIdx := channel.Index(ProposeeIdx) // Prepare and cleanup, e.g., for locking and unlocking parent channel. @@ -275,7 +275,7 @@ func (c *Client) handleChannelProposal(handler ProposalHandler, p wire.Address, } func (c *Client) handleChannelProposalAcc( - ctx context.Context, p wire.Address, + ctx context.Context, p map[wallet.BackendID]wire.Address, prop ChannelProposal, acc ChannelProposalAccept, ) (ch *Channel, err error) { if err := c.validChannelProposalAcc(prop, acc); err != nil { @@ -285,7 +285,7 @@ func (c *Client) handleChannelProposalAcc( // cache version 1 updates c.enableVer1Cache() // replay cached version 1 updates - defer c.releaseVer1Cache() //nolint:contextcheck + defer c.releaseVer1Cache() if ch, err = c.acceptChannelProposal(ctx, prop, p, acc); err != nil { return ch, errors.WithMessage(err, "accept channel proposal") @@ -301,7 +301,7 @@ func (c *Client) handleChannelProposalAcc( func (c *Client) acceptChannelProposal( ctx context.Context, prop ChannelProposal, - p wire.Address, + p map[wallet.BackendID]wire.Address, acc ChannelProposalAccept, ) (*Channel, error) { if acc == nil { @@ -324,7 +324,7 @@ func (c *Client) acceptChannelProposal( } func (c *Client) handleChannelProposalRej( - ctx context.Context, p wire.Address, + ctx context.Context, p map[wallet.BackendID]wire.Address, req ChannelProposal, reason string, ) error { msgReject := &ChannelProposalRejMsg{ @@ -405,7 +405,7 @@ func (c *Client) proposeTwoPartyChannel( func (c *Client) validTwoPartyProposal( proposal ChannelProposal, ourIdx channel.Index, - peerAddr wire.Address, + peerAddr map[wallet.BackendID]wire.Address, ) error { if err := proposal.Valid(); err != nil { return err @@ -432,12 +432,12 @@ func (c *Client) validTwoPartyProposal( peerIdx := ourIdx ^ 1 // In the 2PCPP, the proposer is expected to have index 0 - if !peers[peerIdx].Equal(peerAddr) { + if !channel.EqualWireMaps(peers[peerIdx], peerAddr) { return errors.Errorf("remote peer doesn't have peer index %d", peerIdx) } // In the 2PCPP, the receiver is expected to have index 1 - if !peers[ourIdx].Equal(c.address) { + if !channel.EqualWireMaps(peers[ourIdx], c.address) { return errors.Errorf("we don't have peer index %d", ourIdx) } @@ -468,6 +468,10 @@ func (c *Client) validSubChannelProposal(proposal *SubChannelProposalMsg) error return errors.WithMessage(err, "parent channel and sub-channel assets do not match") } + if err := channel.AssertBackendsEqual(parentState.Backends, base.InitBals.Backends); err != nil { + return errors.New("parent channel and sub-channel backends do not match") + } + if err := parentState.Balances.AssertGreaterOrEqual(base.InitBals.Balances); err != nil { return errors.WithMessage(err, "insufficient funds") } @@ -493,6 +497,10 @@ func (c *Client) validVirtualChannelProposal(prop *VirtualChannelProposalMsg, ou return errors.WithMessage(err, "unequal assets") } + if err := channel.AssertBackendsEqual(parentState.Backends, prop.InitBals.Backends); err != nil { + return errors.New("unequal backends") + } + if !prop.InitBals.Balances.Equal(prop.FundingAgreement) { return errors.WithMessage(err, "unequal funding agreement") } @@ -534,8 +542,8 @@ func (c *Client) validChannelProposalAcc( return nil } -func participants(proposer, proposee wallet.Address) []wallet.Address { - parts := make([]wallet.Address, proposalNumParts) +func participants(proposer, proposee map[wallet.BackendID]wallet.Address) []map[wallet.BackendID]wallet.Address { + parts := make([]map[wallet.BackendID]wallet.Address, proposalNumParts) parts[ProposerIdx] = proposer parts[ProposeeIdx] = proposee return parts @@ -590,9 +598,13 @@ func (c *Client) completeCPP( return nil, errors.New("channel already exists") } - account, err := c.wallet.Unlock(params.Parts[partIdx]) - if err != nil { - return nil, errors.WithMessage(err, "unlocking account") + accounts := make(map[wallet.BackendID]wallet.Account) + var err error + for i, wall := range c.wallet { + accounts[i], err = wall.Unlock(params.Parts[partIdx][i]) + if err != nil { + return nil, errors.WithMessage(err, "unlocking account") + } } parentChannelID, parent, err := c.proposalParent(prop, partIdx) @@ -601,7 +613,7 @@ func (c *Client) completeCPP( } peers := c.proposalPeers(prop) - ch, err := c.newChannel(account, parent, peers, *params) + ch, err := c.newChannel(accounts, parent, peers, *params) if err != nil { return nil, err } @@ -622,7 +634,9 @@ func (c *Client) completeCPP( return ch, errors.WithMessage(err, "exchanging initial sigs and enabling state") } - c.wallet.IncrementUsage(params.Parts[partIdx]) + for i, wall := range c.wallet { + wall.IncrementUsage(params.Parts[partIdx][i]) + } return ch, nil } @@ -648,7 +662,7 @@ func (c *Client) proposalParent(prop ChannelProposal, partIdx channel.Index) (pa func (c *Client) mpcppParts( prop ChannelProposal, acc ChannelProposalAccept, -) (parts []wallet.Address) { +) (parts []map[wallet.BackendID]wallet.Address) { switch p := prop.(type) { case *LedgerChannelProposalMsg: ledgerAcc, ok := acc.(*LedgerChannelProposalAccMsg) @@ -698,7 +712,9 @@ func (c *Client) completeFunding(ctx context.Context, ch *Channel) error { if !c.channels.Put(params.ID(), ch) { return errors.New("channel already exists") } - c.wallet.IncrementUsage(params.Parts[ch.machine.Idx()]) + for i, wall := range c.wallet { + wall.IncrementUsage(params.Parts[ch.machine.Idx()][i]) + } return nil } @@ -769,7 +785,7 @@ func (c *Client) releaseVer1Cache() { c.version1Cache.enabled-- for _, u := range c.version1Cache.cache { - go c.handleChannelUpdate(u.uh, u.p, u.m) //nolint:contextcheck + go c.handleChannelUpdate(u.uh, u.p, u.m) } c.version1Cache.cache = nil } @@ -782,7 +798,7 @@ type version1Cache struct { type cachedUpdate struct { uh UpdateHandler - p wire.Address + p map[wallet.BackendID]wire.Address m ChannelUpdateProposal } @@ -802,3 +818,12 @@ func newChannelFundingError(err error) *ChannelFundingError { func (e ChannelFundingError) Error() string { return fmt.Sprintf("channel funding failed: %v", e.Err.Error()) } + +// WirePeerMap returns the map at a specific index. +func WirePeerMap(peers map[wallet.BackendID][]wire.Address, index int) map[wallet.BackendID]wire.Address { + address := make(map[wallet.BackendID]wire.Address) + for i, p := range peers { + address[i] = p[index] + } + return address +} diff --git a/client/proposal_internal_test.go b/client/proposal_internal_test.go index 981ea8f8f..cf96a4ccb 100644 --- a/client/proposal_internal_test.go +++ b/client/proposal_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,12 +37,12 @@ func TestClient_validTwoPartyProposal(t *testing.T) { // dummy client that only has an id c := &Client{ - address: wiretest.NewRandomAddress(rng), + address: wiretest.NewRandomAddressesMap(rng, 1)[0], } validProp := NewRandomLedgerChannelProposal(rng, channeltest.WithNumParts(2)) validProp.Peers[0] = c.address // set us as the proposer peerAddr := validProp.Peers[1] // peer at 1 as receiver - require.False(t, peerAddr.Equal(c.address)) + require.False(t, channel.EqualWireMaps(peerAddr, c.address)) require.Len(t, validProp.Peers, 2) validProp3Peers := NewRandomLedgerChannelProposal(rng, channeltest.WithNumParts(3)) @@ -51,7 +53,7 @@ func TestClient_validTwoPartyProposal(t *testing.T) { tests := []struct { prop *LedgerChannelProposalMsg ourIdx channel.Index - peerAddr wire.Address + peerAddr map[wallet.BackendID]wire.Address valid bool }{ { @@ -97,13 +99,13 @@ func TestChannelProposal_assertValidNumParts(t *testing.T) { rng := pkgtest.Prng(t) c := NewRandomLedgerChannelProposal(rng) require.NoError(c.assertValidNumParts()) - c.Peers = make([]wire.Address, channel.MaxNumParts+1) + c.Peers = make([]map[wallet.BackendID]wire.Address, channel.MaxNumParts+1) require.Error(c.assertValidNumParts()) } func TestProposalResponder_Accept_Nil(t *testing.T) { p := new(ProposalResponder) - _, err := p.Accept(nil, new(LedgerChannelProposalAccMsg)) //nolint:staticcheck + _, err := p.Accept(nil, new(LedgerChannelProposalAccMsg)) assert.Error(t, err, "context") } @@ -151,10 +153,14 @@ func NewRandomBaseChannelProposal(rng *rand.Rand, opts ...channeltest.RandomOpt) func NewRandomLedgerChannelProposal(rng *rand.Rand, opts ...channeltest.RandomOpt) *LedgerChannelProposalMsg { opt := make(channeltest.RandomOpt).Append(opts...) base := NewRandomBaseChannelProposal(rng, opt) - peers := wiretest.NewRandomAddresses(rng, base.NumPeers()) + peers := wiretest.NewRandomAddressesMap(rng, base.NumPeers()) + var bID wallet.BackendID + for i := range peers[0] { + bID = i + } return &LedgerChannelProposalMsg{ BaseChannelProposal: base, - Participant: wallettest.NewRandomAddress(rng), + Participant: wallettest.NewRandomAddresses(rng, bID), Peers: peers, } } diff --git a/client/proposalmsgs.go b/client/proposalmsgs.go index 6115587ce..0b7903b74 100644 --- a/client/proposalmsgs.go +++ b/client/proposalmsgs.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -114,8 +114,8 @@ type ( // LedgerChannelProposalMsg is a channel proposal for ledger channels. LedgerChannelProposalMsg struct { BaseChannelProposal - Participant wallet.Address // Proposer's address in the channel. - Peers []wire.Address // Participants' wire addresses. + Participant map[wallet.BackendID]wallet.Address // Proposer's address in the channel. + Peers []map[wallet.BackendID]wire.Address // Participants' wire addresses. } // SubChannelProposalMsg is a channel proposal for subchannels. @@ -127,7 +127,7 @@ type ( // proposalPeers returns the wire addresses of a proposed channel's // participants. -func (c *Client) proposalPeers(p ChannelProposal) (peers []wire.Address) { +func (c *Client) proposalPeers(p ChannelProposal) (peers []map[wallet.BackendID]wire.Address) { switch prop := p.(type) { case *LedgerChannelProposalMsg: peers = prop.Peers @@ -230,7 +230,7 @@ func (p *BaseChannelProposal) Valid() error { // Accept constructs an accept message that belongs to a proposal message. It // should be used instead of manually constructing an accept message. func (p LedgerChannelProposalMsg) Accept( - participant wallet.Address, + participant map[wallet.BackendID]wallet.Address, nonceShare ProposalOpts, ) *LedgerChannelProposalAccMsg { if !nonceShare.isNonce() { @@ -259,9 +259,9 @@ func (LedgerChannelProposalMsg) Matches(acc ChannelProposalAccept) bool { // For more information, see ProposalOpts. func NewLedgerChannelProposal( challengeDuration uint64, - participant wallet.Address, + participant map[wallet.BackendID]wallet.Address, initBals *channel.Allocation, - peers []wire.Address, + peers []map[wallet.BackendID]wire.Address, opts ...ProposalOpts, ) (prop *LedgerChannelProposalMsg, err error) { prop = &LedgerChannelProposalMsg{ @@ -287,16 +287,16 @@ func (p LedgerChannelProposalMsg) Encode(w io.Writer) error { } return perunio.Encode(w, p.BaseChannelProposal, - p.Participant, - wire.AddressesWithLen(p.Peers)) + wallet.AddressDecMap(p.Participant), + wire.AddressMapArray(p.Peers)) } // Decode decodes a ledger channel proposal. func (p *LedgerChannelProposalMsg) Decode(r io.Reader) error { err := perunio.Decode(r, &p.BaseChannelProposal, - wallet.AddressDec{Addr: &p.Participant}, - (*wire.AddressesWithLen)(&p.Peers)) + (*wallet.AddressDecMap)(&p.Participant), + (*wire.AddressMapArray)(&p.Peers)) if err != nil { return err } @@ -404,7 +404,7 @@ type ( // each channel instantiation. LedgerChannelProposalAccMsg struct { BaseChannelProposalAcc - Participant wallet.Address // Responder's participant address. + Participant map[wallet.BackendID]wallet.Address // Responder's participant address. } // SubChannelProposalAccMsg is the accept message type corresponding to sub @@ -452,14 +452,14 @@ func (acc *LedgerChannelProposalAccMsg) Base() *BaseChannelProposalAcc { func (acc LedgerChannelProposalAccMsg) Encode(w io.Writer) error { return perunio.Encode(w, acc.BaseChannelProposalAcc, - acc.Participant) + wallet.AddressDecMap(acc.Participant)) } // Decode decodes a LedgerChannelProposalAcc from an io.Reader. func (acc *LedgerChannelProposalAccMsg) Decode(r io.Reader) error { return perunio.Decode(r, &acc.BaseChannelProposalAcc, - wallet.AddressDec{Addr: &acc.Participant}) + (*wallet.AddressDecMap)(&acc.Participant)) } // Type returns wire.SubChannelProposalAcc. @@ -517,26 +517,26 @@ type ( // VirtualChannelProposalMsg is a channel proposal for virtual channels. VirtualChannelProposalMsg struct { BaseChannelProposal - Proposer wallet.Address // Proposer's address in the channel. - Peers []wire.Address // Participants' wire addresses. - Parents []channel.ID // Parent channels for each participant. - IndexMaps [][]channel.Index // Index mapping for each participant in relation to the root channel. + 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. + IndexMaps [][]channel.Index // Index mapping for each participant in relation to the root channel. } // VirtualChannelProposalAccMsg is the accept message type corresponding to // virtual channel proposals. VirtualChannelProposalAccMsg struct { BaseChannelProposalAcc - Responder wallet.Address // Responder's participant address. + Responder map[wallet.BackendID]wallet.Address // Responder's participant address. } ) // NewVirtualChannelProposal creates a virtual channel proposal. func NewVirtualChannelProposal( challengeDuration uint64, - participant wallet.Address, + participant map[wallet.BackendID]wallet.Address, initBals *channel.Allocation, - peers []wire.Address, + peers []map[wallet.BackendID]wire.Address, parents []channel.ID, indexMaps [][]channel.Index, opts ...ProposalOpts, @@ -564,8 +564,8 @@ func (p VirtualChannelProposalMsg) Encode(w io.Writer) error { return perunio.Encode( w, p.BaseChannelProposal, - p.Proposer, - wire.AddressesWithLen(p.Peers), + wallet.AddressDecMap(p.Proposer), + wire.AddressMapArray(p.Peers), channelIDsWithLen(p.Parents), indexMapsWithLen(p.IndexMaps), ) @@ -576,8 +576,8 @@ func (p *VirtualChannelProposalMsg) Decode(r io.Reader) error { return perunio.Decode( r, &p.BaseChannelProposal, - wallet.AddressDec{Addr: &p.Proposer}, - (*wire.AddressesWithLen)(&p.Peers), + (*wallet.AddressDecMap)(&p.Proposer), + (*wire.AddressMapArray)(&p.Peers), (*channelIDsWithLen)(&p.Parents), (*indexMapsWithLen)(&p.IndexMaps), ) @@ -590,7 +590,7 @@ func (VirtualChannelProposalMsg) Type() wire.Type { // Accept constructs an accept message that belongs to a proposal message. func (p VirtualChannelProposalMsg) Accept( - responder wallet.Address, + responder map[wallet.BackendID]wallet.Address, opts ...ProposalOpts, ) *VirtualChannelProposalAccMsg { _opts := union(opts...) @@ -618,10 +618,10 @@ func (acc *VirtualChannelProposalAccMsg) Base() *BaseChannelProposalAcc { // Encode encodes the message into an io.Writer. func (acc VirtualChannelProposalAccMsg) Encode(w io.Writer) error { - return perunio.Encode(w, acc.BaseChannelProposalAcc, acc.Responder) + return perunio.Encode(w, acc.BaseChannelProposalAcc, wallet.AddressDecMap(acc.Responder)) } // Decode decodes a message from an io.Reader. func (acc *VirtualChannelProposalAccMsg) Decode(r io.Reader) error { - return perunio.Decode(r, &acc.BaseChannelProposalAcc, wallet.AddressDec{Addr: &acc.Responder}) + return perunio.Decode(r, &acc.BaseChannelProposalAcc, (*wallet.AddressDecMap)(&acc.Responder)) } diff --git a/client/restore.go b/client/restore.go index ad50cc2ce..1412521e5 100644 --- a/client/restore.go +++ b/client/restore.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package client import ( "context" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -24,7 +26,7 @@ import ( "perun.network/go-perun/wire" ) -type channelFromSourceSig = func(*Client, *persistence.Channel, *Channel, ...wire.Address) (*Channel, error) +type channelFromSourceSig = func(*Client, *persistence.Channel, *Channel, ...map[wallet.BackendID]wire.Address) (*Channel, error) // clientChannelFromSource is the production behaviour of reconstructChannel. // During testing, it is replaced by a simpler function that needs much less @@ -33,9 +35,9 @@ func clientChannelFromSource( c *Client, ch *persistence.Channel, parent *Channel, - peers ...wire.Address, + peers ...map[wallet.BackendID]wire.Address, ) (*Channel, error) { - return c.channelFromSource(ch, parent, peers...) + return c.channelFromSource(ch, parent, peers) } func (c *Client) reconstructChannel( @@ -66,7 +68,7 @@ func (c *Client) reconstructChannel( return ch } -func (c *Client) restorePeerChannels(ctx context.Context, p wire.Address) (err error) { +func (c *Client) restorePeerChannels(ctx context.Context, p map[wallet.BackendID]wire.Address) (err error) { it, err := c.pr.RestorePeer(p) if err != nil { return errors.WithMessagef(err, "restoring channels for peer: %v", err) diff --git a/client/restore_internal_test.go b/client/restore_internal_test.go index c2b8a723f..95cdd48f5 100644 --- a/client/restore_internal_test.go +++ b/client/restore_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -36,10 +36,16 @@ func patchChFromSource( c *Client, ch *persistence.Channel, parent *Channel, - peers ...wire.Address, + peers ...map[wallet.BackendID]wire.Address, ) (*Channel, error) { - acc, _ := wallettest.RandomWallet().Unlock(ch.ParamsV.Parts[ch.IdxV]) - machine, _ := channel.NewStateMachine(acc, *ch.ParamsV) + bID := wallet.BackendID(channel.TestBackendID) + if len(peers) > 0 { + for i := range peers[0] { + bID = i + } + } + acc, _ := wallettest.RandomWallet(bID).Unlock(ch.ParamsV.Parts[ch.IdxV][channel.TestBackendID]) + machine, _ := channel.NewStateMachine(map[wallet.BackendID]wallet.Account{channel.TestBackendID: acc}, *ch.ParamsV) pmachine := persistence.FromStateMachine(machine, nil) _ch := &Channel{parent: parent, machine: pmachine, OnCloser: new(sync.Closer)} @@ -52,10 +58,10 @@ func TestReconstructChannel(t *testing.T) { rng := pkgtest.Prng(t) db := map[channel.ID]*persistence.Channel{} - restParent := mkRndChan(rng) + restParent := mkRndChan(rng, channel.TestBackendID) db[restParent.ID()] = restParent - restChild := mkRndChan(rng) + restChild := mkRndChan(rng, channel.TestBackendID) parentID := restParent.ID() restChild.Parent = &parentID db[restChild.ID()] = restChild @@ -82,7 +88,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++ { - mkRndChanTree(rng, 3, 1, 3, db) + mkRndChanTree(rng, 3, 1, 3, db, channel.TestBackendID) } // Remember channels that have been published. @@ -108,14 +114,14 @@ func TestRestoreChannelCollection(t *testing.T) { } // mkRndChan creates a single random channel. -func mkRndChan(rng *rand.Rand) *persistence.Channel { - parts := make([]wallet.Address, channel.MaxNumParts) +func mkRndChan(rng *rand.Rand, bID wallet.BackendID) *persistence.Channel { + parts := make([]map[wallet.BackendID]wallet.Address, channel.MaxNumParts) for i := range parts { - parts[i] = wallettest.NewRandomAccount(rng).Address() + 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.ParamsV = test.NewRandomParams(rng, test.WithParts(parts...)) + ch.ParamsV = test.NewRandomParams(rng, test.WithParts(parts)) sigs := make([]bool, channel.MaxNumParts) opts := test.WithParams(ch.ParamsV) ch.StagingTXV = *test.NewRandomTransaction(rng, sigs, opts) @@ -131,8 +137,9 @@ func mkRndChanTree( rng *rand.Rand, depth, minChildren, maxChildren int, db map[channel.ID]*persistence.Channel, + bID wallet.BackendID, ) (root *persistence.Channel) { - root = mkRndChan(rng) + root = mkRndChan(rng, bID) db[root.ID()] = root if depth > 0 && maxChildren > 0 { @@ -141,7 +148,7 @@ func mkRndChanTree( minChildren-- } for i := 0; i < children; i++ { - t := mkRndChanTree(rng, depth-1, minChildren, maxChildren-1, db) + 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 8a696ae01..bd6383fe6 100644 --- a/client/serialize.go +++ b/client/serialize.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/subchannel.go b/client/subchannel.go index aced8688d..0dde9ea83 100644 --- a/client/subchannel.go +++ b/client/subchannel.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -39,7 +39,7 @@ func (c *Channel) equalParticipants(_c *Channel) bool { } for i, _a := range a { - if !_a.Equal(b[i]) { + if !channel.EqualWireMaps(_a, b[i]) { return false } } diff --git a/client/subchannel_test.go b/client/subchannel_test.go index 2f07cda0b..fd3371fe1 100644 --- a/client/subchannel_test.go +++ b/client/subchannel_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,10 @@ import ( "math/big" "testing" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "perun.network/go-perun/apps/payment" chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" @@ -30,7 +34,7 @@ import ( func TestSubChannelHappy(t *testing.T) { rng := test.Prng(t) - setups := NewSetups(rng, []string{"Susie", "Tim"}) + setups := NewSetups(rng, []string{"Susie", "Tim"}, channel.TestBackendID) roles := [2]ctest.Executer{ ctest.NewSusie(t, setups[0]), ctest.NewTim(t, setups[1]), @@ -38,8 +42,9 @@ func TestSubChannelHappy(t *testing.T) { cfg := ctest.NewSusieTimExecConfig( ctest.MakeBaseExecConfig( - [2]wire.Address{setups[0].Identity.Address(), setups[1].Identity.Address()}, - chtest.NewRandomAsset(rng), + [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)}, client.WithoutApp(), ), @@ -68,15 +73,16 @@ func TestSubChannelHappy(t *testing.T) { func TestSubChannelDispute(t *testing.T) { rng := test.Prng(t) - setups := NewSetups(rng, []string{"DisputeSusie", "DisputeTim"}) + setups := NewSetups(rng, []string{"DisputeSusie", "DisputeTim"}, channel.TestBackendID) roles := [2]ctest.Executer{ ctest.NewDisputeSusie(t, setups[0]), ctest.NewDisputeTim(t, setups[1]), } baseCfg := ctest.MakeBaseExecConfig( - [2]wire.Address{setups[0].Identity.Address(), setups[1].Identity.Address()}, - chtest.NewRandomAsset(rng), + [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)}, client.WithoutApp(), ) diff --git a/client/sync.go b/client/sync.go index efe60e810..dc9718ba1 100644 --- a/client/sync.go +++ b/client/sync.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "log" "time" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/channel" @@ -32,7 +34,7 @@ var syncReplyTimeout = 10 * time.Second // exists, it just sends the current channel data to the requester. If the // own channel is in the Signing phase, the ongoing update is discarded so that // the channel is reverted to the Acting phase. -func (c *Client) handleSyncMsg(peer wire.Address, msg *ChannelSyncMsg) { +func (c *Client) handleSyncMsg(peer map[wallet.BackendID]wire.Address, msg *ChannelSyncMsg) { log := c.logChan(msg.ID()).WithField("peer", peer) ch, ok := c.channels.Channel(msg.ID()) if !ok { @@ -67,8 +69,7 @@ func (c *Client) handleSyncMsg(peer wire.Address, msg *ChannelSyncMsg) { // 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 wire.Address) (err error) { +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 id := ch.ID() @@ -117,7 +118,8 @@ func (c *Client) syncChannel(ctx context.Context, ch *persistence.Channel, p wir } // validateMessage validates the remote channel sync message. -// nolint:unused, nestif +// +//nolint:nestif func validateMessage(ch *persistence.Channel, msg *ChannelSyncMsg) error { v := ch.CurrentTX().Version mv := msg.CurrentTX.Version @@ -135,19 +137,20 @@ func validateMessage(ch *persistence.Channel, msg *ChannelSyncMsg) error { return errors.New("sigs length mismatch") } for i, sig := range msg.CurrentTX.Sigs { - ok, err := channel.Verify(ch.Params().Parts[i], msg.CurrentTX.State, sig) - if err != nil { - return errors.WithMessagef(err, "validating sig %d", i) - } - if !ok { - return errors.Errorf("invalid sig %d", i) + for _, p := range ch.Params().Parts[i] { + ok, err := channel.Verify(p, msg.CurrentTX.State, sig) + if err != nil { + return errors.WithMessagef(err, "validating sig %d", i) + } + if !ok { + return errors.Errorf("invalid sig %d", i) + } } } } 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/syncmsgs.go b/client/syncmsgs.go index 47f0ec142..bdd119afe 100644 --- a/client/syncmsgs.go +++ b/client/syncmsgs.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/test/backend.go b/client/test/backend.go index 2ea22648e..e9da3d42d 100644 --- a/client/test/backend.go +++ b/client/test/backend.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -39,7 +39,13 @@ type ( latestEvents map[channel.ID]channel.AdjudicatorEvent eventSubs map[channel.ID][]*MockSubscription balances map[addressMapKey]map[assetMapKey]*big.Int - id LedgerID + id multi.LedgerBackendID + } + + // AssetID is the unique asset identifier. + AssetID struct { + backendID uint32 + ledgerID LedgerID } rng interface { @@ -55,6 +61,16 @@ type ( LedgerID string ) +// LedgerID returns the ledger identifier. +func (id AssetID) LedgerID() multi.LedgerID { + return id.ledgerID +} + +// BackendID returns the backend identifier. +func (id AssetID) BackendID() uint32 { + return id.backendID +} + // maximal amount of milliseconds that the Fund method waits before returning. const fundMaxSleepMs = 100 @@ -70,12 +86,12 @@ func NewMockBackend(rng *rand.Rand, id string) *MockBackend { latestEvents: make(map[channel.ID]channel.AdjudicatorEvent), eventSubs: make(map[channel.ID][]*MockSubscription), balances: make(map[string]map[string]*big.Int), - id: LedgerID(id), + id: AssetID{0, LedgerID(id)}, } } // ID returns the ledger's identifier. -func (b *MockBackend) ID() LedgerID { +func (b *MockBackend) ID() multi.LedgerBackendID { return b.id } @@ -526,7 +542,7 @@ func (f *assetHolder) Fund(req channel.FundingReq, b *MockBackend, acc wallet.Ad for i, asset := range req.State.Assets { ma, ok := asset.(*MultiLedgerAsset) - if ok && ma.LedgerID() != b.ID() { + if ok && ma.LedgerBackendID() != b.ID() { continue } diff --git a/client/test/channel.go b/client/test/channel.go index ff023626b..982edb1b5 100644 --- a/client/test/channel.go +++ b/client/test/channel.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,8 @@ import ( "math/big" "time" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" "perun.network/go-perun/client" "perun.network/go-perun/log" @@ -64,10 +66,12 @@ func (ch *paymentChannel) openSubChannel( cfg ExecConfig, initBals []*big.Int, app client.ProposalOpts, + bID wallet.BackendID, ) *paymentChannel { initAlloc := channel.Allocation{ Assets: []channel.Asset{cfg.Asset()}, - Balances: [][]channel.Bal{[]*big.Int{initBals[0], initBals[1]}}, + Backends: []wallet.BackendID{bID}, + Balances: [][]channel.Bal{{initBals[0], initBals[1]}}, } prop := ch.r.SubChannelProposal(rng, cfg, ch.Channel, &initAlloc, app) diff --git a/client/test/fund.go b/client/test/fund.go index 76a15900d..6c9fbe8f3 100644 --- a/client/test/fund.go +++ b/client/test/fund.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "perun.network/go-perun/channel" @@ -92,7 +94,7 @@ func runFredFridaTest( clients := NewClients(t, rng, setups[:]) frida, fred := clients[fridaIdx], clients[fredIdx] - fridaWireAddr, fredWireAddr := frida.Identity.Address(), fred.Identity.Address() + fridaWireAddr, fredWireAddr := wire.AddressMapfromAccountMap(frida.Identity), wire.AddressMapfromAccountMap(fred.Identity) fridaWalletAddr, fredWalletAddr := frida.WalletAddress, fred.WalletAddress // Store client balances before running test. @@ -111,11 +113,15 @@ func runFredFridaTest( AlwaysAcceptChannelHandler(ctx, fredWalletAddr, chsFred, errsFred), AlwaysRejectUpdateHandler(ctx, errsFred), ) - + var bID wallet.BackendID + for i := range fridaWalletAddr { + bID = i + break + } // Create the proposal. - initAlloc := channel.NewAllocation(numParts, asset) + initAlloc := channel.NewAllocation(numParts, []wallet.BackendID{bID}, asset) initAlloc.SetAssetBalances(asset, []*big.Int{fridaInitBal, fredInitBal}) - parts := []wire.Address{fridaWireAddr, fredWireAddr} + parts := []map[wallet.BackendID]wire.Address{fridaWireAddr, fredWireAddr} prop, err := client.NewLedgerChannelProposal( challengeDuration, fridaWalletAddr, @@ -126,6 +132,7 @@ func runFredFridaTest( // Frida sends the proposal. chFrida, err := frida.ProposeChannel(ctx, prop) + t.Log(err.Error()) require.IsType(t, &client.ChannelFundingError{}, err) require.NotNil(t, chFrida) // Frida settles the channel. diff --git a/client/test/handler.go b/client/test/handler.go index b0813985f..875986bcd 100644 --- a/client/test/handler.go +++ b/client/test/handler.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -26,7 +26,7 @@ import ( // AlwaysAcceptChannelHandler returns a channel proposal handler that accepts // all channel proposals. -func AlwaysAcceptChannelHandler(ctx context.Context, addr wallet.Address, channels chan *client.Channel, errs chan<- error) client.ProposalHandlerFunc { +func AlwaysAcceptChannelHandler(ctx context.Context, addr map[wallet.BackendID]wallet.Address, channels chan *client.Channel, errs chan<- error) client.ProposalHandlerFunc { return func(cp client.ChannelProposal, pr *client.ProposalResponder) { switch cp := cp.(type) { case *client.LedgerChannelProposalMsg: diff --git a/client/test/multiledger.go b/client/test/multiledger.go index bf9ee1cdb..5ae9ebfec 100644 --- a/client/test/multiledger.go +++ b/client/test/multiledger.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -59,12 +59,12 @@ func SetupMultiLedgerTest(t *testing.T) MultiLedgerSetup { bus := wire.NewLocalBus() // Setup clients. - c1 := setupClient(t, rng, l1, l2, bus) - c2 := setupClient(t, rng, l1, l2, bus) + c1 := setupClient(t, rng, l1, l2, bus, channel.TestBackendID) + c2 := setupClient(t, rng, l1, l2, bus, channel.TestBackendID) // Define assets. - a1 := NewMultiLedgerAsset(l1.ID(), chtest.NewRandomAsset(rng)) - a2 := NewMultiLedgerAsset(l2.ID(), chtest.NewRandomAsset(rng)) + a1 := NewMultiLedgerAsset(l1.ID(), chtest.NewRandomAsset(rng, channel.TestBackendID)) + a2 := NewMultiLedgerAsset(l2.ID(), chtest.NewRandomAsset(rng, channel.TestBackendID)) return MultiLedgerSetup{ Client1: c1, @@ -92,12 +92,17 @@ func SetupMultiLedgerTest(t *testing.T) MultiLedgerSetup { // MultiLedgerAsset is a multi-ledger asset. type MultiLedgerAsset struct { - id LedgerID + id multi.LedgerBackendID 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 LedgerID, asset channel.Asset) *MultiLedgerAsset { +func NewMultiLedgerAsset(id multi.LedgerBackendID, asset channel.Asset) *MultiLedgerAsset { return &MultiLedgerAsset{ id: id, asset: asset, @@ -111,18 +116,23 @@ func (a *MultiLedgerAsset) Equal(b channel.Asset) bool { return false } - return a.id.MapKey() == bm.id.MapKey() && a.asset.Equal(bm.asset) + return a.id.LedgerID().MapKey() == bm.id.LedgerID().MapKey() && a.asset.Equal(bm.asset) && a.id.BackendID() == bm.id.BackendID() +} + +// Address returns the asset's address. +func (a *MultiLedgerAsset) Address() []byte { + return a.asset.Address() } // LedgerID returns the asset's ledger ID. -func (a *MultiLedgerAsset) LedgerID() multi.LedgerID { +func (a *MultiLedgerAsset) LedgerID() multi.LedgerBackendID { return a.id } // MarshalBinary encodes the asset to its byte representation. func (a *MultiLedgerAsset) MarshalBinary() ([]byte, error) { var buf bytes.Buffer - err := perunio.Encode(&buf, string(a.id), a.asset) + err := perunio.Encode(&buf, string(a.id.LedgerID().MapKey()), a.id.BackendID(), a.asset) if err != nil { return nil, err } @@ -133,14 +143,14 @@ func (a *MultiLedgerAsset) MarshalBinary() ([]byte, error) { // UnmarshalBinary decodes the asset from its byte representation. func (a *MultiLedgerAsset) UnmarshalBinary(data []byte) error { buf := bytes.NewBuffer(data) - return perunio.Decode(buf, string(a.id), a.asset) + return perunio.Decode(buf, string(a.id.LedgerID().MapKey()), a.id.BackendID(), a.asset) } // MultiLedgerClient represents a test client. type MultiLedgerClient struct { *client.Client - WireAddress wire.Address - WalletAddress wallet.Address + WireAddress map[wallet.BackendID]wire.Address + WalletAddress map[wallet.BackendID]wallet.Address Events chan channel.AdjudicatorEvent Adjudicator1, Adjudicator2 channel.Adjudicator BalanceReader1, BalanceReader2 BalanceReader @@ -155,15 +165,16 @@ func (c MultiLedgerClient) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) { func setupClient( t *testing.T, rng *rand.Rand, l1, l2 *MockBackend, bus wire.Bus, + bID wallet.BackendID, ) MultiLedgerClient { t.Helper() require := require.New(t) // Setup identity. - wireAddr := wiretest.NewRandomAddress(rng) + wireAddr := wiretest.NewRandomAddressesMap(rng, 1) // Setup wallet and account. - w := wtest.NewWallet() + w := wtest.NewWallet(bID) acc := w.NewRandomAccount(rng) // Setup funder. @@ -181,19 +192,19 @@ func setupClient( require.NoError(err) c, err := client.New( - wireAddr, + wireAddr[0], bus, funder, adj, - w, + map[wallet.BackendID]wallet.Wallet{channel.TestBackendID: w}, watcher, ) require.NoError(err) return MultiLedgerClient{ Client: c, - WireAddress: wireAddr, - WalletAddress: acc.Address(), + WireAddress: wireAddr[0], + WalletAddress: map[wallet.BackendID]wallet.Address{channel.TestBackendID: acc.Address()}, Events: make(chan channel.AdjudicatorEvent), Adjudicator1: l1.NewAdjudicator(acc.Address()), Adjudicator2: l2.NewAdjudicator(acc.Address()), diff --git a/client/test/multiledger_dispute.go b/client/test/multiledger_dispute.go index b153f4435..6e06d2be2 100644 --- a/client/test/multiledger_dispute.go +++ b/client/test/multiledger_dispute.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -20,6 +20,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -60,9 +62,11 @@ func TestMultiLedgerDispute( // Establish ledger channel between Alice and Bob. + bID1 := wallet.BackendID(mlt.Asset1.LedgerBackendID().BackendID()) + bID2 := wallet.BackendID(mlt.Asset2.LedgerBackendID().BackendID()) // Create channel proposal. - parts := []wire.Address{alice.WireAddress, bob.WireAddress} - initAlloc := channel.NewAllocation(len(parts), mlt.Asset1, mlt.Asset2) + parts := []map[wallet.BackendID]wire.Address{alice.WireAddress, bob.WireAddress} + initAlloc := channel.NewAllocation(len(parts), []wallet.BackendID{bID1, bID2}, mlt.Asset1, mlt.Asset2) initAlloc.Balances = initBals prop, err := client.NewLedgerChannelProposal( challengeDuration, diff --git a/client/test/multiledger_happy.go b/client/test/multiledger_happy.go index 9ec8c021b..53d0810c1 100644 --- a/client/test/multiledger_happy.go +++ b/client/test/multiledger_happy.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "context" "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel" "perun.network/go-perun/client" @@ -41,8 +43,8 @@ func TestMultiLedgerHappy(ctx context.Context, t *testing.T, mlt MultiLedgerSetu // Establish ledger channel between Alice and Bob. // Create channel proposal. - parts := []wire.Address{alice.WireAddress, bob.WireAddress} - initAlloc := channel.NewAllocation(len(parts), mlt.Asset1, mlt.Asset2) + parts := []map[wallet.BackendID]wire.Address{alice.WireAddress, bob.WireAddress} + initAlloc := channel.NewAllocation(len(parts), []wallet.BackendID{wallet.BackendID(mlt.Asset1.LedgerBackendID().BackendID()), wallet.BackendID(mlt.Asset2.LedgerBackendID().BackendID())}, mlt.Asset1, mlt.Asset2) initAlloc.Balances = initBals prop, err := client.NewLedgerChannelProposal( challengeDuration, diff --git a/client/test/persistence.go b/client/test/persistence.go index d44a12fda..86709a8e6 100644 --- a/client/test/persistence.go +++ b/client/test/persistence.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -20,6 +20,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "perun.network/go-perun/channel" "perun.network/go-perun/client" "perun.network/go-perun/wire" @@ -45,7 +47,11 @@ const ( // ReplaceClient replaces the client instance of the Role. Useful for // persistence testing. func (r *multiClientRole) ReplaceClient() { - cl, err := client.New(r.setup.Identity.Address(), r.setup.Bus, r.setup.Funder, r.setup.Adjudicator, r.setup.Wallet, r.setup.Watcher) + 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) r.RequireNoErrorf(err, "recreating client") r.setClient(cl) } @@ -214,11 +220,11 @@ func (r *multiClientRole) Errors() <-chan error { return r.errs } -type addresses []wire.Address +type addresses []map[wallet.BackendID]wire.Address -func (a addresses) contains(b wire.Address) bool { +func (a addresses) contains(b map[wallet.BackendID]wire.Address) bool { for _, addr := range a { - if addr.Equal(b) { + if channel.EqualWireMaps(addr, b) { return true } } diff --git a/client/test/proposalmsgs.go b/client/test/proposalmsgs.go index c8984807a..8be9a03ef 100644 --- a/client/test/proposalmsgs.go +++ b/client/test/proposalmsgs.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "math/rand" "testing" + "perun.network/go-perun/channel" + "github.com/stretchr/testify/require" "perun.network/go-perun/channel/test" @@ -67,7 +69,7 @@ func channelProposalAccSerializationTest(t *testing.T, serializerTest func(t *te t.Run("ledger channel", func(t *testing.T) { for i := 0; i < 16; i++ { proposal := NewRandomLedgerChannelProposal(rng) - m := proposal.Accept(wallettest.NewRandomAddress(rng), client.WithNonceFrom(rng)) + m := proposal.Accept(wallettest.NewRandomAddresses(rng, channel.TestBackendID), client.WithNonceFrom(rng)) serializerTest(t, m) } }) @@ -85,7 +87,7 @@ func channelProposalAccSerializationTest(t *testing.T, serializerTest func(t *te var err error proposal, err := NewRandomVirtualChannelProposal(rng) require.NoError(t, err) - m := proposal.Accept(wallettest.NewRandomAddress(rng)) + m := proposal.Accept(wallettest.NewRandomAddresses(rng, channel.TestBackendID)) serializerTest(t, m) } }) diff --git a/client/test/randomproposal.go b/client/test/randomproposal.go index be2f67cec..6d4ce577b 100644 --- a/client/test/randomproposal.go +++ b/client/test/randomproposal.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package test import ( "math/rand" + "perun.network/go-perun/channel" + channeltest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" "perun.network/go-perun/wallet" @@ -30,18 +32,18 @@ const randomProposalNumParts = 2 // NewRandomLedgerChannelProposal creates a random channel proposal with the supplied // options. Number of participants is fixed to randomProposalNumParts. func NewRandomLedgerChannelProposal(rng *rand.Rand, opts ...client.ProposalOpts) *client.LedgerChannelProposalMsg { - return NewRandomLedgerChannelProposalBy(rng, wallettest.NewRandomAddress(rng), opts...) + return NewRandomLedgerChannelProposalBy(rng, wallettest.NewRandomAddresses(rng, channel.TestBackendID), opts...) } // NewRandomLedgerChannelProposalBy creates a random channel proposal with the // supplied options and proposer. Number of participants is fixed to // randomProposalNumParts. -func NewRandomLedgerChannelProposalBy(rng *rand.Rand, proposer wallet.Address, opts ...client.ProposalOpts) *client.LedgerChannelProposalMsg { +func NewRandomLedgerChannelProposalBy(rng *rand.Rand, proposer map[wallet.BackendID]wallet.Address, opts ...client.ProposalOpts) *client.LedgerChannelProposalMsg { prop, err := client.NewLedgerChannelProposal( rng.Uint64(), proposer, channeltest.NewRandomAllocation(rng, channeltest.WithNumParts(randomProposalNumParts)), - wiretest.NewRandomAddresses(rng, randomProposalNumParts), + wiretest.NewRandomAddressesMap(rng, randomProposalNumParts), opts...) if err != nil { panic("Error generating random channel proposal: " + err.Error()) @@ -65,9 +67,9 @@ func NewRandomVirtualChannelProposal(rng *rand.Rand, opts ...client.ProposalOpts numParts := 2 return client.NewVirtualChannelProposal( rng.Uint64(), - wallettest.NewRandomAddress(rng), + wallettest.NewRandomAddresses(rng, channel.TestBackendID), channeltest.NewRandomAllocation(rng, channeltest.WithNumParts(numParts)), - wiretest.NewRandomAddresses(rng, numParts), + wiretest.NewRandomAddressesMap(rng, numParts), channeltest.NewRandomChannelIDs(rng, numParts), channeltest.NewRandomIndexMaps(rng, numParts, numParts), opts...) diff --git a/client/test/role.go b/client/test/role.go index 211f7bef5..1a6898077 100644 --- a/client/test/role.go +++ b/client/test/role.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -70,12 +70,12 @@ type ( // RoleSetup contains the injectables for setting up the client. RoleSetup struct { Name string - Identity wire.Account + Identity map[wallet.BackendID]wire.Account Bus wire.Bus Funder channel.Funder Adjudicator channel.Adjudicator Watcher watcher.Watcher - Wallet wallettest.Wallet + Wallet map[wallet.BackendID]wallettest.Wallet PR persistence.PersistRestorer // Optional PersistRestorer Timeout time.Duration // Timeout waiting for other role, not challenge duration BalanceReader BalanceReader @@ -85,18 +85,20 @@ type ( // ExecConfig contains additional config parameters for the tests. ExecConfig interface { - Peers() [2]wire.Address // must match the RoleSetup.Identity's - Asset() channel.Asset // single Asset to use in this channel - InitBals() [2]*big.Int // channel deposit of each role - App() client.ProposalOpts // must be either WithApp or WithoutApp + 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 + App() client.ProposalOpts // must be either WithApp or WithoutApp } // BaseExecConfig contains base config parameters. BaseExecConfig struct { - peers [2]wire.Address // must match the RoleSetup.Identity's - asset channel.Asset // single Asset to use in this channel - initBals [2]*big.Int // channel deposit of each role - app client.ProposalOpts // must be either WithApp or WithoutApp + 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 + app client.ProposalOpts // must be either WithApp or WithoutApp } // An Executer is a Role that can execute a protocol. @@ -118,7 +120,7 @@ type ( Client struct { *client.Client RoleSetup - WalletAddress wallet.Address + WalletAddress map[wallet.BackendID]wallet.Address } ) @@ -127,12 +129,20 @@ func NewClients(t *testing.T, rng *rand.Rand, setups []RoleSetup) []*Client { t.Helper() clients := make([]*Client, len(setups)) for i, setup := range setups { - cl, err := client.New(setup.Identity.Address(), setup.Bus, setup.Funder, setup.Adjudicator, setup.Wallet, setup.Watcher) + setupWallet := make(map[wallet.BackendID]wallet.Wallet) + for i, w := range setup.Wallet { + setupWallet[i] = w + } + cl, err := client.New(wire.AddressMapfromAccountMap(setup.Identity), setup.Bus, setup.Funder, setup.Adjudicator, setupWallet, setup.Watcher) assert.NoError(t, err) + walletAddress := make(map[wallet.BackendID]wallet.Address) + for i, w := range setup.Wallet { + walletAddress[i] = w.NewRandomAccount(rng).Address() + } clients[i] = &Client{ Client: cl, RoleSetup: setup, - WalletAddress: setup.Wallet.NewRandomAccount(rng).Address(), + WalletAddress: walletAddress, } } return clients @@ -174,21 +184,23 @@ func ExecuteTwoPartyTest(ctx context.Context, t *testing.T, role [2]Executer, cf // MakeBaseExecConfig creates a new BaseExecConfig. func MakeBaseExecConfig( - peers [2]wire.Address, + peers [2]map[wallet.BackendID]wire.Address, asset channel.Asset, + backend wallet.BackendID, initBals [2]*big.Int, app client.ProposalOpts, ) BaseExecConfig { return BaseExecConfig{ peers: peers, asset: asset, + backend: backend, initBals: initBals, app: app, } } // Peers returns the peer addresses. -func (c *BaseExecConfig) Peers() [2]wire.Address { +func (c *BaseExecConfig) Peers() [2]map[wallet.BackendID]wire.Address { return c.peers } @@ -197,6 +209,11 @@ func (c *BaseExecConfig) Asset() channel.Asset { return c.asset } +// Backend returns the asset. +func (c *BaseExecConfig) Backend() wallet.BackendID { + return c.backend +} + // InitBals returns the initial balances. func (c *BaseExecConfig) InitBals() [2]*big.Int { return c.initBals @@ -218,8 +235,12 @@ func makeRole(t *testing.T, setup RoleSetup, numStages int) (r role) { numStages: numStages, challengeDuration: setup.ChallengeDuration, } - cl, err := client.New(r.setup.Identity.Address(), - r.setup.Bus, r.setup.Funder, r.setup.Adjudicator, r.setup.Wallet, r.setup.Watcher) + 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) } @@ -297,10 +318,10 @@ func (r *role) waitStage() { // 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]wire.Address) (our, their channel.Index) { - if r.setup.Identity.Address().Equal(peers[0]) { +func (r *role) Idxs(peers [2]map[wallet.BackendID]wire.Address) (our, their channel.Index) { + if channel.EqualWireMaps(wire.AddressMapfromAccountMap(r.setup.Identity), peers[0]) { return 0, 1 - } else if r.setup.Identity.Address().Equal(peers[1]) { + } else if channel.EqualWireMaps(wire.AddressMapfromAccountMap(r.setup.Identity), peers[1]) { return 1, 0 } panic("identity not in peers") @@ -397,12 +418,12 @@ func (r *role) LedgerChannelProposal(rng *rand.Rand, cfg ExecConfig) *client.Led } peers, asset, bals := cfg.Peers(), cfg.Asset(), cfg.InitBals() - alloc := channel.NewAllocation(len(peers), asset) + alloc := channel.NewAllocation(len(peers), []wallet.BackendID{cfg.Backend()}, asset) alloc.SetAssetBalances(asset, bals[:]) prop, err := client.NewLedgerChannelProposal( r.challengeDuration, - r.setup.Wallet.NewRandomAccount(rng).Address(), + map[wallet.BackendID]wallet.Address{cfg.Backend(): r.setup.Wallet[cfg.Backend()].NewRandomAccount(rng).Address()}, alloc, peers[:], client.WithNonceFrom(rng), @@ -455,7 +476,11 @@ func (h *acceptNextPropHandler) HandleProposal(prop client.ChannelProposal, res func (h *acceptNextPropHandler) Next() (*paymentChannel, error) { var prop client.ChannelProposal var res *client.ProposalResponder - + var bID wallet.BackendID + for i := range h.r.setup.Identity { + bID = i + break + } select { case pr := <-h.props: prop = pr.prop @@ -471,8 +496,8 @@ func (h *acceptNextPropHandler) Next() (*paymentChannel, error) { var acc client.ChannelProposalAccept switch p := prop.(type) { case *client.LedgerChannelProposalMsg: - part := h.r.setup.Wallet.NewRandomAccount(h.rng).Address() - acc = p.Accept(part, client.WithNonceFrom(h.rng)) + part := h.r.setup.Wallet[bID].NewRandomAccount(h.rng).Address() + acc = p.Accept(map[wallet.BackendID]wallet.Address{bID: part}, client.WithNonceFrom(h.rng)) h.r.log.Debugf("Accepting ledger channel proposal with participant: %v", part) case *client.SubChannelProposalMsg: diff --git a/client/test/subchannel.go b/client/test/subchannel.go index 3480751d5..eeeeeeb84 100644 --- a/client/test/subchannel.go +++ b/client/test/subchannel.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -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) + return parentChannel.openSubChannel(rng, cfg, funds, app, cfg.backend) } var subChannels []*paymentChannel diff --git a/client/test/subchannel_dispute.go b/client/test/subchannel_dispute.go index ea6721724..68709a73a 100644 --- a/client/test/subchannel_dispute.go +++ b/client/test/subchannel_dispute.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -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()) + subChannel := ledgerChannel.openSubChannel(rng, cfg, cfg.SubChannelFunds[:], client.WithoutApp(), _cfg.Backend()) subReq0 := client.NewTestChannel(subChannel.Channel).AdjudicatorReq() // Store AdjudicatorReq for version 0 r.waitStage() diff --git a/client/test/updatemsgs.go b/client/test/updatemsgs.go index 676617cba..20bad50d9 100644 --- a/client/test/updatemsgs.go +++ b/client/test/updatemsgs.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -94,7 +94,7 @@ func channelUpdateAccSerializationTest(t *testing.T, serializerTest func(t *test t.Helper() rng := pkgtest.Prng(t) for i := 0; i < 4; i++ { - sig := newRandomSig(rng) + sig := newRandomSig(rng, channel.TestBackendID) m := &client.ChannelUpdateAccMsg{ ChannelID: test.NewRandomChannelID(rng), Version: uint64(rng.Int63()), @@ -121,7 +121,7 @@ func channelUpdateRejSerializationTest(t *testing.T, serializerTest func(t *test func newRandomMsgChannelUpdate(rng *rand.Rand) *client.ChannelUpdateMsg { state := test.NewRandomState(rng) - sig := newRandomSig(rng) + sig := newRandomSig(rng, channel.TestBackendID) return &client.ChannelUpdateMsg{ ChannelUpdate: client.ChannelUpdate{ State: state, @@ -133,8 +133,8 @@ func newRandomMsgChannelUpdate(rng *rand.Rand) *client.ChannelUpdateMsg { // newRandomSig generates a random account and then returns the signature on // some random data. -func newRandomSig(rng *rand.Rand) wallet.Sig { - acc := wallettest.NewRandomAccount(rng) +func newRandomSig(rng *rand.Rand, bID wallet.BackendID) wallet.Sig { + acc := wallettest.NewRandomAccount(rng, bID) maxLenOfData := 256 data := make([]byte, rng.Intn(maxLenOfData)) rng.Read(data) @@ -149,7 +149,7 @@ func newRandomSig(rng *rand.Rand) wallet.Sig { func newRandomSigs(rng *rand.Rand, n int) (a []wallet.Sig) { a = make([]wallet.Sig, n) for i := range a { - a[i] = newRandomSig(rng) + a[i] = newRandomSig(rng, channel.TestBackendID) } return } diff --git a/client/test/virtualchannel.go b/client/test/virtualchannel.go index 5d1449160..ad7888aec 100644 --- a/client/test/virtualchannel.go +++ b/client/test/virtualchannel.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -217,8 +219,12 @@ func setupVirtualChannelTest( go ingrid.Client.Handle(openingProposalHandlerIngrid, updateProposalHandlerIngrid) // Establish ledger channel between Alice and Ingrid. - peersAlice := []wire.Address{alice.Identity.Address(), ingrid.Identity.Address()} - initAllocAlice := channel.NewAllocation(len(peersAlice), asset) + peersAlice := []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(alice.Identity), wire.AddressMapfromAccountMap(ingrid.Identity)} + var bID wallet.BackendID + for i := range peersAlice[0] { + bID = i + } + initAllocAlice := channel.NewAllocation(len(peersAlice), []wallet.BackendID{bID}, asset) initAllocAlice.SetAssetBalances(asset, vct.initBalsAlice) lcpAlice, err := client.NewLedgerChannelProposal( setup.ChallengeDuration, @@ -237,8 +243,8 @@ func setupVirtualChannelTest( } // Establish ledger channel between Bob and Ingrid. - peersBob := []wire.Address{bob.Identity.Address(), ingrid.Identity.Address()} - initAllocBob := channel.NewAllocation(len(peersBob), asset) + peersBob := []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(bob.Identity), wire.AddressMapfromAccountMap(ingrid.Identity)} + initAllocBob := channel.NewAllocation(len(peersBob), []wallet.BackendID{bID}, asset) initAllocBob.SetAssetBalances(asset, vct.initBalsBob) lcpBob, err := client.NewLedgerChannelProposal( setup.ChallengeDuration, @@ -286,6 +292,7 @@ func setupVirtualChannelTest( initAllocVirtual := channel.Allocation{ Assets: []channel.Asset{asset}, Balances: [][]channel.Bal{initBalsVirtual}, + Backends: []wallet.BackendID{bID}, } indexMapAlice := []channel.Index{0, 1} indexMapBob := []channel.Index{1, 0} @@ -293,7 +300,7 @@ func setupVirtualChannelTest( setup.ChallengeDuration, alice.WalletAddress, &initAllocVirtual, - []wire.Address{alice.Identity.Address(), bob.Identity.Address()}, + []map[wallet.BackendID]wire.Address{wire.AddressMapfromAccountMap(alice.Identity), wire.AddressMapfromAccountMap(bob.Identity)}, []channel.ID{vct.chAliceIngrid.ID(), vct.chBobIngrid.ID()}, [][]channel.Index{indexMapAlice, indexMapBob}, ) diff --git a/client/update.go b/client/update.go index 363b84c0f..d0c93553d 100644 --- a/client/update.go +++ b/client/update.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -33,7 +33,7 @@ import ( // is unknown, an error is logged. // // This handler is dispatched from the Client.Handle routine. -func (c *Client) handleChannelUpdate(uh UpdateHandler, p wire.Address, m ChannelUpdateProposal) { +func (c *Client) handleChannelUpdate(uh UpdateHandler, p map[wallet.BackendID]wire.Address, m ChannelUpdateProposal) { ch, ok := c.channels.Channel(m.Base().ID()) if !ok { if !c.cacheVersion1Update(uh, p, m) { @@ -42,10 +42,10 @@ func (c *Client) handleChannelUpdate(uh UpdateHandler, p wire.Address, m Channel return } pidx := ch.Idx() ^ 1 - ch.handleUpdateReq(pidx, m, uh) //nolint:contextcheck + ch.handleUpdateReq(pidx, m, uh) } -func (c *Client) cacheVersion1Update(uh UpdateHandler, p wire.Address, m ChannelUpdateProposal) bool { +func (c *Client) cacheVersion1Update(uh UpdateHandler, p map[wallet.BackendID]wire.Address, m ChannelUpdateProposal) bool { c.version1Cache.mu.Lock() defer c.version1Cache.mu.Unlock() @@ -287,10 +287,10 @@ func (c *Channel) handleUpdateReq( // Check whether we have an update related to a virtual channel. switch prop := req.(type) { case *VirtualChannelFundingProposalMsg: - client.handleVirtualChannelFundingProposal(c, prop, responder) //nolint:contextcheck + client.handleVirtualChannelFundingProposal(c, prop, responder) return case *VirtualChannelSettlementProposalMsg: - client.handleVirtualChannelSettlementProposal(c, prop, responder) //nolint:contextcheck + client.handleVirtualChannelSettlementProposal(c, prop, responder) return } diff --git a/client/updateinterception.go b/client/updateinterception.go index 3e5ea6c67..fcbcd1bb8 100644 --- a/client/updateinterception.go +++ b/client/updateinterception.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/updatemsgs.go b/client/updatemsgs.go index 6a06f3250..e144fd014 100644 --- a/client/updatemsgs.go +++ b/client/updatemsgs.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/client/virtual_channel.go b/client/virtual_channel.go index c903fc88f..3dba3acb6 100644 --- a/client/virtual_channel.go +++ b/client/virtual_channel.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -90,7 +90,7 @@ func (c *Client) handleVirtualChannelFundingProposal( ) { err := c.validateVirtualChannelFundingProposal(ch, prop) if err != nil { - c.rejectProposal(responder, err.Error()) //nolint:contextcheck + c.rejectProposal(responder, err.Error()) } ctx, cancel := context.WithTimeout(c.Ctx(), virtualFundingTimeout) @@ -98,10 +98,10 @@ func (c *Client) handleVirtualChannelFundingProposal( err = c.fundingWatcher.Await(ctx, prop) if err != nil { - c.rejectProposal(responder, err.Error()) //nolint:contextcheck + c.rejectProposal(responder, err.Error()) } - c.acceptProposal(responder) //nolint:contextcheck + c.acceptProposal(responder) } func (c *Channel) watchVirtual() error { @@ -163,14 +163,18 @@ func (a *dummyAccount) SignData([]byte) ([]byte, error) { const hubIndex = 0 // The hub's index in a virtual channel machine. -func (c *Client) persistVirtualChannel(ctx context.Context, parent *Channel, peers []wire.Address, params channel.Params, state channel.State, sigs []wallet.Sig) (*Channel, error) { +func (c *Client) persistVirtualChannel(ctx context.Context, parent *Channel, peers []map[wallet.BackendID]wire.Address, params channel.Params, state channel.State, sigs []wallet.Sig) (*Channel, error) { cID := params.ID() if _, err := c.Channel(cID); err == nil { return nil, errors.New("channel already exists") } // We use a dummy account because we don't have the keys to the account. - ch, err := c.newChannel(&dummyAccount{params.Parts[hubIndex]}, parent, peers, params) + accs := make(map[wallet.BackendID]wallet.Account) + for i, part := range params.Parts[hubIndex] { + accs[i] = &dummyAccount{part} + } + ch, err := c.newChannel(accs, parent, peers, params) if err != nil { return nil, err } @@ -234,6 +238,7 @@ func (c *Channel) pushVirtualUpdate(ctx context.Context, state *channel.State, s return err } +//nolint:funlen func (c *Client) validateVirtualChannelFundingProposal( ch *Channel, prop *VirtualChannelFundingProposalMsg, @@ -249,15 +254,17 @@ func (c *Client) validateVirtualChannelFundingProposal( // Validate signatures. for i, sig := range prop.Initial.Sigs { - ok, err := channel.Verify( - prop.Initial.Params.Parts[i], - prop.Initial.State, - sig, - ) - if err != nil { - return err - } else if !ok { - return errors.New("invalid signature") + for _, part := range prop.Initial.Params.Parts[i] { + ok, err := channel.Verify( + part, + prop.Initial.State, + sig, + ) + if err != nil { + return err + } else if !ok { + return errors.New("invalid signature") + } } } @@ -286,6 +293,11 @@ func (c *Client) validateVirtualChannelFundingProposal( return errors.WithMessage(err, "assets do not match") } + // Assert equal backends. + if err := channel.AssertBackendsEqual(ch.state().Backends, prop.Initial.State.Backends); err != nil { + return errors.WithMessage(err, "backends do not match") + } + // 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 { @@ -349,7 +361,7 @@ func (c *Client) matchFundingProposal(ctx context.Context, a, b interface{}) boo go func() { // The context will be derived from the channel context. - err := virtual.watchVirtual() //nolint:contextcheck + err := virtual.watchVirtual() c.log.Debugf("channel %v: watcher stopped: %v", virtual.ID(), err) }() return true @@ -379,8 +391,8 @@ func (c *Client) gatherChannels(props ...*VirtualChannelFundingProposalMsg) ([]* return channels, nil } -func (c *Client) gatherPeers(channels ...*Channel) (peers []wire.Address) { - peers = make([]wire.Address, len(channels)) +func (c *Client) gatherPeers(channels ...*Channel) (peers []map[wallet.BackendID]wire.Address) { + peers = make([]map[wallet.BackendID]wire.Address, len(channels)) for i, ch := range channels { chPeers := ch.Peers() if len(chPeers) != gatherNumPeers { diff --git a/client/virtual_channel_settlement.go b/client/virtual_channel_settlement.go index 71abb0880..b55ccc2fb 100644 --- a/client/virtual_channel_settlement.go +++ b/client/virtual_channel_settlement.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -79,7 +79,7 @@ func (c *Client) handleVirtualChannelSettlementProposal( ) { err := c.validateVirtualChannelSettlementProposal(parent, prop) if err != nil { - c.rejectProposal(responder, err.Error()) //nolint:contextcheck + c.rejectProposal(responder, err.Error()) } ctx, cancel := context.WithTimeout(c.Ctx(), virtualSettlementTimeout) @@ -90,7 +90,7 @@ func (c *Client) handleVirtualChannelSettlementProposal( resp: responder, }) if err != nil { - c.rejectProposal(responder, err.Error()) //nolint:contextcheck + c.rejectProposal(responder, err.Error()) } } @@ -105,15 +105,17 @@ func (c *Client) validateVirtualChannelSettlementProposal( // Validate signatures. for i, sig := range prop.Final.Sigs { - ok, err := channel.Verify( - prop.Final.Params.Parts[i], - prop.Final.State, - sig, - ) - if err != nil { - return err - } else if !ok { - return errors.New("invalid signature") + for _, p := range prop.Final.Params.Parts[i] { + ok, err := channel.Verify( + p, + prop.Final.State, + sig, + ) + if err != nil { + return err + } else if !ok { + return errors.New("invalid signature") + } } } diff --git a/client/virtual_channel_test.go b/client/virtual_channel_test.go index 4e8eda21e..7ed87f700 100644 --- a/client/virtual_channel_test.go +++ b/client/virtual_channel_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,13 +21,15 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + chtest "perun.network/go-perun/channel/test" ctest "perun.network/go-perun/client/test" "polycry.pt/poly-go/test" ) const ( - challengeDuration = 10 + challengeDuration = 15 testDuration = 10 * time.Second ) @@ -53,7 +55,7 @@ func makeVirtualChannelSetup(rng *rand.Rand) ctest.VirtualChannelSetup { return ctest.VirtualChannelSetup{ Clients: createVirtualChannelClients(rng), ChallengeDuration: challengeDuration, - Asset: chtest.NewRandomAsset(rng), + Asset: chtest.NewRandomAsset(rng, channel.TestBackendID), Balances: ctest.VirtualChannelBalances{ InitBalsAliceIngrid: []*big.Int{big.NewInt(10), big.NewInt(10)}, InitBalsBobIngrid: []*big.Int{big.NewInt(10), big.NewInt(10)}, @@ -70,7 +72,7 @@ func makeVirtualChannelSetup(rng *rand.Rand) ctest.VirtualChannelSetup { func createVirtualChannelClients(rng *rand.Rand) [3]ctest.RoleSetup { var setupsArray [3]ctest.RoleSetup - setups := NewSetups(rng, []string{"Alice", "Bob", "Ingrid"}) + setups := NewSetups(rng, []string{"Alice", "Bob", "Ingrid"}, channel.TestBackendID) copy(setupsArray[:], setups) return setupsArray } diff --git a/go.sum b/go.sum index 22c350b7c..021d88d67 100644 --- a/go.sum +++ b/go.sum @@ -18,7 +18,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw 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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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= @@ -98,7 +97,6 @@ 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= diff --git a/log/logrus/logrus_internal_test.go b/log/logrus/logrus_internal_test.go index e95884423..bcfc462c9 100644 --- a/log/logrus/logrus_internal_test.go +++ b/log/logrus/logrus_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -30,6 +30,9 @@ import ( pkgtest "polycry.pt/poly-go/test" ) +// TestBackendID is the identifier for the simulated Backend. +const TestBackendID = 0 + func TestLogrus(t *testing.T) { t.Run("Info", testLogrusInfo) t.Run("Stringer", testLogrusStringer) @@ -49,7 +52,7 @@ func testLogrusInfo(t *testing.T) { func testLogrusStringer(t *testing.T) { rng := pkgtest.Prng(t) - addr := wtest.NewRandomAddress(rng) + addr := wtest.NewRandomAddress(rng, TestBackendID) var data [32]byte rng.Read(data[:]) logger, hook := test.NewNullLogger() diff --git a/wallet/address.go b/wallet/address.go index ec89873e1..599e671f5 100644 --- a/wallet/address.go +++ b/wallet/address.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -27,6 +27,9 @@ import ( "perun.network/go-perun/wire/perunio" ) +// BackendID is a unique identifier for a backend. +type BackendID int + // Address represents a identifier used in a cryptocurrency. // It is dependent on the currency and needs to be implemented for every blockchain. type Address interface { @@ -42,14 +45,37 @@ type Address interface { // Equal returns wether the two addresses are equal. The implementation // must be equivalent to checking `Address.Cmp(Address) == 0`. Equal(Address) bool + // BackendID returns the id of the backend that created this address. + BackendID() BackendID +} + +// Equal compares two BackendIDs for equality. +func (a *BackendID) Equal(b BackendID) bool { + return *a == b } // IndexOfAddr returns the index of the given address in the address slice, // or -1 if it is not part of the slice. -func IndexOfAddr(addrs []Address, addr Address) int { - for i, a := range addrs { - if addr.Equal(a) { - return i +func IndexOfAddr(addrs []map[BackendID]Address, addr Address) int { + for i, as := range addrs { + for _, a := range as { + if a.Equal(addr) { + return i + } + } + } + + return -1 +} + +// IndexOfAddrs returns the index of the given address in the address slice, +// or -1 if it is not part of the slice. +func IndexOfAddrs(addrs []map[BackendID]Address, addr map[BackendID]Address) int { + for i, as := range addrs { + for j, a := range as { + if a.Equal(addr[j]) { + return i + } } } @@ -64,7 +90,7 @@ func CloneAddress(a Address) Address { log.WithError(err).Panic("error binary-marshaling Address") } - clone := NewAddress() + clone := NewAddress(a.BackendID()) if err := clone.UnmarshalBinary(data); err != nil { log.WithError(err).Panic("error binary-unmarshaling Address") } @@ -82,76 +108,97 @@ func CloneAddresses(as []Address) []Address { return clones } -// Addresses is a helper type for encoding and decoding address slices in -// situations where the length of the slice is known. -type Addresses []Address - -// AddressesWithLen is a helper type for encoding and decoding address slices -// of unknown length. -type AddressesWithLen []Address - -// addressSliceLen is needed to break the import cycle with channel. It should -// be the same as channel.Index. -type addressSliceLen = uint16 +// CloneAddressesMap returns a clone of a map of Addresses using their binary +// marshaling implementation. It panics if an error occurs during binary +// (un)marshaling. +func CloneAddressesMap(as map[BackendID]Address) map[BackendID]Address { + clones := make(map[BackendID]Address) + for i, a := range as { + clones[i] = CloneAddress(a) + } + return clones +} -// AddressDec is a helper type to decode single wallet addresses. -type AddressDec struct { - Addr *Address +// AddressMapArray is a helper type for encoding and decoding arrays of address maps. +type AddressMapArray struct { + Addr []map[BackendID]Address } +// AddressDecMap is a helper type for encoding and decoding address maps. +type AddressDecMap map[BackendID]Address + // AddrKey is a non-human readable representation of an `Address`. // It can be compared and therefore used as a key in a map. type AddrKey string -// Encode encodes a wallet address slice, the length of which is known to the -// following decode operation. -func (a Addresses) Encode(w stdio.Writer) error { +// 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)) + 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 { + return errors.WithMessage(err, "encoding map index") + } if err := perunio.Encode(w, addr); err != nil { - return errors.WithMessagef(err, "encoding %d-th address", i) + return errors.WithMessagef(err, "encoding %d-th address map entry", i) } } - return nil } -// Encode encodes a wallet address slice, the length of which is unknown to the -// following decode operation. -func (a AddressesWithLen) Encode(w stdio.Writer) error { - return perunio.Encode(w, - addressSliceLen(len(a)), - (Addresses)(a)) -} - -// Decode decodes a wallet address slice of known length. The slice has to be -// allocated to the correct size already. -func (a Addresses) Decode(r stdio.Reader) (err error) { - for i := range a { - a[i] = NewAddress() - err = perunio.Decode(r, a[i]) - if err != nil { - return errors.WithMessagef(err, "decoding %d-th address", i) +// 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)) + if err := perunio.Encode(w, length); 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 { + return errors.WithMessagef(err, "encoding %d-th address array entry", i) } } return nil } -// Decode decodes a wallet address slice of unknown length. -func (a *AddressesWithLen) Decode(r stdio.Reader) (err error) { - var parts addressSliceLen - if err = perunio.Decode(r, &parts); err != nil { - return errors.WithMessage(err, "decoding count") +// 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 { + return errors.WithMessage(err, "decoding map length") } - - *a = make(AddressesWithLen, parts) - return (*Addresses)(a).Decode(r) + *a = make(map[BackendID]Address, mapLen) + for i := 0; i < int(mapLen); i++ { + var idx int32 + if err := perunio.Decode(r, &idx); err != nil { + return errors.WithMessage(err, "decoding map index") + } + addr := NewAddress(BackendID(idx)) + if err := perunio.Decode(r, addr); err != nil { + return errors.WithMessagef(err, "decoding %d-th address map entry", i) + } + (*a)[BackendID(idx)] = addr + } + return nil } -// Decode decodes a single wallet address. -func (a AddressDec) Decode(r stdio.Reader) (err error) { - *a.Addr = NewAddress() - err = perunio.Decode(r, *a.Addr) - return err +// 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 { + return errors.WithMessage(err, "decoding array length") + } + a.Addr = make([]map[BackendID]Address, mapLen) + for i := 0; i < int(mapLen); i++ { + if err := perunio.Decode(r, (*AddressDecMap)(&a.Addr[i])); err != nil { + return errors.WithMessagef(err, "decoding %d-th address map entry", i) + } + } + return nil } // Key returns the `AddrKey` corresponding to the passed `Address`. @@ -159,6 +206,9 @@ func (a AddressDec) 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 { + panic("Could not encode id in AddrKey: " + err.Error()) + } if err := perunio.Encode(&buff, a); err != nil { panic("Could not encode address in AddrKey: " + err.Error()) } @@ -169,10 +219,16 @@ func Key(a Address) AddrKey { // created by `Key`. // Panics when the `Address` can't be decoded. func FromKey(k AddrKey) Address { - a := NewAddress() - err := perunio.Decode(bytes.NewBuffer([]byte(k)), a) + 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 in FromKey: " + err.Error()) + panic("Could not decode address: " + err.Error()) } return a } @@ -180,5 +236,6 @@ func FromKey(k AddrKey) Address { // Equal Returns whether the passed `Address` has the same key as the // receiving `AddrKey`. func (k AddrKey) Equal(a Address) bool { - return k == Key(a) + key := Key(a) + return k == key } diff --git a/wallet/address_test.go b/wallet/address_test.go index afb536e1d..b41950989 100644 --- a/wallet/address_test.go +++ b/wallet/address_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -27,8 +27,11 @@ import ( pkgtest "polycry.pt/poly-go/test" ) +// TestBackendID is the identifier for the simulated Backend. +const TestBackendID = wallet.BackendID(0) + type testAddresses struct { - addrs wallet.AddressesWithLen + addrs wallet.AddressMapArray } func (t *testAddresses) Encode(w io.Writer) error { @@ -39,22 +42,36 @@ func (t *testAddresses) Decode(r io.Reader) error { return t.addrs.Decode(r) } +type testAddress struct { + addrs wallet.AddressDecMap +} + +func (t *testAddress) Encode(w io.Writer) error { + return t.addrs.Encode(w) +} + +func (t *testAddress) Decode(r io.Reader) error { + return t.addrs.Decode(r) +} + func TestAddresses_Serializer(t *testing.T) { rng := pkgtest.Prng(t) + addr := wallettest.NewRandomAddressesMap(rng, 1, TestBackendID)[0] + peruniotest.GenericSerializerTest(t, &testAddress{addrs: addr}) - addrs := wallettest.NewRandomAddresses(rng, 0) - peruniotest.GenericSerializerTest(t, &testAddresses{addrs}) + addrs := wallettest.NewRandomAddressesMap(rng, 0, TestBackendID) + peruniotest.GenericSerializerTest(t, &testAddresses{addrs: wallet.AddressMapArray{Addr: addrs}}) - addrs = wallettest.NewRandomAddresses(rng, 1) - peruniotest.GenericSerializerTest(t, &testAddresses{addrs}) + addrs = wallettest.NewRandomAddressesMap(rng, 1, TestBackendID) + peruniotest.GenericSerializerTest(t, &testAddresses{addrs: wallet.AddressMapArray{Addr: addrs}}) - addrs = wallettest.NewRandomAddresses(rng, 5) - peruniotest.GenericSerializerTest(t, &testAddresses{addrs}) + addrs = wallettest.NewRandomAddressesMap(rng, 5, TestBackendID) + peruniotest.GenericSerializerTest(t, &testAddresses{addrs: wallet.AddressMapArray{Addr: addrs}}) } func TestAddrKey_Equal(t *testing.T) { rng := pkgtest.Prng(t) - addrs := wallettest.NewRandomAddresses(rng, 10) + addrs := wallettest.NewRandomAddressArray(rng, 10, TestBackendID) // Test all properties of an equivalence relation. for i, a := range addrs { @@ -79,7 +96,7 @@ func TestAddrKey_Equal(t *testing.T) { func TestAddrKey(t *testing.T) { rng := pkgtest.Prng(t) - addrs := wallettest.NewRandomAddresses(rng, 10) + addrs := wallettest.NewRandomAddressArray(rng, 10, TestBackendID) for _, a := range addrs { // Test that Key and FromKey are dual to each other. @@ -91,7 +108,7 @@ func TestAddrKey(t *testing.T) { func TestCloneAddress(t *testing.T) { rng := pkgtest.Prng(t) - addr := wallettest.NewRandomAddress(rng) + addr := wallettest.NewRandomAddress(rng, TestBackendID) addr0 := wallet.CloneAddress(addr) require.Equal(t, addr, addr0) require.NotSame(t, addr, addr0) @@ -99,7 +116,7 @@ func TestCloneAddress(t *testing.T) { func TestCloneAddresses(t *testing.T) { rng := pkgtest.Prng(t) - addrs := wallettest.NewRandomAddresses(rng, 3) + addrs := wallettest.NewRandomAddressArray(rng, 3, TestBackendID) addrs0 := wallet.CloneAddresses(addrs) require.Equal(t, addrs, addrs0) require.NotSame(t, addrs, addrs0) diff --git a/wallet/backend.go b/wallet/backend.go index 43565fc78..6839a4a52 100644 --- a/wallet/backend.go +++ b/wallet/backend.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -15,12 +15,13 @@ package wallet import ( + "fmt" "io" ) // backend is set to the global wallet backend. Must not be set directly but // through importing the needed backend. -var backend Backend +var backend map[BackendID]Backend // Backend provides useful methods for this blockchain. type Backend interface { @@ -40,25 +41,36 @@ type Backend interface { // SetBackend sets the global wallet backend. Must not be called directly but // through importing the needed backend. -func SetBackend(b Backend) { - if backend != nil { +func SetBackend(b Backend, id int) { + if backend == nil { + backend = make(map[BackendID]Backend) + } + if backend[BackendID(id)] != nil { panic("wallet backend already set") } - backend = b + backend[BackendID(id)] = b } // NewAddress returns a variable of type Address, which can be used // for unmarshalling an address from its binary representation. -func NewAddress() Address { - return backend.NewAddress() +func NewAddress(id BackendID) Address { + return backend[id].NewAddress() } -// DecodeSig calls DecodeSig of the current backend. +// DecodeSig calls DecodeSig of all Backends and returns an error if none return a valid signature. func DecodeSig(r io.Reader) (Sig, error) { - return backend.DecodeSig(r) + var err error + for _, b := range backend { + sig, err := b.DecodeSig(r) + if err == nil { + return sig, nil + } + } + + return nil, fmt.Errorf("no valid signature found: %v", err) } // VerifySignature calls VerifySignature of the current backend. func VerifySignature(msg []byte, sign Sig, a Address) (bool, error) { - return backend.VerifySignature(msg, sign, a) + return backend[a.BackendID()].VerifySignature(msg, sign, a) } diff --git a/wallet/sig.go b/wallet/sig.go index 98a0375ec..0bc435a34 100644 --- a/wallet/sig.go +++ b/wallet/sig.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -47,7 +47,8 @@ const bitsPerByte = 8 // SigDec is a helper type to decode signatures. type SigDec struct { - Sig *Sig + Sig *Sig + BackendID int } // Decode decodes a single signature. diff --git a/wallet/test/randomizer.go b/wallet/test/randomizer.go index 4171d120b..a244d6f6b 100644 --- a/wallet/test/randomizer.go +++ b/wallet/test/randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -51,59 +51,95 @@ type ( // randomizer is the currently set wallet testing randomizer. It is initially set to // the default randomizer. -var randomizer Randomizer +var randomizer map[wallet.BackendID]Randomizer // SetRandomizer sets the wallet randomizer. It may be set multiple times. -func SetRandomizer(b Randomizer) { - if randomizer != nil { +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") } - randomizer = b + randomizer[bID] = b } // NewRandomAddress returns a new random address by calling the currently set // wallet randomizer. -func NewRandomAddress(rng *rand.Rand) wallet.Address { - return randomizer.NewRandomAddress(rng) +func NewRandomAddress(rng *rand.Rand, bID wallet.BackendID) wallet.Address { + return randomizer[bID].NewRandomAddress(rng) +} + +// NewRandomAddresses returns a new random address by calling the currently set +// wallet randomizer. +func NewRandomAddresses(rng *rand.Rand, bID wallet.BackendID) map[wallet.BackendID]wallet.Address { + return map[wallet.BackendID]wallet.Address{bID: randomizer[bID].NewRandomAddress(rng)} } // RandomWallet returns the randomizer backend's wallet. All accounts created // with NewRandomAccount can be found in this wallet. -func RandomWallet() Wallet { - return randomizer.RandomWallet() +func RandomWallet(bID wallet.BackendID) Wallet { + return randomizer[bID].RandomWallet() } // NewRandomAccount returns a new random account by calling the currently set // wallet randomizer. The account is generated from the randomizer wallet // available via RandomWallet. It should already be unlocked. -func NewRandomAccount(rng *rand.Rand) wallet.Account { - return randomizer.RandomWallet().NewRandomAccount(rng) +func NewRandomAccount(rng *rand.Rand, bID wallet.BackendID) wallet.Account { + return randomizer[bID].RandomWallet().NewRandomAccount(rng) } // NewWallet returns a fresh, temporary Wallet for testing purposes that doesn't // hold any accounts yet. New random accounts can be generated using method // NewRandomAccount. -func NewWallet() Wallet { - return randomizer.NewWallet() +func NewWallet(bID wallet.BackendID) Wallet { + return randomizer[bID].NewWallet() } // NewRandomAccounts returns a slice of new random accounts // by calling NewRandomAccount. -func NewRandomAccounts(rng *rand.Rand, n int) ([]wallet.Account, []wallet.Address) { - accs := make([]wallet.Account, n) - addrs := make([]wallet.Address, n) +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] = NewRandomAccount(rng) - addrs[i] = accs[i].Address() + accs[i] = map[wallet.BackendID]wallet.Account{bID: NewRandomAccount(rng, bID)} + addrs[i] = map[wallet.BackendID]wallet.Address{bID: accs[i][bID].Address()} } return accs, addrs } -// NewRandomAddresses returns a slice of new random addresses. -func NewRandomAddresses(rng *rand.Rand, n int) []wallet.Address { +// NewRandomAccountMapSlice returns a slice of new random accounts map +// by calling NewRandomAccount. +func NewRandomAccountMapSlice(rng *rand.Rand, bID wallet.BackendID, n int) []map[wallet.BackendID]wallet.Account { + accs := make([]map[wallet.BackendID]wallet.Account, n) + for i := range accs { + accs[i] = map[wallet.BackendID]wallet.Account{bID: NewRandomAccount(rng, bID)} + } + return accs +} + +// NewRandomAccountMap returns a slice of new random accounts +// by calling NewRandomAccount. +func NewRandomAccountMap(rng *rand.Rand, bID wallet.BackendID) map[wallet.BackendID]wallet.Account { + accs := make(map[wallet.BackendID]wallet.Account) + accs[bID] = NewRandomAccount(rng, bID) + return accs +} + +// NewRandomAddressArray returns a slice of new random addresses. +func NewRandomAddressArray(rng *rand.Rand, n int, bID wallet.BackendID) []wallet.Address { addrs := make([]wallet.Address, n) for i := range addrs { - addrs[i] = NewRandomAddress(rng) + addrs[i] = NewRandomAddress(rng, bID) + } + return addrs +} + +// NewRandomAddressesMap returns a slice of new random address maps. +func NewRandomAddressesMap(rng *rand.Rand, n int, bID wallet.BackendID) []map[wallet.BackendID]wallet.Address { + addrs := make([]map[wallet.BackendID]wallet.Address, n) + for i := range addrs { + addrs[i] = NewRandomAddresses(rng, bID) } return addrs } diff --git a/wallet/test/wallet.go b/wallet/test/wallet.go index 83d469076..be65912d8 100644 --- a/wallet/test/wallet.go +++ b/wallet/test/wallet.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -49,7 +49,7 @@ func TestAccountWithWalletAndBackend(t *testing.T, s *Setup) { //nolint:revive / // Check unlocked account sig, err := acc.SignData(s.DataToSign) assert.NoError(t, err, "Sign with unlocked account should succeed") - valid, err := s.Backend.VerifySignature(s.DataToSign, sig, acc.Address()) + 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") @@ -64,20 +64,20 @@ 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, acc.Address()) + 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, acc.Address()) + 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") } // Expand the signature and check for error //nolint:gocritic tampered = append(sig, 0) - valid, err = s.Backend.VerifySignature(s.DataToSign, tampered, acc.Address()) + 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") } diff --git a/wallet/test/walletbench.go b/wallet/test/walletbench.go index 5ba272bae..406afbbf1 100644 --- a/wallet/test/walletbench.go +++ b/wallet/test/walletbench.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -53,7 +53,7 @@ func GenericBackendBenchmark(b *testing.B, s *Setup) { func benchBackendVerifySig(b *testing.B, s *Setup) { b.Helper() - // We dont want to measure the SignDataWithPW here, just need it for the verification + // We do not want to measure the SignDataWithPW here, just need it for the verification b.StopTimer() perunAcc, err := s.Wallet.Unlock(s.AddressInWallet) require.NoError(b, err) diff --git a/watcher/internal/mocks/RegisterSubscriber.go b/watcher/internal/mocks/RegisterSubscriber.go index cd0a1b57a..47f07bdfe 100644 --- a/watcher/internal/mocks/RegisterSubscriber.go +++ b/watcher/internal/mocks/RegisterSubscriber.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.12.1. DO NOT EDIT. +// Code generated by mockery v2.45.0. DO NOT EDIT. package mocks diff --git a/watcher/local/adjudicatorpubsub.go b/watcher/local/adjudicatorpubsub.go index 6f94a9571..abf1e50ac 100644 --- a/watcher/local/adjudicatorpubsub.go +++ b/watcher/local/adjudicatorpubsub.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -49,13 +49,13 @@ func newAdjudicatorEventsPubSub() *adjudicatorPubSub { // // Panics if the pub-sub instance is already closed. It is implemented this // way, because -// 1. The watcher will publish on this pub-sub only when it receives an -// adjudicator event from the blockchain. -// 2. When de-registering a channel from the watcher, watcher will close the -// subscription for adjudicator events from blockchain, before closing this -// pub-sub. -// 3. This way, it can be guaranteed that, this method will never be called -// after the pub-sub instance is closed. +// 1. The watcher will publish on this pub-sub only when it receives an +// adjudicator event from the blockchain. +// 2. When de-registering a channel from the watcher, watcher will close the +// subscription for adjudicator events from blockchain, before closing this +// pub-sub. +// 3. This way, it can be guaranteed that, this method will never be called +// after the pub-sub instance is closed. func (a *adjudicatorPubSub) publish(e channel.AdjudicatorEvent) { a.pipe <- e } diff --git a/watcher/local/registry.go b/watcher/local/registry.go index 49d87c14c..a42f84eeb 100644 --- a/watcher/local/registry.go +++ b/watcher/local/registry.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/watcher/local/statespubsub.go b/watcher/local/statespubsub.go index 90ef993fd..270787e75 100644 --- a/watcher/local/statespubsub.go +++ b/watcher/local/statespubsub.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -53,11 +53,11 @@ func newStatesPubSub() *statesPubSub { // // Panics if the pub-sub instance is already closed. It is implemented this // way, because -// 1. Watcher requires that, the Publish method must not be called after stop -// watching for a channel. See docs of watcher.StatesPub for more details. -// 2. Hence, by properly integrating the watcher into the client, it can be -// guaranteed that, this method will never be called after the pub-sub -// instance is closed and that, this method will never panic. +// 1. Watcher requires that, the Publish method must not be called after stop +// watching for a channel. See docs of watcher.StatesPub for more details. +// 2. Hence, by properly integrating the watcher into the client, it can be +// guaranteed that, this method will never be called after the pub-sub +// instance is closed and that, this method will never panic. func (s *statesPubSub) Publish(_ context.Context, tx channel.Transaction) error { s.pipe <- tx return nil diff --git a/watcher/local/watcher.go b/watcher/local/watcher.go index b74de505f..7d2dd3515 100644 --- a/watcher/local/watcher.go +++ b/watcher/local/watcher.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -186,7 +186,7 @@ func (w *Watcher) startWatching( var statesPubSub *statesPubSub var eventsToClientPubSub *adjudicatorPubSub - chInitializer := func() (*ch, error) { + chInitializer1 := func() (*ch, error) { eventsFromChainSub, err := w.rs.Subscribe(ctx, id) if err != nil { return nil, errors.WithMessage(err, "subscribing to adjudicator events from blockchain") @@ -197,7 +197,7 @@ func (w *Watcher) startWatching( return newCh(id, parent, signedState.Params, eventsFromChainSub, eventsToClientPubSub, statesPubSub, multiLedger), nil } - ch, err := w.registry.addIfSucceeds(id, chInitializer) + ch, err := w.registry.addIfSucceeds(id, chInitializer1) if err != nil { return nil, nil, err } @@ -206,7 +206,7 @@ func (w *Watcher) startWatching( Sigs: signedState.Sigs, } ch.Go(func() { ch.handleStatesFromClient(initialTx) }) - ch.Go(func() { ch.handleEventsFromChain(w.rs, w.registry) }) //nolint:contextcheck + ch.Go(func() { ch.handleEventsFromChain(w.rs, w.registry) }) return statesPubSub, eventsToClientPubSub, nil } diff --git a/watcher/local/watcher_test.go b/watcher/local/watcher_test.go index fc6872b3f..978defe45 100644 --- a/watcher/local/watcher_test.go +++ b/watcher/local/watcher_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -774,7 +774,6 @@ 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() } diff --git a/watcher/watcher.go b/watcher/watcher.go index ce528bae0..314e4fbf9 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/wire/account.go b/wire/account.go index d8e2dc908..f458aa143 100644 --- a/wire/account.go +++ b/wire/account.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "encoding/binary" "fmt" "io" + + "perun.network/go-perun/wallet" ) func init() { @@ -80,12 +82,21 @@ func (m *AuthResponseMsg) Decode(r io.Reader) (err error) { } // NewAuthResponseMsg creates an authentication response message. -func NewAuthResponseMsg(acc Account) (Msg, error) { - addressBytes, err := acc.Address().MarshalBinary() - if err != nil { - return nil, fmt.Errorf("failed to marshal address: %w", err) +func NewAuthResponseMsg(acc map[wallet.BackendID]Account) (Msg, error) { + addressMap := make(map[wallet.BackendID]Address) + for id, a := range acc { + addressMap[id] = a.Address() + } + var addressBytes []byte + addressBytes = append(addressBytes, byte(len(addressMap))) + for _, addr := range addressMap { + addrBytes, err := addr.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("failed to marshal address: %w", err) + } + addressBytes = append(addressBytes, addrBytes...) } - signature, err := acc.Sign(addressBytes) + signature, err := acc[0].Sign(addressBytes) if err != nil { return nil, fmt.Errorf("failed to sign address: %w", err) } diff --git a/wire/address.go b/wire/address.go index de48ab1ec..3d28c86be 100644 --- a/wire/address.go +++ b/wire/address.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,15 +17,18 @@ package wire import ( "encoding" stdio "io" + "sort" "strings" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire/perunio" ) var ( - _ perunio.Serializer = (*Addresses)(nil) - _ perunio.Serializer = (*AddressesWithLen)(nil) + _ perunio.Serializer = (*AddressDecMap)(nil) + _ perunio.Serializer = (*AddressMapArray)(nil) ) // Address is a Perun node's network address, which is used as a permanent @@ -46,55 +49,80 @@ type Address interface { Verify(msg []byte, sig []byte) error } -// Addresses is a helper type for encoding and decoding address slices in -// situations where the length of the slice is known. -type Addresses []Address - -// AddressesWithLen is a helper type for encoding and decoding address slices -// of unknown length. -type AddressesWithLen []Address +// AddressMapArray is a helper type for encoding and decoding address maps. +type AddressMapArray []map[wallet.BackendID]Address -type addressSliceLen = uint16 +// AddressDecMap is a helper type for encoding and decoding arrays of address maps. +type AddressDecMap map[wallet.BackendID]Address -// Encode encodes wire addresses. -func (a Addresses) Encode(w stdio.Writer) error { +// 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)) + 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 { + return errors.WithMessage(err, "encoding map index") + } if err := perunio.Encode(w, addr); err != nil { - return errors.WithMessagef(err, "encoding %d-th address", i) + return errors.WithMessagef(err, "encoding %d-th address map entry", i) } } - return nil } -// Encode encodes wire addresses with length. -func (a AddressesWithLen) Encode(w stdio.Writer) error { - return perunio.Encode(w, - addressSliceLen(len(a)), - (Addresses)(a)) +// 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)) + if err := perunio.Encode(w, length); 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) + } + } + return nil } -// Decode decodes wallet addresses. -func (a Addresses) Decode(r stdio.Reader) error { - for i := range a { - a[i] = NewAddress() - err := perunio.Decode(r, a[i]) - if err != nil { - return errors.WithMessagef(err, "decoding %d-th address", i) +// 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 { + return errors.WithMessage(err, "decoding map length") + } + *a = make(map[wallet.BackendID]Address, mapLen) + for i := 0; i < int(mapLen); i++ { + var idx int32 + if err := perunio.Decode(r, &idx); err != nil { + return errors.WithMessage(err, "decoding map index") + } + addr := NewAddress() + if err := perunio.Decode(r, addr); err != nil { + return errors.WithMessagef(err, "decoding %d-th address map entry", i) } + (*a)[wallet.BackendID(idx)] = addr } return nil } -// Decode decodes a wallet address slice of unknown length. -func (a *AddressesWithLen) Decode(r stdio.Reader) error { - var n addressSliceLen - if err := perunio.Decode(r, &n); err != nil { - return errors.WithMessage(err, "decoding count") +// Decode decodes the array length first, then all AddressDecMaps in the array. +// 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 { + return errors.WithMessage(err, "decoding array length") } - - *a = make(AddressesWithLen, n) - return (*Addresses)(a).Decode(r) + *a = make([]map[wallet.BackendID]Address, mapLen) + for i := 0; i < int(mapLen); i++ { + if err := perunio.Decode(r, (*AddressDecMap)(&(*a)[i])); err != nil { + return errors.WithMessagef(err, "decoding %d-th address map entry", i) + } + } + return nil } // IndexOfAddr returns the index of the given address in the address slice, @@ -109,6 +137,30 @@ func IndexOfAddr(addrs []Address, addr Address) int { return -1 } +// IndexOfAddrs returns the index of the given address in the address slice, +// or -1 if it is not part of the slice. +func IndexOfAddrs(addrs []map[wallet.BackendID]Address, addr map[wallet.BackendID]Address) int { + for i, a := range addrs { + if addrEqual(a, addr) { + return i + } + } + + return -1 +} + +func addrEqual(a, b map[wallet.BackendID]Address) bool { + if len(a) != len(b) { + return false + } + for i, addr := range a { + if !addr.Equal(b[i]) { + return false + } + } + return true +} + // AddrKey is a non-human readable representation of an `Address`. // It can be compared and therefore used as a key in a map. type AddrKey string @@ -123,3 +175,28 @@ func Key(a Address) AddrKey { } return AddrKey(buff.String()) } + +// Keys returns the `AddrKey` corresponding to the passed `map[wallet.BackendID]Address`. +func Keys(addressMap map[wallet.BackendID]Address) AddrKey { + var indexes []int //nolint:prealloc + for i := range addressMap { + indexes = append(indexes, int(i)) + } + sort.Ints(indexes) + + keyParts := make([]string, len(indexes)) + for i, index := range indexes { + key := Key(addressMap[wallet.BackendID(index)]) + keyParts[i] = string(key) + } + return AddrKey(strings.Join(keyParts, "|")) +} + +// AddressMapfromAccountMap converts a map of accounts to a map of addresses. +func AddressMapfromAccountMap(accs map[wallet.BackendID]Account) map[wallet.BackendID]Address { + addresses := make(map[wallet.BackendID]Address) + for id, a := range accs { + addresses[id] = a.Address() + } + return addresses +} diff --git a/wire/bus.go b/wire/bus.go index e19c4cb2a..265ebfeb0 100644 --- a/wire/bus.go +++ b/wire/bus.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -14,6 +14,8 @@ package wire +import "perun.network/go-perun/wallet" + // A Bus is a central message bus over which all clients of a channel network // communicate. It is used as the transport layer abstraction for the // client.Client. @@ -22,5 +24,5 @@ type Bus interface { // SubscribeClient should route all messages with clientAddr as recipient to // the provided Consumer. Every address may only be subscribed to once. - SubscribeClient(c Consumer, clientAddr Address) error + SubscribeClient(c Consumer, clientAddr map[wallet.BackendID]Address) error } diff --git a/wire/encode.go b/wire/encode.go index eec724d2b..11145f0cf 100644 --- a/wire/encode.go +++ b/wire/encode.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "io" "strconv" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire/perunio" @@ -35,8 +37,8 @@ type ( // An Envelope encapsulates a message with routing information, that is, the // sender and intended recipient. Envelope struct { - Sender Address // Sender of the message. - Recipient Address // Recipient of the message. + Sender map[wallet.BackendID]Address // Sender of the message. + Recipient map[wallet.BackendID]Address // Recipient of the message. // Msg contained in this Envelope. Not embedded so Envelope doesn't implement Msg. Msg Msg } diff --git a/wire/hybridbus.go b/wire/hybridbus.go index 18569860b..6ac59421a 100644 --- a/wire/hybridbus.go +++ b/wire/hybridbus.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "context" "fmt" + "perun.network/go-perun/wallet" + "polycry.pt/poly-go/errors" "polycry.pt/poly-go/sync/atomic" ) @@ -88,7 +90,7 @@ func (b *hybridBus) Publish(ctx context.Context, e *Envelope) error { } // SubscribeClient subscribes an envelope consumer to all sub-buses. -func (b *hybridBus) SubscribeClient(c Consumer, receiver Address) error { +func (b *hybridBus) SubscribeClient(c Consumer, receiver map[wallet.BackendID]Address) error { errg := errors.NewGatherer() for _, bus := range b.buses { errg.Add(bus.SubscribeClient(c, receiver)) diff --git a/wire/hybridbus_test.go b/wire/hybridbus_test.go index 32d7f6c77..152d17065 100644 --- a/wire/hybridbus_test.go +++ b/wire/hybridbus_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package wire_test import ( "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/require" . "perun.network/go-perun/wire" @@ -41,7 +43,7 @@ func TestHybridBus(t *testing.T) { hybridBus := NewHybridBus(buses...) i := 0 - test.GenericBusTest(t, func(Account) (pub Bus, sub Bus) { + test.GenericBusTest(t, func(map[wallet.BackendID]Account) (pub Bus, sub Bus) { i++ // Split the clients evenly among the sub-buses, and let them publish // over the hybrid bus. @@ -51,7 +53,7 @@ func TestHybridBus(t *testing.T) { func TestHybridBus_Single(t *testing.T) { hybridBus := NewHybridBus(NewLocalBus()) - test.GenericBusTest(t, func(Account) (Bus, Bus) { + test.GenericBusTest(t, func(map[wallet.BackendID]Account) (Bus, Bus) { return hybridBus, hybridBus }, 16, 10) } diff --git a/wire/localbus.go b/wire/localbus.go index 499c24c41..cd84ba795 100644 --- a/wire/localbus.go +++ b/wire/localbus.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,8 @@ import ( "context" "sync" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/log" @@ -58,7 +60,7 @@ func (h *LocalBus) Publish(ctx context.Context, e *Envelope) error { // SubscribeClient implements wire.Bus.SubscribeClient. There can only be one // subscription per receiver address. // When the Consumer closes, its subscription is removed. -func (h *LocalBus) SubscribeClient(c Consumer, receiver Address) error { +func (h *LocalBus) SubscribeClient(c Consumer, receiver map[wallet.BackendID]Address) error { recv := h.ensureRecv(receiver) recv.recv = c close(recv.exists) @@ -66,7 +68,7 @@ func (h *LocalBus) SubscribeClient(c Consumer, receiver Address) error { c.OnCloseAlways(func() { h.mutex.Lock() defer h.mutex.Unlock() - delete(h.recvs, Key(receiver)) + delete(h.recvs, Keys(receiver)) log.WithField("id", receiver).Debug("Client unsubscribed.") }) @@ -77,8 +79,8 @@ func (h *LocalBus) SubscribeClient(c Consumer, receiver Address) error { // ensureRecv ensures that there is an entry for a recipient address in the // bus' receiver map, and returns it. If it creates a new receiver, it is only // a placeholder until a subscription appears. -func (h *LocalBus) ensureRecv(a Address) *localBusReceiver { - key := Key(a) +func (h *LocalBus) ensureRecv(a map[wallet.BackendID]Address) *localBusReceiver { + key := Keys(a) // First, we only use a read lock, hoping that the receiver already exists. h.mutex.RLock() recv, ok := h.recvs[key] diff --git a/wire/localbus_test.go b/wire/localbus_test.go index 971f70123..cdc394a30 100644 --- a/wire/localbus_test.go +++ b/wire/localbus_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,13 +17,15 @@ package wire_test import ( "testing" + "perun.network/go-perun/wallet" + . "perun.network/go-perun/wire" "perun.network/go-perun/wire/test" ) func TestLocalBus(t *testing.T) { bus := NewLocalBus() - test.GenericBusTest(t, func(Account) (Bus, Bus) { + test.GenericBusTest(t, func(map[wallet.BackendID]Account) (Bus, Bus) { return bus, bus }, 16, 10) } diff --git a/wire/net/bus.go b/wire/net/bus.go index 90161aad0..291ea788f 100644 --- a/wire/net/bus.go +++ b/wire/net/bus.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "sync" "time" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/log" @@ -44,13 +46,13 @@ const ( // NewBus creates a new network bus. The dialer and listener are used to // establish new connections internally, while id is this node's identity. -func NewBus(id wire.Account, d Dialer, s wire.EnvelopeSerializer) *Bus { +func NewBus(id map[wallet.BackendID]wire.Account, d Dialer, s wire.EnvelopeSerializer) *Bus { b := &Bus{ mainRecv: wire.NewReceiver(), recvs: make(map[wire.AddrKey]wire.Consumer), } - onNewEndpoint := func(wire.Address) wire.Consumer { return b.mainRecv } + onNewEndpoint := func(map[wallet.BackendID]wire.Address) wire.Consumer { return b.mainRecv } b.reg = NewEndpointRegistry(id, onNewEndpoint, d, s) go b.dispatchMsgs() @@ -65,7 +67,7 @@ func (b *Bus) Listen(l Listener) { // SubscribeClient subscribes a new client to the bus. Duplicate subscriptions // are forbidden and will cause a panic. The supplied consumer will receive all // messages that are sent to the requested address. -func (b *Bus) SubscribeClient(c wire.Consumer, addr wire.Address) error { +func (b *Bus) SubscribeClient(c wire.Consumer, addr map[wallet.BackendID]wire.Address) error { b.addSubscriber(c, addr) c.OnCloseAlways(func() { b.removeSubscriber(addr) }) return nil @@ -114,15 +116,15 @@ func (b *Bus) Close() error { return b.reg.Close() } -func (b *Bus) addSubscriber(c wire.Consumer, addr wire.Address) { +func (b *Bus) addSubscriber(c wire.Consumer, addr map[wallet.BackendID]wire.Address) { b.mutex.Lock() defer b.mutex.Unlock() - if _, ok := b.recvs[wire.Key(addr)]; ok { + if _, ok := b.recvs[wire.Keys(addr)]; ok { log.Panic("duplicate SubscribeClient") } - b.recvs[wire.Key(addr)] = c + b.recvs[wire.Keys(addr)] = c } // ctx returns the context of the bus' registry. @@ -140,7 +142,7 @@ func (b *Bus) dispatchMsgs() { } b.mutex.Lock() - r, ok := b.recvs[wire.Key(e.Recipient)] + r, ok := b.recvs[wire.Keys(e.Recipient)] b.mutex.Unlock() if !ok { log.WithField("sender", e.Sender). @@ -152,13 +154,13 @@ func (b *Bus) dispatchMsgs() { } } -func (b *Bus) removeSubscriber(addr wire.Address) { +func (b *Bus) removeSubscriber(addr map[wallet.BackendID]wire.Address) { b.mutex.Lock() defer b.mutex.Unlock() - if _, ok := b.recvs[wire.Key(addr)]; !ok { + if _, ok := b.recvs[wire.Keys(addr)]; !ok { log.Panic("deleting nonexisting subscriber") } - delete(b.recvs, wire.Key(addr)) + delete(b.recvs, wire.Keys(addr)) } diff --git a/wire/net/bus_test.go b/wire/net/bus_test.go index c93973bd4..825efe4fd 100644 --- a/wire/net/bus_test.go +++ b/wire/net/bus_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package net_test import ( "testing" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "perun.network/go-perun/wire" @@ -32,10 +34,10 @@ func TestBus(t *testing.T) { var hub nettest.ConnHub - wiretest.GenericBusTest(t, func(acc wire.Account) (wire.Bus, wire.Bus) { + wiretest.GenericBusTest(t, func(acc map[wallet.BackendID]wire.Account) (wire.Bus, wire.Bus) { bus := net.NewBus(acc, hub.NewNetDialer(), perunio.Serializer()) hub.OnClose(func() { bus.Close() }) - go bus.Listen(hub.NewNetListener(acc.Address())) + go bus.Listen(hub.NewNetListener(wire.AddressMapfromAccountMap(acc))) return bus, bus }, numClients, numMsgs) diff --git a/wire/net/dialer.go b/wire/net/dialer.go index 967fc48ca..2c270aedf 100644 --- a/wire/net/dialer.go +++ b/wire/net/dialer.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package net import ( "context" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" ) @@ -31,7 +33,7 @@ type Dialer interface { // // Dial needs to be reentrant, and concurrent calls to Close() must abort // any ongoing Dial() calls. - Dial(ctx context.Context, addr wire.Address, ser wire.EnvelopeSerializer) (Conn, error) + Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, ser wire.EnvelopeSerializer) (Conn, error) // Close aborts any ongoing calls to Dial(). // // Close() needs to be reentrant, and repeated calls to Close() need to diff --git a/wire/net/endpoint.go b/wire/net/endpoint.go index 5b0b5d8b1..4c1dc31ab 100644 --- a/wire/net/endpoint.go +++ b/wire/net/endpoint.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "fmt" "io" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" @@ -34,8 +36,8 @@ import ( // Sending messages to a node is done via the Send() method. To receive messages // from an Endpoint, use the Receiver helper type (by subscribing). type Endpoint struct { - Address wire.Address // The Endpoint's Perun address. - conn Conn // The Endpoint's connection. + Address map[wallet.BackendID]wire.Address // The Endpoint's Perun address. + conn Conn // The Endpoint's connection. sending sync.Mutex // Blocks multiple Send calls. } @@ -96,7 +98,7 @@ func (p *Endpoint) Close() (err error) { } // newEndpoint creates a new Endpoint from a wire Address and connection. -func newEndpoint(addr wire.Address, conn Conn) *Endpoint { +func newEndpoint(addr map[wallet.BackendID]wire.Address, conn Conn) *Endpoint { return &Endpoint{ Address: addr, conn: conn, diff --git a/wire/net/endpoint_internal_test.go b/wire/net/endpoint_internal_test.go index d1ca3f691..35b1fefad 100644 --- a/wire/net/endpoint_internal_test.go +++ b/wire/net/endpoint_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,9 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -53,7 +56,7 @@ func makeSetup(rng *rand.Rand) *setup { } // Dial simulates creating a connection to a. -func (s *setup) Dial(ctx context.Context, addr wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { +func (s *setup) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { s.mutex.RLock() defer s.mutex.RUnlock() @@ -64,16 +67,14 @@ func (s *setup) Dial(ctx context.Context, addr wire.Address, _ wire.EnvelopeSeri // a: Alice's end, b: Bob's end. a, b := newPipeConnPair() - //nolint:gocritic - if addr.Equal(s.alice.endpoint.Address) { // Dialing Bob? + if channel.EqualWireMaps(addr, s.alice.endpoint.Address) { // Dialing Bob? s.bob.Registry.addEndpoint(s.bob.endpoint.Address, b, true) // Bob accepts connection. return a, nil - } else if addr.Equal(s.bob.endpoint.Address) { // Dialing Alice? + } else if channel.EqualWireMaps(addr, s.bob.endpoint.Address) { // Dialing Alice? s.alice.Registry.addEndpoint(s.alice.endpoint.Address, a, true) // Alice accepts connection. return b, nil - } else { - return nil, errors.New("unknown peer") } + return nil, errors.New("unknown peer") } func (s *setup) Close() error { @@ -97,7 +98,7 @@ type client struct { // makeClient creates a simulated test client. func makeClient(conn Conn, rng *rand.Rand, dialer Dialer) *client { receiver := wire.NewReceiver() - registry := NewEndpointRegistry(wiretest.NewRandomAccount(rng), func(wire.Address) wire.Consumer { + registry := NewEndpointRegistry(wiretest.NewRandomAccountMap(rng, channel.TestBackendID), func(map[wallet.BackendID]wire.Address) wire.Consumer { return receiver }, dialer, perunio.Serializer()) diff --git a/wire/net/endpoint_registry.go b/wire/net/endpoint_registry.go index 149edf1b3..9e2a614a0 100644 --- a/wire/net/endpoint_registry.go +++ b/wire/net/endpoint_registry.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,10 @@ import ( "time" "unsafe" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/log" @@ -31,9 +35,9 @@ import ( // dialingEndpoint is an endpoint that is being dialed, but has no connection // associated with it yet. type dialingEndpoint struct { - Address wire.Address // The Endpoint's address. - created chan struct{} // Triggered when the Endpoint is created. - createdAt *Endpoint // Contains the finished Endpoint when it exists. + Address map[wallet.BackendID]wire.Address // The Endpoint's address. + created chan struct{} // Triggered when the Endpoint is created. + createdAt *Endpoint // Contains the finished Endpoint when it exists. } // fullEndpoint describes an endpoint that is held within the registry. @@ -51,7 +55,7 @@ func newFullEndpoint(e *Endpoint) *fullEndpoint { } } -func newDialingEndpoint(addr wire.Address) *dialingEndpoint { +func newDialingEndpoint(addr map[wallet.BackendID]wire.Address) *dialingEndpoint { return &dialingEndpoint{ Address: addr, created: make(chan struct{}), @@ -63,9 +67,9 @@ func newDialingEndpoint(addr wire.Address) *dialingEndpoint { // connections. It should not be used manually, but only internally by a // wire.Bus. type EndpointRegistry struct { - id wire.Account // The identity of the node. - dialer Dialer // Used for dialing peers. - onNewEndpoint func(wire.Address) wire.Consumer // Selects Consumer for new Endpoints' receive loop. + 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. ser wire.EnvelopeSerializer endpoints map[wire.AddrKey]*fullEndpoint // The list of all of all established Endpoints. @@ -82,8 +86,8 @@ const exchangeAddrsTimeout = 10 * time.Second // The provided callback is used to set up new peer's subscriptions and it is // called before the peer starts receiving messages. func NewEndpointRegistry( - id wire.Account, - onNewEndpoint func(wire.Address) wire.Consumer, + id map[wallet.BackendID]wire.Account, + onNewEndpoint func(map[wallet.BackendID]wire.Address) wire.Consumer, dialer Dialer, ser wire.EnvelopeSerializer, ) *EndpointRegistry { @@ -96,7 +100,7 @@ func NewEndpointRegistry( endpoints: make(map[wire.AddrKey]*fullEndpoint), dialing: make(map[wire.AddrKey]*dialingEndpoint), - Embedding: log.MakeEmbedding(log.WithField("id", id.Address())), + Embedding: log.MakeEmbedding(log.WithField("id", id)), } } @@ -167,7 +171,7 @@ func (r *EndpointRegistry) setupConn(conn Conn) error { ctx, cancel := context.WithTimeout(r.Ctx(), exchangeAddrsTimeout) defer cancel() - var peerAddr wire.Address + var peerAddr map[wallet.BackendID]wire.Address var err error if peerAddr, err = ExchangeAddrsPassive(ctx, r.id, conn); err != nil { conn.Close() @@ -175,7 +179,7 @@ func (r *EndpointRegistry) setupConn(conn Conn) error { return err } - if peerAddr.Equal(r.id.Address()) { + if channel.EqualWireMaps(peerAddr, wire.AddressMapfromAccountMap(r.id)) { r.Log().Error("dialed by self") return errors.New("dialed by self") } @@ -187,11 +191,11 @@ func (r *EndpointRegistry) setupConn(conn Conn) error { // 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. -func (r *EndpointRegistry) Endpoint(ctx context.Context, addr wire.Address) (*Endpoint, error) { +func (r *EndpointRegistry) Endpoint(ctx context.Context, addr map[wallet.BackendID]wire.Address) (*Endpoint, error) { log := r.Log().WithField("peer", addr) - key := wire.Key(addr) + key := wire.Keys(addr) - if addr.Equal(r.id.Address()) { + if channel.EqualWireMaps(addr, wire.AddressMapfromAccountMap(r.id)) { log.Panic("tried to dial self") } @@ -217,11 +221,11 @@ func (r *EndpointRegistry) Endpoint(ctx context.Context, addr wire.Address) (*En func (r *EndpointRegistry) authenticatedDial( ctx context.Context, - addr wire.Address, + addr map[wallet.BackendID]wire.Address, de *dialingEndpoint, created bool, ) (ret *Endpoint, _ error) { - key := wire.Key(addr) + key := wire.Keys(addr) // Short cut: another dial for that peer is already in progress. if !created { @@ -261,8 +265,8 @@ func (r *EndpointRegistry) authenticatedDial( } // dialingEndpoint retrieves or creates a dialingEndpoint for the passed address. -func (r *EndpointRegistry) dialingEndpoint(a wire.Address) (_ *dialingEndpoint, created bool) { - key := wire.Key(a) +func (r *EndpointRegistry) dialingEndpoint(a map[wallet.BackendID]wire.Address) (_ *dialingEndpoint, created bool) { + key := wire.Keys(a) entry, ok := r.dialing[key] if !ok { entry = newDialingEndpoint(a) @@ -283,23 +287,23 @@ func (r *EndpointRegistry) NumPeers() int { // 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 wire.Address) bool { +func (r *EndpointRegistry) Has(addr map[wallet.BackendID]wire.Address) bool { r.mutex.Lock() defer r.mutex.Unlock() - _, ok := r.endpoints[wire.Key(addr)] + _, ok := r.endpoints[wire.Keys(addr)] return ok } // addEndpoint adds a new peer to the registry. -func (r *EndpointRegistry) addEndpoint(addr wire.Address, conn Conn, dialer bool) *Endpoint { +func (r *EndpointRegistry) addEndpoint(addr map[wallet.BackendID]wire.Address, conn Conn, dialer bool) *Endpoint { r.Log().WithField("peer", addr).Trace("EndpointRegistry.addEndpoint") e := newEndpoint(addr, conn) fe, created := r.fullEndpoint(addr, e) if !created { - if e, closed := fe.replace(e, r.id.Address(), dialer); closed { + if e, closed := fe.replace(e, wire.AddressMapfromAccountMap(r.id), dialer); closed { return e } } @@ -317,8 +321,8 @@ func (r *EndpointRegistry) addEndpoint(addr wire.Address, conn Conn, dialer bool } // fullEndpoint retrieves or creates a fullEndpoint for the passed address. -func (r *EndpointRegistry) fullEndpoint(addr wire.Address, e *Endpoint) (_ *fullEndpoint, created bool) { - key := wire.Key(addr) +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] @@ -332,7 +336,7 @@ func (r *EndpointRegistry) fullEndpoint(addr wire.Address, e *Endpoint) (_ *full // replace sets a new endpoint and resolves ties when both parties dial each // other concurrently. It returns the endpoint that is selected after potential // tie resolving, and whether the supplied endpoint was closed in the process. -func (p *fullEndpoint) replace(newValue *Endpoint, self wire.Address, dialer bool) (updated *Endpoint, closed bool) { +func (p *fullEndpoint) replace(newValue *Endpoint, self map[wallet.BackendID]wire.Address, dialer bool) (updated *Endpoint, closed bool) { // If there was no previous endpoint, just set the new one. wasNil := atomic.CompareAndSwapPointer(&p.endpoint, nil, unsafe.Pointer(newValue)) if wasNil { @@ -344,11 +348,24 @@ func (p *fullEndpoint) replace(newValue *Endpoint, self wire.Address, dialer boo // close on both sides. Close the endpoint that is created by the dialer // with the lesser Perun address and return the previously existing // endpoint. - if dialer == (self.Cmp(newValue.Address) < 0) { - if err := newValue.Close(); err != nil { - log.Warn("newValue dialer already closed") + for key, selfAddr := range self { + // Check if the same key exists in newValue.Address + newAddr, exists := newValue.Address[key] + + // If the key does not exist in newValue.Address, you might skip it or handle it + if !exists { + continue // or handle this scenario according to your requirements + } + + // Compare the addresses + if dialer == (selfAddr.Cmp(newAddr) < 0) { + // If selfAddr is "lesser", close the new value + if err := newValue.Close(); err != nil { + log.Warn("newValue dialer already closed") + } + // Return the existing endpoint associated with this key + return p.Endpoint(), true } - return p.Endpoint(), true } // Otherwise, install the new endpoint and close the old endpoint. @@ -369,10 +386,10 @@ func (p *fullEndpoint) delete(expectedOldValue *Endpoint) { atomic.CompareAndSwapPointer(&p.endpoint, unsafe.Pointer(expectedOldValue), nil) } -func (r *EndpointRegistry) find(addr wire.Address) *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.Key(addr)]; ok { + if e, ok := r.endpoints[wire.Keys(addr)]; ok { return e.Endpoint() } return nil diff --git a/wire/net/endpoint_registry_external_test.go b/wire/net/endpoint_registry_external_test.go index 486ade7e5..a1593665e 100644 --- a/wire/net/endpoint_registry_external_test.go +++ b/wire/net/endpoint_registry_external_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,9 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -34,7 +37,7 @@ import ( var timeout = 100 * time.Millisecond -func nilConsumer(wire.Address) wire.Consumer { return nil } +func nilConsumer(map[wallet.BackendID]wire.Address) wire.Consumer { return nil } // Two nodes (1 dialer, 1 listener node) .Get() each other. func TestEndpointRegistry_Get_Pair(t *testing.T) { @@ -42,11 +45,11 @@ func TestEndpointRegistry_Get_Pair(t *testing.T) { assert, require := assert.New(t), require.New(t) rng := test.Prng(t) var hub nettest.ConnHub - dialerID := wiretest.NewRandomAccount(rng) - listenerID := wiretest.NewRandomAccount(rng) + dialerID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + listenerID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) dialerReg := net.NewEndpointRegistry(dialerID, nilConsumer, hub.NewNetDialer(), perunio.Serializer()) listenerReg := net.NewEndpointRegistry(listenerID, nilConsumer, nil, perunio.Serializer()) - listener := hub.NewNetListener(listenerID.Address()) + listener := hub.NewNetListener(wire.AddressMapfromAccountMap(listenerID)) done := make(chan struct{}) go func() { @@ -56,17 +59,17 @@ func TestEndpointRegistry_Get_Pair(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*timeout) defer cancel() - p, err := dialerReg.Endpoint(ctx, listenerID.Address()) + p, err := dialerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(listenerID)) assert.NoError(err) require.NotNil(p) - assert.True(p.Address.Equal(listenerID.Address())) + 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, dialerID.Address()) + p, err = listenerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(dialerID)) assert.NoError(err) require.NotNil(p) - assert.True(p.Address.Equal(dialerID.Address())) + assert.True(channel.EqualWireMaps(p.Address, wire.AddressMapfromAccountMap(dialerID))) listenerReg.Close() dialerReg.Close() @@ -82,16 +85,16 @@ func TestEndpointRegistry_Get_Multiple(t *testing.T) { assert := assert.New(t) rng := test.Prng(t) var hub nettest.ConnHub - dialerID := wiretest.NewRandomAccount(rng) - listenerID := wiretest.NewRandomAccount(rng) + dialerID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + listenerID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) dialer := hub.NewNetDialer() - logPeer := func(addr wire.Address) wire.Consumer { - t.Logf("subscribing %s\n", addr) + logPeer := func(addr map[wallet.BackendID]wire.Address) wire.Consumer { + t.Logf("subscribing %s\n", wire.Keys(addr)) return nil } dialerReg := net.NewEndpointRegistry(dialerID, logPeer, dialer, perunio.Serializer()) listenerReg := net.NewEndpointRegistry(listenerID, logPeer, nil, perunio.Serializer()) - listener := hub.NewNetListener(listenerID.Address()) + listener := hub.NewNetListener(wire.AddressMapfromAccountMap(listenerID)) done := make(chan struct{}) go func() { @@ -106,10 +109,10 @@ func TestEndpointRegistry_Get_Multiple(t *testing.T) { peers := make(chan *net.Endpoint, N) for i := 0; i < N; i++ { go func() { - p, err := dialerReg.Endpoint(ctx, listenerID.Address()) + p, err := dialerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(listenerID)) assert.NoError(err) if p != nil { - assert.True(p.Address.Equal(listenerID.Address())) + assert.True(channel.EqualWireMaps(p.Address, wire.AddressMapfromAccountMap(listenerID))) } peers <- p }() @@ -135,10 +138,10 @@ func TestEndpointRegistry_Get_Multiple(t *testing.T) { // should allow the listener routine to add the peer to its registry time.Sleep(timeout) - p, err := listenerReg.Endpoint(ctx, dialerID.Address()) + p, err := listenerReg.Endpoint(ctx, wire.AddressMapfromAccountMap(dialerID)) assert.NoError(err) assert.NotNil(p) - assert.True(p.Address.Equal(dialerID.Address())) + assert.True(channel.EqualWireMaps(p.Address, wire.AddressMapfromAccountMap(dialerID))) assert.Equal(1, listener.NumAccepted()) listenerReg.Close() diff --git a/wire/net/endpoint_registry_internal_test.go b/wire/net/endpoint_registry_internal_test.go index 34160e1e6..b0e55b76b 100644 --- a/wire/net/endpoint_registry_internal_test.go +++ b/wire/net/endpoint_registry_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,10 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -51,7 +55,7 @@ func (d *mockDialer) Close() error { return nil } -func (d *mockDialer) Dial(ctx context.Context, addr wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { +func (d *mockDialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, _ wire.EnvelopeSerializer) (Conn, error) { d.mutex.Lock() defer d.mutex.Unlock() @@ -104,7 +108,7 @@ func newMockListener() *mockListener { return &mockListener{dialer: mockDialer{dial: make(chan Conn)}} } -func nilConsumer(wire.Address) wire.Consumer { return nil } +func nilConsumer(map[wallet.BackendID]wire.Address) wire.Consumer { return nil } // TestRegistry_Get tests that when calling Get(), existing peers are returned, // and when unknown peers are requested, a temporary peer is create that is @@ -114,9 +118,9 @@ func nilConsumer(wire.Address) wire.Consumer { return nil } func TestRegistry_Get(t *testing.T) { t.Parallel() rng := test.Prng(t) - id := wiretest.NewRandomAccount(rng) - peerID := wiretest.NewRandomAccount(rng) - peerAddr := peerID.Address() + id := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + peerID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + peerAddr := wire.AddressMapfromAccountMap(peerID) t.Run("peer already in progress (existing)", func(t *testing.T) { t.Parallel() @@ -125,7 +129,7 @@ func TestRegistry_Get(t *testing.T) { r := NewEndpointRegistry(id, nilConsumer, dialer, perunio.Serializer()) existing := newEndpoint(peerAddr, newMockConn()) - r.endpoints[wire.Key(peerAddr)] = newFullEndpoint(existing) + r.endpoints[wire.Keys(peerAddr)] = newFullEndpoint(existing) ctxtest.AssertTerminates(t, timeout, func() { p, err := r.Endpoint(context.Background(), peerAddr) assert.NoError(t, err) @@ -179,12 +183,12 @@ func TestRegistry_Get(t *testing.T) { func TestRegistry_authenticatedDial(t *testing.T) { t.Parallel() rng := test.Prng(t) - id := wiretest.NewRandomAccount(rng) + id := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) d := &mockDialer{dial: make(chan Conn)} r := NewEndpointRegistry(id, nilConsumer, d, perunio.Serializer()) - remoteID := wiretest.NewRandomAccount(rng) - remoteAddr := remoteID.Address() + remoteID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + remoteAddr := wire.AddressMapfromAccountMap(remoteID) t.Run("dial fail", func(t *testing.T) { addr := wiretest.NewRandomAddress(rng) @@ -206,7 +210,7 @@ func TestRegistry_authenticatedDial(t *testing.T) { } err := b.Send(&wire.Envelope{ Sender: remoteAddr, - Recipient: id.Address(), + Recipient: wire.AddressMapfromAccountMap(id), Msg: wire.NewPingMsg(), }) if err != nil { @@ -228,7 +232,7 @@ func TestRegistry_authenticatedDial(t *testing.T) { a, b := newPipeConnPair() go ct.Stage("passive", func(rt test.ConcT) { d.put(a) - _, err := ExchangeAddrsPassive(ctx, wiretest.NewRandomAccount(rng), b) + _, err := ExchangeAddrsPassive(ctx, wiretest.NewRandomAccountMap(rng, channel.TestBackendID), b) require.True(rt, IsAuthenticationError(err)) }) de, created := r.dialingEndpoint(remoteAddr) @@ -261,8 +265,8 @@ func TestRegistry_authenticatedDial(t *testing.T) { func TestRegistry_setupConn(t *testing.T) { t.Parallel() rng := test.Prng(t) - id := wiretest.NewRandomAccount(rng) - remoteID := wiretest.NewRandomAccount(rng) + id := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + remoteID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) t.Run("ExchangeAddrs fail", func(t *testing.T) { d := &mockDialer{dial: make(chan Conn)} @@ -270,8 +274,8 @@ func TestRegistry_setupConn(t *testing.T) { a, b := newPipeConnPair() go func() { err := b.Send(&wire.Envelope{ - Sender: id.Address(), - Recipient: remoteID.Address(), + Sender: wire.AddressMapfromAccountMap(id), + Recipient: wire.AddressMapfromAccountMap(remoteID), Msg: wire.NewPingMsg(), }) if err != nil { @@ -288,13 +292,13 @@ func TestRegistry_setupConn(t *testing.T) { r := NewEndpointRegistry(id, nilConsumer, d, perunio.Serializer()) a, b := newPipeConnPair() go func() { - err := ExchangeAddrsActive(context.Background(), remoteID, id.Address(), b) + err := ExchangeAddrsActive(context.Background(), remoteID, wire.AddressMapfromAccountMap(id), b) if err != nil { panic(err) } }() - r.addEndpoint(remoteID.Address(), newMockConn(), false) + r.addEndpoint(wire.AddressMapfromAccountMap(remoteID), newMockConn(), false) ctxtest.AssertTerminates(t, timeout, func() { assert.NoError(t, r.setupConn(a)) }) @@ -305,7 +309,7 @@ func TestRegistry_setupConn(t *testing.T) { r := NewEndpointRegistry(id, nilConsumer, d, perunio.Serializer()) a, b := newPipeConnPair() go func() { - err := ExchangeAddrsActive(context.Background(), remoteID, id.Address(), b) + err := ExchangeAddrsActive(context.Background(), remoteID, wire.AddressMapfromAccountMap(id), b) if err != nil { panic(err) } @@ -323,10 +327,10 @@ func TestRegistry_Listen(t *testing.T) { rng := test.Prng(t) - id := wiretest.NewRandomAccount(rng) - addr := id.Address() - remoteID := wiretest.NewRandomAccount(rng) - remoteAddr := remoteID.Address() + id := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + addr := wire.AddressMapfromAccountMap(id) + remoteID := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) + remoteAddr := wire.AddressMapfromAccountMap(remoteID) d := newMockDialer() l := newMockListener() @@ -365,8 +369,8 @@ func TestRegistry_addEndpoint_Subscribe(t *testing.T) { rng := test.Prng(t) called := false r := NewEndpointRegistry( - wiretest.NewRandomAccount(rng), - func(wire.Address) wire.Consumer { called = true; return nil }, + wiretest.NewRandomAccountMap(rng, channel.TestBackendID), + func(map[wallet.BackendID]wire.Address) wire.Consumer { called = true; return nil }, nil, perunio.Serializer(), ) @@ -383,7 +387,7 @@ func TestRegistry_Close(t *testing.T) { t.Run("double close error", func(t *testing.T) { r := NewEndpointRegistry( - wiretest.NewRandomAccount(rng), + wiretest.NewRandomAccountMap(rng, channel.TestBackendID), nilConsumer, nil, perunio.Serializer(), @@ -396,7 +400,7 @@ func TestRegistry_Close(t *testing.T) { d := &mockDialer{dial: make(chan Conn)} d.Close() r := NewEndpointRegistry( - wiretest.NewRandomAccount(rng), + wiretest.NewRandomAccountMap(rng, channel.TestBackendID), nilConsumer, d, perunio.Serializer(), diff --git a/wire/net/exchange_addr.go b/wire/net/exchange_addr.go index 658052f0d..9538a0ee4 100644 --- a/wire/net/exchange_addr.go +++ b/wire/net/exchange_addr.go @@ -1,4 +1,4 @@ -// Copyright 2021 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,10 @@ import ( "context" "fmt" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" @@ -27,11 +31,11 @@ import ( // AuthenticationError describes an error which occures when the ExchangeAddrs // protcol fails because it got a different Address than expected. type AuthenticationError struct { - Sender, Receiver, Own wire.Address + Sender, Receiver, Own map[wallet.BackendID]wire.Address } // NewAuthenticationError creates a new AuthenticationError. -func NewAuthenticationError(sender, receiver, own wire.Address, msg string) error { +func NewAuthenticationError(sender, receiver, own map[wallet.BackendID]wire.Address, msg string) error { return errors.Wrap(&AuthenticationError{ Sender: sender, Receiver: receiver, @@ -56,7 +60,7 @@ func IsAuthenticationError(err error) bool { // In the future, it will be extended to become a proper authentication // protocol. The protocol will then exchange Perun addresses and establish // authenticity. -func ExchangeAddrsActive(ctx context.Context, id wire.Account, peer wire.Address, conn Conn) error { +func ExchangeAddrsActive(ctx context.Context, id map[wallet.BackendID]wire.Account, peer map[wallet.BackendID]wire.Address, conn Conn) error { var err error ok := pkg.TerminatesCtx(ctx, func() { authMsg, err2 := wire.NewAuthResponseMsg(id) @@ -65,7 +69,7 @@ func ExchangeAddrsActive(ctx context.Context, id wire.Account, peer wire.Address return } err = conn.Send(&wire.Envelope{ - Sender: id.Address(), + Sender: wire.AddressMapfromAccountMap(id), Recipient: peer, Msg: authMsg, }) @@ -79,11 +83,13 @@ func ExchangeAddrsActive(ctx context.Context, id wire.Account, peer wire.Address err = errors.WithMessage(err, "receiving message") } else if _, ok := e.Msg.(*wire.AuthResponseMsg); !ok { err = errors.Errorf("expected AuthResponse wire msg, got %v", e.Msg.Type()) - } else if check := VerifyAddressSignature(peer, e.Msg.(*wire.AuthResponseMsg).Signature); check != nil { - err = errors.WithMessage(err, "verifying peer address's signature") - } else if !e.Recipient.Equal(id.Address()) && - !e.Sender.Equal(peer) { - err = NewAuthenticationError(e.Sender, e.Recipient, id.Address(), "unmatched response sender or recipient") + } else if msg, ok := e.Msg.(*wire.AuthResponseMsg); ok { + if check := VerifyAddressSignature(peer, msg.Signature); check != nil { + err = errors.WithMessage(check, "verifying peer address's signature") + } + } else if !channel.EqualWireMaps(e.Recipient, wire.AddressMapfromAccountMap(id)) && + !channel.EqualWireMaps(e.Sender, peer) { + err = NewAuthenticationError(e.Sender, e.Recipient, wire.AddressMapfromAccountMap(id), "unmatched response sender or recipient") } }) @@ -97,19 +103,22 @@ func ExchangeAddrsActive(ctx context.Context, id wire.Account, peer wire.Address // ExchangeAddrsPassive executes the passive role of the address exchange // protocol. It is executed by the person that listens for incoming connections. -func ExchangeAddrsPassive(ctx context.Context, id wire.Account, conn Conn) (wire.Address, error) { - var addr wire.Address +func ExchangeAddrsPassive(ctx context.Context, id map[wallet.BackendID]wire.Account, conn Conn) (map[wallet.BackendID]wire.Address, error) { + var addr map[wallet.BackendID]wire.Address var err error + addrs := wire.AddressMapfromAccountMap(id) ok := pkg.TerminatesCtx(ctx, func() { var e *wire.Envelope if e, err = conn.Recv(); err != nil { err = errors.WithMessage(err, "receiving auth message") } else if _, ok := e.Msg.(*wire.AuthResponseMsg); !ok { err = errors.Errorf("expected AuthResponse wire msg, got %v", e.Msg.Type()) - } else if !e.Recipient.Equal(id.Address()) { - err = NewAuthenticationError(e.Sender, e.Recipient, id.Address(), "unmatched response sender or recipient") - } else if err = VerifyAddressSignature(e.Sender, e.Msg.(*wire.AuthResponseMsg).Signature); err != nil { - err = errors.WithMessage(err, "verifying peer address's signature") + } else if !channel.EqualWireMaps(e.Recipient, addrs) { + err = NewAuthenticationError(e.Sender, e.Recipient, wire.AddressMapfromAccountMap(id), "unmatched response sender or recipient") + } else if msg, ok := e.Msg.(*wire.AuthResponseMsg); ok { + if err = VerifyAddressSignature(e.Sender, msg.Signature); err != nil { + err = errors.WithMessage(err, "verifying peer address's signature") + } } if err != nil { @@ -122,7 +131,7 @@ func ExchangeAddrsPassive(ctx context.Context, id wire.Account, conn Conn) (wire return } addr, err = e.Sender, conn.Send(&wire.Envelope{ - Sender: id.Address(), + Sender: wire.AddressMapfromAccountMap(id), Recipient: e.Sender, Msg: authMsg, }) @@ -140,10 +149,21 @@ func ExchangeAddrsPassive(ctx context.Context, id wire.Account, conn Conn) (wire // VerifyAddressSignature verifies a signature against the hash of an address. // It relies on the MarshalBinary method of the provided wire.Address interface to generate the address hash. // In case the MarshalBinary method doesn't produce the expected hash, the verification may fail. -func VerifyAddressSignature(addr wire.Address, sig []byte) error { - addressBytes, err := addr.MarshalBinary() - if err != nil { - return fmt.Errorf("failed to marshal address: %w", err) +func VerifyAddressSignature(addrs map[wallet.BackendID]wire.Address, sig []byte) error { + var addressBytes []byte + addressBytes = append(addressBytes, byte(len(addrs))) + for _, addr := range addrs { + addrBytes, err := addr.MarshalBinary() + if err != nil { + return fmt.Errorf("failed to marshal address: %w", err) + } + addressBytes = append(addressBytes, addrBytes...) + } + for _, addr := range addrs { + err := addr.Verify(addressBytes, sig) + if err != nil { + return err + } } - return addr.Verify(addressBytes, sig) + return nil } diff --git a/wire/net/exchange_addr_internal_test.go b/wire/net/exchange_addr_internal_test.go index 4abf6f83c..bba650b23 100644 --- a/wire/net/exchange_addr_internal_test.go +++ b/wire/net/exchange_addr_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "sync" "testing" + "perun.network/go-perun/channel" + "github.com/stretchr/testify/assert" "perun.network/go-perun/wire" @@ -31,7 +33,7 @@ func TestExchangeAddrs_ConnFail(t *testing.T) { rng := test.Prng(t) a, _ := newPipeConnPair() a.Close() - addr, err := ExchangeAddrsPassive(context.Background(), wiretest.NewRandomAccount(rng), a) + addr, err := ExchangeAddrsPassive(context.Background(), wiretest.NewRandomAccountMap(rng, channel.TestBackendID), a) assert.Nil(t, addr) assert.Error(t, err) } @@ -40,7 +42,7 @@ func TestExchangeAddrs_Success(t *testing.T) { rng := test.Prng(t) conn0, conn1 := newPipeConnPair() defer conn0.Close() - account0, account1 := wiretest.NewRandomAccount(rng), wiretest.NewRandomAccount(rng) + account0, account1 := wiretest.NewRandomAccountMap(rng, channel.TestBackendID), wiretest.NewRandomAccountMap(rng, channel.TestBackendID) var wg sync.WaitGroup wg.Add(1) @@ -50,10 +52,10 @@ func TestExchangeAddrs_Success(t *testing.T) { recvAddr0, err := ExchangeAddrsPassive(context.Background(), account1, conn1) assert.NoError(t, err) - assert.True(t, recvAddr0.Equal(account0.Address())) + assert.True(t, channel.EqualWireMaps(recvAddr0, wire.AddressMapfromAccountMap(account0))) }() - err := ExchangeAddrsActive(context.Background(), account0, account1.Address(), conn0) + err := ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0) assert.NoError(t, err) wg.Wait() @@ -66,7 +68,7 @@ func TestExchangeAddrs_Timeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() ctxtest.AssertTerminates(t, 2*timeout, func() { - addr, err := ExchangeAddrsPassive(ctx, wiretest.NewRandomAccount(rng), a) + addr, err := ExchangeAddrsPassive(ctx, wiretest.NewRandomAccountMap(rng, channel.TestBackendID), a) assert.Nil(t, addr) assert.Error(t, err) }) @@ -74,7 +76,7 @@ func TestExchangeAddrs_Timeout(t *testing.T) { func TestExchangeAddrs_BogusMsg(t *testing.T) { rng := test.Prng(t) - acc := wiretest.NewRandomAccount(rng) + 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/simple/address.go b/wire/net/simple/address.go index 7ed03e226..1dcdbe1ed 100644 --- a/wire/net/simple/address.go +++ b/wire/net/simple/address.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -23,9 +23,14 @@ import ( "math/big" "math/rand" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" ) +// testBackendID is the identifier for the simulated Backend. +const testBackendID = 0 + // Address is a wire address. type Address struct { Name string @@ -33,7 +38,7 @@ type Address struct { } // NewAddress returns a new address. -func NewAddress(host string) *Address { +func NewAddress(host string) wire.Address { return &Address{ Name: host, } @@ -94,6 +99,16 @@ func (a *Address) UnmarshalBinary(data []byte) error { return nil } +// String returns the string representation of the address. +func (a *Address) String() string { + return a.Name +} + +// Backend returns the backend ID of the address. +func (a *Address) Backend() wallet.BackendID { + return 0 +} + // encodePublicKey encodes the public key into the buffer. func encodePublicKey(buf *bytes.Buffer, key *rsa.PublicKey) error { // Encode modulus length and modulus @@ -192,6 +207,21 @@ func NewRandomAddress(rng *rand.Rand) *Address { return a } +// NewRandomAddresses returns a new random peer address. +func NewRandomAddresses(rng *rand.Rand) map[wallet.BackendID]wire.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 map[wallet.BackendID]wire.Address{testBackendID: &a} +} + // Verify verifies a message signature. func (a *Address) Verify(msg []byte, sig []byte) error { hashed := sha256.Sum256(msg) diff --git a/wire/net/simple/dialer.go b/wire/net/simple/dialer.go index 82016ef27..1b9f179c9 100644 --- a/wire/net/simple/dialer.go +++ b/wire/net/simple/dialer.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -21,6 +21,8 @@ import ( "sync" "time" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" wirenet "perun.network/go-perun/wire/net" @@ -77,11 +79,11 @@ func (d *Dialer) host(key wire.AddrKey) (string, bool) { } // Dial implements Dialer.Dial(). -func (d *Dialer) Dial(ctx context.Context, addr wire.Address, ser wire.EnvelopeSerializer) (wirenet.Conn, error) { +func (d *Dialer) Dial(ctx context.Context, addr map[wallet.BackendID]wire.Address, ser wire.EnvelopeSerializer) (wirenet.Conn, error) { done := make(chan struct{}) defer close(done) - host, ok := d.host(wire.Key(addr)) + host, ok := d.host(wire.Keys(addr)) if !ok { return nil, errors.New("peer not found") } @@ -107,9 +109,9 @@ func (d *Dialer) Dial(ctx context.Context, addr wire.Address, ser wire.EnvelopeS } // Register registers a network address for a peer address. -func (d *Dialer) Register(addr wire.Address, address string) { +func (d *Dialer) Register(addr map[wallet.BackendID]wire.Address, address string) { d.mutex.Lock() defer d.mutex.Unlock() - d.peers[wire.Key(addr)] = address + d.peers[wire.Keys(addr)] = address } diff --git a/wire/net/simple/dialer_internal_test.go b/wire/net/simple/dialer_internal_test.go index a493eb9fb..8f8f57f91 100644 --- a/wire/net/simple/dialer_internal_test.go +++ b/wire/net/simple/dialer_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -28,6 +28,10 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -66,7 +70,7 @@ func TestDialer_Register(t *testing.T) { _, ok := d.host(key) require.False(t, ok) - d.Register(addr, "host") + d.Register(map[wallet.BackendID]wire.Address{channel.TestBackendID: addr}, "host") host, ok := d.host(key) assert.True(t, ok) @@ -77,7 +81,7 @@ func TestDialer_Dial(t *testing.T) { timeout := 100 * time.Millisecond rng := test.Prng(t) lhost := "127.0.0.1:7357" - laddr := wiretest.NewRandomAccount(rng).Address() + laddr := wire.AddressMapfromAccountMap(wiretest.NewRandomAccountMap(rng, channel.TestBackendID)) commonName := "127.0.0.1" sans := []string{"127.0.0.1", "localhost"} @@ -91,7 +95,7 @@ func TestDialer_Dial(t *testing.T) { ser := perunio.Serializer() d := NewTCPDialer(timeout, dConfig) d.Register(laddr, lhost) - daddr := wiretest.NewRandomAccount(rng).Address() + daddr := wire.AddressMapfromAccountMap(wiretest.NewRandomAccountMap(rng, channel.TestBackendID)) defer d.Close() t.Run("happy", func(t *testing.T) { @@ -135,7 +139,7 @@ func TestDialer_Dial(t *testing.T) { }) t.Run("unknown host", func(t *testing.T) { - noHostAddr := NewRandomAddress(rng) + noHostAddr := NewRandomAddresses(rng) d.Register(noHostAddr, "no such host") ctxtest.AssertTerminates(t, timeout, func() { @@ -147,7 +151,7 @@ func TestDialer_Dial(t *testing.T) { t.Run("unknown address", func(t *testing.T) { ctxtest.AssertTerminates(t, timeout, func() { - unkownAddr := NewRandomAddress(rng) + unkownAddr := NewRandomAddresses(rng) conn, err := d.Dial(context.Background(), unkownAddr, ser) assert.Error(t, err) assert.Nil(t, conn) diff --git a/wire/net/simple/simple_exchange_addr_test.go b/wire/net/simple/simple_exchange_addr_test.go index 4dbd13646..b02da64af 100644 --- a/wire/net/simple/simple_exchange_addr_test.go +++ b/wire/net/simple/simple_exchange_addr_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -26,6 +26,8 @@ import ( "testing" "time" + "perun.network/go-perun/channel" + "github.com/stretchr/testify/assert" "perun.network/go-perun/wire" @@ -42,7 +44,7 @@ func TestExchangeAddrs_ConnFail(t *testing.T) { rng := test.Prng(t) a, _ := newPipeConnPair() a.Close() - addr, err := wirenet.ExchangeAddrsPassive(context.Background(), wiretest.NewRandomAccount(rng), a) + addr, err := wirenet.ExchangeAddrsPassive(context.Background(), wiretest.NewRandomAccountMap(rng, channel.TestBackendID), a) assert.Nil(t, addr) assert.Error(t, err) } @@ -51,7 +53,7 @@ func TestExchangeAddrs_Success(t *testing.T) { rng := test.Prng(t) conn0, conn1 := newPipeConnPair() defer conn0.Close() - account0, account1 := wiretest.NewRandomAccount(rng), wiretest.NewRandomAccount(rng) + account0, account1 := wiretest.NewRandomAccountMap(rng, channel.TestBackendID), wiretest.NewRandomAccountMap(rng, channel.TestBackendID) var wg sync.WaitGroup wg.Add(1) @@ -61,10 +63,10 @@ func TestExchangeAddrs_Success(t *testing.T) { recvAddr0, err := wirenet.ExchangeAddrsPassive(context.Background(), account1, conn1) assert.NoError(t, err) - assert.True(t, recvAddr0.Equal(account0.Address())) + assert.True(t, channel.EqualWireMaps(recvAddr0, wire.AddressMapfromAccountMap(account0))) }() - err := wirenet.ExchangeAddrsActive(context.Background(), account0, account1.Address(), conn0) + err := wirenet.ExchangeAddrsActive(context.Background(), account0, wire.AddressMapfromAccountMap(account1), conn0) assert.NoError(t, err) wg.Wait() @@ -77,7 +79,7 @@ func TestExchangeAddrs_Timeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() ctxtest.AssertTerminates(t, 20*timeout, func() { - addr, err := wirenet.ExchangeAddrsPassive(ctx, wiretest.NewRandomAccount(rng), a) + addr, err := wirenet.ExchangeAddrsPassive(ctx, wiretest.NewRandomAccountMap(rng, channel.TestBackendID), a) assert.Nil(t, addr) assert.Error(t, err) }) @@ -85,7 +87,7 @@ func TestExchangeAddrs_Timeout(t *testing.T) { func TestExchangeAddrs_BogusMsg(t *testing.T) { rng := test.Prng(t) - acc := wiretest.NewRandomAccount(rng) + acc := wiretest.NewRandomAccountMap(rng, channel.TestBackendID) conn := newMockConn() conn.recvQueue <- newRandomEnvelope(rng, wire.NewPingMsg()) addr, err := wirenet.ExchangeAddrsPassive(context.Background(), acc, conn) @@ -105,8 +107,8 @@ func newPipeConnPair() (a wirenet.Conn, b wirenet.Conn) { // recipient generated using randomness from rng. func newRandomEnvelope(rng *rand.Rand, m wire.Msg) *wire.Envelope { return &wire.Envelope{ - Sender: NewRandomAddress(rng), - Recipient: NewRandomAddress(rng), + Sender: NewRandomAddresses(rng), + Recipient: NewRandomAddresses(rng), Msg: m, } } diff --git a/wire/net/test/connhub.go b/wire/net/test/connhub.go index 1c958b78c..6221e5fa7 100644 --- a/wire/net/test/connhub.go +++ b/wire/net/test/connhub.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package test import ( gosync "sync" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" @@ -35,7 +37,7 @@ type ConnHub struct { // NewNetListener creates a new test listener for the given address. // Registers the new listener in the hub. Panics if the address was already // entered or the hub is closed. -func (h *ConnHub) NewNetListener(addr wire.Address) *Listener { +func (h *ConnHub) NewNetListener(addr map[wallet.BackendID]wire.Address) *Listener { h.mutex.RLock() defer h.mutex.RUnlock() diff --git a/wire/net/test/connhub_internal_test.go b/wire/net/test/connhub_internal_test.go index f061c765d..b0f63ce49 100644 --- a/wire/net/test/connhub_internal_test.go +++ b/wire/net/test/connhub_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -18,6 +18,10 @@ import ( "context" "testing" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -121,7 +125,7 @@ func TestConnHub_Close(t *testing.T) { l := c.NewNetListener(wiretest.NewRandomAddress(rng)) l2 := NewNetListener() l2.Close() - err := c.insert(wiretest.NewRandomAccount(rng).Address(), l2) + err := c.insert(map[wallet.BackendID]wire.Address{channel.TestBackendID: wiretest.NewRandomAccount(rng).Address()}, l2) assert.NoError(err) assert.Error(c.Close()) assert.True(l.IsClosed()) diff --git a/wire/net/test/dialer.go b/wire/net/test/dialer.go index b6bec0738..b6f9a991f 100644 --- a/wire/net/test/dialer.go +++ b/wire/net/test/dialer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "net" "sync/atomic" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" @@ -44,7 +46,7 @@ func NewDialer(hub *ConnHub) *Dialer { } // Dial tries to connect to a wire. -func (d *Dialer) Dial(ctx context.Context, address wire.Address, ser wire.EnvelopeSerializer) (wirenet.Conn, error) { +func (d *Dialer) Dial(ctx context.Context, address map[wallet.BackendID]wire.Address, ser wire.EnvelopeSerializer) (wirenet.Conn, error) { if d.IsClosed() { return nil, errors.New("dialer closed") } diff --git a/wire/net/test/listenermap.go b/wire/net/test/listenermap.go index 8592002be..1bc4c7ff6 100644 --- a/wire/net/test/listenermap.go +++ b/wire/net/test/listenermap.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,13 +17,17 @@ package test import ( "sync" + "perun.network/go-perun/channel" + + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "perun.network/go-perun/wire" ) // listenerMapEntry is a key-value entry inside a listener map. type listenerMapEntry struct { - key wire.Address + key map[wallet.BackendID]wire.Address value *Listener } @@ -35,9 +39,9 @@ type listenerMap struct { // findEntry is not mutexed, and is only to be called from within the type's // other functions. -func (m *listenerMap) findEntry(key wire.Address) (listenerMapEntry, int, bool) { +func (m *listenerMap) findEntry(key map[wallet.BackendID]wire.Address) (listenerMapEntry, int, bool) { for i, v := range m.entries { - if v.key.Equal(key) { + if channel.EqualWireMaps(v.key, key) { return v, i, true } } @@ -45,7 +49,7 @@ func (m *listenerMap) findEntry(key wire.Address) (listenerMapEntry, int, bool) return listenerMapEntry{}, -1, false } -func (m *listenerMap) find(key wire.Address) (*Listener, bool) { +func (m *listenerMap) find(key map[wallet.BackendID]wire.Address) (*Listener, bool) { m.mutex.RLock() defer m.mutex.RUnlock() @@ -55,7 +59,7 @@ func (m *listenerMap) find(key wire.Address) (*Listener, bool) { return nil, false } -func (m *listenerMap) insert(key wire.Address, value *Listener) error { +func (m *listenerMap) insert(key map[wallet.BackendID]wire.Address, value *Listener) error { m.mutex.Lock() defer m.mutex.Unlock() if _, _, ok := m.findEntry(key); ok { @@ -65,7 +69,7 @@ func (m *listenerMap) insert(key wire.Address, value *Listener) error { return nil } -func (m *listenerMap) erase(key wire.Address) error { +func (m *listenerMap) erase(key map[wallet.BackendID]wire.Address) error { m.mutex.Lock() defer m.mutex.Unlock() diff --git a/wire/perunio/serializer/serializer.go b/wire/perunio/serializer/serializer.go index ff86ae6d1..d1172542e 100644 --- a/wire/perunio/serializer/serializer.go +++ b/wire/perunio/serializer/serializer.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -31,7 +31,7 @@ type serializer struct{} // Encode encodes the envelope into the wire using perunio encoding format. func (serializer) Encode(w io.Writer, env *wire.Envelope) error { - if err := perunio.Encode(w, env.Sender, env.Recipient); err != nil { + if err := perunio.Encode(w, wire.AddressDecMap(env.Sender), wire.AddressDecMap(env.Recipient)); err != nil { return err } return wire.EncodeMsg(env.Msg, w) @@ -40,14 +40,22 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) error { // 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{} - env.Sender = wire.NewAddress() - if err = perunio.Decode(r, env.Sender); err != nil { - return env, errors.WithMessage(err, "decoding sender address") + err = perunio.Decode(r, (*wire.AddressDecMap)(&env.Sender)) + if err != nil { + return env, errors.WithMessage(err, "decoding sender addresses") } - env.Recipient = wire.NewAddress() - if err = perunio.Decode(r, env.Recipient); err != nil { - return env, errors.WithMessage(err, "decoding recipient address") + + // Decode the Recipient map + err = perunio.Decode(r, (*wire.AddressDecMap)(&env.Recipient)) + if err != nil { + return env, errors.WithMessage(err, "decoding recipient addresses") } + + // Decode the message env.Msg, err = wire.DecodeMsg(r) - return env, err + if err != nil { + return env, errors.WithMessage(err, "decoding message") + } + + return env, nil } diff --git a/wire/perunio/test/serializertest.go b/wire/perunio/test/serializertest.go index 709f5eec9..fbb793eb9 100644 --- a/wire/perunio/test/serializertest.go +++ b/wire/perunio/test/serializertest.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -46,6 +46,7 @@ func genericDecodeEncodeTest(t *testing.T, serializers ...perunio.Serializer) { } w.Close() }() + t.Log("v: ", v) dest := reflect.New(reflect.TypeOf(v).Elem()) err := perunio.Decode(br, dest.Interface().(perunio.Serializer)) diff --git a/wire/protobuf/proposalmsgs.go b/wire/protobuf/proposalmsgs.go index af1b6b44b..b046ed6bc 100644 --- a/wire/protobuf/proposalmsgs.go +++ b/wire/protobuf/proposalmsgs.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -15,6 +15,8 @@ package protobuf import ( + "bytes" + "encoding/binary" "fmt" "math" "math/big" @@ -126,35 +128,65 @@ func ToChannelProposalRejMsg(protoEnvMsg *Envelope_ChannelProposalRejMsg) (msg * } // ToWalletAddr converts a protobuf wallet address to a wallet.Address. -func ToWalletAddr(protoAddr []byte) (wallet.Address, error) { - addr := wallet.NewAddress() - return addr, addr.UnmarshalBinary(protoAddr) +func ToWalletAddr(protoAddr *Address) (map[wallet.BackendID]wallet.Address, error) { + addrMap := make(map[wallet.BackendID]wallet.Address) + for i := range protoAddr.AddressMapping { + var k int32 + if err := binary.Read(bytes.NewReader(protoAddr.AddressMapping[i].Key), 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 { + return nil, fmt.Errorf("failed to unmarshal address for key %d: %w", k, err) + } + + addrMap[wallet.BackendID(k)] = addr + } + return addrMap, nil +} + +// 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 { + var k int32 + if err := binary.Read(bytes.NewReader(protoAddr.AddressMapping[i].Key), 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 { + return nil, fmt.Errorf("failed to unmarshal address for key %d: %w", k, err) + } + + addrMap[wallet.BackendID(k)] = addr + } + return addrMap, nil } // ToWalletAddrs converts protobuf wallet addresses to a slice of wallet.Address. -func ToWalletAddrs(protoAddrs [][]byte) ([]wallet.Address, error) { - addrs := make([]wallet.Address, len(protoAddrs)) +func ToWalletAddrs(protoAddrs []*Address) ([]map[wallet.BackendID]wallet.Address, error) { + addrs := make([]map[wallet.BackendID]wallet.Address, len(protoAddrs)) for i := range protoAddrs { - addrs[i] = wallet.NewAddress() - err := addrs[i].UnmarshalBinary(protoAddrs[i]) + addrMap, err := ToWalletAddr(protoAddrs[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th address", i) } + addrs[i] = addrMap } return addrs, nil } // ToWireAddrs converts protobuf wire addresses to a slice of wire.Address. -func ToWireAddrs(protoAddrs [][]byte) ([]wire.Address, error) { - addrs := make([]wire.Address, len(protoAddrs)) - for i := range protoAddrs { - addrs[i] = wire.NewAddress() - err := addrs[i].UnmarshalBinary(protoAddrs[i]) +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) if err != nil { return nil, errors.WithMessagef(err, "%d'th address", i) } } - return addrs, nil + return addrMap, nil } // ToBaseChannelProposal converts a protobuf BaseChannelProposal to a client BaseChannelProposal. @@ -167,9 +199,6 @@ func ToBaseChannelProposal(protoProp *BaseChannelProposal) (prop client.BaseChan return prop, errors.WithMessage(err, "init bals") } prop.FundingAgreement = ToBalances(protoProp.FundingAgreement) - if err != nil { - return prop, errors.WithMessage(err, "funding agreement") - } prop.App, prop.InitData, err = ToAppAndData(protoProp.App, protoProp.InitData) return prop, err } @@ -187,7 +216,7 @@ func ToApp(protoApp []byte) (app channel.App, err error) { app = channel.NoApp() return app, nil } - appDef := channel.NewAppID() + appDef, _ := channel.NewAppID() err = appDef.UnmarshalBinary(protoApp) if err != nil { return app, err @@ -203,7 +232,7 @@ func ToAppAndData(protoApp, protoData []byte) (app channel.App, data channel.Dat data = channel.NoData() return app, data, nil } - appDef := channel.NewAppID() + appDef, _ := channel.NewAppID() err = appDef.UnmarshalBinary(protoApp) if err != nil { return nil, nil, err @@ -216,12 +245,37 @@ func ToAppAndData(protoApp, protoData []byte) (app channel.App, data channel.Dat return app, data, data.UnmarshalBinary(protoData) } +// ToIntSlice converts a [][]byte field from a protobuf message to a []int. +func ToIntSlice(backends [][]byte) ([]wallet.BackendID, error) { + ints := make([]wallet.BackendID, len(backends)) + + for i, backend := range backends { + if len(backend) != 4 { //nolint:gomnd + return nil, fmt.Errorf("backend %d length is not 4 bytes", i) + } + + var value int32 + err := binary.Read(bytes.NewReader(backend), binary.BigEndian, &value) + if err != nil { + return nil, fmt.Errorf("failed to convert backend %d bytes to int: %w", i, err) + } + + ints[i] = wallet.BackendID(value) + } + + return ints, nil +} + // 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) + if err != nil { + return nil, errors.WithMessage(err, "backends") + } alloc.Assets = make([]channel.Asset, len(protoAlloc.Assets)) for i := range protoAlloc.Assets { - alloc.Assets[i] = channel.NewAsset() + alloc.Assets[i] = channel.NewAsset(alloc.Backends[i]) err = alloc.Assets[i].UnmarshalBinary(protoAlloc.Assets[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th asset", i) @@ -366,15 +420,58 @@ func FromChannelProposalRejMsg(msg *client.ChannelProposalRejMsg) (_ *Envelope_C } // FromWalletAddr converts a wallet.Address to a protobuf wallet address. -func FromWalletAddr(addr wallet.Address) ([]byte, error) { - return addr.MarshalBinary() +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)) + + addressBytes, err := address.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("failed to marshal address for key %d: %w", key, err) + } + + addressMappings = append(addressMappings, &AddressMapping{ + Key: keyBytes, + Address: addressBytes, + }) + } + + return &Address{ + AddressMapping: addressMappings, + }, nil +} + +// FromWireAddr converts a wallet.Address to a protobuf wire address. +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)) + + addressBytes, err := address.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("failed to marshal address for key %d: %w", key, err) + } + + addressMappings = append(addressMappings, &AddressMapping{ + Key: keyBytes, + Address: addressBytes, + }) + } + + return &Address{ + AddressMapping: addressMappings, + }, nil } // FromWalletAddrs converts a slice of wallet.Address to protobuf wallet addresses. -func FromWalletAddrs(addrs []wallet.Address) (protoAddrs [][]byte, err error) { - protoAddrs = make([][]byte, len(addrs)) +func FromWalletAddrs(addrs []map[wallet.BackendID]wallet.Address) (protoAddrs []*Address, err error) { + protoAddrs = make([]*Address, len(addrs)) for i := range addrs { - protoAddrs[i], err = addrs[i].MarshalBinary() + protoAddrs[i], err = FromWalletAddr(addrs[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th address", i) } @@ -383,10 +480,10 @@ func FromWalletAddrs(addrs []wallet.Address) (protoAddrs [][]byte, err error) { } // FromWireAddrs converts a slice of wire.Address to protobuf wire addresses. -func FromWireAddrs(addrs []wire.Address) (protoAddrs [][]byte, err error) { - protoAddrs = make([][]byte, len(addrs)) +func FromWireAddrs(addrs []map[wallet.BackendID]wire.Address) (protoAddrs []*Address, err error) { + protoAddrs = make([]*Address, len(addrs)) for i := range addrs { - protoAddrs[i], err = addrs[i].MarshalBinary() + protoAddrs[i], err = FromWireAddr(addrs[i]) if err != nil { return nil, errors.WithMessagef(err, "%d'th address", i) } @@ -453,6 +550,11 @@ 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:gomnd + binary.BigEndian.PutUint32(protoAlloc.Backends[i], uint32(alloc.Backends[i])) + } protoAlloc.Assets = make([][]byte, len(alloc.Assets)) for i := range alloc.Assets { protoAlloc.Assets[i], err = alloc.Assets[i].MarshalBinary() diff --git a/wire/protobuf/serializer.go b/wire/protobuf/serializer.go index ef18432f8..f2a03dc07 100644 --- a/wire/protobuf/serializer.go +++ b/wire/protobuf/serializer.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "fmt" "io" + "perun.network/go-perun/wallet" + "github.com/pkg/errors" "google.golang.org/protobuf/proto" "perun.network/go-perun/client" @@ -79,7 +81,8 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) (err error) { //nolint if err != nil { return err } - protoEnv.Sender, protoEnv.Recipient, err = marshalSenderRecipient(env) + sender, recipient, err := marshalSenderRecipient(env) + protoEnv.Sender, protoEnv.Recipient = sender, recipient if err != nil { return err } @@ -87,12 +90,12 @@ func (serializer) Encode(w io.Writer, env *wire.Envelope) (err error) { //nolint return writeEnvelope(w, protoEnv) } -func marshalSenderRecipient(env *wire.Envelope) ([]byte, []byte, error) { - sender, err := env.Sender.MarshalBinary() +func marshalSenderRecipient(env *wire.Envelope) (*Address, *Address, error) { + sender, err := FromWireAddr(env.Sender) if err != nil { return nil, nil, errors.WithMessage(err, "marshalling sender address") } - recipient, err := env.Recipient.MarshalBinary() + recipient, err := FromWireAddr(env.Recipient) return sender, recipient, errors.WithMessage(err, "marshalling recipient address") } @@ -118,7 +121,8 @@ func (serializer) Decode(r io.Reader) (env *wire.Envelope, err error) { //nolint return nil, err } - env.Sender, env.Recipient, err = unmarshalSenderRecipient(protoEnv) + sender, recipient, err := unmarshalSenderRecipient(protoEnv) + env.Sender, env.Recipient = sender, recipient if err != nil { return nil, err } @@ -179,12 +183,14 @@ func readEnvelope(r io.Reader) (*Envelope, error) { return &protoEnv, errors.Wrap(proto.Unmarshal(data, &protoEnv), "unmarshalling envelope") } -func unmarshalSenderRecipient(protoEnv *Envelope) (wire.Address, wire.Address, error) { - sender := wire.NewAddress() - if err := sender.UnmarshalBinary(protoEnv.Sender); err != nil { +func unmarshalSenderRecipient(protoEnv *Envelope) (map[wallet.BackendID]wire.Address, map[wallet.BackendID]wire.Address, error) { + sender, err := ToWireAddr(protoEnv.Sender) + if err != nil { return nil, nil, errors.Wrap(err, "unmarshalling sender address") } - recipient := wire.NewAddress() - err := recipient.UnmarshalBinary(protoEnv.Recipient) - return sender, recipient, errors.Wrap(err, "unmarshalling recipient address") + recipient, err1 := ToWireAddr(protoEnv.Recipient) + if err1 != nil { + return nil, nil, errors.Wrap(err, "unmarshalling recipient address") + } + return sender, recipient, nil } diff --git a/wire/protobuf/updatemsgs.go b/wire/protobuf/updatemsgs.go index dc30b00ce..6b6d580ae 100644 --- a/wire/protobuf/updatemsgs.go +++ b/wire/protobuf/updatemsgs.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. diff --git a/wire/protobuf/wire.pb.go b/wire/protobuf/wire.pb.go index 49a605f46..d95bfa8fa 100644 --- a/wire/protobuf/wire.pb.go +++ b/wire/protobuf/wire.pb.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -16,17 +16,18 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v4.25.2 +// protoc-gen-go v1.34.2 +// protoc v3.20.3 // source: wire.proto package protobuf import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -44,12 +45,13 @@ type Envelope struct { unknownFields protoimpl.UnknownFields // sender of the message. - Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Sender *Address `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // intended recipient of the message. - Recipient []byte `protobuf:"bytes,2,opt,name=recipient,proto3" json:"recipient,omitempty"` + Recipient *Address `protobuf:"bytes,2,opt,name=recipient,proto3" json:"recipient,omitempty"` // msg should contain on the valid message types. // // Types that are assignable to Msg: + // // *Envelope_PingMsg // *Envelope_PongMsg // *Envelope_ShutdownMsg @@ -102,14 +104,14 @@ func (*Envelope) Descriptor() ([]byte, []int) { return file_wire_proto_rawDescGZIP(), []int{0} } -func (x *Envelope) GetSender() []byte { +func (x *Envelope) GetSender() *Address { if x != nil { return x.Sender } return nil } -func (x *Envelope) GetRecipient() []byte { +func (x *Envelope) GetRecipient() *Address { if x != nil { return x.Recipient } @@ -446,6 +448,110 @@ func (x *Balances) GetBalances() []*Balance { return nil } +// AddressMapping represents an element of a address mapping. +type AddressMapping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Address []byte `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *AddressMapping) Reset() { + *x = AddressMapping{} + if protoimpl.UnsafeEnabled { + mi := &file_wire_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddressMapping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddressMapping) ProtoMessage() {} + +func (x *AddressMapping) ProtoReflect() protoreflect.Message { + mi := &file_wire_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddressMapping.ProtoReflect.Descriptor instead. +func (*AddressMapping) Descriptor() ([]byte, []int) { + return file_wire_proto_rawDescGZIP(), []int{3} +} + +func (x *AddressMapping) GetKey() []byte { + if x != nil { + return x.Key + } + return nil +} + +func (x *AddressMapping) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +// Address represents the map of addresses for each participant in the channel. +type Address struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AddressMapping []*AddressMapping `protobuf:"bytes,1,rep,name=address_mapping,json=addressMapping,proto3" json:"address_mapping,omitempty"` +} + +func (x *Address) Reset() { + *x = Address{} + if protoimpl.UnsafeEnabled { + mi := &file_wire_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Address) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Address) ProtoMessage() {} + +func (x *Address) ProtoReflect() protoreflect.Message { + mi := &file_wire_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Address.ProtoReflect.Descriptor instead. +func (*Address) Descriptor() ([]byte, []int) { + return file_wire_proto_rawDescGZIP(), []int{4} +} + +func (x *Address) GetAddressMapping() []*AddressMapping { + if x != nil { + return x.AddressMapping + } + return nil +} + // IndexMap represents the mapping of a participant indices in a sub allocation // or a virtual channel funding proposal to the corresponding indices in the // parent channel. @@ -460,7 +566,7 @@ type IndexMap struct { func (x *IndexMap) Reset() { *x = IndexMap{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[3] + mi := &file_wire_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -473,7 +579,7 @@ func (x *IndexMap) String() string { func (*IndexMap) ProtoMessage() {} func (x *IndexMap) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[3] + mi := &file_wire_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -486,7 +592,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_proto_rawDescGZIP(), []int{5} } func (x *IndexMap) GetIndexMap() []uint32 { @@ -510,7 +616,7 @@ type SubAlloc struct { func (x *SubAlloc) Reset() { *x = SubAlloc{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[4] + mi := &file_wire_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -523,7 +629,7 @@ func (x *SubAlloc) String() string { func (*SubAlloc) ProtoMessage() {} func (x *SubAlloc) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[4] + mi := &file_wire_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -536,7 +642,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_proto_rawDescGZIP(), []int{6} } func (x *SubAlloc) GetId() []byte { @@ -566,15 +672,16 @@ type Allocation struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Assets [][]byte `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"` - Balances *Balances `protobuf:"bytes,2,opt,name=balances,proto3" json:"balances,omitempty"` - Locked []*SubAlloc `protobuf:"bytes,3,rep,name=locked,proto3" json:"locked,omitempty"` + Backends [][]byte `protobuf:"bytes,1,rep,name=backends,proto3" json:"backends,omitempty"` + Assets [][]byte `protobuf:"bytes,2,rep,name=assets,proto3" json:"assets,omitempty"` + Balances *Balances `protobuf:"bytes,3,opt,name=balances,proto3" json:"balances,omitempty"` + Locked []*SubAlloc `protobuf:"bytes,4,rep,name=locked,proto3" json:"locked,omitempty"` } func (x *Allocation) Reset() { *x = Allocation{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[5] + mi := &file_wire_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -587,7 +694,7 @@ func (x *Allocation) String() string { func (*Allocation) ProtoMessage() {} func (x *Allocation) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[5] + mi := &file_wire_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -600,7 +707,14 @@ 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_proto_rawDescGZIP(), []int{7} +} + +func (x *Allocation) GetBackends() [][]byte { + if x != nil { + return x.Backends + } + return nil } func (x *Allocation) GetAssets() [][]byte { @@ -642,7 +756,7 @@ type BaseChannelProposal struct { func (x *BaseChannelProposal) Reset() { *x = BaseChannelProposal{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[6] + mi := &file_wire_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -655,7 +769,7 @@ func (x *BaseChannelProposal) String() string { func (*BaseChannelProposal) ProtoMessage() {} func (x *BaseChannelProposal) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[6] + mi := &file_wire_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -668,7 +782,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_proto_rawDescGZIP(), []int{8} } func (x *BaseChannelProposal) GetProposalId() []byte { @@ -733,7 +847,7 @@ type BaseChannelProposalAcc struct { func (x *BaseChannelProposalAcc) Reset() { *x = BaseChannelProposalAcc{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[7] + mi := &file_wire_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -746,7 +860,7 @@ func (x *BaseChannelProposalAcc) String() string { func (*BaseChannelProposalAcc) ProtoMessage() {} func (x *BaseChannelProposalAcc) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[7] + mi := &file_wire_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -759,7 +873,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_proto_rawDescGZIP(), []int{9} } func (x *BaseChannelProposalAcc) GetProposalId() []byte { @@ -782,19 +896,19 @@ type Params struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - ChallengeDuration uint64 `protobuf:"varint,2,opt,name=challenge_duration,json=challengeDuration,proto3" json:"challenge_duration,omitempty"` - Parts [][]byte `protobuf:"bytes,3,rep,name=parts,proto3" json:"parts,omitempty"` - App []byte `protobuf:"bytes,4,opt,name=app,proto3" json:"app,omitempty"` - 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"` + Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ChallengeDuration uint64 `protobuf:"varint,2,opt,name=challenge_duration,json=challengeDuration,proto3" json:"challenge_duration,omitempty"` + Parts []*Address `protobuf:"bytes,3,rep,name=parts,proto3" json:"parts,omitempty"` + App []byte `protobuf:"bytes,4,opt,name=app,proto3" json:"app,omitempty"` + 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"` } func (x *Params) Reset() { *x = Params{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[8] + mi := &file_wire_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -807,7 +921,7 @@ func (x *Params) String() string { func (*Params) ProtoMessage() {} func (x *Params) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[8] + mi := &file_wire_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -820,7 +934,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_proto_rawDescGZIP(), []int{10} } func (x *Params) GetId() []byte { @@ -837,7 +951,7 @@ func (x *Params) GetChallengeDuration() uint64 { return 0 } -func (x *Params) GetParts() [][]byte { +func (x *Params) GetParts() []*Address { if x != nil { return x.Parts } @@ -889,7 +1003,7 @@ type State struct { func (x *State) Reset() { *x = State{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[9] + mi := &file_wire_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -902,7 +1016,7 @@ func (x *State) String() string { func (*State) ProtoMessage() {} func (x *State) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[9] + mi := &file_wire_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -915,7 +1029,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_proto_rawDescGZIP(), []int{11} } func (x *State) GetId() []byte { @@ -973,7 +1087,7 @@ type Transaction struct { func (x *Transaction) Reset() { *x = Transaction{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[10] + mi := &file_wire_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -986,7 +1100,7 @@ func (x *Transaction) String() string { func (*Transaction) ProtoMessage() {} func (x *Transaction) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[10] + mi := &file_wire_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -999,7 +1113,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_proto_rawDescGZIP(), []int{12} } func (x *Transaction) GetState() *State { @@ -1030,7 +1144,7 @@ type SignedState struct { func (x *SignedState) Reset() { *x = SignedState{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[11] + mi := &file_wire_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1043,7 +1157,7 @@ func (x *SignedState) String() string { func (*SignedState) ProtoMessage() {} func (x *SignedState) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[11] + mi := &file_wire_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1056,7 +1170,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_proto_rawDescGZIP(), []int{13} } func (x *SignedState) GetParams() *Params { @@ -1093,7 +1207,7 @@ type ChannelUpdate struct { func (x *ChannelUpdate) Reset() { *x = ChannelUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[12] + mi := &file_wire_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1106,7 +1220,7 @@ func (x *ChannelUpdate) String() string { func (*ChannelUpdate) ProtoMessage() {} func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[12] + mi := &file_wire_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1119,7 +1233,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_proto_rawDescGZIP(), []int{14} } func (x *ChannelUpdate) GetState() *State { @@ -1148,7 +1262,7 @@ type PingMsg struct { func (x *PingMsg) Reset() { *x = PingMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[13] + mi := &file_wire_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1161,7 +1275,7 @@ func (x *PingMsg) String() string { func (*PingMsg) ProtoMessage() {} func (x *PingMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[13] + mi := &file_wire_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1174,7 +1288,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_proto_rawDescGZIP(), []int{15} } func (x *PingMsg) GetCreated() int64 { @@ -1196,7 +1310,7 @@ type PongMsg struct { func (x *PongMsg) Reset() { *x = PongMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[14] + mi := &file_wire_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1209,7 +1323,7 @@ func (x *PongMsg) String() string { func (*PongMsg) ProtoMessage() {} func (x *PongMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[14] + mi := &file_wire_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1222,7 +1336,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_proto_rawDescGZIP(), []int{16} } func (x *PongMsg) GetCreated() int64 { @@ -1244,7 +1358,7 @@ type ShutdownMsg struct { func (x *ShutdownMsg) Reset() { *x = ShutdownMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[15] + mi := &file_wire_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1257,7 +1371,7 @@ func (x *ShutdownMsg) String() string { func (*ShutdownMsg) ProtoMessage() {} func (x *ShutdownMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[15] + mi := &file_wire_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1270,7 +1384,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_proto_rawDescGZIP(), []int{17} } func (x *ShutdownMsg) GetReason() string { @@ -1292,7 +1406,7 @@ type AuthResponseMsg struct { func (x *AuthResponseMsg) Reset() { *x = AuthResponseMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[16] + mi := &file_wire_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1305,7 +1419,7 @@ func (x *AuthResponseMsg) String() string { func (*AuthResponseMsg) ProtoMessage() {} func (x *AuthResponseMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[16] + mi := &file_wire_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1318,7 +1432,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_proto_rawDescGZIP(), []int{18} } func (x *AuthResponseMsg) GetSignature() []byte { @@ -1335,14 +1449,14 @@ type LedgerChannelProposalMsg struct { unknownFields protoimpl.UnknownFields BaseChannelProposal *BaseChannelProposal `protobuf:"bytes,1,opt,name=base_channel_proposal,json=baseChannelProposal,proto3" json:"base_channel_proposal,omitempty"` - Participant []byte `protobuf:"bytes,2,opt,name=participant,proto3" json:"participant,omitempty"` - Peers [][]byte `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + Participant *Address `protobuf:"bytes,2,opt,name=participant,proto3" json:"participant,omitempty"` + Peers []*Address `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` } func (x *LedgerChannelProposalMsg) Reset() { *x = LedgerChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[17] + mi := &file_wire_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1355,7 +1469,7 @@ func (x *LedgerChannelProposalMsg) String() string { func (*LedgerChannelProposalMsg) ProtoMessage() {} func (x *LedgerChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[17] + mi := &file_wire_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1368,7 +1482,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_proto_rawDescGZIP(), []int{19} } func (x *LedgerChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1378,14 +1492,14 @@ func (x *LedgerChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal return nil } -func (x *LedgerChannelProposalMsg) GetParticipant() []byte { +func (x *LedgerChannelProposalMsg) GetParticipant() *Address { if x != nil { return x.Participant } return nil } -func (x *LedgerChannelProposalMsg) GetPeers() [][]byte { +func (x *LedgerChannelProposalMsg) GetPeers() []*Address { if x != nil { return x.Peers } @@ -1399,13 +1513,13 @@ type LedgerChannelProposalAccMsg struct { unknownFields protoimpl.UnknownFields BaseChannelProposalAcc *BaseChannelProposalAcc `protobuf:"bytes,1,opt,name=base_channel_proposal_acc,json=baseChannelProposalAcc,proto3" json:"base_channel_proposal_acc,omitempty"` - Participant []byte `protobuf:"bytes,2,opt,name=participant,proto3" json:"participant,omitempty"` + Participant *Address `protobuf:"bytes,2,opt,name=participant,proto3" json:"participant,omitempty"` } func (x *LedgerChannelProposalAccMsg) Reset() { *x = LedgerChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[18] + mi := &file_wire_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1418,7 +1532,7 @@ func (x *LedgerChannelProposalAccMsg) String() string { func (*LedgerChannelProposalAccMsg) ProtoMessage() {} func (x *LedgerChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[18] + mi := &file_wire_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1431,7 +1545,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_proto_rawDescGZIP(), []int{20} } func (x *LedgerChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1441,7 +1555,7 @@ func (x *LedgerChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelPr return nil } -func (x *LedgerChannelProposalAccMsg) GetParticipant() []byte { +func (x *LedgerChannelProposalAccMsg) GetParticipant() *Address { if x != nil { return x.Participant } @@ -1461,7 +1575,7 @@ type SubChannelProposalMsg struct { func (x *SubChannelProposalMsg) Reset() { *x = SubChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[19] + mi := &file_wire_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1474,7 +1588,7 @@ func (x *SubChannelProposalMsg) String() string { func (*SubChannelProposalMsg) ProtoMessage() {} func (x *SubChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[19] + mi := &file_wire_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1487,7 +1601,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_proto_rawDescGZIP(), []int{21} } func (x *SubChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1516,7 +1630,7 @@ type SubChannelProposalAccMsg struct { func (x *SubChannelProposalAccMsg) Reset() { *x = SubChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[20] + mi := &file_wire_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1529,7 +1643,7 @@ func (x *SubChannelProposalAccMsg) String() string { func (*SubChannelProposalAccMsg) ProtoMessage() {} func (x *SubChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[20] + mi := &file_wire_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1542,7 +1656,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_proto_rawDescGZIP(), []int{22} } func (x *SubChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1559,8 +1673,8 @@ type VirtualChannelProposalMsg struct { unknownFields protoimpl.UnknownFields BaseChannelProposal *BaseChannelProposal `protobuf:"bytes,1,opt,name=base_channel_proposal,json=baseChannelProposal,proto3" json:"base_channel_proposal,omitempty"` - Proposer []byte `protobuf:"bytes,2,opt,name=proposer,proto3" json:"proposer,omitempty"` - Peers [][]byte `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + Proposer *Address `protobuf:"bytes,2,opt,name=proposer,proto3" json:"proposer,omitempty"` + Peers []*Address `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` Parents [][]byte `protobuf:"bytes,4,rep,name=parents,proto3" json:"parents,omitempty"` IndexMaps []*IndexMap `protobuf:"bytes,5,rep,name=index_maps,json=indexMaps,proto3" json:"index_maps,omitempty"` } @@ -1568,7 +1682,7 @@ type VirtualChannelProposalMsg struct { func (x *VirtualChannelProposalMsg) Reset() { *x = VirtualChannelProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[21] + mi := &file_wire_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1581,7 +1695,7 @@ func (x *VirtualChannelProposalMsg) String() string { func (*VirtualChannelProposalMsg) ProtoMessage() {} func (x *VirtualChannelProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[21] + mi := &file_wire_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1708,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_proto_rawDescGZIP(), []int{23} } func (x *VirtualChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposal { @@ -1604,14 +1718,14 @@ func (x *VirtualChannelProposalMsg) GetBaseChannelProposal() *BaseChannelProposa return nil } -func (x *VirtualChannelProposalMsg) GetProposer() []byte { +func (x *VirtualChannelProposalMsg) GetProposer() *Address { if x != nil { return x.Proposer } return nil } -func (x *VirtualChannelProposalMsg) GetPeers() [][]byte { +func (x *VirtualChannelProposalMsg) GetPeers() []*Address { if x != nil { return x.Peers } @@ -1639,13 +1753,13 @@ type VirtualChannelProposalAccMsg struct { unknownFields protoimpl.UnknownFields BaseChannelProposalAcc *BaseChannelProposalAcc `protobuf:"bytes,1,opt,name=base_channel_proposal_acc,json=baseChannelProposalAcc,proto3" json:"base_channel_proposal_acc,omitempty"` - Responder []byte `protobuf:"bytes,2,opt,name=responder,proto3" json:"responder,omitempty"` + Responder *Address `protobuf:"bytes,2,opt,name=responder,proto3" json:"responder,omitempty"` } func (x *VirtualChannelProposalAccMsg) Reset() { *x = VirtualChannelProposalAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[22] + mi := &file_wire_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1658,7 +1772,7 @@ func (x *VirtualChannelProposalAccMsg) String() string { func (*VirtualChannelProposalAccMsg) ProtoMessage() {} func (x *VirtualChannelProposalAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[22] + mi := &file_wire_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1671,7 +1785,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_proto_rawDescGZIP(), []int{24} } func (x *VirtualChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelProposalAcc { @@ -1681,7 +1795,7 @@ func (x *VirtualChannelProposalAccMsg) GetBaseChannelProposalAcc() *BaseChannelP return nil } -func (x *VirtualChannelProposalAccMsg) GetResponder() []byte { +func (x *VirtualChannelProposalAccMsg) GetResponder() *Address { if x != nil { return x.Responder } @@ -1701,7 +1815,7 @@ type ChannelProposalRejMsg struct { func (x *ChannelProposalRejMsg) Reset() { *x = ChannelProposalRejMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[23] + mi := &file_wire_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1714,7 +1828,7 @@ func (x *ChannelProposalRejMsg) String() string { func (*ChannelProposalRejMsg) ProtoMessage() {} func (x *ChannelProposalRejMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[23] + mi := &file_wire_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1727,7 +1841,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_proto_rawDescGZIP(), []int{25} } func (x *ChannelProposalRejMsg) GetProposalId() []byte { @@ -1757,7 +1871,7 @@ type ChannelUpdateMsg struct { func (x *ChannelUpdateMsg) Reset() { *x = ChannelUpdateMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[24] + mi := &file_wire_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1770,7 +1884,7 @@ func (x *ChannelUpdateMsg) String() string { func (*ChannelUpdateMsg) ProtoMessage() {} func (x *ChannelUpdateMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[24] + mi := &file_wire_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1783,7 +1897,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_proto_rawDescGZIP(), []int{26} } func (x *ChannelUpdateMsg) GetChannelUpdate() *ChannelUpdate { @@ -1815,7 +1929,7 @@ type VirtualChannelFundingProposalMsg struct { func (x *VirtualChannelFundingProposalMsg) Reset() { *x = VirtualChannelFundingProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[25] + mi := &file_wire_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1828,7 +1942,7 @@ func (x *VirtualChannelFundingProposalMsg) String() string { func (*VirtualChannelFundingProposalMsg) ProtoMessage() {} func (x *VirtualChannelFundingProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[25] + mi := &file_wire_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1841,7 +1955,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_proto_rawDescGZIP(), []int{27} } func (x *VirtualChannelFundingProposalMsg) GetChannelUpdateMsg() *ChannelUpdateMsg { @@ -1879,7 +1993,7 @@ type VirtualChannelSettlementProposalMsg struct { func (x *VirtualChannelSettlementProposalMsg) Reset() { *x = VirtualChannelSettlementProposalMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[26] + mi := &file_wire_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1892,7 +2006,7 @@ func (x *VirtualChannelSettlementProposalMsg) String() string { func (*VirtualChannelSettlementProposalMsg) ProtoMessage() {} func (x *VirtualChannelSettlementProposalMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[26] + mi := &file_wire_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1905,7 +2019,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_proto_rawDescGZIP(), []int{28} } func (x *VirtualChannelSettlementProposalMsg) GetChannelUpdateMsg() *ChannelUpdateMsg { @@ -1936,7 +2050,7 @@ type ChannelUpdateAccMsg struct { func (x *ChannelUpdateAccMsg) Reset() { *x = ChannelUpdateAccMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[27] + mi := &file_wire_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1949,7 +2063,7 @@ func (x *ChannelUpdateAccMsg) String() string { func (*ChannelUpdateAccMsg) ProtoMessage() {} func (x *ChannelUpdateAccMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[27] + mi := &file_wire_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1962,7 +2076,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_proto_rawDescGZIP(), []int{29} } func (x *ChannelUpdateAccMsg) GetChannelId() []byte { @@ -2000,7 +2114,7 @@ type ChannelUpdateRejMsg struct { func (x *ChannelUpdateRejMsg) Reset() { *x = ChannelUpdateRejMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[28] + mi := &file_wire_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2013,7 +2127,7 @@ func (x *ChannelUpdateRejMsg) String() string { func (*ChannelUpdateRejMsg) ProtoMessage() {} func (x *ChannelUpdateRejMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[28] + mi := &file_wire_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2026,7 +2140,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_proto_rawDescGZIP(), []int{30} } func (x *ChannelUpdateRejMsg) GetChannelId() []byte { @@ -2063,7 +2177,7 @@ type ChannelSyncMsg struct { func (x *ChannelSyncMsg) Reset() { *x = ChannelSyncMsg{} if protoimpl.UnsafeEnabled { - mi := &file_wire_proto_msgTypes[29] + mi := &file_wire_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2076,7 +2190,7 @@ func (x *ChannelSyncMsg) String() string { func (*ChannelSyncMsg) ProtoMessage() {} func (x *ChannelSyncMsg) ProtoReflect() protoreflect.Message { - mi := &file_wire_proto_msgTypes[29] + mi := &file_wire_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2089,7 +2203,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_proto_rawDescGZIP(), []int{31} } func (x *ChannelSyncMsg) GetPhase() uint32 { @@ -2110,327 +2224,348 @@ 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, - 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, + 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x22, 0xf5, 0x0c, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x65, + 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x12, 0x30, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 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, 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, + 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, 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, - 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, - 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, - 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x25, - 0x0a, 0x0e, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 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, + 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, 0x3c, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x4d, + 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x42, 0x0a, 0x0f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0e, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 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, 0x9e, 0x01, 0x0a, + 0x0a, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x18, 0x02, 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, 0x03, 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, 0x04, 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, - 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, + 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, 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, 0xe9, 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, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, + 0x70, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x14, 0x0a, + 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 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, 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, - 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, + 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, 0xce, 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, 0x34, 0x0a, 0x0b, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x12, + 0x28, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0xb1, 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, 0x34, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, + 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, + 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 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, 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, + 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x22, 0x97, 0x02, + 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, + 0x2e, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x12, + 0x28, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x70, 0x65, 0x72, 0x75, 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 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, 0xae, 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, 0x30, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x65, 0x72, 0x75, + 0x6e, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 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, 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, - 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, + 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, 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 ( @@ -2445,88 +2580,100 @@ func file_wire_proto_rawDescGZIP() []byte { return file_wire_proto_rawDescData } -var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 30) -var file_wire_proto_goTypes = []interface{}{ +var file_wire_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_wire_proto_goTypes = []any{ (*Envelope)(nil), // 0: perunwire.Envelope (*Balance)(nil), // 1: perunwire.Balance (*Balances)(nil), // 2: perunwire.Balances - (*IndexMap)(nil), // 3: perunwire.IndexMap - (*SubAlloc)(nil), // 4: perunwire.SubAlloc - (*Allocation)(nil), // 5: perunwire.Allocation - (*BaseChannelProposal)(nil), // 6: perunwire.BaseChannelProposal - (*BaseChannelProposalAcc)(nil), // 7: perunwire.BaseChannelProposalAcc - (*Params)(nil), // 8: perunwire.Params - (*State)(nil), // 9: perunwire.State - (*Transaction)(nil), // 10: perunwire.Transaction - (*SignedState)(nil), // 11: perunwire.SignedState - (*ChannelUpdate)(nil), // 12: perunwire.ChannelUpdate - (*PingMsg)(nil), // 13: perunwire.PingMsg - (*PongMsg)(nil), // 14: perunwire.PongMsg - (*ShutdownMsg)(nil), // 15: perunwire.ShutdownMsg - (*AuthResponseMsg)(nil), // 16: perunwire.AuthResponseMsg - (*LedgerChannelProposalMsg)(nil), // 17: perunwire.LedgerChannelProposalMsg - (*LedgerChannelProposalAccMsg)(nil), // 18: perunwire.LedgerChannelProposalAccMsg - (*SubChannelProposalMsg)(nil), // 19: perunwire.SubChannelProposalMsg - (*SubChannelProposalAccMsg)(nil), // 20: perunwire.SubChannelProposalAccMsg - (*VirtualChannelProposalMsg)(nil), // 21: perunwire.VirtualChannelProposalMsg - (*VirtualChannelProposalAccMsg)(nil), // 22: perunwire.VirtualChannelProposalAccMsg - (*ChannelProposalRejMsg)(nil), // 23: perunwire.ChannelProposalRejMsg - (*ChannelUpdateMsg)(nil), // 24: perunwire.ChannelUpdateMsg - (*VirtualChannelFundingProposalMsg)(nil), // 25: perunwire.VirtualChannelFundingProposalMsg - (*VirtualChannelSettlementProposalMsg)(nil), // 26: perunwire.VirtualChannelSettlementProposalMsg - (*ChannelUpdateAccMsg)(nil), // 27: perunwire.ChannelUpdateAccMsg - (*ChannelUpdateRejMsg)(nil), // 28: perunwire.ChannelUpdateRejMsg - (*ChannelSyncMsg)(nil), // 29: perunwire.ChannelSyncMsg + (*AddressMapping)(nil), // 3: perunwire.AddressMapping + (*Address)(nil), // 4: perunwire.Address + (*IndexMap)(nil), // 5: perunwire.IndexMap + (*SubAlloc)(nil), // 6: perunwire.SubAlloc + (*Allocation)(nil), // 7: perunwire.Allocation + (*BaseChannelProposal)(nil), // 8: perunwire.BaseChannelProposal + (*BaseChannelProposalAcc)(nil), // 9: perunwire.BaseChannelProposalAcc + (*Params)(nil), // 10: perunwire.Params + (*State)(nil), // 11: perunwire.State + (*Transaction)(nil), // 12: perunwire.Transaction + (*SignedState)(nil), // 13: perunwire.SignedState + (*ChannelUpdate)(nil), // 14: perunwire.ChannelUpdate + (*PingMsg)(nil), // 15: perunwire.PingMsg + (*PongMsg)(nil), // 16: perunwire.PongMsg + (*ShutdownMsg)(nil), // 17: perunwire.ShutdownMsg + (*AuthResponseMsg)(nil), // 18: perunwire.AuthResponseMsg + (*LedgerChannelProposalMsg)(nil), // 19: perunwire.LedgerChannelProposalMsg + (*LedgerChannelProposalAccMsg)(nil), // 20: perunwire.LedgerChannelProposalAccMsg + (*SubChannelProposalMsg)(nil), // 21: perunwire.SubChannelProposalMsg + (*SubChannelProposalAccMsg)(nil), // 22: perunwire.SubChannelProposalAccMsg + (*VirtualChannelProposalMsg)(nil), // 23: perunwire.VirtualChannelProposalMsg + (*VirtualChannelProposalAccMsg)(nil), // 24: perunwire.VirtualChannelProposalAccMsg + (*ChannelProposalRejMsg)(nil), // 25: perunwire.ChannelProposalRejMsg + (*ChannelUpdateMsg)(nil), // 26: perunwire.ChannelUpdateMsg + (*VirtualChannelFundingProposalMsg)(nil), // 27: perunwire.VirtualChannelFundingProposalMsg + (*VirtualChannelSettlementProposalMsg)(nil), // 28: perunwire.VirtualChannelSettlementProposalMsg + (*ChannelUpdateAccMsg)(nil), // 29: perunwire.ChannelUpdateAccMsg + (*ChannelUpdateRejMsg)(nil), // 30: perunwire.ChannelUpdateRejMsg + (*ChannelSyncMsg)(nil), // 31: perunwire.ChannelSyncMsg } var file_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 - 16, // 3: perunwire.Envelope.auth_response_msg:type_name -> perunwire.AuthResponseMsg - 17, // 4: perunwire.Envelope.ledger_channel_proposal_msg:type_name -> perunwire.LedgerChannelProposalMsg - 18, // 5: perunwire.Envelope.ledger_channel_proposal_acc_msg:type_name -> perunwire.LedgerChannelProposalAccMsg - 19, // 6: perunwire.Envelope.sub_channel_proposal_msg:type_name -> perunwire.SubChannelProposalMsg - 20, // 7: perunwire.Envelope.sub_channel_proposal_acc_msg:type_name -> perunwire.SubChannelProposalAccMsg - 21, // 8: perunwire.Envelope.virtual_channel_proposal_msg:type_name -> perunwire.VirtualChannelProposalMsg - 22, // 9: perunwire.Envelope.virtual_channel_proposal_acc_msg:type_name -> perunwire.VirtualChannelProposalAccMsg - 23, // 10: perunwire.Envelope.channel_proposal_rej_msg:type_name -> perunwire.ChannelProposalRejMsg - 24, // 11: perunwire.Envelope.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg - 25, // 12: perunwire.Envelope.virtual_channel_funding_proposal_msg:type_name -> perunwire.VirtualChannelFundingProposalMsg - 26, // 13: perunwire.Envelope.virtual_channel_settlement_proposal_msg:type_name -> perunwire.VirtualChannelSettlementProposalMsg - 27, // 14: perunwire.Envelope.channel_update_acc_msg:type_name -> perunwire.ChannelUpdateAccMsg - 28, // 15: perunwire.Envelope.channel_update_rej_msg:type_name -> perunwire.ChannelUpdateRejMsg - 29, // 16: perunwire.Envelope.channel_sync_msg:type_name -> perunwire.ChannelSyncMsg - 1, // 17: perunwire.Balances.balances:type_name -> perunwire.Balance - 1, // 18: perunwire.SubAlloc.bals:type_name -> perunwire.Balance - 3, // 19: perunwire.SubAlloc.index_map:type_name -> perunwire.IndexMap - 2, // 20: perunwire.Allocation.balances:type_name -> perunwire.Balances - 4, // 21: perunwire.Allocation.locked:type_name -> perunwire.SubAlloc - 5, // 22: perunwire.BaseChannelProposal.init_bals:type_name -> perunwire.Allocation - 2, // 23: perunwire.BaseChannelProposal.funding_agreement:type_name -> perunwire.Balances - 5, // 24: perunwire.State.allocation:type_name -> perunwire.Allocation - 9, // 25: perunwire.Transaction.state:type_name -> perunwire.State - 8, // 26: perunwire.SignedState.params:type_name -> perunwire.Params - 9, // 27: perunwire.SignedState.state:type_name -> perunwire.State - 9, // 28: perunwire.ChannelUpdate.state:type_name -> perunwire.State - 6, // 29: perunwire.LedgerChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal - 7, // 30: perunwire.LedgerChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc - 6, // 31: perunwire.SubChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal - 7, // 32: perunwire.SubChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc - 6, // 33: perunwire.VirtualChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal - 3, // 34: perunwire.VirtualChannelProposalMsg.index_maps:type_name -> perunwire.IndexMap - 7, // 35: perunwire.VirtualChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc - 12, // 36: perunwire.ChannelUpdateMsg.channel_update:type_name -> perunwire.ChannelUpdate - 24, // 37: perunwire.VirtualChannelFundingProposalMsg.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg - 11, // 38: perunwire.VirtualChannelFundingProposalMsg.initial:type_name -> perunwire.SignedState - 3, // 39: perunwire.VirtualChannelFundingProposalMsg.index_map:type_name -> perunwire.IndexMap - 24, // 40: perunwire.VirtualChannelSettlementProposalMsg.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg - 11, // 41: perunwire.VirtualChannelSettlementProposalMsg.final:type_name -> perunwire.SignedState - 10, // 42: perunwire.ChannelSyncMsg.current_tx:type_name -> perunwire.Transaction - 43, // [43:43] is the sub-list for method output_type - 43, // [43:43] is the sub-list for method input_type - 43, // [43:43] is the sub-list for extension type_name - 43, // [43:43] is the sub-list for extension extendee - 0, // [0:43] is the sub-list for field type_name + 4, // 0: perunwire.Envelope.sender:type_name -> perunwire.Address + 4, // 1: perunwire.Envelope.recipient:type_name -> perunwire.Address + 15, // 2: perunwire.Envelope.ping_msg:type_name -> perunwire.PingMsg + 16, // 3: perunwire.Envelope.pong_msg:type_name -> perunwire.PongMsg + 17, // 4: perunwire.Envelope.shutdown_msg:type_name -> perunwire.ShutdownMsg + 18, // 5: perunwire.Envelope.auth_response_msg:type_name -> perunwire.AuthResponseMsg + 19, // 6: perunwire.Envelope.ledger_channel_proposal_msg:type_name -> perunwire.LedgerChannelProposalMsg + 20, // 7: perunwire.Envelope.ledger_channel_proposal_acc_msg:type_name -> perunwire.LedgerChannelProposalAccMsg + 21, // 8: perunwire.Envelope.sub_channel_proposal_msg:type_name -> perunwire.SubChannelProposalMsg + 22, // 9: perunwire.Envelope.sub_channel_proposal_acc_msg:type_name -> perunwire.SubChannelProposalAccMsg + 23, // 10: perunwire.Envelope.virtual_channel_proposal_msg:type_name -> perunwire.VirtualChannelProposalMsg + 24, // 11: perunwire.Envelope.virtual_channel_proposal_acc_msg:type_name -> perunwire.VirtualChannelProposalAccMsg + 25, // 12: perunwire.Envelope.channel_proposal_rej_msg:type_name -> perunwire.ChannelProposalRejMsg + 26, // 13: perunwire.Envelope.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg + 27, // 14: perunwire.Envelope.virtual_channel_funding_proposal_msg:type_name -> perunwire.VirtualChannelFundingProposalMsg + 28, // 15: perunwire.Envelope.virtual_channel_settlement_proposal_msg:type_name -> perunwire.VirtualChannelSettlementProposalMsg + 29, // 16: perunwire.Envelope.channel_update_acc_msg:type_name -> perunwire.ChannelUpdateAccMsg + 30, // 17: perunwire.Envelope.channel_update_rej_msg:type_name -> perunwire.ChannelUpdateRejMsg + 31, // 18: perunwire.Envelope.channel_sync_msg:type_name -> perunwire.ChannelSyncMsg + 1, // 19: perunwire.Balances.balances:type_name -> perunwire.Balance + 3, // 20: perunwire.Address.address_mapping:type_name -> perunwire.AddressMapping + 1, // 21: perunwire.SubAlloc.bals:type_name -> perunwire.Balance + 5, // 22: perunwire.SubAlloc.index_map:type_name -> perunwire.IndexMap + 2, // 23: perunwire.Allocation.balances:type_name -> perunwire.Balances + 6, // 24: perunwire.Allocation.locked:type_name -> perunwire.SubAlloc + 7, // 25: perunwire.BaseChannelProposal.init_bals:type_name -> perunwire.Allocation + 2, // 26: perunwire.BaseChannelProposal.funding_agreement:type_name -> perunwire.Balances + 4, // 27: perunwire.Params.parts:type_name -> perunwire.Address + 7, // 28: perunwire.State.allocation:type_name -> perunwire.Allocation + 11, // 29: perunwire.Transaction.state:type_name -> perunwire.State + 10, // 30: perunwire.SignedState.params:type_name -> perunwire.Params + 11, // 31: perunwire.SignedState.state:type_name -> perunwire.State + 11, // 32: perunwire.ChannelUpdate.state:type_name -> perunwire.State + 8, // 33: perunwire.LedgerChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal + 4, // 34: perunwire.LedgerChannelProposalMsg.participant:type_name -> perunwire.Address + 4, // 35: perunwire.LedgerChannelProposalMsg.peers:type_name -> perunwire.Address + 9, // 36: perunwire.LedgerChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc + 4, // 37: perunwire.LedgerChannelProposalAccMsg.participant:type_name -> perunwire.Address + 8, // 38: perunwire.SubChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal + 9, // 39: perunwire.SubChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc + 8, // 40: perunwire.VirtualChannelProposalMsg.base_channel_proposal:type_name -> perunwire.BaseChannelProposal + 4, // 41: perunwire.VirtualChannelProposalMsg.proposer:type_name -> perunwire.Address + 4, // 42: perunwire.VirtualChannelProposalMsg.peers:type_name -> perunwire.Address + 5, // 43: perunwire.VirtualChannelProposalMsg.index_maps:type_name -> perunwire.IndexMap + 9, // 44: perunwire.VirtualChannelProposalAccMsg.base_channel_proposal_acc:type_name -> perunwire.BaseChannelProposalAcc + 4, // 45: perunwire.VirtualChannelProposalAccMsg.responder:type_name -> perunwire.Address + 14, // 46: perunwire.ChannelUpdateMsg.channel_update:type_name -> perunwire.ChannelUpdate + 26, // 47: perunwire.VirtualChannelFundingProposalMsg.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg + 13, // 48: perunwire.VirtualChannelFundingProposalMsg.initial:type_name -> perunwire.SignedState + 5, // 49: perunwire.VirtualChannelFundingProposalMsg.index_map:type_name -> perunwire.IndexMap + 26, // 50: perunwire.VirtualChannelSettlementProposalMsg.channel_update_msg:type_name -> perunwire.ChannelUpdateMsg + 13, // 51: perunwire.VirtualChannelSettlementProposalMsg.final:type_name -> perunwire.SignedState + 12, // 52: perunwire.ChannelSyncMsg.current_tx:type_name -> perunwire.Transaction + 53, // [53:53] is the sub-list for method output_type + 53, // [53:53] is the sub-list for method input_type + 53, // [53:53] is the sub-list for extension type_name + 53, // [53:53] is the sub-list for extension extendee + 0, // [0:53] is the sub-list for field type_name } func init() { file_wire_proto_init() } @@ -2535,7 +2682,7 @@ func file_wire_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_wire_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Envelope); i { case 0: return &v.state @@ -2547,7 +2694,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Balance); i { case 0: return &v.state @@ -2559,7 +2706,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*Balances); i { case 0: return &v.state @@ -2571,7 +2718,31 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*AddressMapping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_wire_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*Address); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_wire_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*IndexMap); i { case 0: return &v.state @@ -2583,7 +2754,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*SubAlloc); i { case 0: return &v.state @@ -2595,7 +2766,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*Allocation); i { case 0: return &v.state @@ -2607,7 +2778,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*BaseChannelProposal); i { case 0: return &v.state @@ -2619,7 +2790,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*BaseChannelProposalAcc); i { case 0: return &v.state @@ -2631,7 +2802,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*Params); i { case 0: return &v.state @@ -2643,7 +2814,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[11].Exporter = func(v any, i int) any { switch v := v.(*State); i { case 0: return &v.state @@ -2655,7 +2826,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[12].Exporter = func(v any, i int) any { switch v := v.(*Transaction); i { case 0: return &v.state @@ -2667,7 +2838,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[13].Exporter = func(v any, i int) any { switch v := v.(*SignedState); i { case 0: return &v.state @@ -2679,7 +2850,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[14].Exporter = func(v any, i int) any { switch v := v.(*ChannelUpdate); i { case 0: return &v.state @@ -2691,7 +2862,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[15].Exporter = func(v any, i int) any { switch v := v.(*PingMsg); i { case 0: return &v.state @@ -2703,7 +2874,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*PongMsg); i { case 0: return &v.state @@ -2715,7 +2886,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[17].Exporter = func(v any, i int) any { switch v := v.(*ShutdownMsg); i { case 0: return &v.state @@ -2727,7 +2898,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[18].Exporter = func(v any, i int) any { switch v := v.(*AuthResponseMsg); i { case 0: return &v.state @@ -2739,7 +2910,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[19].Exporter = func(v any, i int) any { switch v := v.(*LedgerChannelProposalMsg); i { case 0: return &v.state @@ -2751,7 +2922,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*LedgerChannelProposalAccMsg); i { case 0: return &v.state @@ -2763,7 +2934,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[21].Exporter = func(v any, i int) any { switch v := v.(*SubChannelProposalMsg); i { case 0: return &v.state @@ -2775,7 +2946,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*SubChannelProposalAccMsg); i { case 0: return &v.state @@ -2787,7 +2958,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[23].Exporter = func(v any, i int) any { switch v := v.(*VirtualChannelProposalMsg); i { case 0: return &v.state @@ -2799,7 +2970,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[24].Exporter = func(v any, i int) any { switch v := v.(*VirtualChannelProposalAccMsg); i { case 0: return &v.state @@ -2811,7 +2982,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[25].Exporter = func(v any, i int) any { switch v := v.(*ChannelProposalRejMsg); i { case 0: return &v.state @@ -2823,7 +2994,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[26].Exporter = func(v any, i int) any { switch v := v.(*ChannelUpdateMsg); i { case 0: return &v.state @@ -2835,7 +3006,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[27].Exporter = func(v any, i int) any { switch v := v.(*VirtualChannelFundingProposalMsg); i { case 0: return &v.state @@ -2847,7 +3018,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[28].Exporter = func(v any, i int) any { switch v := v.(*VirtualChannelSettlementProposalMsg); i { case 0: return &v.state @@ -2859,7 +3030,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[29].Exporter = func(v any, i int) any { switch v := v.(*ChannelUpdateAccMsg); i { case 0: return &v.state @@ -2871,7 +3042,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[30].Exporter = func(v any, i int) any { switch v := v.(*ChannelUpdateRejMsg); i { case 0: return &v.state @@ -2883,7 +3054,7 @@ func file_wire_proto_init() { return nil } } - file_wire_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_wire_proto_msgTypes[31].Exporter = func(v any, i int) any { switch v := v.(*ChannelSyncMsg); i { case 0: return &v.state @@ -2896,7 +3067,7 @@ func file_wire_proto_init() { } } } - file_wire_proto_msgTypes[0].OneofWrappers = []interface{}{ + file_wire_proto_msgTypes[0].OneofWrappers = []any{ (*Envelope_PingMsg)(nil), (*Envelope_PongMsg)(nil), (*Envelope_ShutdownMsg)(nil), @@ -2921,7 +3092,7 @@ func file_wire_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_wire_proto_rawDesc, NumEnums: 0, - NumMessages: 30, + NumMessages: 32, NumExtensions: 0, NumServices: 0, }, diff --git a/wire/protobuf/wire.proto b/wire/protobuf/wire.proto index dec4edbcb..7381ec790 100644 --- a/wire/protobuf/wire.proto +++ b/wire/protobuf/wire.proto @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -24,9 +24,9 @@ option go_package = "perun.network/go-perun/wire/protobuf"; // the sender and the intended receiver. message Envelope { // sender of the message. - bytes sender = 1; + Address sender = 1; // intended recipient of the message. - bytes recipient = 2; + Address recipient = 2; // msg should contain on the valid message types. oneof msg { PingMsg ping_msg = 3; @@ -61,6 +61,17 @@ message Balances { repeated Balance balances = 1; } +// AddressMapping represents an element of a address mapping. +message AddressMapping { + bytes key = 1; + bytes address = 2; +} + +// Address represents the map of addresses for each participant in the channel. +message Address { + repeated AddressMapping address_mapping = 1; +} + // IndexMap represents the mapping of a participant indices in a sub allocation // or a virtual channel funding proposal to the corresponding indices in the // parent channel. @@ -77,9 +88,10 @@ message SubAlloc { // Allocation represents channel.Allocation. message Allocation { - repeated bytes assets = 1; - Balances balances = 2; - repeated SubAlloc locked = 3; + repeated bytes backends = 1; + repeated bytes assets = 2; + Balances balances = 3; + repeated SubAlloc locked = 4; } // BaseChannelProposal represents client.BaseChannelProposal. @@ -103,7 +115,7 @@ message BaseChannelProposalAcc { message Params { bytes id = 1; uint64 challenge_duration = 2; - repeated bytes parts = 3; + repeated Address parts = 3; bytes app = 4; bytes nonce = 5; bool ledger_channel = 6; @@ -163,14 +175,14 @@ message AuthResponseMsg { // LedgerChannelProposalMsg represents client.LedgerChannelProposalMsg. message LedgerChannelProposalMsg { BaseChannelProposal base_channel_proposal = 1; - bytes participant = 2; - repeated bytes peers = 3; + Address participant = 2; + repeated Address peers = 3; } // LedgerChannelProposalAccMsg represents client.LedgerChannelProposalAccMsg. message LedgerChannelProposalAccMsg { BaseChannelProposalAcc base_channel_proposal_acc = 1; - bytes participant = 2; + Address participant = 2; } // SubChannelProposalMsg represents client.SubChannelProposalMsg. @@ -187,8 +199,8 @@ message SubChannelProposalAccMsg { // VirtualChannelProposalMsg represents client.VirtualChannelProposalMsg. message VirtualChannelProposalMsg { BaseChannelProposal base_channel_proposal = 1; - bytes proposer = 2; - repeated bytes peers = 3; + Address proposer = 2; + repeated Address peers = 3; repeated bytes parents = 4; repeated IndexMap index_maps = 5; } @@ -196,7 +208,7 @@ message VirtualChannelProposalMsg { // VirtualChannelProposalAccMsg represents client.VirtualChannelProposalAccMsg. message VirtualChannelProposalAccMsg { BaseChannelProposalAcc base_channel_proposal_acc = 1; - bytes responder = 2; + Address responder = 2; } // ChannelProposalRejMsg represents client.ChannelProposalRejMsg. diff --git a/wire/test/bustest.go b/wire/test/bustest.go index ab8f34cb4..2534f37be 100644 --- a/wire/test/bustest.go +++ b/wire/test/bustest.go @@ -1,4 +1,4 @@ -// Copyright 2020 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -19,6 +19,8 @@ import ( "testing" "time" + "perun.network/go-perun/wallet" + "github.com/stretchr/testify/require" "perun.network/go-perun/wire" @@ -26,7 +28,10 @@ import ( ) // timeout testNoReceive sub-test. -const testNoReceiveTimeout = 10 * time.Millisecond +const ( + testNoReceiveTimeout = 10 * time.Millisecond + TestBackendID = 0 +) // GenericBusTest tests the general functionality of a bus in the happy case: it // tests that messages sent over the bus arrive at the correct destination. The @@ -38,7 +43,7 @@ const testNoReceiveTimeout = 10 * time.Millisecond // can either return the same bus twice, or separately select a bus to subscribe // the client to, and a bus the client should use for publishing messages. func GenericBusTest(t *testing.T, - busAssigner func(wire.Account) (pub wire.Bus, sub wire.Bus), + busAssigner func(map[wallet.BackendID]wire.Account) (pub wire.Bus, sub wire.Bus), numClients, numMsgs int, ) { t.Helper() @@ -49,13 +54,13 @@ func GenericBusTest(t *testing.T, type Client struct { r *wire.Relay pub, sub wire.Bus - id wire.Account + id map[wallet.BackendID]wire.Account } clients := make([]Client, numClients) for i := range clients { clients[i].r = wire.NewRelay() - clients[i].id = NewRandomAccount(rng) + clients[i].id = NewRandomAccountMap(rng, TestBackendID) clients[i].pub, clients[i].sub = busAssigner(clients[i].id) } @@ -95,14 +100,14 @@ func GenericBusTest(t *testing.T, } sender, recipient := sender, recipient origEnv := &wire.Envelope{ - Sender: clients[sender].id.Address(), - Recipient: clients[recipient].id.Address(), + Sender: wire.AddressMapfromAccountMap(clients[sender].id), + Recipient: wire.AddressMapfromAccountMap(clients[recipient].id), Msg: wire.NewPingMsg(), } // Only subscribe to the current sender. recv := wire.NewReceiver() err := clients[recipient].r.Subscribe(recv, func(e *wire.Envelope) bool { - return e.Sender.Equal(clients[sender].id.Address()) + return equalMaps(e.Sender, wire.AddressMapfromAccountMap(clients[sender].id)) }) require.NoError(t, err) @@ -139,7 +144,7 @@ func GenericBusTest(t *testing.T, // publishing. testPublishAndReceive(t, func() { for i := range clients { - err := clients[i].sub.SubscribeClient(clients[i].r, clients[i].id.Address()) + err := clients[i].sub.SubscribeClient(clients[i].r, wire.AddressMapfromAccountMap(clients[i].id)) require.NoError(t, err) } }) @@ -149,3 +154,15 @@ func GenericBusTest(t *testing.T, // publishing. testPublishAndReceive(t, func() {}) } + +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 + } + } + return true +} diff --git a/wire/test/msgstest.go b/wire/test/msgstest.go index 5d876097a..9a64977d7 100644 --- a/wire/test/msgstest.go +++ b/wire/test/msgstest.go @@ -1,4 +1,4 @@ -// Copyright 2022 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -40,7 +40,7 @@ func AuthMsgsSerializationTest(t *testing.T, serializerTest func(t *testing.T, m t.Helper() rng := pkgtest.Prng(t) - testMsg, err := wire.NewAuthResponseMsg(NewRandomAccount(rng)) + testMsg, err := wire.NewAuthResponseMsg(NewRandomAccountMap(rng, TestBackendID)) if err != nil { t.Fatal(err) } diff --git a/wire/test/randomizer.go b/wire/test/randomizer.go index 39b3f4c88..60387919e 100644 --- a/wire/test/randomizer.go +++ b/wire/test/randomizer.go @@ -1,4 +1,4 @@ -// Copyright 2019 - See NOTICE file for copyright holders. +// Copyright 2024 - 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. @@ -17,6 +17,8 @@ package test import ( "math/rand" + "perun.network/go-perun/wallet" + "perun.network/go-perun/wire" ) @@ -43,8 +45,8 @@ func SetNewRandomAccount(f NewRandomAccountFunc) { } // NewRandomAddress returns a new random address. -func NewRandomAddress(rng *rand.Rand) wire.Address { - return newRandomAddress(rng) +func NewRandomAddress(rng *rand.Rand) map[wallet.BackendID]wire.Address { + return map[wallet.BackendID]wire.Address{TestBackendID: newRandomAddress(rng)} } // NewRandomAccount returns a new random account. @@ -52,9 +54,23 @@ func NewRandomAccount(rng *rand.Rand) wire.Account { return newRandomAccount(rng) } +// NewRandomAccountMap returns a new random account. +func NewRandomAccountMap(rng *rand.Rand, bID wallet.BackendID) map[wallet.BackendID]wire.Account { + return map[wallet.BackendID]wire.Account{bID: newRandomAccount(rng)} +} + // NewRandomAddresses returns a slice of random peer addresses. -func NewRandomAddresses(rng *rand.Rand, n int) []wire.Address { - addresses := make([]wire.Address, n) +func NewRandomAddresses(rng *rand.Rand, n int) []map[wallet.BackendID]wire.Address { + addresses := make([]map[wallet.BackendID]wire.Address, n) + for i := range addresses { + addresses[i] = NewRandomAddress(rng) + } + return addresses +} + +// NewRandomAddressesMap returns a slice of random peer addresses. +func NewRandomAddressesMap(rng *rand.Rand, n int) []map[wallet.BackendID]wire.Address { + addresses := make([]map[wallet.BackendID]wire.Address, n) for i := range addresses { addresses[i] = NewRandomAddress(rng) }