Skip to content

Perun Channels on Stellar

Hendrik A edited this page Feb 16, 2024 · 11 revisions

Introduction

In this document, we present a detailed overview of the ongoing Perun payment channel protocol's implementation on the Stellar blockchain. Our goal is to empower Stellar blockchain users with the ability to conduct swift and secure peer-to-peer transactions through the utilization of our scientifically peer-reviewed Perun protocol. We aim to deploy our single-asset payment channel protocol as a Stellar Smart Contract to enable generation, usage and conclusion of our channels on the Stellar blockchain.

The Perun protocol is a blockchain-agnostic state machine that governs state transitions within payment channels. You can find an introduction in the next section and a detailed description here. To implement this protocol on a settlement layer, basic Smart Contract functionality is required. This enables us to deploy an on-chain representation of Perun payment channels and construct an interface linking it to the off-chain protocol. This interface requires basic properties, such as state encoding, cryptographic signature verification and querying of on-chain events. As each blockchain manages these features differently, we customize the interface to meet the specific needs of the settlement layer in question. Consequently, the development workload can be divided into two main tasks. In the first part, we have developed a Perun Smart Contract that embodies our payment channel protocol on the Stellar blockchain. In the second task, which is more technically demanding and time-consuming, we develop the interface between our Perun protocol go-perun and its Smart Contract representation on-chain.

In the following sections, we will describe the different components of Perun channels and track the progress of the integration into the Stellar ecosystem.

Overview of the Perun L2 technology 

Perun protocol: The Perun channel protocol is an instance of a state channel protocol that results from a series of renowned research papers (EUROCRYPT, CCS, S&P). The protocol allows a fixed group of participants to open a channel between each other such that the deposited funds can then be transferred within the group with a local consensus at zero cost and nearly zero latency using off-chain transactions. The Perun protocol guarantees the security of the funds at all times. If a dispute arises (for example, one party goes offline) the protocol provides a way for honest participants to receive a payout according to the most recently agreed off-chain state.

Custom transaction logic: The Perun protocol also allows for imposing arbitrary logic on a channel so that transactions can be made even without full off-chain consensus. For example, this allows for realizing atomic off-chain swaps with complex transaction logic.

Cross-chain transactions: Due to the local consensus mechanism, the Perun protocol can be extended to supporting cross-chain channels, where the deposited assets may lie on different blockchain networks (e.g., ETH and ADA). Implementing this functionality is work in progress but we eventually plan to bring it to all supported blockchain backends.

Implementation: The Go-Perun library implements the Perun protocol in an abstract sense so that it can be used with arbitrary blockchain backends given an implementation of the corresponding backend module. We currently offer backends for Ethereum, Cosmos, Internet Computer, Cardano, Nervos and Polkadot.

Virtual Channels: The main focus of this proposal lies on the virtual channel technology. A virtual channel is a channel that is established off-chain, resulting in great scalability, minimum delays and fees. It allows connecting parties that do already have an open ledger channel with the same intermediary (Hub). The idea is that if the parties have an open ledger channel to the same intermediary (Hub), then a virtual channel between the parties can be established through transactions in the open ledger channels. This is much more efficient compared to opening a channel via slow and costly on-chain transactions. Our papers [1, 2] describe how to realize virtual channels on a single network.

  1. White paper: https://perun.network/wp-content/uploads/Perun2.0.pdf (with references to the scientific papers)
  2. Main scientific paper “Perun: Virtual Payment Hubs over Cryptocurrencies”: Published at IEEE S&P 2019, https://ieeexplore.ieee.org/document/8835315, Open access: https://eprint.iacr.org/2017/635

go-perun architecture

In the following we give an overview of the main packages and their functionality. At the core of the library lies the client package, which represents a channel client and holds the core protocol logic. For communication with other clients, the client package uses the wire package, which is an abstract definition of the communication layer that is to be instantiated with a concrete implementation suitable for the given application context. Furthermore, the client package interfaces with the wallet package and the channel package. The wallet package and the channel package lie at the core of any blockchain backend implementation. The wallet package provides functionality for account management and signature generation. The channel package provides the core data structures and functionality for interacting with the blockchain. Besides that, the library contains the persistence package and the log package, which are abstractions for persistence and logging functionality whose implementations can be injected depending on the application context.

go-perun Architecture

In essence, creating a Stellar backend for go-perun means implementing the wallet and channel abstractions for Stellar. On the one hand, this comprises an on-chain component written in Rust that provides the necessary smart contract logic. On the other hand, this comprises an off-chain component written in Go that acts as a gateway from the abstract protocol implementation of go-perun to the smart contract logic running on the blockchain. Once these components are implemented, Perun Channels on Stellar are accessible via the go-perun library. The Perun Developer Tutorial explains how go-perun can be used for realizing simple and secure micro-transactions over any supported blockchain backend.

Developing Perun Channels on Soroban

The core component of the Soroban SDK is the Contract Execution Environment (soroban_sdk::Env), which stores the relevant information about the currently executing contract. It provides access to key components that are required for implementing Perun payment channels. In what follows, we describe these components and their role in the Perun payment channel contract.

The soroban_sdk::Env::storage() function is used to store and retrieve contract data. This object is utilized to define our payment channel along with all its parameters. Here, every channel object is initialized with a unique identifier and stores the channel's "State" and "Parameter" objects. These two structs fully define the properties and current state of the payment channel. They can be accessed and modified as needed throughout the channel's lifetime.

The soroban_sdk::Env::ledger() is primarily used to log timestamps and sequence relevant events for channel users. Perun relies on timestamps for two key reasons. Firstly, it uses time constants to measure the time taken by users to respond to specific requests. Secondly, it determines state transitions based on the chronological order of emitted events. As such, the ability to track time on-chain becomes integral to our payment channel implementation.

The soroban_sdk::Env::events() function provides the capacity to publish significant events that occur on-chain. This enables a crucial part of the communication between the Smart Contract representation of Perun and the off-chain protocol implementation. The events are typically triggered by the channel users. Such events can be initiated, e.g. by depositing the requested collateral into the Perun Smart Contract, or requesting the conclusion of the channel and subsequent settlement of deposited funds. Perun processes these events and determines the consequent state transitions.

Further, soroban_sdk::token allows us to invoke token contract functions that are deployed on the Stellar blockchain. This includes minting tokens that are deposited into our channel during the funding phase and transferring them back to the users when necessary. The Stellar blockchain users themselves are identified by their public address provided by soroban_sdk::Address. This identifier is necessary to send, receive tokens but also to authenticate usage of contract invocation.

Current status and Outlook

After developing a concept for Perun Channels on Soroban and implementing an initial version of the channel contract the next steps in developing Perun Channels for Stellar are outlined below. The development is split into two major parts: On-Chain (smart contracts) and Off-Chain (Make Perun Channels accessible via the go-perun library).

On-Chain part

The Perun Channel Contract will be implemented in Rust using the Soroban Framework. Implementing the script requires an adaption to the Perun protocol, such that it is compatible with Stellar. Alongside getting to know Soroban and its workflow, challenges such as a compatible funding protocol, on-chain state handling and unique on-chain channel identification have to be addressed.

Preparation (completed)

Description: Setup development environment for contract development using soroban. ✔️

Things needed from Stellar side: A working toolchain for contract development. ✔️

Soroban channel design (completed)

Description: Adapt Perun channel protocol to Soroban. ✔️

Things needed from Stellar side: Documentation for Soroban. ✔️

Initial channel contract (completed)

Description: Start with a stripped down, easy version of our contract to familiarize with Soroban contract development. ✔️

Things needed from Stellar side: Nice to have would be example Scripts which could be used as a reference. ✔️

Extend channel contract to full functionality (completed)

Description: The result will be a secure two-party payment-channel contract. ✔️

Things needed from Stellar side: On-chain: Signature verification, UDT support, Unique channel identification, Storage handling: The ability to store the channel parameters / the state

Tests (completed)

Description: Setting up a testsuite with Soroban and developing Unit tests for the Perun channel contract code. This only concerns the on-chain part w/o any interaction from off-chain code. ✔️

Things needed from Stellar side: Documentation and/or examples ✔️ Testnet and/or access to local devnet ✔️

Off-Chain part

Our Off-Chain part consists of both the contract bindings, as well as the perun-stellar-backend implementing the necessary interfaces to connect with our go-perun core. We plan to use the Go Horizon SDK for this. In general, regarding “Things needed from Stellar side” the execution of this part profits greatly from a powerful go-sdk for stellar. Major issues will be encoding data (e.g. signing state so that it can be verified on-chain) and observing events on-chain.

General steps

Preparation (completed)

Description: Familiarize with the Go Horizon SDK ✔️

Things needed from Stellar side: Documentation, Examples ✔️

Encoding (completed)

Description: This is best described with an example: The on-chain script will have to reason about custom structs such as ChannelState and ChannelParameters. We will have to read those from the chain, write those to the chain and sign e.g. the ChannelState so that it can be verified on-chain. ✔️

Things needed from Stellar side: A collection of algorithms that are accessible both on-chain and through go (offchain):Encode (e.g. struct -> bytes)Decode (e.g. bytes -> struct)VerifySignatureSignData (only off-chain, so that the signature can be verified on encoded data on-chain) ✔️

Contract bindings (completed)

Description: Implement an offchain interface for the Perun Channel Script, which allows to open, close, fund, etc. ✔️

Things needed from Stellar side:

Wallet implementation

type Wallet interface: Represents a set of accounts.

  • func Unlock(Address) (Account, error): Unlocks a wallet account. type Account interface
  • func Address() Address: Returns the account address.
  • func SignData(data []byte) ([]byte, error): Signs a byte array.

type Address interface: Represents an account address.

  • func MarshalBinary() ([]byte, error): Encodes the address.
  • func UnmarshalBinary([]byte) error: Decodes an address.

type Backend interface: Provides utilities for the wallet abstraction.

  • func NewAddress() Address: Returns the concrete address type.
  • func DecodeSig(io.Reader) (Sig, error): Decodes a signature.
  • func VerifySignature(msg []byte, sign Sig, a Address) (bool, error): Verifies a signature.

Preparation (completed)

Description: Plan the wallet implementation. This depends on the results from “General steps”. ✔️

Things needed from Stellar side:

Implement the go-perun interfaces (completed)

Description: See above ✔️

Things needed from Stellar side:

Filesystem Wallet (completed)

Description: Signature and Verification of messages happening offchain are handled by a wallet accessible through Go. Most backends required a dedicated wallet implementation handling our use-cases. ✔️

Things needed from Stellar side:Might be given by Go Horizon SDK ✔️

Channel implementation

type Backend interface: Provides utilities for the channel abstraction.

  • func CalcID(*Params) ID: Calculates the channel identifier from the parameters.
  • func Sign(wallet.Account, *State) (wallet.Sig, error): Creates a signature for a given channel state.
  • func Verify(addr wallet.Address, state *State, sig wallet.Sig) (bool, error): Verifies a signature for a given channel state.
  • func NewAsset() Asset: Returns the asset type.

type Funder interface: Provides functionality for on-chain channel funding.

  • func Fund(context.Context, FundingReq) error: Transfers funds to a given channel on-chain.

type Adjudicator interface: Provides functionality for calling the on-chain adjudication logic.

  • func Register(context.Context, AdjudicatorReq, []SignedState) error: Registers channel state on-chain.
  • func Withdraw(context.Context, AdjudicatorReq, StateMap) error: Withdraws channel funds on-chain.
  • func Subscribe(context.Context, *Params) (AdjudicatorSubscription, error): Creates an on-chain event subscription.

type AdjudicatorSubscription interface: Provides functionality for consuming relevant on-chain events.

  • func Next() AdjudicatorEvent: Reads the next event.
  • func Err() error: Returns a subscription error.
  • func Close() error: Closes the subscription.

Preparation (completed)

Description: How to observe onchain state changes? Relevant for adjudicator and associated subscription ✔️

Things needed from Stellar side:

Adjudicator Subscription (completed)

Description: Implement an event subscription listening to onchain events related to a channel handled by a Perun Channel Contract. ✔️

Things needed from Stellar side:

Adjudicator (completed)

Description: Implement the adjudicator interface using the aforementioned subscription. ✔️

Things needed from Stellar side:

Funder (completed)

Description: Implement offchain logic to handle funding of channels. ✔️

Things needed from Stellar side:

Backend (completed)

Description: Implement backend interface ✔️

Things needed from Stellar side:

Tests (completed)

Description: Unit tests for wallet and channel as well as On-chain integration tests. ✔️

Things needed from Stellar side: Testnet and/or access to local devnet ✔️

CLI Demo client

CLI executable (completed)

Description: Allows opening a channel with another user, send payments and settle the channel ✔️

Things needed from Stellar side:

Optional test trace (completed)

Description: See above. The trace will automatically be executed and log the resulting trace. ✔️

Things needed from Stellar side: