diff --git a/apps/payment/app.go b/apps/payment/app.go index af2252c70..4619656db 100644 --- a/apps/payment/app.go +++ b/apps/payment/app.go @@ -20,17 +20,16 @@ import ( "perun.network/go-perun/channel" "perun.network/go-perun/log" - "perun.network/go-perun/wallet" ) // App is a payment app. type App struct { - Addr wallet.Address + ID channel.AppID } // Def returns the address of this payment app. -func (a *App) Def() wallet.Address { - return a.Addr +func (a *App) Def() channel.AppID { + return a.ID } // NewData returns a new instance of data specific to the payment app, diff --git a/apps/payment/app_internal_test.go b/apps/payment/app_internal_test.go index 4a511a89e..7834afc65 100644 --- a/apps/payment/app_internal_test.go +++ b/apps/payment/app_internal_test.go @@ -22,15 +22,14 @@ import ( "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" - wallettest "perun.network/go-perun/wallet/test" pkgtest "polycry.pt/poly-go/test" ) func TestApp_Def(t *testing.T) { rng := pkgtest.Prng(t) - def := wallettest.NewRandomAddress(rng) + def := test.NewRandomAppID(rng) app := &App{def} - assert.True(t, def.Equal(app.Def())) + assert.True(t, app.Def().Equal(app.Def())) } func TestApp_ValidInit(t *testing.T) { diff --git a/apps/payment/randomizer.go b/apps/payment/randomizer.go index 43676b246..52c0fb649 100644 --- a/apps/payment/randomizer.go +++ b/apps/payment/randomizer.go @@ -19,7 +19,6 @@ import ( "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" - wtest "perun.network/go-perun/wallet/test" ) // Randomizer implements channel.test.AppRandomizer. @@ -29,7 +28,7 @@ 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{wtest.NewRandomAddress(rng)} + return &App{test.NewRandomAppID(rng)} } // NewRandomData returns NoData because a PaymentApp does not have data. diff --git a/apps/payment/resolver.go b/apps/payment/resolver.go index c5f130206..e12cd7d50 100644 --- a/apps/payment/resolver.go +++ b/apps/payment/resolver.go @@ -16,13 +16,12 @@ package payment import ( "perun.network/go-perun/channel" - "perun.network/go-perun/wallet" ) // Resolver is the payment app resolver. type Resolver struct{} // Resolve returns a payment app with the given definition. -func (b *Resolver) Resolve(def wallet.Address) (channel.App, error) { +func (b *Resolver) Resolve(def channel.AppID) (channel.App, error) { return &App{def}, nil } diff --git a/apps/payment/resolver_internal_test.go b/apps/payment/resolver_internal_test.go index 1b61cccd1..6c7de90b3 100644 --- a/apps/payment/resolver_internal_test.go +++ b/apps/payment/resolver_internal_test.go @@ -22,7 +22,7 @@ import ( _ "perun.network/go-perun/backend/sim" // backend init "perun.network/go-perun/channel" - "perun.network/go-perun/wallet/test" + ctest "perun.network/go-perun/channel/test" pkgtest "polycry.pt/poly-go/test" ) @@ -32,7 +32,7 @@ func TestResolver(t *testing.T) { rng := pkgtest.Prng(t) assert, require := assert.New(t), require.New(t) - def := test.NewRandomAddress(rng) + def := ctest.NewRandomAppID(rng) channel.RegisterAppResolver(def.Equal, &Resolver{}) app, err := channel.Resolve(def) diff --git a/backend/sim/channel/app.go b/backend/sim/channel/app.go new file mode 100644 index 000000000..c7502b3da --- /dev/null +++ b/backend/sim/channel/app.go @@ -0,0 +1,52 @@ +// Copyright 2022 - See NOTICE file for copyright holders. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package channel + +import ( + "math/rand" + + "perun.network/go-perun/backend/sim/wallet" + "perun.network/go-perun/channel" +) + +// AppID represents an app identifier. +type AppID struct { + *wallet.Address +} + +// Equal returns whether the object is equal to the given object. +func (id AppID) Equal(b channel.AppID) bool { + bTyped, ok := b.(AppID) + if !ok { + return false + } + + return id.Address.Equal(bTyped.Address) +} + +// Key returns the key representation of this app identifier. +func (id AppID) Key() channel.AppIDKey { + b, err := id.MarshalBinary() + if err != nil { + panic(err) + } + return channel.AppIDKey(b) +} + +// NewRandomAppID generates a new random app identifier. +func NewRandomAppID(rng *rand.Rand) AppID { + addr := wallet.NewRandomAddress(rng) + return AppID{addr} +} diff --git a/backend/sim/channel/asset_test.go b/backend/sim/channel/asset_test.go index 43e3a7781..7761e391e 100644 --- a/backend/sim/channel/asset_test.go +++ b/backend/sim/channel/asset_test.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package channel_test import ( diff --git a/backend/sim/channel/backend.go b/backend/sim/channel/backend.go index 2ebfafa5b..57e53ad7b 100644 --- a/backend/sim/channel/backend.go +++ b/backend/sim/channel/backend.go @@ -20,6 +20,7 @@ import ( "github.com/pkg/errors" + simwallet "perun.network/go-perun/backend/sim/wallet" "perun.network/go-perun/channel" "perun.network/go-perun/log" "perun.network/go-perun/wallet" @@ -79,3 +80,10 @@ func (b *backend) NewAsset() channel.Asset { addr := Asset{} return &addr } + +// 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 { + addr := &simwallet.Address{} + return AppID{addr} +} diff --git a/backend/sim/channel/init.go b/backend/sim/channel/init.go index 7c29ad6c3..b8dac016b 100644 --- a/backend/sim/channel/init.go +++ b/backend/sim/channel/init.go @@ -15,6 +15,8 @@ package channel import ( + "math/rand" + "perun.network/go-perun/channel" "perun.network/go-perun/channel/test" ) @@ -22,4 +24,7 @@ import ( func init() { channel.SetBackend(new(backend)) test.SetRandomizer(new(randomizer)) + test.SetNewRandomAppID(func(r *rand.Rand) channel.AppID { + return NewRandomAppID(r) + }) } diff --git a/channel/app.go b/channel/app.go index 1e066bf33..417bd41f1 100644 --- a/channel/app.go +++ b/channel/app.go @@ -20,11 +20,20 @@ import ( "github.com/pkg/errors" - "perun.network/go-perun/wallet" "perun.network/go-perun/wire/perunio" ) type ( + // AppID represents an app identifier. + AppID interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + Equal(AppID) bool + + // Key returns the object key which can be used as a map key. + Key() AppIDKey + } + // An App is an abstract interface for an app definition. Either a StateApp or // ActionApp should be implemented. App interface { @@ -33,7 +42,7 @@ type ( // what valid actions or transitions are. // Calling this function on a NoApp panics, so ensure that IsNoApp // returns false. - Def() wallet.Address + Def() AppID // NewData returns a new instance of data specific to NoApp, intialized // to its zero value. @@ -117,11 +126,11 @@ type ( // AppResolver provides functionality to create an App from an Address. // The AppResolver needs to be implemented for every state channel application. AppResolver interface { - // Resolve creates an app from its defining address. It is - // possible that multiple apps are in use, which is why creation happens - // over a central Resolve function. This function is intended to resolve - // app definitions coming in on the wire. - Resolve(wallet.Address) (App, error) + // Resolve creates an app from its defining identifier. It is possible that + // multiple apps are in use, which is why creation happens over a central + // Resolve function. This function is intended to resolve app definitions + // coming in on the wire. + Resolve(AppID) (App, error) } ) @@ -165,7 +174,7 @@ func (d OptAppDec) Decode(r io.Reader) (err error) { *d.App = NoApp() return nil } - appDef := wallet.NewAddress() + appDef := backend.NewAppID() err = perunio.Decode(r, appDef) if err != nil { return errors.WithMessage(err, "decode app address") diff --git a/channel/appregistry.go b/channel/appregistry.go index 191bd65d2..5200081fe 100644 --- a/channel/appregistry.go +++ b/channel/appregistry.go @@ -19,33 +19,38 @@ import ( "sync" "github.com/pkg/errors" - "perun.network/go-perun/wallet" ) // appRegistry is the global registry for `AppResolver`s. -var appRegistry = appReg{singles: make(map[wallet.AddrKey]App)} +var appRegistry = appReg{singles: make(map[AppIDKey]App)} + +// AppIDKey is the key representation of an app identifier. +type AppIDKey string type appReg struct { sync.RWMutex resolvers []appRegEntry - singles map[wallet.AddrKey]App + singles map[AppIDKey]App defaultRes AppResolver } +// AppIDPredicate is a function for filtering app identifiers. +type AppIDPredicate = func(AppID) bool + type appRegEntry struct { - pred wallet.AddressPredicate + pred AppIDPredicate res AppResolver } // Resolve is a global wrapper call to the global `appRegistry`. // This function is intended to resolve app definitions coming in on the wire. -func Resolve(def wallet.Address) (App, error) { +func Resolve(def AppID) (App, error) { appRegistry.RLock() defer appRegistry.RUnlock() if def == nil { log.Panic("resolving nil address") } - if app, ok := appRegistry.singles[wallet.Key(def)]; ok { + if app, ok := appRegistry.singles[def.Key()]; ok { return app, nil } for _, e := range appRegistry.resolvers { @@ -59,14 +64,14 @@ func Resolve(def wallet.Address) (App, error) { return appRegistry.defaultRes.Resolve(def) } -// RegisterAppResolver appends the given `AddressPredicate` and `AppResolver` to the -// global `appRegistry`. -func RegisterAppResolver(pred wallet.AddressPredicate, appRes AppResolver) { +// RegisterAppResolver appends the given `AppIDPredicate` and `AppResolver` to +// the global `appRegistry`. +func RegisterAppResolver(pred AppIDPredicate, appRes AppResolver) { appRegistry.Lock() defer appRegistry.Unlock() if pred == nil || appRes == nil { - log.Panic("nil AddressPredicate or AppResolver") + log.Panic("nil AppIDPredicate or AppResolver") } appRegistry.resolvers = append(appRegistry.resolvers, appRegEntry{pred, appRes}) @@ -81,7 +86,7 @@ func RegisterApp(app App) { log.Panic("nil Address or App") } - appRegistry.singles[wallet.Key(app.Def())] = app + appRegistry.singles[app.Def().Key()] = app } // RegisterDefaultApp allows to specify a default `AppResolver` which is used by diff --git a/channel/appregistry_internal_test.go b/channel/appregistry_internal_test.go index aa573990d..08e74606e 100644 --- a/channel/appregistry_internal_test.go +++ b/channel/appregistry_internal_test.go @@ -15,13 +15,13 @@ package channel import ( + "bytes" + "fmt" "math/rand" "testing" "github.com/stretchr/testify/assert" - "perun.network/go-perun/wallet" - wtest "perun.network/go-perun/wallet/test" "polycry.pt/poly-go/test" ) @@ -31,7 +31,7 @@ func TestAppRegistry(t *testing.T) { backup := struct { resolvers []appRegEntry - singles map[wallet.AddrKey]App + singles map[AppIDKey]App defaultRes AppResolver }{ resolvers: appRegistry.resolvers, @@ -57,7 +57,7 @@ func testAppRegistryPanicsAndErrors(t *testing.T) { t.Helper() resetAppRegistry() assert.Panics(t, func() { RegisterAppResolver(nil, nil) }) - assert.Panics(t, func() { RegisterAppResolver(func(wallet.Address) bool { return true }, nil) }) + assert.Panics(t, func() { RegisterAppResolver(func(AppID) bool { return true }, nil) }) assert.Panics(t, func() { RegisterAppResolver(nil, &MockAppResolver{}) }) assert.Panics(t, func() { RegisterApp(nil) }) @@ -72,9 +72,9 @@ func testAppRegistryPanicsAndErrors(t *testing.T) { assert.Panics(t, func() { Resolve(nil) }) //nolint:errcheck } -type defaultRes struct{ def wallet.Address } +type defaultRes struct{ def AppID } -func (r defaultRes) Resolve(wallet.Address) (App, error) { +func (r defaultRes) Resolve(AppID) (App, error) { return NewMockApp(r.def), nil } @@ -102,13 +102,49 @@ func assertIdentity(t *testing.T, expected App) { } func newRandomMockApp(rng *rand.Rand) App { - return NewMockApp(wtest.NewRandomAddress(rng)) + return NewMockApp(newRandomAppID(rng)) } func resetAppRegistry() { appRegistry.Lock() defer appRegistry.Unlock() appRegistry.resolvers = nil - appRegistry.singles = make(map[wallet.AddrKey]App) + appRegistry.singles = make(map[AppIDKey]App) appRegistry.defaultRes = nil } + +func newRandomAppID(rng *rand.Rand) AppID { + id := appID{} + rng.Read(id[:]) + return id +} + +const appIDLength = 32 + +type appID [appIDLength]byte + +func (id appID) MarshalBinary() (data []byte, err error) { + return id[:], nil +} + +func (id appID) UnmarshalBinary(data []byte) error { + l := len(data) + if l != appIDLength { + return fmt.Errorf("invalid length: %v", l) + } + copy(id[:], data) + return nil +} + +func (id appID) Equal(b AppID) bool { + bTyped, ok := b.(appID) + return ok && bytes.Equal(id[:], bTyped[:]) +} + +func (id appID) Key() AppIDKey { + b, err := id.MarshalBinary() + if err != nil { + panic(err) + } + return AppIDKey(b) +} diff --git a/channel/backend.go b/channel/backend.go index 9d3b9bf26..be57819de 100644 --- a/channel/backend.go +++ b/channel/backend.go @@ -43,6 +43,10 @@ type Backend interface { // NewAsset returns a variable of type Asset, which can be used // for unmarshalling an asset from its binary representation. NewAsset() Asset + + // NewAppID returns an object of type AppID, which can be used for + // unmarshalling an app identifier from its binary representation. + NewAppID() AppID } // SetBackend sets the global channel backend. Must not be called directly but @@ -74,3 +78,9 @@ func Verify(addr wallet.Address, state *State, sig wallet.Sig) (bool, error) { func NewAsset() Asset { return backend.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() +} diff --git a/channel/mock_app.go b/channel/mock_app.go index 86990dfa2..b8dc4f6be 100644 --- a/channel/mock_app.go +++ b/channel/mock_app.go @@ -19,14 +19,12 @@ import ( "fmt" "github.com/pkg/errors" - - "perun.network/go-perun/wallet" ) // MockApp a mocked App whose behaviour is determined by the MockOp passed to it either as State.Data or Action. // It is a StateApp and ActionApp at the same time. type MockApp struct { - definition wallet.Address + definition AppID } var ( @@ -91,12 +89,12 @@ func (o MockOp) Clone() Data { } // NewMockApp create an App with the given definition. -func NewMockApp(definition wallet.Address) *MockApp { +func NewMockApp(definition AppID) *MockApp { return &MockApp{definition: definition} } // Def returns the definition on the MockApp. -func (a MockApp) Def() wallet.Address { +func (a MockApp) Def() AppID { return a.definition } @@ -188,6 +186,6 @@ type MockAppResolver struct{} var _ AppResolver = &MockAppResolver{} // Resolve creates an app from its defining address. -func (m *MockAppResolver) Resolve(addr wallet.Address) (App, error) { +func (m *MockAppResolver) Resolve(addr AppID) (App, error) { return NewMockApp(addr), nil } diff --git a/channel/mock_app_internal_test.go b/channel/mock_app_internal_test.go index 44238fb57..c0f4b1c00 100644 --- a/channel/mock_app_internal_test.go +++ b/channel/mock_app_internal_test.go @@ -19,7 +19,6 @@ import ( "github.com/stretchr/testify/assert" - wallettest "perun.network/go-perun/wallet/test" wiretest "perun.network/go-perun/wire/test" pkgtest "polycry.pt/poly-go/test" ) @@ -27,11 +26,11 @@ import ( func TestMockApp(t *testing.T) { rng := pkgtest.Prng(t) - address := wallettest.NewRandomAddress(rng) - app := NewMockApp(address) + appID := newRandomAppID(rng) + app := NewMockApp(appID) t.Run("App", func(t *testing.T) { - assert.Equal(t, address, app.Def()) + assert.Equal(t, appID, app.Def()) }) t.Run("StateApp", func(t *testing.T) { diff --git a/channel/noapp.go b/channel/noapp.go index b33c1be1b..927129952 100644 --- a/channel/noapp.go +++ b/channel/noapp.go @@ -18,7 +18,6 @@ import ( "github.com/pkg/errors" "perun.network/go-perun/log" - "perun.network/go-perun/wallet" ) type ( @@ -38,7 +37,7 @@ func IsNoApp(a App) bool { var _ StateApp = noApp{} // Def panics and should not be called. -func (noApp) Def() wallet.Address { +func (noApp) Def() AppID { log.Panic("must not call Def() on NoApp") return nil // needed to keep the compiler happy. } diff --git a/channel/test/app_randomizer.go b/channel/test/app_randomizer.go index f8211eddc..3cc69a1cf 100644 --- a/channel/test/app_randomizer.go +++ b/channel/test/app_randomizer.go @@ -78,3 +78,18 @@ func NewRandomAppAndData(rng *rand.Rand, opts ...RandomOpt) (channel.App, channe opt := mergeRandomOpts(opts...) return NewRandomApp(rng, opt), NewRandomData(rng, opt) } + +// NewRandomAppIDFunc is an app identifier randomizer function. +type NewRandomAppIDFunc = func(*rand.Rand) channel.AppID + +var newRandomAppID NewRandomAppIDFunc + +// SetNewRandomAppID sets the function generating a new app identifier. +func SetNewRandomAppID(f NewRandomAppIDFunc) { + newRandomAppID = f +} + +// NewRandomAppID creates a new random channel.AppID. +func NewRandomAppID(rng *rand.Rand) channel.AppID { + return newRandomAppID(rng) +} diff --git a/channel/test/mock_app_randomizer.go b/channel/test/mock_app_randomizer.go index 8b0bbbd3d..a03a9cff0 100644 --- a/channel/test/mock_app_randomizer.go +++ b/channel/test/mock_app_randomizer.go @@ -18,7 +18,6 @@ import ( "math/rand" "perun.network/go-perun/channel" - "perun.network/go-perun/wallet/test" ) // MockAppRandomizer implements the AppRandomizer interface. @@ -26,7 +25,7 @@ type MockAppRandomizer struct{} // NewRandomApp creates a new MockApp with a random address. func (MockAppRandomizer) NewRandomApp(rng *rand.Rand) channel.App { - return channel.NewMockApp(test.NewRandomAddress(rng)) + return channel.NewMockApp(NewRandomAppID(rng)) } // NewRandomData creates a new MockOp with a random operation. diff --git a/channel/test/randomopts.go b/channel/test/randomopts.go index 9bef9f851..ba4d348e3 100644 --- a/channel/test/randomopts.go +++ b/channel/test/randomopts.go @@ -58,7 +58,7 @@ func WithAllocation(alloc *channel.Allocation) RandomOpt { // WithApp sets the `App` that should be used. // Also defines `WithDef`. func WithApp(app channel.App) RandomOpt { - var appDef wallet.Address + var appDef channel.AppID if !channel.IsNoApp(app) { appDef = app.Def() } @@ -275,11 +275,11 @@ func (o RandomOpt) AppRandomizer() AppRandomizer { // AppDef returns the `AppDef` value of the `RandomOpt`. // If not present, returns nil. -func (o RandomOpt) AppDef() wallet.Address { +func (o RandomOpt) AppDef() channel.AppID { if _, ok := o["appDef"]; !ok { return nil } - return o["appDef"].(wallet.Address) + return o["appDef"].(channel.AppID) } // Assets returns the `Assets` value of the `RandomOpt`. diff --git a/client/appchannel_test.go b/client/appchannel_test.go index 98dc9c4e7..fbbeae345 100644 --- a/client/appchannel_test.go +++ b/client/appchannel_test.go @@ -23,7 +23,6 @@ import ( chtest "perun.network/go-perun/channel/test" "perun.network/go-perun/client" clienttest "perun.network/go-perun/client/test" - "perun.network/go-perun/wallet/test" "perun.network/go-perun/wire" pkgtest "polycry.pt/poly-go/test" ) @@ -37,7 +36,7 @@ func TestProgression(t *testing.T) { clienttest.NewPaula(t, setups[1]), } - appAddress := test.NewRandomAddress(rng) + appAddress := chtest.NewRandomAppID(rng) app := channel.NewMockApp(appAddress) channel.RegisterApp(app) diff --git a/wallet/address.go b/wallet/address.go index 56e5ca559..ec89873e1 100644 --- a/wallet/address.go +++ b/wallet/address.go @@ -82,9 +82,6 @@ func CloneAddresses(as []Address) []Address { return clones } -// AddressPredicate is a function for filtering Addresses. -type AddressPredicate = func(Address) bool - // Addresses is a helper type for encoding and decoding address slices in // situations where the length of the slice is known. type Addresses []Address diff --git a/wire/protobuf/init_test.go b/wire/protobuf/init_test.go index fc6fffb57..466ecf6dd 100644 --- a/wire/protobuf/init_test.go +++ b/wire/protobuf/init_test.go @@ -15,5 +15,5 @@ package protobuf_test import ( - _ "perun.network/go-perun/backend/sim/wire" // backend init + _ "perun.network/go-perun/backend/sim" // backend init ) diff --git a/wire/protobuf/proposalmsgs.go b/wire/protobuf/proposalmsgs.go index 59974a3ed..84321cd32 100644 --- a/wire/protobuf/proposalmsgs.go +++ b/wire/protobuf/proposalmsgs.go @@ -169,7 +169,7 @@ func toApp(protoApp []byte) (app channel.App, err error) { app = channel.NoApp() return app, nil } - appDef := wallet.NewAddress() + appDef := channel.NewAppID() err = appDef.UnmarshalBinary(protoApp) if err != nil { return app, err @@ -184,7 +184,7 @@ func toAppAndData(protoApp, protoData []byte) (app channel.App, data channel.Dat data = channel.NoData() return app, data, nil } - appDef := wallet.NewAddress() + appDef := channel.NewAppID() err = appDef.UnmarshalBinary(protoApp) if err != nil { return nil, nil, err diff --git a/wire/protobuf/serializer_test.go b/wire/protobuf/serializer_test.go index b03b1b0e7..d025a7b1d 100644 --- a/wire/protobuf/serializer_test.go +++ b/wire/protobuf/serializer_test.go @@ -17,8 +17,6 @@ package protobuf_test import ( "testing" - _ "perun.network/go-perun/backend/sim/channel" - _ "perun.network/go-perun/backend/sim/wallet" clienttest "perun.network/go-perun/client/test" protobuftest "perun.network/go-perun/wire/protobuf/test" wiretest "perun.network/go-perun/wire/test"