Skip to content

Commit

Permalink
* refactor(channel/AdjEventSub): Set AdjEventSub to adhere to Adjudic…
Browse files Browse the repository at this point in the history
…atorSubscription in go-perun. In run method, do not return if adjEvent is found.

* refactor(channel/subscribe.go): Add IMPORTANT fix: In the DifferencesInControls function, if current state and next state are both Closed in the payment contract, then return a CloseEvent. This  triggers a ConcludedEvent in Next(). This fix removes potential problems in edge cases in which the contract is closed, but no event was caught. This could have blocked the conclusion of the channel. The added code prevents such cases and guarantees that a closed channel in the contract is always detected.
Rename variables in Next() to be more descriptive.

* test(channel_test): Add client_test.go, testing the go-perun client functionality with the payment channel workflow.

* refactor(channel/test): Add wallet.EphemeralWallet as return value in MakeRandPerunAccWallet(), satisfying requirements in the client_test.go test. Improve file path parsing, using getDataFilePath.

* refactor(event): Rename PerunEvent methods to adhere to AdjudicatorEvent interface from go-perun.

*add(payment): Add payment package to include test infrastructure for the go-perun client, used by client_test.go.
  • Loading branch information
iljabvh committed Apr 15, 2024
1 parent 1266747 commit 53cf6e9
Show file tree
Hide file tree
Showing 13 changed files with 525 additions and 61 deletions.
16 changes: 7 additions & 9 deletions channel/adjudicator_sub.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const (
DefaultSubscriptionPollingInterval = time.Duration(5) * time.Second
)

// AdjudicatorSub implements the AdjudicatorSubscription interface.
type AdjEventSub struct {
challengeDuration *time.Duration
stellarClient *client.Client
Expand All @@ -49,7 +48,7 @@ type AdjEventSub struct {
log log.Embedding
}

func NewAdjudicatorSub(ctx context.Context, cid pchannel.ID, stellarClient *client.Client, perunAddr xdr.ScAddress, assetAddr xdr.ScAddress, challengeDuration *time.Duration) (*AdjEventSub, error) {
func NewAdjudicatorSub(ctx context.Context, cid pchannel.ID, stellarClient *client.Client, perunAddr xdr.ScAddress, assetAddr xdr.ScAddress, challengeDuration *time.Duration) (pchannel.AdjudicatorSubscription, error) {

sub := &AdjEventSub{
challengeDuration: challengeDuration,
Expand Down Expand Up @@ -92,15 +91,14 @@ polling:
finish(err)
return
case <-ctx.Done():
s.log.Log().Debug("Timeout during Adjudicator Subscription")
finish(nil)
return
case <-time.After(s.pollInterval):

newChanInfo, err := s.stellarClient.GetChannelInfo(ctx, s.perunAddr, s.cid) // getChanControl()
newChanInfo, err := s.stellarClient.GetChannelInfo(ctx, s.perunAddr, s.cid)
newChanControl = newChanInfo.Control

if err != nil {

s.subErrors <- err
}
adjEvent, err := DifferencesInControls(s.chanControl, newChanControl)
Expand All @@ -117,7 +115,6 @@ polling:
s.log.Log().Debug("Contract event detected, evaluating...")
s.log.Log().Debugf("Found contract event: %v", adjEvent)
s.events <- adjEvent
return
}
}
}
Expand All @@ -129,9 +126,6 @@ func DifferencesInControls(controlCurr, controlNext wire.Control) (event.PerunEv
if controlCurr.FundedA {
return nil, errors.New("channel cannot be unfunded A before withdrawal")
}
if controlNext.WithdrawnA && controlNext.WithdrawnB {
return &event.FundEvent{}, nil
}
}

if controlCurr.FundedB != controlNext.FundedB {
Expand All @@ -153,6 +147,10 @@ func DifferencesInControls(controlCurr, controlNext wire.Control) (event.PerunEv
return &event.CloseEvent{}, nil
}

if controlCurr.Closed && controlNext.Closed {
return &event.CloseEvent{}, nil
}

if controlCurr.WithdrawnA != controlNext.WithdrawnA {
if controlCurr.WithdrawnA {
return nil, errors.New("channel cannot be unwithdrawn")
Expand Down
9 changes: 7 additions & 2 deletions channel/adjudicator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,21 @@ func TestHappyChannel(t *testing.T) {
Idx: pchannel.Index(1),
Secondary: false}

_, err = adjAlice.Subscribe(ctx, next.ID)
require.NoError(t, err)
require.NoError(t, adjAlice.Withdraw(ctxAliceWithdraw, reqAlice, nil))
// ev := subAlice.Next()
// if _, ok := ev.(pchannel.ConcludedEvent); ok {
// fmt.Println("event concluded is here")
// }

perunAddrAlice := adjAlice.GetPerunAddr()
stellarChanAlice, err := adjAlice.StellarClient.GetChannelInfo(ctx, perunAddrAlice, next.ID)

require.True(t, stellarChanAlice.Control.WithdrawnA)

require.NoError(t, err)
require.NoError(t, adjBob.Withdraw(ctx, reqBob, nil))
perunAddrBob := adjBob.GetPerunAddr()

stellarChanBob, err := adjBob.StellarClient.GetChannelInfo(ctxBobWithdraw, perunAddrBob, next.ID)

require.NoError(t, err)
Expand Down
1 change: 1 addition & 0 deletions channel/funder.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (f *Funder) fundParty(ctx context.Context, req pchannel.FundingReq) error {
case <-ctx.Done():
timeoutErr := makeTimeoutErr([]pchannel.Index{req.Idx}, 0)
errAbort := f.AbortChannel(ctx, req.State)
log.Printf("%s: Aborting channel due to timeout...\n", party)
if errAbort != nil {
return errAbort
}
Expand Down
24 changes: 11 additions & 13 deletions channel/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func (s *AdjEventSub) Next() pchannel.AdjudicatorEvent {
if s.getEvents() == nil {
return nil
}

select {
case ev := <-s.getEvents():
if ev == nil {
Expand All @@ -38,27 +37,26 @@ func (s *AdjEventSub) Next() pchannel.AdjudicatorEvent {

switch e := ev.(type) {
case *event.DisputedEvent:
log.Println("DisputedEvent received")
log.Println("DisputedEvent received - build RegisteredEvent")
dispEvent := pchannel.AdjudicatorEventBase{
VersionV: e.GetVersion(),
IDV: e.GetID(),
VersionV: e.Version(),
IDV: e.ID(),
TimeoutV: event.MakeTimeout(*s.challengeDuration),
}
ddn := &pchannel.RegisteredEvent{AdjudicatorEventBase: dispEvent, State: nil, Sigs: nil}
s.closer.Close()
return ddn
adjDispEvent := &pchannel.RegisteredEvent{AdjudicatorEventBase: dispEvent, State: nil, Sigs: nil}
return adjDispEvent

case *event.CloseEvent:

log.Println("CloseEvent received")
log.Println("CloseEvent received - build ConcludedEvent")

conclEvent := pchannel.AdjudicatorEventBase{
VersionV: e.GetVersion(),
IDV: e.GetID(),
VersionV: e.Version(),
IDV: e.ID(),
TimeoutV: event.MakeTimeout(*s.challengeDuration),
}
ccn := &pchannel.ConcludedEvent{AdjudicatorEventBase: conclEvent}
s.closer.Close()
return ccn
adjConclEvent := &pchannel.ConcludedEvent{AdjudicatorEventBase: conclEvent}
return adjConclEvent

default:
log.Printf("Received an unknown event type: %v\n", reflect.TypeOf(e))
Expand Down
87 changes: 72 additions & 15 deletions channel/test/setup.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 PolyCrypt GmbH
// Copyright 2024 PolyCrypt GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -26,6 +26,7 @@ import (
"log"
"math/big"
mathrand "math/rand"
"path/filepath"
pchannel "perun.network/go-perun/channel"
ptest "perun.network/go-perun/channel/test"
pwallet "perun.network/go-perun/wallet"
Expand All @@ -34,21 +35,23 @@ import (
"perun.network/perun-stellar-backend/client"
"perun.network/perun-stellar-backend/wallet"
pkgtest "polycry.pt/poly-go/test"
"runtime"
"testing"
"time"
)

const (
PerunContractPath = "../testdata/perun_soroban_contract.wasm"
StellarAssetContractPath = "../testdata/perun_soroban_token.wasm"
PerunContractPath = "testdata/perun_soroban_contract.wasm"
StellarAssetContractPath = "testdata/perun_soroban_token.wasm"
initLumensBalance = "10000000"
initTokenBalance = uint64(2000000)
DefaultTestTimeout = 20
DefaultTestTimeout = 30
)

type Setup struct {
t *testing.T
accs []*wallet.Account
ws []*wallet.EphemeralWallet
stellarClients []*client.Client
Rng *mathrand.Rand
funders []*channel.Funder
Expand Down Expand Up @@ -76,36 +79,68 @@ func (s *Setup) GetAccounts() []*wallet.Account {
return s.accs
}

func (s *Setup) GetWallets() []*wallet.EphemeralWallet {
return s.ws
}

func getProjectRoot() (string, error) {
_, b, _, _ := runtime.Caller(1)
basepath := filepath.Dir(b)

fp, err := filepath.Abs(filepath.Join(basepath, "../..")) //filepath.Abs(filepath.Join(basepath, "../.."))
return fp, err
}

func getDataFilePath(filename string) (string, error) {
root, err := getProjectRoot()
if err != nil {
return "", err
}

fp := filepath.Join(root, "", filename) //filepath.Join(root, "testdata", filename)
return fp, nil
}

func NewTestSetup(t *testing.T) *Setup {

accs, kpsToFund := MakeRandPerunAccs(4)
accs, kpsToFund, ws := MakeRandPerunAccsWallets(4)
require.NoError(t, CreateFundStellarAccounts(kpsToFund, initLumensBalance))

depTokenKp := kpsToFund[2]
depPerunKp := kpsToFund[3]

tokenAddress, _ := Deploy(t, depTokenKp, StellarAssetContractPath)
perunAddress, _ := Deploy(t, depPerunKp, PerunContractPath)
relPathPerun, err := getDataFilePath(PerunContractPath)
require.NoError(t, err)
relPathAsset, err := getDataFilePath(StellarAssetContractPath)
require.NoError(t, err)

tokenAddress, _ := Deploy(t, depTokenKp, relPathAsset)
perunAddress, _ := Deploy(t, depPerunKp, relPathPerun)

require.NoError(t, InitTokenContract(depTokenKp, tokenAddress))

setupAccountsAndContracts(t, depTokenKp, kpsToFund[:2], tokenAddress, initTokenBalance)
SetupAccountsAndContracts(t, depTokenKp, kpsToFund[:2], tokenAddress, initTokenBalance)

assetContractID, err := types.NewStellarAssetFromScAddress(tokenAddress)
require.NoError(t, err)

stellarClients := NewStellarClients(kpsToFund)

aliceClient := stellarClients[0]
aliceWallet := ws[0]
bobClient := stellarClients[1]
bobWallet := ws[1]

channelAccs := []*wallet.Account{accs[0], accs[1]}
channelClients := []*client.Client{aliceClient, bobClient}
channelWallets := []*wallet.EphemeralWallet{aliceWallet, bobWallet}

funders, adjs := createFundersAndAdjudicators(channelAccs, stellarClients, perunAddress, tokenAddress)
funders, adjs := CreateFundersAndAdjudicators(channelAccs, stellarClients, perunAddress, tokenAddress)

setup := Setup{
t: t,
accs: channelAccs,
ws: channelWallets,
stellarClients: channelClients,
funders: funders,
adjs: adjs,
Expand All @@ -115,15 +150,15 @@ func NewTestSetup(t *testing.T) *Setup {
return &setup
}

func setupAccountsAndContracts(t *testing.T, deployerKp *keypair.Full, kps []*keypair.Full, tokenAddress xdr.ScAddress, tokenBalance uint64) {
func SetupAccountsAndContracts(t *testing.T, deployerKp *keypair.Full, kps []*keypair.Full, tokenAddress xdr.ScAddress, tokenBalance uint64) {
for _, kp := range kps {
addr, err := types.MakeAccountAddress(kp)
require.NoError(t, err)
require.NoError(t, MintToken(deployerKp, tokenAddress, tokenBalance, addr))
}
}

func createFundersAndAdjudicators(accs []*wallet.Account, clients []*client.Client, perunAddress, tokenAddress xdr.ScAddress) ([]*channel.Funder, []*channel.Adjudicator) {
func CreateFundersAndAdjudicators(accs []*wallet.Account, clients []*client.Client, perunAddress, tokenAddress xdr.ScAddress) ([]*channel.Funder, []*channel.Adjudicator) {
funders := make([]*channel.Funder, len(accs))
adjs := make([]*channel.Adjudicator, len(accs))
for i, acc := range accs {
Expand All @@ -141,16 +176,18 @@ func NewStellarClients(kps []*keypair.Full) []*client.Client {
return clients
}

func MakeRandPerunAccs(count int) ([]*wallet.Account, []*keypair.Full) {
func MakeRandPerunAccsWallets(count int) ([]*wallet.Account, []*keypair.Full, []*wallet.EphemeralWallet) {
accs := make([]*wallet.Account, count)
kps := make([]*keypair.Full, count)
ws := make([]*wallet.EphemeralWallet, count)

for i := 0; i < count; i++ {
acc, kp := MakeRandPerunAcc()
acc, kp, w := MakeRandPerunAccWallet()
accs[i] = acc
kps[i] = kp
ws[i] = w
}
return accs, kps
return accs, kps, ws
}

func MakeRandPerunAcc() (*wallet.Account, *keypair.Full) {
Expand All @@ -173,6 +210,26 @@ func MakeRandPerunAcc() (*wallet.Account, *keypair.Full) {
return acc, kp
}

func MakeRandPerunAccWallet() (*wallet.Account, *keypair.Full, *wallet.EphemeralWallet) {
w := wallet.NewEphemeralWallet()

var b [8]byte
_, err := rand.Read(b[:])
if err != nil {
panic(err)
}

seed := binary.LittleEndian.Uint64(b[:])

r := mathrand.New(mathrand.NewSource(int64(seed)))

acc, kp, err := w.AddNewAccount(r)
if err != nil {
panic(err)
}
return acc, kp, w
}

func CreateFundStellarAccounts(pairs []*keypair.Full, initialBalance string) error {

numKps := len(pairs)
Expand Down Expand Up @@ -230,7 +287,7 @@ func CreateFundStellarAccounts(pairs []*keypair.Full, initialBalance string) err
}

for _, keys := range pairs {
log.Printf("Funded %s (%s) with %s XLM.\n",
log.Printf("Funded Stellar L1 account %s (%s) with %s XLM.\n",
keys.Seed(), keys.Address(), initialBalance)
}

Expand Down
Loading

0 comments on commit 53cf6e9

Please sign in to comment.