Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ jobs:
restore-keys: |
${{ runner.os }}-go-

- uses: dfinity/setup-dfx@main
- name: Install Internet Computer SDK
uses: dfinity/setup-dfx@main
with:
dfx-version: 0.25.1
dfx-version: 0.24.0

- name: Build Stellar Devchain Image
working-directory: payment-channel-xlm
run: |
chmod +x ./testdata/docker/build.sh
./testdata/docker/build.sh

- name: Pull ganache-cli image
run: docker pull ${{ env.ganache-image }}
Expand All @@ -47,19 +54,18 @@ jobs:
- name: Payment Channel XLM
working-directory: payment-channel-xlm
run: |
pkill -f quickstart.sh || true
docker stop stellar soroban-preview || true

sleep 5
chmod +x ./testdata/docker/build.sh
./testdata/docker/build.sh

chmod +x ./quickstart.sh
./quickstart.sh standalone &
./quickstart.sh standalone-detached

sleep 50
go run ./
docker stop stellar
sleep 5
docker stop stellar

- name: Payment Channel Cross-Contract
working-directory: payment-channel-cc
Expand Down Expand Up @@ -106,7 +112,6 @@ jobs:
./stopdfx.sh || true # stop any running dfx
./startdeploy.sh

sleep 15
go run ./

./stopdfx.sh || true
Expand All @@ -115,7 +120,7 @@ jobs:
working-directory: payment-channel-dot
run: |
docker run --name polkadot --detach --rm -it -p 9944:9944 ghcr.io/perun-network/polkadot-test-node:0.4.0
sleep 5
sleep 10
go run .
docker stop polkadot

Expand Down
12 changes: 12 additions & 0 deletions payment-channel-xlm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ This example shows how to set up a payment channel on Stellar, which utilizes th

# Setup

To install the docker image, you need to download and build it first, using the build.sh script in testdata/docker:

```sh
./testdata/docker/build.sh
```

or using `Makefile`

```sh
make
```

Spin up the local Stellar blockchain, serving as a local testnet for demonstration purposes.

```
Expand Down
14 changes: 14 additions & 0 deletions payment-channel-xlm/client/channel.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2025 PolyCrypt GmbH
//
// 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 client

import (
Expand Down
82 changes: 53 additions & 29 deletions payment-channel-xlm/client/client.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,79 @@
// Copyright 2025 PolyCrypt GmbH
//
// 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 client

import (
"fmt"
"log"
"math/big"

"perun.network/go-perun/wire/net/simple"

"context"
"errors"

pchannel "perun.network/go-perun/channel"
pclient "perun.network/go-perun/client"
"perun.network/go-perun/channel"
"perun.network/go-perun/client"
"perun.network/go-perun/wallet"
"perun.network/go-perun/watcher/local"
"perun.network/go-perun/wire"
"perun.network/perun-stellar-backend/channel"
"perun.network/perun-stellar-backend/wallet"

stellarwallet "perun.network/perun-stellar-backend/wallet"
"perun.network/perun-stellar-backend/wallet/types"
)

type PaymentClient struct {
perunClient *pclient.Client
account *wallet.Account
currencies []pchannel.Asset
perunClient *client.Client
account map[wallet.BackendID]wallet.Address // The account we use for on-chain and off-chain transactions.
waddress map[wallet.BackendID]wire.Address
currencies []channel.Asset
channels chan *PaymentChannel
Channel *PaymentChannel
wAddr wire.Address
balance *big.Int
}

func SetupPaymentClient(
w *wallet.EphemeralWallet,
acc *wallet.Account,
stellarTokenIDs []pchannel.Asset,
bus *wire.LocalBus,
funder *channel.Funder,
adj *channel.Adjudicator,

w *stellarwallet.EphemeralWallet,
stellarAccount wallet.Account,
wireAddr wire.Address,

stellarTokenIDs []channel.Asset,
bus wire.Bus,
funder channel.Funder,
adj channel.Adjudicator,
) (*PaymentClient, error) {
watcher, err := local.NewWatcher(adj)
if err != nil {
return nil, fmt.Errorf("intializing watcher: %w", err)
}
// Setup Perun client.
wireAddr := simple.NewAddress(acc.Address().String())
perunClient, err := pclient.New(wireAddr, bus, funder, adj, w, watcher)
addresses := map[wallet.BackendID]wire.Address{types.StellarBackendID: wireAddr}
stellarWallet := map[wallet.BackendID]wallet.Wallet{types.StellarBackendID: w}

// Setup Accounts
account := map[wallet.BackendID]wallet.Address{types.StellarBackendID: stellarAccount.Address()}

perunClient, err := client.New(addresses, bus, funder, adj, stellarWallet, watcher)
if err != nil {
return nil, errors.New("creating client")
}

c := &PaymentClient{
perunClient: perunClient,
account: acc,
account: account,
waddress: addresses,
currencies: stellarTokenIDs,
channels: make(chan *PaymentChannel, 1),
wAddr: wireAddr,
balance: big.NewInt(0),
}

Expand All @@ -62,7 +82,7 @@ func SetupPaymentClient(
}

// startWatching starts the dispute watcher for the specified channel.
func (c *PaymentClient) startWatching(ch *pclient.Channel) {
func (c *PaymentClient) startWatching(ch *client.Channel) {
go func() {
err := ch.Watch(c)
if err != nil {
Expand All @@ -71,21 +91,25 @@ func (c *PaymentClient) startWatching(ch *pclient.Channel) {
}()
}

func (c *PaymentClient) OpenChannel(peer wire.Address, balances pchannel.Balances) {
func (c *PaymentClient) OpenChannel(peer map[wallet.BackendID]wire.Address, balances channel.Balances) {
// We define the channel participants. The proposer has always index 0. Here
// we use the on-chain addresses as off-chain addresses, but we could also
// use different ones.

participants := []wire.Address{c.WireAddress(), peer}
participants := []map[wallet.BackendID]wire.Address{c.waddress, peer}

initAlloc := pchannel.NewAllocation(2, c.currencies...)
backends := make([]wallet.BackendID, len(c.currencies))
for i := range c.currencies {
backends[i] = types.StellarBackendID
}
initAlloc := channel.NewAllocation(2, backends, c.currencies...)
initAlloc.Balances = balances

// Prepare the channel proposal by defining the channel parameters.
challengeDuration := uint64(10) // On-chain challenge duration in seconds.
proposal, err := pclient.NewLedgerChannelProposal(
proposal, err := client.NewLedgerChannelProposal(
challengeDuration,
c.account.Address(),
c.account,
initAlloc,
participants,
)
Expand All @@ -104,8 +128,8 @@ func (c *PaymentClient) OpenChannel(peer wire.Address, balances pchannel.Balance
c.Channel = newPaymentChannel(ch, c.currencies)
}

func (p *PaymentClient) WireAddress() wire.Address {
return p.wAddr
func (p *PaymentClient) WireAddress() map[wallet.BackendID]wire.Address {
return p.waddress
}

func (c *PaymentClient) AcceptedChannel() *PaymentChannel {
Expand Down
18 changes: 16 additions & 2 deletions payment-channel-xlm/client/handle.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2025 PolyCrypt GmbH
//
// 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 client

import (
Expand Down Expand Up @@ -51,7 +65,7 @@ func (c *PaymentClient) HandleProposal(p client.ChannelProposal, r *client.Propo

// Create a channel accept message and send it.
accept := lcp.Accept(
c.account.Address(), // The account we use in the channel.
c.account, // The account we use in the channel.
client.WithRandomNonce(), // Our share of the channel nonce.
)
ch, err := r.Accept(context.TODO(), accept)
Expand Down Expand Up @@ -106,7 +120,7 @@ func (c *PaymentClient) HandleUpdate(cur *channel.State, next client.ChannelUpda

// HandleAdjudicatorEvent is the callback for smart contract events.
func (c *PaymentClient) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) {
log.Printf("Adjudicator event: type = %T, client = %v", e, c.account.Address())
log.Printf("Adjudicator event: type = %T, client = %v", e, c.account)
}

func (c *PaymentClient) GetChannel() (*PaymentChannel, error) {
Expand Down
Loading
Loading