Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore compatibility between go-perun post-0.10.6 and eth-backend #44

Merged
merged 7 commits into from
Jan 31, 2024
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ linters-settings:
goheader:
values:
regexp:
ANY_YEAR: "20(19|20|21|22)" # 2019-2022
ANY_YEAR: "20(19|20|21|22|23|24)" # 2019-2024
template-path: .copyright-header
forbidigo:
forbid:
Expand Down
78 changes: 78 additions & 0 deletions channel/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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.
// 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"

ethwallet "github.com/perun-network/perun-eth-backend/wallet"
ethtestwallet "github.com/perun-network/perun-eth-backend/wallet/test"
"perun.network/go-perun/channel"
)

var _ channel.AppID = new(AppID)

// AppID described an app identifier.
type AppID struct {
*ethwallet.Address
}

// AppIDKey is the key representation of an app identifier.
type AppIDKey string

// Equal compares two AppID objects for equality.
func (a AppID) Equal(b channel.AppID) bool {
bTyped, ok := b.(*AppID)
if !ok {
return false
}
return a.Address.Equal(bTyped.Address)
}

// Key returns the key representation of this app identifier.
func (a AppID) Key() channel.AppIDKey {
b, err := a.MarshalBinary()
if err != nil {
panic(err)
}
return channel.AppIDKey(b)
}

// MarshalBinary marshals the contents of AppID into a byte string.
func (a AppID) MarshalBinary() ([]byte, error) {
data, err := a.Address.MarshalBinary()
if err != nil {
return nil, err
}
return data, nil
}

// UnmarshalBinary converts a bytestring, representing AppID into the AppID struct.
func (a *AppID) UnmarshalBinary(data []byte) error {
addr := &ethwallet.Address{}
err := addr.UnmarshalBinary(data)
if err != nil {
return err
}
appaddr := &AppID{addr}
*a = *appaddr
return nil
}

// NewRandomAppID calls NewRandomAddress to generate a random Ethereum test address.
func NewRandomAppID(rng *rand.Rand) *AppID {
addr := ethtestwallet.NewRandomAddress(rng)
return &AppID{&addr}
}
12 changes: 11 additions & 1 deletion channel/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ func (*Backend) CalcID(p *channel.Params) (id channel.ID) {
return CalcID(p)
}

// NewAppID creates a new app identifier, using an empty ethereum struct.
func (b *Backend) NewAppID() channel.AppID {
addr := &ethwallet.Address{}
return &AppID{addr}
}

// Sign signs the channel state as needed by the ethereum smart contracts.
func (*Backend) Sign(acc wallet.Account, s *channel.State) (wallet.Sig, error) {
return Sign(acc, s)
Expand Down Expand Up @@ -152,7 +158,11 @@ func Verify(addr wallet.Address, s *channel.State, sig wallet.Sig) (bool, error)
func ToEthParams(p *channel.Params) adjudicator.ChannelParams {
var app common.Address
if p.App != nil && !channel.IsNoApp(p.App) {
app = ethwallet.AsEthAddr(p.App.Def())
appDef, ok := p.App.Def().(*AppID)
DragonDev1906 marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
panic("appDef is not of type *AppID")
}
app = ethwallet.AsEthAddr(appDef.Address)
}

return adjudicator.ChannelParams{
Expand Down
81 changes: 3 additions & 78 deletions channel/conclude.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,16 @@ import (
"context"
"fmt"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"

"github.com/perun-network/perun-eth-backend/bindings"
"github.com/perun-network/perun-eth-backend/bindings/adjudicator"
cherrors "github.com/perun-network/perun-eth-backend/channel/errors"
"github.com/perun-network/perun-eth-backend/subscription"
"github.com/pkg/errors"
"perun.network/go-perun/channel"
"perun.network/go-perun/log"
)

const (
secondaryWaitBlocks = 2
adjEventBuffSize = 10
adjHeaderBuffSize = 10
adjEventBuffSize = 10
)

// ensureConcluded ensures that conclude or concludeFinal (for non-final and
Expand All @@ -51,16 +45,8 @@ func (a *Adjudicator) ensureConcluded(ctx context.Context, req channel.Adjudicat
return nil
}

// If the secondary flag is set, we wait for someone else to conclude.
concluded, err := a.waitConcludedSecondary(ctx, req)
if err != nil {
return errors.WithMessage(err, "waiting for secondary conclude")
} else if concluded {
return nil
}

// Wait until we can conclude.
err = a.waitConcludable(ctx, req)
err := a.waitConcludable(ctx, req)
if err != nil {
return fmt.Errorf("waiting for concludability: %w", err)
}
Expand Down Expand Up @@ -160,25 +146,6 @@ func (a *Adjudicator) checkConcludedState(
}
}

func (a *Adjudicator) waitConcludedSecondary(ctx context.Context, req channel.AdjudicatorReq) (concluded bool, err error) {
// In final Register calls, as the non-initiator, we optimistically wait for
// the other party to send the transaction first for
// `secondaryWaitBlocks + TxFinalityDepth` many blocks.
if req.Tx.IsFinal && req.Secondary {
// Create subscription.
sub, events, subErr, err := a.createEventSub(ctx, req.Tx.ID, false)
if err != nil {
return false, errors.WithMessage(err, "subscribing")
}
defer sub.Close()

// Wait for concluded event.
waitBlocks := secondaryWaitBlocks + int(a.txFinalityDepth)
return waitConcludedForNBlocks(ctx, a, events, subErr, waitBlocks)
}
return false, nil
}

func (a *Adjudicator) conclude(ctx context.Context, req channel.AdjudicatorReq, subStates channel.StateMap) error {
// If the on-chain state resulted from forced execution, we do not have a fully-signed state and cannot call concludeFinal.
forceExecuted, err := a.isForceExecuted(ctx, req.Params.ID())
Expand Down Expand Up @@ -335,45 +302,3 @@ func updateEventType(channelID [32]byte) subscription.EventFactory {
}
}
}

// waitConcludedForNBlocks waits for up to numBlocks blocks for a Concluded
// event on the concluded channel. If an event is emitted, true is returned.
// Otherwise, if numBlocks blocks have passed, false is returned.
//
// cr is the ChainReader used for setting up a block header subscription. sub is
// the Concluded event subscription instance.
func waitConcludedForNBlocks(ctx context.Context,
cr ethereum.ChainReader,
concluded <-chan *subscription.Event,
subErr <-chan error,
numBlocks int,
) (bool, error) {
h := make(chan *types.Header, adjHeaderBuffSize)
hsub, err := cr.SubscribeNewHead(ctx, h)
if err != nil {
err = cherrors.CheckIsChainNotReachableError(err)
return false, errors.WithMessage(err, "subscribing to new blocks")
}
defer hsub.Unsubscribe()
for i := 0; i < numBlocks; i++ {
select {
case <-h: // do nothing, wait another block
case _e := <-concluded: // other participant performed transaction
e, ok := _e.Data.(*adjudicator.AdjudicatorChannelUpdate)
if !ok {
log.Panic("wrong event type")
}
if e.Phase == phaseConcluded {
return true, nil
}
case <-ctx.Done():
return false, errors.Wrap(ctx.Err(), "context cancelled")
case err = <-hsub.Err():
err = cherrors.CheckIsChainNotReachableError(err)
return false, errors.WithMessage(err, "header subscription error")
case err = <-subErr:
return false, errors.WithMessage(err, "event subscription error")
}
}
return false, nil
}
40 changes: 14 additions & 26 deletions channel/conclude_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,28 +71,18 @@ func testConcludeFinal(t *testing.T, numParts int) {
ctx, cancel := context.WithTimeout(context.Background(), defaultTxTimeout)
defer cancel()
ct = pkgtest.NewConcurrent(t)
initiator := int(rng.Int31n(int32(numParts))) // pick a random initiator
for i := 0; i < numParts; i++ {
i := i
go ct.StageN("register", numParts, func(t pkgtest.ConcT) {
req := channel.AdjudicatorReq{
Params: params,
Acc: s.Accs[i],
Idx: channel.Index(i),
Tx: tx,
Secondary: (i != initiator),
Params: params,
Acc: s.Accs[i],
Idx: channel.Index(i),
Tx: tx,
}
diff, err := test.NonceDiff(s.Accs[i].Address(), s.Adjs[i], func() error {
return s.Adjs[i].Register(ctx, req, nil)
})
err := s.Adjs[i].Register(ctx, req, nil)

require.NoError(t, err, "Withdrawing should succeed")
if !req.Secondary {
// The Initiator must send a TX.
require.Equal(t, diff, 1)
} else {
// Everyone else must NOT send a TX.
require.Equal(t, diff, 0)
}
})
}
ct.Wait("register")
Expand Down Expand Up @@ -254,11 +244,10 @@ func register(ctx context.Context, adj *test.SimAdjudicator, accounts []*keystor
}

req := channel.AdjudicatorReq{
Params: ch.params,
Acc: accounts[0],
Idx: 0,
Tx: tx,
Secondary: false,
Params: ch.params,
Acc: accounts[0],
Idx: 0,
Tx: tx,
}
return adj.Register(ctx, req, sub)
}
Expand All @@ -277,11 +266,10 @@ func withdraw(ctx context.Context, adj *test.SimAdjudicator, accounts []*keystor

for i, a := range accounts {
req := channel.AdjudicatorReq{
Params: c.params,
Acc: a,
Idx: channel.Index(i),
Tx: tx,
Secondary: i != 0,
Params: c.params,
Acc: a,
Idx: channel.Index(i),
Tx: tx,
}

if err := adj.Withdraw(ctx, req, subStates); err != nil {
Expand Down
14 changes: 10 additions & 4 deletions channel/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,18 +172,20 @@ func (a *Adjudicator) convertEvent(ctx context.Context, e *adjudicator.Adjudicat
if err != nil {
return nil, errors.WithMessage(err, "fetching call data")
}

ch, ok := args.signedState(e.ChannelID)
if !ok {
return nil, errors.Errorf("channel not found in calldata: %v", e.ChannelID)
}

var app channel.App
var zeroAddress common.Address
if ch.Params.App == zeroAddress {
app = channel.NoApp()
} else {
app, err = channel.Resolve(wallet.AsWalletAddr(ch.Params.App))
appAddr := wallet.AsWalletAddr(ch.Params.App)
appID := &AppID{
Address: appAddr,
}
app, err = channel.Resolve(appID)
if err != nil {
return nil, err
}
Expand All @@ -201,7 +203,11 @@ func (a *Adjudicator) convertEvent(ctx context.Context, e *adjudicator.Adjudicat
if err != nil {
return nil, errors.WithMessage(err, "fetching call data")
}
app, err := channel.Resolve(wallet.AsWalletAddr(args.Params.App))
appAddr := wallet.AsWalletAddr(args.Params.App)
appID := &AppID{
Address: appAddr,
}
app, err := channel.Resolve(appID)
if err != nil {
return nil, errors.WithMessage(err, "resolving app")
}
Expand Down
3 changes: 1 addition & 2 deletions channel/test/adjudicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ import (

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"

ethchannel "github.com/perun-network/perun-eth-backend/channel"
"github.com/pkg/errors"

"perun.network/go-perun/channel"
"perun.network/go-perun/log"
Expand Down
7 changes: 7 additions & 0 deletions channel/test/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@
package test

import (
"math/rand"

"github.com/perun-network/perun-eth-backend/channel"
pchannel "perun.network/go-perun/channel"
"perun.network/go-perun/channel/test"
)

func init() {
test.SetRandomizer(new(randomizer))
test.SetNewRandomAppID(func(r *rand.Rand) pchannel.AppID {
return channel.NewRandomAppID(r)
})
}
1 change: 1 addition & 0 deletions channel/withdraw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func TestWithdrawZeroBalance(t *testing.T) {
}

// shouldFunders decides who should fund. 1 indicates funding, 0 indicates skipping.

//nolint:thelper // Not a helper.
func testWithdrawZeroBalance(t *testing.T, n int) {
rng := pkgtest.Prng(t)
Expand Down
4 changes: 3 additions & 1 deletion client/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ func TestProgression(t *testing.T) {
}

appAddress := deployMockApp(t, backendSetup)
app := channel.NewMockApp(appAddress)
appAddrBackend := appAddress.(*ethwallet.Address)
appID := &ethchannel.AppID{Address: appAddrBackend}
app := channel.NewMockApp(appID)
channel.RegisterApp(app)

execConfig := &clienttest.ProgressionExecConfig{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
perun.network/go-perun v0.10.5
perun.network/go-perun v0.10.7-0.20240115114750-da863c83cb9f
polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,10 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
perun.network/go-perun v0.10.5 h1:bIaAoLLh8R+RXdPh+MkCJcbtumsFkNvoVAJwb60JKjg=
perun.network/go-perun v0.10.5/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8=
perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e h1:4SOKO0WZtcsQUwP5nKVUrLUohgUPIhMa8wto5iNCA/k=
perun.network/go-perun v0.10.7-0.20230808153546-74844191e56e/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8=
perun.network/go-perun v0.10.7-0.20240115114750-da863c83cb9f h1:suN6qyoBSBuNBirStAtX+5gIc3tplHzbdB1H+RWlRTc=
perun.network/go-perun v0.10.7-0.20240115114750-da863c83cb9f/go.mod h1:BGBZC3npkX457u87pjDd0NEIXr1a4dsH4H/YpLdGGe8=
polycry.pt/poly-go v0.0.0-20220222131629-aa4bdbaab60b/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU=
polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37 h1:iA5GzEa/hHfVlQpimEjPV09NATwHXxSjWNB0VVodtew=
polycry.pt/poly-go v0.0.0-20220301085937-fb9d71b45a37/go.mod h1:XUBrNtqgEhN3EEOP/5gh7IBd3xVHKidCjXDZfl9+kMU=
Expand Down
Loading