The 21 Micropayment Channels Client Library (two1.channels)

Posted by Tyler Julian

The 21 Payment Channels Client Library

The payment channel protocol allows for fast, high-volume payments to occur from one party (a customer) to another (a merchant) in a trust-less manner. There are further derivations that allow for payments to occur both ways (bi-directionally); however, this implementation covers the unidirectional case.

Payment channels consist of negotiating an open stage, an active ready stage where many payments can be made, and a closing stage where a final transaction is broadcast.

Payment Channels Protocol

Channel open

Customer requests merchants public key; merchant responds with fresh (compressed) public key.

    Merchant pubkey:
       02d79987da792634d39f5c14741774311ab9421d2775c2bc9a608489de9277aa83

Customer creates an CLTV-style https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki refund/payment transaction and a deposit that pays to its redeem script.

     Redeem script instructions:
         if
           <merchant pubkey> checksigverify
         else
           <timestamp> checklocktimeverify drop
         endif
         <customer pubkey> checksig

     Redeem script hex:
         632102d79987da792634d39f5c14741774311ab9421d2775c2bc9a608489de9277aa83ad670464da7156b175682103a5c2c5fe32a8ae5a8f67a314042cdd9eb33be822c6214d46109654ee269519faac

     Redeem script address:
         3MuV5ndotUyvgMc4cq73JPHwBEVMEgHUSa

This transaction will always require a signature by the customer (the last line in the script); however, the transaction can be spent by either: (1) adding a signature from the merchant, or (2) if the value of the nLockTime field exceeds the timestamp (aka the transaction can be spent).

Next we create a deposit that pays into the hash of the above script (pay-to-script-hash). In this example we want to open a channel for 100,000 satoshis - notice that we pay a bit more than that to allow for transaction fees on the way back.

   Deposit transaction layout:
       input(s):
         - <previous tx> <index> | <script sig> <script pubkey>
       nLockTime 0
       outputs:
         - dup hash160 <customer change address> equalverify checksig
         - hash160 <redeem script address> equal [111,000 satoshis]

   Deposit transaction hex:
     0100000001cd7edc5280aa3bf6120c98a587fa9691f843447905d03212dc1487e35c2b1993000000006b483045022100a15a21215db068aae693b5ea2344e112f1c460ef41dc5716724f5a7d020189e002202681e8833c69b248c999be2168be0c722314031979c10b2f00a1b8a5e7de8785012103d567c82c4578080bc07e695e660d10d38d8ebba7d24f3e4888ff439015491979ffffffff028ccc0800000000001976a914206acc7cc7b959ec8d9466cddaaadf4a2fd1e7b088ac98b101000000000017a914ddbe2dc0ce28de6648b2980b9bd705ca608fe7a18700000000

How do we spend these transactions? Well in terms of a refund, you'll notice that the CLTV-style refund/payment transaction can behave in two different ways. The OP_IF opcode will branch depending on whether the value immediately preceeding it is 0 or 1. In the case of a refund, we craft it as follows. Notice that we pay back less than we initially deposited, leaving fees for the transaction to be confirmed.

   Refund layout:
       input:
           <customer script sig> 0 <redeem script>
       nLockTime: 1450302052
       output:
           dup hash160 <customer address> equalverify checksig [101,000 satoshis]

   Refund hex:
       010000000140185784207a4c3fe8c98fc0f7f85ac5774126307c76e36b1b735406f68aee81010000009c47304402203e08227a6db6517740afb9be61cdb7c40bbcd6e740b13e7aadc31c1c4d66b3c802204d6ae4e9f4e99456e1023685acdbe4d08faf5ca764d4abc8484ed8e625eacbe10101004c50632102d79987da792634d39f5c14741774311ab9421d2775c2bc9a608489de9277aa83ad670464da7156b175682103a5c2c5fe32a8ae5a8f67a314042cdd9eb33be822c6214d46109654ee269519faac0000000001888a0100000000001976a914206acc7cc7b959ec8d9466cddaaadf4a2fd1e7b088ac64da7156

This causes the if statement in the redeem script to branch to the else section, where it will check the provided timestamp against the nLockTime field, and not require a merchant signature. A transaction of this style cannot be included in a block until the current time is after the nLockTime.

Customer sends deposit transaction and CLTV redeem script to merchant. The merchant verifies that the redeem script is correctly formed, and that it requires a merchant signature. It validates the customer's deposit against the redeem script, and optionally waits until the deposit has been confirmed on the blockchain.

Channel payments

Customer creates a payment transaction and sends to merchant. This transaction is similar to the refund transaction in that they both spend the same UTXO, however, we use a 1 to branch earlier in the if statement to require a merchant signature.

   Half-signed payment layout:
       input:
           <customer script sig> 1 <redeem script>
       nLockTime: 0
       outputs:
         - dup hash160 <merchant address> equalverify checksig [73,000 satoshis]
         - dup hash160 <customer address> equalverify checksig [28,000 satoshis]

   Half-signed payment hex:
       010000000140185784207a4c3fe8c98fc0f7f85ac5774126307c76e36b1b735406f68aee81010000009c483045022100f94e3073697be7138b00bf70e7ac60ba6dfcc2423df99d0f8b6f13966e1d3b7e022021bede7dd9fd09b6c72c3c51c70831df8c918cd4e4af175a59e511d5fc4eece601514c50632102d79987da792634d39f5c14741774311ab9421d2775c2bc9a608489de9277aa83ad670464da7156b175682103a5c2c5fe32a8ae5a8f67a314042cdd9eb33be822c6214d46109654ee269519faacffffffff02606d0000000000001976a9142a7a762597e0d97f8044a2eca976faa3af811eda88ac281d0100000000001976a914206acc7cc7b959ec8d9466cddaaadf4a2fd1e7b088ac00000000

Merchant verifies and saves the transaction.

Channel close

Customer requests merchant to close the channel. This happens if the customer spends the the full balance of the channel, or simply wishes to discontinue making payments in the channel. It is not a required part of the protocol, but it is generally courteous for the customer to do so. The merchant would then sign the remaining half of the transaction and broadcast it to the network.

   Fully-signed payment layout:
       input:
           <customer script sig> <merchant script sig> 1 <redeem script>
       nLockTime: 0
       outputs:
         - dup hash160 <merchant address> equalverify checksig [73,000 satoshis]
         - dup hash160 <customer address> equalverify checksig [28,000 satoshis]

   Fully-signed payment hex:
       010000000140185784207a4c3fe8c98fc0f7f85ac5774126307c76e36b1b735406f68aee8101000000e5483045022100f94e3073697be7138b00bf70e7ac60ba6dfcc2423df99d0f8b6f13966e1d3b7e022021bede7dd9fd09b6c72c3c51c70831df8c918cd4e4af175a59e511d5fc4eece601483045022100b26264031ddaf1104781e03893f6633669138be0050c813234be69fed81b9ff502203b93e0dfca7fa6a6f28041df8c56bcdb666be4b6e3494a84ce85d15bff104f2a01514c50632102d79987da792634d39f5c14741774311ab9421d2775c2bc9a608489de9277aa83ad670464da7156b175682103a5c2c5fe32a8ae5a8f67a314042cdd9eb33be822c6214d46109654ee269519faacffffffff02606d0000000000001976a9142a7a762597e0d97f8044a2eca976faa3af811eda88ac281d0100000000001976a914206acc7cc7b959ec8d9466cddaaadf4a2fd1e7b088ac00000000

OR

Merchant closes the channel when it approaches the channel's expiration time. The merchant has potentially received payment for goods/services, but will only lock those funds to their own address if they broadcast the last fully signed payment transaction before the channel expires. The channel's expiration time is dictated by the timestamp value in the CLTV-style transaction's redeem script.

OR

Customer refunds its deposit after the expiration time has elapsed without any action by the merchant. In this case, the customer already has a fully signed refund that it can broadcast, and does so without any requiring any interaction from the merchant.

Architecture

Each payment channel is modeled by a state machine PaymentChannelStateMachine class, which provides an interface to manipulating all of the client-side channel state. This state is stored in a PaymentChannelModel object, which can be stored and restored to and from the channels database. The PaymentChannelStateMachine class provides the low-level transition functions on the channel state and is responsible for creating and returning the underlying refund, deposit, and payment transactions. It uses a WalletWrapper class to sign transactions, but otherwise has no interaction with the outside world.

The PaymentChannel class provides an internal API (open, pay, sync, close, and properties) to a payment channel, operates transitions on the state machine, and is the the glue between state machine and the outside world -- the database, the blockchain, and the payment channel server.

The PaymentChannelClient class provides the top-level API for applications to open(), pay(), sync(), status(), close(), and list() payment channels by URL.

Finally, the channels cli is click-based cli implemented in.

The ChannelRequests class takes in a wallet and creates a PaymentChannelClient in its constructor. It uses the PaymentChannelClient and PaymentChannel APIs to lookup and operate payment channels, subject to an overridable hard-coded policy (initial deposit amount = 100000, expiration time = 86400 seconds, and close out amount = 1000).

Developer Testing

Use the following steps to get started using payment channels. Please note that having a confirmed, spendable bitcoin balance is a prerequisite for the following.

Setup

Fire up a barebones payment channel flask server.py:

import flask
from two1.wallet import Wallet
from two1.bitserv.flask import Payment

app = flask.Flask(__name__)
payment = Payment(app, Wallet())


@app.route('/current-temperature')
@payment.required(50)
def current_temperature():
    return 'Probably about 65 degrees Fahrenheit.'

if __name__ == "__main__":
    app.run(host="::", port=5000)

Set up a client.py to consume the server's REST API.

from two1.wallet import Wallet
from two1.bitrequests import ChannelRequests

requests = ChannelRequests(Wallet())

response = requests.get("http://[::]:5000/current-temperature")
print(response.text)

Start up the server:

python3 server.py

In a new window, run the client:

python3 client.py

Or you can simply buy at the command line:

21 buy -p channel http://[::]:5000/current-temperature

And use the channels CLI tool to check the status of your channels:

channels list

two1.channels: module contents

The two1.channels module is organized into the following submodules:

two1.channels.paymentchannelclient

A high-level client that can open, close, and pay across many channels.

exception two1.channels.paymentchannelclient.NotFoundError

Bases: IndexError

Channel not found error.

class two1.channels.paymentchannelclient.PaymentChannelClient(wallet, db_path='~/.two1/channels/channels.sqlite3', _database=None, _blockchain=None)

Bases: object

“Payment channel client.

DEFAULT_CHANNELS_DB_PATH = '~/.two1/channels/channels.sqlite3'

Default payment channel database path.

DEFAULT_TWENTYONE_BLOCKCHAIN_URL = 'https://blockchain.21.co/blockchain/bitcoin'

Default mainnet blockchain URL.

DEFAULT_TWENTYONE_TESTNET_BLOCKCHAIN_URL = 'https://blockchain.21.co/blockchain/testnet3'

Default testnet blockchain URL.

close(url)

Close the payment channel.

Parameters:

url (str) – Payment channel URL.

Raises:
  • NotFoundError – If the payment channel was not found.
  • NotReadyError – If payment channel is not ready.
  • NoPaymentError – If payment channel has no payments.
list(url=None)

Get a list of payment channel URLs.

Parameters:url (str or None) – Optional URL to limit channels to.
Returns:List of urls (str), sorted by readiness.
Return type:list
open(url, deposit, expiration, fee=30000, zeroconf=False, use_unconfirmed=False)

Open a payment channel at the specified URL.

Parameters:
  • url (str) – Payment channel server URL.
  • deposit (int) – Deposit amount in satoshis.
  • expiration (int) – Relative expiration time in seconds.
  • fee (int) – Fee in in satoshis.
  • zeroconf (bool) – Use payment channel without deposit confirmation.
  • use_unconfirmed (bool) – Use unconfirmed transactions to build deposit transaction.
Returns:

Payment channel URL.

Return type:

str

Raises:

InsufficientBalanceError – If wallet has insufficient balance to make deposit for payment channel.

pay(url, amount)

Pay to the payment channel.

Parameters:
  • url (str) – Payment channel URL.
  • amount (int) – Amount to pay in satoshis.
Returns:

Redeemable token for the payment.

Return type:

str

Raises:
  • NotFoundError – If the payment channel was not found.
  • NotReadyError – If payment channel is not ready for payment.
  • InsufficientBalanceError – If payment channel balance is insufficient to pay.
  • ClosedError – If payment channel is closed or was closed by server.
  • PaymentChannelError – If an unknown server error occurred.
status(url, include_txs=False)

Get payment channel status and information.

Parameters:
  • url (str) – Payment channel URL.
  • include_txs (bool) – Include raw channel transactions.
Returns:

Named tuple with url (str), state (PaymentChannelState), ready (bool), balance (int, satoshis), deposit (int, satoshis), fee (int, satoshis), creation_time (float, UNIX time), expiration_time (int, UNIX time), expired (bool), deposit_txid (str), spend_txid (str).

Return type:

PaymentChannelStatus

sync(url=None)

Synchronize one or more payment channels with the blockchain.

Update the payment channel in the cases of deposit confirmation or deposit spend, and refund the payment channel in the case of channel expiration.

Parameters:url (str or None) – Optional URL to limit synchronization to.
Raises:NotFoundError – If the payment channel was not found.
class two1.channels.paymentchannelclient.PaymentChannelStatus(url, state, ready, balance, deposit, fee, creation_time, expiration_time, expired, deposit_txid, spend_txid, transactions)

Bases: tuple

Container for the status information of a payment channel.

balance

Alias for field number 3

creation_time

Alias for field number 6

deposit

Alias for field number 4

deposit_txid

Alias for field number 9

expiration_time

Alias for field number 7

expired

Alias for field number 8

fee

Alias for field number 5

ready

Alias for field number 2

spend_txid

Alias for field number 10

state

Alias for field number 1

transactions

Alias for field number 11

url

Alias for field number 0

class two1.channels.paymentchannelclient.PaymentChannelTransactions(deposit_tx, refund_tx, payment_tx, spend_tx)

Bases: tuple

Container for the raw transactions of a payment channel.

deposit_tx

Alias for field number 0

payment_tx

Alias for field number 2

refund_tx

Alias for field number 1

spend_tx

Alias for field number 3

two1.channels.paymentchannel

Provides and object to represent and manage a payment channel.

exception two1.channels.paymentchannel.ClosedError

Bases: two1.channels.paymentchannel.PaymentChannelError

Channel closed error.

exception two1.channels.paymentchannel.InsufficientBalanceError

Bases: two1.channels.paymentchannel.PaymentChannelError

Insufficient balance error.

exception two1.channels.paymentchannel.NoPaymentError

Bases: two1.channels.paymentchannel.PaymentChannelError

No payment to close channel error.

exception two1.channels.paymentchannel.NotReadyError

Bases: two1.channels.paymentchannel.PaymentChannelError

Channel not ready error.

class two1.channels.paymentchannel.PaymentChannel(url, database, wallet, blockchain)

Bases: object

Payment channel.

DEPOSIT_REBROADCAST_TIMEOUT = 3600

Rebroadcast timeout for the deposit, if it hasn’t been confirmed by the blockchain.

MIN_EXPIRATION_TIMEOUT = 345600

Minimum channel duration (in seconds).

REFUND_BROADCAST_TIME_OFFSET = 5400

Refund broadcast time offset to take into account the effect of Median time-past (MTP) or BIP113. Broadcasting before this offset to the locktime may result in a non-final transaction classification and error from a blockchain data provider.

balance

Get payment channel balance amount.

Returns:Balance amount.
Return type:int
close()

Close the payment channel.

Raises:
creation_time

Get payment channel creation time.

Returns:Creation absolute time (UNIX time).
Return type:float
deposit

Get payment channel deposit amount.

Returns:Deposit amount.
Return type:int
deposit_tx

Get deposit transaction.

Returns:Serialized deposit transaction (ASCII hex).
Return type:str or None
deposit_txid

Get deposit transaction ID.

Returns:Deposit transaction ID (RPC byte order).
Return type:str or None
expiration_time

Get payment channel expiration time.

Returns:Expiration absolute time (UNIX time).
Return type:int
expired

Get expiration status of payment channel.

Returns:True if payment channel is expired, False if it is not.
Return type:bool
fee

Get payment channel fee amount.

Returns:Fee amount.
Return type:int
static open(database, wallet, blockchain, url, deposit_amount, expiration_time, fee_amount, zeroconf, use_unconfirmed=False)

Open a payment channel at the specified URL.

Parameters:
  • database (DatabaseBase) – Instance of the database interface.
  • wallet (WalletWrapperBase) – Instance of the wallet interface.
  • blockchain (BlockchainBase) – Instance of the blockchain interface.
  • url (str) – Payment channel server URL.
  • deposit_amount (int) – Deposit amount in satoshis.
  • expiration_time (int) – Relative expiration time in seconds.
  • fee_amount (int) – Fee amount in in satoshis.
  • zeroconf (bool) – Use payment channel without deposit confirmation.
  • use_unconfirmed (bool) – Use unconfirmed transactions to build deposit transaction.
Returns:

instance of PaymentChannel.

Return type:

PaymentChannel

Raises:
pay(amount)

Pay to the payment channel.

Parameters:

amount (int) – Amount to pay in satoshis.

Returns:

Redeemable token for the payment.

Return type:

str

Raises:
payment_tx

Get last half-signed payment transaction.

Returns:Serialized payment transaction (ASCII hex).
Return type:str or None
ready

Get readiness of payment channel.

Returns:True if payment channel is ready for payments, False if it is not.
Return type:bool
refund_tx

Get refund transaction.

Returns:Serialized refund transaction (ASCII hex).
Return type:str or None
refund_txid

Get refund transaction ID.

Returns:Deposit transaction ID (RPC byte order).
Return type:str or None
spend_tx

Get spend transaction.

Returns:Serialized spend transaction (ASCII hex).
Return type:str or None
spend_txid

Get spend transaction ID.

Returns:Spend transaction ID (RPC byte order).
Return type:str or None
state

Get payment channel state.

Returns:Payment channel state.
Return type:PaymentChannelState
sync()

Synchronize the payment channel with the blockchain.

Update the payment channel in the cases of deposit confirmation or deposit spend, and refund the payment channel in the case of channel expiration.

url

Get payment channel URL.

Returns:Payment channel URL.
Return type:str
exception two1.channels.paymentchannel.PaymentChannelError

Bases: Exception

Base class for Payment Channel errors.

two1.channels.paymentchannel.SupportedProtocols = {'https': <class 'two1.channels.server.HTTPPaymentChannelServer'>, 'http': <class 'two1.channels.server.HTTPPaymentChannelServer'>, 'test': <class 'two1.channels.server.TestPaymentChannelServer'>}

Supported protocols table.

exception two1.channels.paymentchannel.UnsupportedProtocolError

Bases: two1.channels.paymentchannel.PaymentChannelError

Unsupported protocol error.

two1.channels.statemachine

Manages state transitions for PaymentChannel objects.

exception two1.channels.statemachine.InsufficientBalanceError

Bases: ValueError

Insufficient balance error.

exception two1.channels.statemachine.InvalidTransactionError

Bases: ValueError

Invalid transaction error.

class two1.channels.statemachine.PaymentChannelModel(**kwargs)

Bases: object

Payment channel state model. This contains the core state of a payment channel.

class two1.channels.statemachine.PaymentChannelRedeemScript(merchant_public_key, customer_public_key, expiration_time)

Bases: two1.bitcoin.script.Script

Derived class of Script to create and access the payment channel redeem script.

customer_public_key

Get channel customer public key.

Returns:Customer public key.
Return type:bitcoin.PublicKey
expiration_time

Get channel expiration time.

Returns:Expiration absolute time (UNIX time).
Return type:int
classmethod from_bytes(b)

Instantiate a payment channel redeem script from bytes.

Parameters:b (bytes) – Serialized payment channel redeem script.
Returns:Instance of PaymentChannelRedeemScript.
Return type:PaymentChannelRedeemScript
merchant_public_key

Get channel merchant public key.

Returns:merchant public key.
Return type:bitcoin.PublicKey
class two1.channels.statemachine.PaymentChannelState

Bases: enum.Enum

Payment Channel State.

class two1.channels.statemachine.PaymentChannelStateMachine(model, wallet)

Bases: object

Customer payment channel state machine interface.

PAYMENT_TX_MIN_OUTPUT_AMOUNT = 3000

Minimum payment transaction output (above dust limit).

balance_amount

Get channel balance amount.

Returns:Balance amount in satoshis.
Return type:int
close(spend_txid)

Close the channel.

State machine transitions from READY to CONFIRMING_SPEND, OUTSTANDING to CONFIRMING_SPEND, CONFIRMING_DEPOSIT to CONFIRMING_SPEND, or CONFIRMING_SPEND to CONFIRMING_SPEND.

Parameters:spend_txid (str or None) – Transaction ID of spending transaction, either payment or refund (RPC byte order)
Raises:StateTransitionError – If channel is already closed.
confirm()

Confirm the deposit of the payment channel.

State machine transitions from CONFIRMING_DEPOSIT to READY.

Raises:StateTransitionError – If channel is not in CONFIRMING_DEPOSIT state.
create(merchant_public_key, deposit_amount, expiration_time, fee_amount, zeroconf, use_unconfirmed=False)

Open a new payment channel.

State machine transitions from OPENING to CONFIRMING_DEPOSIT if zeroconf is False, and to READY if zeroconf is True.

Parameters:
  • merchant_public_key (str) – Serialized compressed public key of the merchant (ASCII hex).
  • deposit_amount (int) – Depost amount in satoshis.
  • expiration_time (int) – Expiration absolute time (UNIX time).
  • fee_amount (int) – Fee amount in satoshis.
  • zeroconf (bool) – Use payment channel without deposit confirmation.
  • use_unconfirmed (bool) – Use unconfirmed transactions to build deposit transaction.
Returns:

Serialized deposit transaction (ASCII hex), and serialized redeem script (ASCII hex).

Return type:

tuple

Raises:
  • StateTransitionError – If channel is already open.
  • TypeError – If merchant_public_key type is not str, deposit type is not int, or expiration type is not int.
  • ValueError – If relative expiration time, deposit, or fee are negative.
  • InsufficientBalanceError – If wallet has insufficient balance to make deposit for payment channel.
creation_time

Get channel creation time.

Returns:Creation absolute time (UNIX time).
Return type:float
deposit_amount

Get channel deposit amount.

Returns:Deposit amount in satoshis.
Return type:int
deposit_tx

Get channel deposit transaction.

Returns:Serialized deposit transaction (ASCII hex).
Return type:str or None
deposit_tx_utxo_index

Get channel deposit transaction P2SH output index.

Returns:Output index in deposit transaction.
Return type:int or None
deposit_txid

Get channel deposit transaction ID.

Returns:Deposit transaction ID (RPC byte order).
Return type:str or None
deposit_txid_signature

Get channel deposit transaction ID signature, to authenticate channel close with server.

Returns:DER-encoded signature (ASCII hex).
Return type:str or None
expiration_time

Get channel expiration time.

Returns:Expiration absolute time (UNIX time).
Return type:int
fee_amount

Get channel fee amount.

Returns:Fee amount in satoshis.
Return type:int
finalize(spend_tx)

Finalize the channel. This commits the spending transaction of the channel so the final balance can be ascertained.

State machine transitions from CONFIRMING_SPEND to CLOSED, READY to CLOSED, OUTSTANDING to CLOSED, or CLOSED to CLOSED.

Raises:
pay(amount)

Create a half-signed payment to the channel.

State machine state transitions from READY to OUTSTANDING.

Parameters:

amount (int) – Amount to pay in satoshis.

Returns:

Serialized half-signed payment transaction (ASCII hex).

Return type:

str

Raises:
  • StateTransitionError – If channel is not in READY state.
  • TypeError – If amount type is not int.
  • ValueError – if amount is negative or zero.
  • InsufficientBalanceError – If payment channel balance is insufficient to pay.
pay_ack()

Acknowledge a successful payment to the channel.

State machine transitions from OUTSTANDING to READY.

Raises:StateTransitionError – If channel is not in OUTSTANDING state.
pay_nack()

Negative-acknowledge a successful payment to the channel.

State machine transitions from OUTSTANDING to READY.

Raises:StateTransitionError – If channel is not in OUTSTANDING state.
payment_tx

Get channel half-signed payment transaction.

Returns:Serialized payment transaction (ASCII hex).
Return type:str or None
refund_tx

Get channel refund transaction.

Returns:Serialized refund transaction (ASCII hex).
Return type:str or None
refund_txid

Get channel refund transaction ID.

Returns:Refund transaction ID (RPC byte order).
Return type:str or None
spend_tx

Get channel spend transaction.

Returns:Serialized spend transaction (ASCII hex).
Return type:str or None
spend_txid

Get channel spend transaction ID.

Returns:Spend transaction ID (RPC byte order).
Return type:str or None
state

Get channel state machine state.

Returns:State machine state.
Return type:PaymentChannelState
exception two1.channels.statemachine.StateTransitionError

Bases: AssertionError

Invalid state transition error.