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
86 changes: 54 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,39 +119,61 @@ jobs:
go run .
docker stop polkadot

- name: Payment Channel CKB
working-directory: payment-channel-ckb

- name: Install CKB contract dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq sed gawk tmux tmuxp expect make
curl -LO https://github.com/nervosnetwork/ckb/releases/download/v0.109.0/ckb_v0.109.0_x86_64-unknown-linux-gnu.tar.gz
tar -xzf ckb_v0.109.0_x86_64-unknown-linux-gnu.tar.gz
sudo cp ckb_v0.109.0_x86_64-unknown-linux-gnu/ckb /usr/local/bin/
curl -LO https://github.com/nervosnetwork/ckb-cli/releases/download/v1.4.0/ckb-cli_v1.4.0_x86_64-unknown-linux-gnu.tar.gz
tar -xzf ckb-cli_v1.4.0_x86_64-unknown-linux-gnu.tar.gz
sudo cp ckb-cli_v1.4.0_x86_64-unknown-linux-gnu/ckb-cli /usr/local/bin/
curl -LO https://github.com/nervosnetwork/capsule/releases/download/v0.9.2/capsule_v0.9.2_x86_64-linux.tar.gz
tar -xzf capsule_v0.9.2_x86_64-linux.tar.gz
sudo cp capsule_v0.9.2_x86_64-linux/capsule /usr/local/bin/
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb
sudo dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb
cd ./devnet
chmod +x setup-devnet.sh print_accounts.sh deploy_contracts.sh sudt_helper.sh
./setup-devnet.sh
ckb run > /dev/null 2>&1 &
sleep 3
ckb miner > /dev/null 2>&1 &
sleep 3
./print_accounts.sh
sleep 6
expect fund_accounts.expect
sleep 10
./deploy_contracts.sh
sleep 15
./sudt_helper.sh fund
sleep 10
./sudt_helper.sh balances
sudo apt-get install -y gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf libc6-dev-riscv64-cross libc6-riscv64-cross linux-libc-dev-riscv64-cross
wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 18 && rm llvm.sh
cargo install cargo-generate
rustup target add riscv64imac-unknown-none-elf

- name: Create Symlink
run: sudo ln -s /usr/riscv64-linux-gnu/include/gnu/stubs-lp64d.h /usr/riscv64-linux-gnu/include/gnu/stubs-lp64.h

- name: Start devnet in background
working-directory: payment-channel-ckb
env:
RUSTFLAGS: "-C linker=rust-lld"
CARGO_TARGET_RISCV64IMAC_UNKNOWN_NONE_ELF_LINKER: rust-lld
CC_riscv64imac_unknown_none_elf: riscv64-unknown-elf-gcc
AR_riscv64imac_unknown_none_elf: riscv64-unknown-elf-ar
C_INCLUDE_PATH: /usr/riscv64-linux-gnu/include
CFLAGS: "-I/usr/riscv64-linux-gnu/include"
TARGET_CFLAGS: "-I/usr/riscv64-linux-gnu/include"
run: |
sudo apt-get update
sudo apt-get install -y jq sed gawk tmux tmuxp expect make
curl -LO https://github.com/nervosnetwork/ckb/releases/download/v0.201.0/ckb_v0.201.0_x86_64-unknown-linux-gnu.tar.gz
tar -xzf ckb_v0.201.0_x86_64-unknown-linux-gnu.tar.gz
sudo cp ckb_v0.201.0_x86_64-unknown-linux-gnu/ckb /usr/local/bin/
curl -LO https://github.com/nervosnetwork/ckb-cli/releases/download/v1.4.0/ckb-cli_v1.4.0_x86_64-unknown-linux-gnu.tar.gz
curl -LO https://github.com/nervosnetwork/ckb-cli/releases/download/v1.13.0/ckb-cli_v1.13.0_x86_64-unknown-linux-gnu.tar.gz
tar -xzf ckb-cli_v1.13.0_x86_64-unknown-linux-gnu.tar.gz
sudo cp ckb-cli_v1.13.0_x86_64-unknown-linux-gnu/ckb-cli /usr/local/bin/
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb
sudo dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb

git submodule update --init --recursive
cd ./devnet
chmod +x setup-devnet.sh print_accounts.sh deploy_contracts.sh sudt_helper.sh
./setup-devnet.sh
ckb run > /dev/null 2>&1 &
sleep 3
ckb miner > /dev/null 2>&1 &
sleep 3
./print_accounts.sh
sleep 6
expect fund_accounts.expect
sleep 10
./deploy_contracts.sh
sleep 15
./sudt_helper.sh fund
sleep 10
./sudt_helper.sh balances
cd ../..
- name: Payment Channel CKB
working-directory: payment-channel-ckb
run: |
sleep 30
cd ..
go run main.go
go run .
43 changes: 43 additions & 0 deletions payment-channel-ckb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Payment Chanenl on CKB

This demo connects to our [Perun-CKB-Contracts] that run on the Local CKB Devnet byusing our [CKB Backend].

## Requirement
We use various tools to enable a convenient setup for the local development devnet. If you want to use our `setup-devnet.sh` script, make sure the following commandline tools are installed:
* `jq`:
- Used to parse and edit some configuration files.
* `sed` and `awk`:
- We modify some fields of the files generated by the `ckb init --chain dev` command using `sed` and `awk`.
* `tmux` and `tmuxp`:
- `tmuxp` is a session manager for `tmux` that allows to easily create descriptive `.yaml` configuration files to create and attach to `tmux` sessions.
* `expect`:
- We completely automize the process for test-wallet creation, deploying of contracts etc. To make this work reliably, we use `expect` which allows to describe how a commandline application is fed input.
* `make`:
- Not strictly necessary, but it should be available on most systems by default. In case you do not want to install `make` check out the `Makefile` content and issue the command on your own.
* `ckb` with version `0.201.0` or higher.
* `ckb-cli` with version `1.13.0` or higher.

Also follow the installation of [Perun-CKB-Contracts] requirements.

Before going through the demo, initialize the submodule.
```
git submodule update --init --recursive
```

## Example Walkthrough
1. In another terminal, start the local CKB Devnet and wait 10-15 seconds for the contracts' deployment:
```sh
cd devnet
make dev
```

1. On the second terminal, run the demo.
```sh
cd payment-channel-ckb
go run .
```

<!-- Links -->
[Perun-CKB-Contracts]: https://github.com/perun-network/perun-ckb-contract/

[CKB Backend]: https://github.com/perun-network/perun-ckb-backend
3 changes: 2 additions & 1 deletion payment-channel-ckb/client/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/nervosnetwork/ckb-sdk-go/v2/indexer"
"github.com/nervosnetwork/ckb-sdk-go/v2/types"
"perun.network/perun-ckb-backend/channel"
"perun.network/perun-ckb-backend/wallet/address"
)

Expand All @@ -45,7 +46,7 @@ func sudtBalanceExtractor(cell *indexer.LiveCell) *big.Int {
func (p *PaymentClient) PollBalances() {
pollingInterval := time.Second
searchKey := &indexer.SearchKey{
Script: address.AsParticipant(p.Account.Address()).PaymentScript,
Script: address.AsParticipant(p.WalletAddress()[channel.CKBBackendID]).PaymentScript,
ScriptType: types.ScriptTypeLock,
ScriptSearchMode: types.ScriptSearchModeExact,
Filter: nil,
Expand Down
18 changes: 13 additions & 5 deletions payment-channel-ckb/client/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"perun.network/go-perun/channel"
"perun.network/go-perun/client"
"perun.network/perun-ckb-backend/channel/asset"
)

type PaymentChannel struct {
Expand All @@ -46,14 +47,21 @@ func (c PaymentChannel) SendPayment(amounts map[channel.Asset]float64) {
actor := c.ch.Idx()
peer := 1 - actor
for a, amount := range amounts {

if amount < 0 {
continue
}

shannonAmount := CKByteToShannon(big.NewFloat(amount))
state.Allocation.TransferBalance(actor, peer, a, shannonAmount)

switch a := a.(type) {
case *asset.Asset:
if a.IsCKBytes {
shannonAmount := CKByteToShannon(big.NewFloat(amount))
state.Allocation.TransferBalance(actor, peer, a, shannonAmount)
} else {
sudtAmmount := new(big.Int).SetUint64(uint64(amount))
state.Allocation.TransferBalance(actor, peer, a, sudtAmmount)
}
default:
panic("Asset is not of type *asset.Asset")
}
}

})
Expand Down
77 changes: 31 additions & 46 deletions payment-channel-ckb/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ import (
"github.com/nervosnetwork/ckb-sdk-go/v2/rpc"
"github.com/nervosnetwork/ckb-sdk-go/v2/types"
"github.com/perun-network/perun-libp2p-wire/p2p"
"perun.network/go-perun/channel"
gpchannel "perun.network/go-perun/channel"
"perun.network/go-perun/channel/persistence"
"perun.network/go-perun/client"
gpwallet "perun.network/go-perun/wallet"
"perun.network/go-perun/watcher/local"
"perun.network/go-perun/wire"
"perun.network/perun-ckb-backend/backend"
"perun.network/perun-ckb-backend/channel"
"perun.network/perun-ckb-backend/channel/adjudicator"
"perun.network/perun-ckb-backend/channel/asset"
"perun.network/perun-ckb-backend/channel/funder"
Expand All @@ -45,8 +44,8 @@ type PaymentClient struct {
Name string
balance *big.Int
sudtBalance *big.Int
Account *wallet.Account
wAddr wire.Address
walletAccs map[gpwallet.BackendID]gpwallet.Account
wireAddrs map[gpwallet.BackendID]wire.Address
Network types.Network
PerunClient *client.Client
net *p2p.Net
Expand All @@ -59,10 +58,9 @@ func NewPaymentClient(
network types.Network,
deployment backend.Deployment,
rpcUrl string,
account *wallet.Account,
walletAcc *wallet.Account,
key secp256k1.PrivateKey,
wallet *wallet.EphemeralWallet,
persistRestorer persistence.PersistRestorer,
wAddr wire.Address,
net *p2p.Net,

Expand All @@ -71,7 +69,7 @@ func NewPaymentClient(
if err != nil {
return nil, err
}
signer := backend.NewSignerInstance(address.AsParticipant(account.Address()).ToCKBAddress(network), key, network)
signer := backend.NewSignerInstance(address.AsParticipant(walletAcc.Address()).ToCKBAddress(network), key, network)

ckbClient, err := ckbclient.NewClient(backendRPCClient, *signer, deployment)
if err != nil {
Expand All @@ -84,11 +82,18 @@ func NewPaymentClient(
return nil, err
}

perunClient, err := client.New(wAddr, net.Bus, f, a, wallet, watcher)
waddresses := map[gpwallet.BackendID]wire.Address{channel.CKBBackendID: wAddr}
wallets := map[gpwallet.BackendID]gpwallet.Wallet{
channel.CKBBackendID: wallet,
}
walletAccs := map[gpwallet.BackendID]gpwallet.Account{
channel.CKBBackendID: walletAcc,
}

perunClient, err := client.New(waddresses, net.Bus, f, a, wallets, watcher)
if err != nil {
return nil, err
}
perunClient.EnablePersistence(persistRestorer)

balanceRPC, err := rpc.Dial(rpcUrl)
if err != nil {
Expand All @@ -98,8 +103,8 @@ func NewPaymentClient(
Name: name,
balance: big.NewInt(0),
sudtBalance: big.NewInt(0),
Account: account,
wAddr: wAddr,
walletAccs: walletAccs,
wireAddrs: waddresses,
Network: network,
PerunClient: perunClient,
channels: make(chan *PaymentChannel, 1),
Expand All @@ -112,16 +117,20 @@ func NewPaymentClient(
}

// WalletAddress returns the wallet address of the client.
func (p *PaymentClient) WalletAddress() gpwallet.Address {
return p.Account.Address()
func (p *PaymentClient) WalletAddress() map[gpwallet.BackendID]gpwallet.Address {
addresses := make(map[gpwallet.BackendID]gpwallet.Address, len(p.walletAccs))
for backendID, acc := range p.walletAccs {
addresses[backendID] = acc.Address()
}
return addresses
}

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

func (p *PaymentClient) PeerID() string {
walletAddr := p.wAddr.(*p2p.Address)
walletAddr := p.wireAddrs[channel.CKBBackendID].(*p2p.Address)
return walletAddr.ID.String()
}

Expand All @@ -138,22 +147,24 @@ func (p *PaymentClient) GetBalances() string {
}

// OpenChannel opens a new channel with the specified peer and funding.
func (p *PaymentClient) OpenChannel(peer wire.Address, peerID string, amounts map[gpchannel.Asset]float64) *PaymentChannel {
func (p *PaymentClient) OpenChannel(peer map[gpwallet.BackendID]wire.Address, peerID string, amounts map[gpchannel.Asset]float64) *PaymentChannel {
// We define the channel participants. The proposer always has index 0. Here
// we use the on-chain addresses as off-chain addresses, but we could also
// use different ones.
participants := []wire.Address{p.WireAddress(), peer}
participants := []map[gpwallet.BackendID]wire.Address{p.WireAddress(), peer}
p.net.Dialer.Register(peer, peerID)

assets := make([]gpchannel.Asset, len(amounts))
backends := make([]gpwallet.BackendID, len(amounts))
i := 0
for a := range amounts {
assets[i] = a
backends[i] = channel.CKBBackendID
i++
}

// We create an initial allocation which defines the starting balances.
initAlloc := gpchannel.NewAllocation(2, assets...)
initAlloc := gpchannel.NewAllocation(2, backends, assets...)
for a, amount := range amounts {
switch a := a.(type) {
case *asset.Asset:
Expand All @@ -179,7 +190,7 @@ func (p *PaymentClient) OpenChannel(peer wire.Address, peerID string, amounts ma
challengeDuration := uint64(10) // On-chain challenge duration in seconds.
proposal, err := client.NewLedgerChannelProposal(
challengeDuration,
p.Account.Address(),
p.WalletAddress(),
initAlloc,
participants,
)
Expand Down Expand Up @@ -216,29 +227,3 @@ func (p *PaymentClient) AcceptedChannel() *PaymentChannel {
func (p *PaymentClient) Shutdown() {
p.PerunClient.Close()
}

func (c *PaymentClient) Restore(peer wire.Address, peerID string) []*PaymentChannel {
var restoredChannels []*client.Channel
//c.net.Dialer.Register(peer, peerID)
//TODO: Remove this hack. Find why asset is not found upon restoring
c.PerunClient.OnNewChannel(func(ch *client.Channel) {
restoredChannels = append(restoredChannels, ch)
})

err := c.PerunClient.Restore(context.TODO())
if err != nil {
fmt.Println("Error restoring channels")
}

paymentChannels := make([]*PaymentChannel, len(restoredChannels))
assets := make([]channel.Asset, 1)
assets = append(assets, &asset.Asset{
IsCKBytes: true,
SUDT: nil,
})
for i, ch := range restoredChannels {
paymentChannels[i] = newPaymentChannel(ch, assets)
}

return paymentChannels
}
3 changes: 2 additions & 1 deletion payment-channel-ckb/client/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"perun.network/go-perun/channel"
"perun.network/go-perun/client"
ckbchannel "perun.network/perun-ckb-backend/channel"
)

// HandleProposal is the callback for incoming channel proposals.
Expand Down Expand Up @@ -102,5 +103,5 @@ func (p *PaymentClient) HandleUpdate(cur *channel.State, next client.ChannelUpda

// HandleAdjudicatorEvent is the callback for smart contract events.
func (p *PaymentClient) HandleAdjudicatorEvent(e channel.AdjudicatorEvent) {
log.Printf("Adjudicator event: type = %T, client = %v", e, p.Account)
log.Printf("Adjudicator event: type = %T, client = %v", e, p.WalletAddress()[ckbchannel.CKBBackendID])
}
Loading
Loading