-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cc46438
commit f287602
Showing
27 changed files
with
1,613 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
Go-Perun | ||
======== | ||
|
||
`go-perun <https://github.com/perun-network/go-perun>`_ is a Go implementation of the Perun state channel protocols. | ||
|
||
Developer Tutorial | ||
------------------ | ||
|
||
The :ref:`developer-tutorial` shows how go-perun is used to build a simple scalable payment application on top of the Ethereum blockchain. | ||
|
||
.. toctree:: | ||
:maxdepth: 2 | ||
|
||
tutorial/index | ||
|
||
Feedback | ||
-------- | ||
|
||
Feel free to send your feedback to `info@perun.network <mailto:info@perun.network>`_. | ||
|
||
Additional Resources | ||
-------------------- | ||
|
||
* `Perun Network Website <https://perun.network>`_ | ||
* `Perun CLI Demo <https://github.com/perun-network/perun-eth-demo>`_ | ||
* `Perun Node <https://github.com/hyperledger-labs/perun-node>`_ | ||
|
||
Security Disclaimer | ||
------------------- | ||
|
||
go-perun is still alpha software. | ||
It should not be used in production. | ||
The purpose of the `current release <https://github.com/perun-network/go-perun/releases>`_ is to give potential users a general impression and invite feedback. | ||
The authors take no responsibility for any loss of digital assets or other damage caused by the use of this software. | ||
**Do not use this software with real funds**. | ||
|
||
Project Funding | ||
--------------- | ||
|
||
This project is currently being developed by a group of dedicated hackers at the Chair of Applied Cryptography at Technische Universität Darmstadt, Germany. | ||
We thank the German Federal Ministry of Education and Research (BMBF) for their funding through the StartUpSecure grants program as well as the German Science Foundation (DFG), the Foundation for Polish Science (FNP) and the Ethereum Foundation for their support in the research that preceded this implementation. | ||
|
||
Copyright | ||
--------- | ||
|
||
Copyright 2021 - PolyCrypt GmbH, Germany. | ||
Use of the source code is governed by the Apache 2.0 license that can be found in the `LICENSE file <https://github.com/hyperledger-labs/go-perun/blob/dev/LICENSE>`_. | ||
|
||
Contact us at `info@perun.network <mailto:info@perun.network>`_. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
Closing | ||
======= | ||
|
||
Closing a channel can be done in two ways, either cooperative or non-cooperative. This example focuses on the cooperative way. As you would expect from closing an off-chain | ||
channel, the on-chain balances will be updated accordingly. But before that can happen | ||
there are some other steps that we will go through first. | ||
|
||
.. _the-watcher: | ||
|
||
The Watcher | ||
^^^^^^^^^^^ | ||
|
||
*go-perun* reacts automatically to on-chain events of a channel as long as its `watcher` routine is running. You should start the watcher in the `NewChannel` handler in a new | ||
go-routine since `channel.Watch`_ blocks. | ||
|
||
.. _finalizing: | ||
|
||
Finalizing | ||
^^^^^^^^^^ | ||
|
||
The state of a channel in *go-perun* has a public boolean `IsFinal`_ flag. | ||
Final states can directly be closed on-chain without raising a dispute. | ||
This allows for a faster collaborative closing process. | ||
As soon as a channel has a final state, we call it *finalized* since it can not | ||
be updated anymore. Have a look again at :ref:`updating` on how to do it. | ||
|
||
Registering | ||
^^^^^^^^^^^ | ||
|
||
Registering a channel means pushing its latest state onto the `Adjudicator`. | ||
A registered channel state is openly visible on the blockchain. This should only be done | ||
when a channel should be closed or disputed. | ||
|
||
.. note:: | ||
|
||
Registering non-finalized channels will raise a dispute. | ||
|
||
Settlement | ||
^^^^^^^^^^ | ||
|
||
Settlement is the last step in the lifetime of a channel. It consists of two steps: | ||
*conclude* and *withdraw*. *go-perun* takes care of both when `channel.Settle`_ is called. | ||
|
||
*conclude* waits for any on-chain disputes to be resolved and then calls the `Adjudicator` | ||
to close the channel. After this is done, the channel can be *withdrawn*. This is done only | ||
once by one of the channel participants. | ||
|
||
The last step is for each participant to *withdraw* their funds from the `AssetHolder`. | ||
The balance that can be withdrawn is the same as the final balance of the channel. | ||
Ethereum transaction fees still apply. | ||
|
||
.. warning:: | ||
|
||
Trying to settle a channel that was not registered before is not advised and | ||
can result in a dispute. | ||
|
||
Keep in mind that we already *finalized* the channel in the update that we sent. | ||
We therefore just need to *register* and *settle* which looks like this: | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 130-147 | ||
|
||
The other participant would then have its `AdjudicatorEvent` handler called with a | ||
`ConcludedEvent`_ and should then also execute `closeChannel`. | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 149-155 | ||
|
||
.. _IsFinal: https://pkg.go.dev/perun.network/go-perun/channel#State | ||
.. _channel.Settle: https://pkg.go.dev/perun.network/go-perun/client#Channel.Settle | ||
.. _channel.Watch: https://pkg.go.dev/perun.network/go-perun/client#Channel.Watch | ||
.. _ConcludedEvent: https://pkg.go.dev/perun.network/go-perun/channel#ConcludedEvent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
Disputes | ||
======== | ||
|
||
A *dispute* occurs when the channel participants cannot agree on the current state. | ||
Say for example *Bob* is sure that he has 5 *ETH* but *Alice* insists that he has only 4 *ETH*. | ||
|
||
Disputes are resolved on-chain. | ||
The Perun protocols guarantee that the most recent state agreed to by all channel participants can be redeemed. | ||
Disputes are handled by the `Adjudicator` contract which determines the valid state by comparing the version numbers of the provided channel states. | ||
|
||
A dispute is raised as soon as one participant registers a non-final state in the | ||
`Adjudicator`. The other participant then has `challengeDuration`-seconds time to react by submitting a newer state to prove that he is honest and knows a newer valid state. | ||
If the other participant cannot do this, he loses the dispute and the first state is | ||
accepted as final. The disputed channel is then *settled* and therefore closed. | ||
|
||
.. note:: | ||
|
||
The `challengeDuration` is part of the `channel proposal`_ and is agreed upon by | ||
both participants. | ||
|
||
.. _channel proposal: https://pkg.go.dev/perun.network/go-perun/client#NewLedgerChannelProposal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Channels | ||
======== | ||
|
||
With all the components explained, we can now look at channels. | ||
The following pages will explain how to open, update and close a channel. | ||
Disputes will only be outlined as they are not focused on in this tutorial. | ||
|
||
.. toctree:: | ||
:maxdepth: 1 | ||
|
||
opening | ||
updating | ||
closing | ||
disputes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
Opening | ||
======= | ||
|
||
Opening a state channel works by defining the initial asset allocation, setting the | ||
channel parameters, creating a proposal and sending that proposal to all participants. | ||
The channel is open after all participants accept the proposal and finish the on-chain funding. | ||
|
||
It looks like this: | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 38-68 | ||
|
||
The `ProposeChannel` call blocks until *Alice* either accepted or rejected the channel | ||
and funded it. | ||
|
||
.. warning:: | ||
|
||
The channel that is returned by `ProposeChannel` should only be used to retrieve | ||
its *id*. | ||
|
||
HandleProposal | ||
^^^^^^^^^^^^^^ | ||
|
||
An example Proposal handler looks like this: | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 70-89 | ||
|
||
You can add additional check logic here but in our simple use case we always accept | ||
incoming proposals. After the channel is open, both participants will have their `NewChannel` callback called. | ||
|
||
.. warning:: The `Channel` that `ProposalResponder.Accept`_ returns should only be used to retrieve its *ID*. | ||
|
||
NewChannel | ||
^^^^^^^^^^ | ||
|
||
*go-perun* expects this handler to finish quickly. Use *go* routines if you want to do | ||
time-intensive tasks. You should also start the :ref:`watcher <the-watcher>` as shown below: | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 91-100 | ||
|
||
.. note:: | ||
|
||
Starting the watcher is not mandatory but strongly advised. *go-perun* can otherwise | ||
not react to malicious behavior of other participants. | ||
|
||
.. _ProposalResponder.Accept: https://pkg.go.dev/perun.network/go-perun/client#ProposalResponder.Accept |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
.. _updating: | ||
|
||
Updating | ||
======== | ||
|
||
We now give the `node` an `updateChannel` function to update the channel by sending Ether from | ||
*Bob* to *Alice*. | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 102-115 | ||
:emphasize-lines: 8,9 | ||
|
||
In the highlighted lines you can see that we use index `0` for the `Balances` slice. | ||
This means that we access the funds of the first asset. Since there is only one asset, | ||
this is the only entry. We also :ref:`finalize <finalizing>` the channel, which will be important later and implies that it can not be updated again. | ||
|
||
.. note:: | ||
|
||
*go-perun* checks that an update preserves the sum of each asset. | ||
|
||
HandleUpdate | ||
^^^^^^^^^^^^ | ||
|
||
The update that was initiated with the `updateChannel` function above would then arrive | ||
at the `HandleUpdate` function of the other participant. In `HandleUpdate` you can decide on whether you want to accept the incoming update or not. | ||
This example function accepts all updates: | ||
|
||
.. literalinclude:: ../go-perun-test/node.go | ||
:language: go | ||
:lines: 117-124 | ||
|
||
An update can also be rejected with a reason. This starts the `dispute process`_. | ||
|
||
.. code-block:: go | ||
responder.Reject(ctx, "do not like") | ||
.. _UpdateBy: https://pkg.go.dev/perun.network/go-perun/client#Channel.UpdateBy | ||
.. _Update: https://pkg.go.dev/perun.network/go-perun/client#Channel.Update | ||
.. _dispute process: disputes.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
.. _Getting Started: | ||
|
||
Getting Started | ||
=============== | ||
|
||
First we setup some dependencies: Golang and ganache-cli. But no worries, we will walk you through it 😌 | ||
|
||
Tutorial Source Code | ||
-------------------- | ||
|
||
To make it easier to follow the tutorial, you may already clone the source code repository: | ||
|
||
.. code-block:: bash | ||
cd $GOPATH/src | ||
git clone https://github.com/perun-network/perun-tutorial.git | ||
cd perun-tutorial/go-perun-test | ||
# Initialize Golang | ||
go mod tidy | ||
Running the code is the :ref:`last part <run-the-app>`, but feel free to try it out first. | ||
|
||
Golang | ||
------ | ||
|
||
The tutorial source code will be written in *Golang*. | ||
The official *Golang* installation guide can be found `here <https://golang.org/doc/install>`_. | ||
Restart your shell and check the installation by running:: | ||
|
||
go version | ||
|
||
Ganache CLI | ||
----------- | ||
|
||
For the purpose of this tutorial we will use `ganache-cli <https://github.com/trufflesuite/ganache-cli>`_ for providing us with a local Ethereum blockchain for testing our application locally. | ||
Install ganache-cli by following `the instructions on the web page <https://github.com/trufflesuite/ganache-cli#installation>`_. | ||
You can check if ganache-cli is installed by running:: | ||
|
||
ganache-cli --version | ||
|
||
When we run our local blockchain, we usually configure accounts that we want to prefund. | ||
We will do this by specifying a mnemonic. | ||
A mnemonic is a sequence of randomly chosen words from which account secret keys can be derived. | ||
For the purpose of this tutorial we will use the following mnemonic:: | ||
|
||
"pistol kiwi shrug future ozone ostrich match remove crucial oblige cream critic" | ||
|
||
.. warning:: | ||
Always keep your *mnemonic* private. Do not use the example *mnemonic* | ||
with real funds. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright (c) 2021, PolyCrypt GmbH, Germany. All rights reserved. | ||
// This file is part of perun-tutorial. Use of this source code is | ||
// governed by the Apache 2.0 license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/accounts" | ||
"github.com/ethereum/go-ethereum/common" | ||
|
||
ethchannel "perun.network/go-perun/backend/ethereum/channel" | ||
ethwallet "perun.network/go-perun/backend/ethereum/wallet" | ||
"perun.network/go-perun/channel" | ||
"perun.network/go-perun/wallet" | ||
"perun.network/go-perun/wire/net" | ||
"perun.network/go-perun/wire/net/simple" | ||
) | ||
|
||
func setupFunder(contractBackend ethchannel.ContractBackend, account accounts.Account, assetHolder common.Address) channel.Funder { | ||
ethDepositor := new(ethchannel.ETHDepositor) | ||
accounts := map[ethchannel.Asset]accounts.Account{ethwallet.Address(assetHolder): account} | ||
depositors := map[ethchannel.Asset]ethchannel.Depositor{ethwallet.Address(assetHolder): ethDepositor} | ||
return ethchannel.NewFunder(contractBackend, accounts, depositors) | ||
} | ||
|
||
func setupNetwork(role Role, account wallet.Account) (listener net.Listener, bus *net.Bus, err error) { | ||
dialer := simple.NewTCPDialer(10 * time.Second) | ||
dialer.Register(cfg.addrs[1-role], cfg.hosts[1-role]) | ||
|
||
listener, err = simple.NewTCPListener(cfg.hosts[role]) | ||
fmt.Printf("Setting up listener for %s\n", cfg.hosts[role]) | ||
if err != nil { | ||
err = fmt.Errorf("creating listener: %w", err) | ||
return | ||
} | ||
|
||
bus = net.NewBus(account, dialer) | ||
return | ||
} |
Oops, something went wrong.