This document describes the Starlight payment-channel protocol for the Stellar network.
Starlight allows two participants to transact off-network in a secure fashion. They do so by creating an on-network “channel” that locks up the funds they intend to use. During the lifetime of the channel, the two parties send payments to each other privately (i.e., without publishing transactions to the network) using those funds. When they’re finished, they close the channel and claim their respective shares of the channel’s balance, as determined by those intermediate payments.
The mechanism is secure against fraud and obstruction by either party, as long as the assumptions hold true. If one of the parties disappears or refuses to cooperate, their counterparty will be able to withdraw their current balance from the channel, after waiting for a delay. Cooperating parties should be able to keep a channel open indefinitely, and close it, with no delay, at any time.
This specification is for payment channels that lock up and transfer lumens, the native currency of the Stellar network. It generalizes, with some minor modifications, to payment channels in other assets issued on Stellar.
The Starlight protocol is still under active development. Until it is stabilized, we may make incompatible changes and upgrades to it without notice.
The Starlight protocol makes use of several built-in features and functionalities of the Stellar Protocol, including multisignature accounts, sequence numbers, and mintime and maxtime constraints. You can learn more about the relevant features of the Stellar protocol in the appendix.
There are two parties in a Starlight channel: a Host and a Guest. The Host is the one proposing the channel and the Guest is the one accepting it.
Both parties run a software agent that executes the steps of the protocol on their behalf. The agent must be highly available: it has to be online to receive channel payments and must also interact with each channel periodically to protect the funds they contain. The agent listens for commands from the user, monitors the Stellar network for relevant transactions, sends and receives RPC messages to and from other users’ Starlight agents, and triggers time-based events.
For an agent to be a Guest on a channel,
its agent must have a publicly accessible URL or IP address.
Host does not need to have a publicly accessible server.
Host sends messages to Guest using HTTP POST
requests.
To receive messages from Guest,
Host must send long-polling GET
requests to Guest.
The Host’s agent sets up a channel by creating various Stellar accounts as described below. The Host is also responsible for funding the channel with:
- Lumens to cover each account’s required reserve balance. These lumens are fully recovered by the Host when the channel closes.
- Lumens to cover fees for the on-network parts of the Starlight protocol. Some of these may be recovered by the Host when the channel closes.
- The balance to be used for making payments from the Host to the Guest. Once the Guest has received some payments in the channel, he or she may use their balance to make payments to the Host.
The Host has a Stellar account with a single public key.
We’ll refer to this account as HostAccount
and the key as HostAccountKey
.
The Guest also has a Stellar account with a single public key:
GuestAccount
and GuestAccountKey
.
The Guest’s account must be well-known
(i.e.,
an account that the Host is able to find).
When creating a channel, the Host (via his or her agent) sends a channel-proposal protocol message to the Guest. The Guest responds with a message accepting the channel. (Since a channel does not require any funding from the Guest, he or she can accept all incoming channel proposals automatically.)
The funds in the channel are held in a new
EscrowAccount
created for this purpose.
Funds in that account can be spent only when both parties sign a transaction.
Two other accounts,
the
HostRatchetAccount
and the
GuestRatchetAccount,
are created for use with the channel but do not hold funds
(other than their required minimum balances).
Instead,
they serve as dedicated Stellar-transaction “source accounts” with predictable sequence numbers.
All three accounts are merged back to HostAccount
when the channel closes.
Once the channel is set up, the parties may make any number of (private, instant) payments to each other using the funds in the channel. The Host may “top up” the channel with additional funds. Either party may decide to close the channel at any time. Closing the channel pays each party his or her respective balance of the funds in the channel and returns reserves and unused fees to the Host.
Making a payment involves the off-ledger exchange of signed, unpublished Stellar transactions. These transactions guarantee that if one party does not cooperate in continuing or closing the channel, the other party can close the channel unilaterally and extract their correct balance.
In each payment round,
the parties first exchange signed settlement transactions that distribute the funds in the
EscrowAccount
according to the respective balances in the channel.
The settlement transactions have a too-high sequence number,
however,
so they are unpublishable until EscrowAccount
’s sequence number is raised to that level.
Next, the parties exchange signed ratchet transactions. These use the ratchet accounts to bump up the sequence number of the EscrowAccount to set up the settlement transactions.
A ratchet transaction must therefore be published before it’s possible to publish the corresponding settlement transactions.
But it’s possible for one party to publish an outdated ratchet transaction
(one from an earlier payment round),
closing the channel with outdated settlement transactions.
To combat this
(which might allow one party to extract more from the channel than they’re entitled to),
there is a gap between the maximum time of the ratchet transactions and the minimum time of the settlement transactions.
In this time interval,
it’s possible for the other party to notice the discrepancy and publish a more up-to-date ratchet transaction.
This renders the old settlement transactions unpublishable
(since EscrowAccount
’s sequence number is now too high).
These ratchet and settlement transactions are insurance for both parties in case either becomes unavailable or declines to cooperate. They allow one party unilaterally to close the channel, but as described, they involve a delay. If, however, the Host and the Guest agree to close the channel, they can sign and publish a new cooperative close transaction that closes the channel without delay.
Host creates a channel by:
- Generating keypairs and choosing parameters
- Publishing a transaction to create three accounts:
EscrowAccount
,HostRatchetAccount
, andGuestRatchetAccount
- Proposing the channel to Guest and receiving a required signature from Guest
- Publishing a funding transaction
Host’s account must have a balance sufficient to fund the payment channel to the desired level while still maintaining its own minimum balance.
In addition to the funds that Host and Guest will use for transacting with each other,
channel setup requires 5 + 10·Feerate
lumens to be locked up
(where Feerate is a channel parameter):
- The minimum balance for Guest’s ratchet account (2 lumens, as there will be at most two additional signers on the account, and each additional signer adds a .5 lumen minimum balance);
- The minimum balance for Host’s ratchet account (1.5 lumen, as there will be at most one additional signer);
- The minimum balance for the escrow account (1.5 lumens, as there will be at most one additional signer).
- The maximum that may be required in
fees
from the escrow account
(
8·Feerate
XLM) and the ratchet accounts (1·Feerate
XLM each).
Host recovers the minimum balance of each account when the account is closed.
In a cooperative close,
Host should expect to recover most of the prepaid fees deposited
(all but 4·Feerate
XLM).
In a force-close,
it is possible for all of those fees to be spent.
The channel creation process begins when the Host agent receives an
OpenChannel
command from the user to create a channel with an initial Host deposit of HostAmount
XLM.
Before this time,
the channel is considered to be in the
Start
state.
Host begins by generating keypairs for the following three keys:
HostEscrowPubKey
. This is used during setup of the escrow account and during cooperative channel closing. This is also used as theChannelID
.FirstThrowawayPubKey
. This is used briefly during setup ofHostRatchetAccount
, to provide a unique ID for that account.SecondThrowawayPubKey
. This is used briefly during setup ofGuestRatchetAccount
, to provide a unique ID for that account.
Host also chooses these parameters:
Host first creates the three accounts needed for the channel.
Host signs and publishes three
SetupAccountTxs
to the network,
with the following respective TargetAccount
s:
HostRatchetAccount,
GuestRatchetAccount,
and
EscrowAccount.
(As with all other transactions from
HostAccount,
the specific sequence numbers are left to the implementation,
but they must be in that order.
Specifically,
the sequence number on the
SetupAccountTx
for the
EscrowAccount
must be higher than the sequence numbers on the other two transactions.)
It does not matter if these transactions succeed or fail, since the only way they can fail is if the account they are attempting to create already exists.
Host transitions to the SettingUp state, waits until all three of the transactions have either succeeded or failed, then moves on to the next step.
Host observes EscrowAccount.SequenceNumber
and marks it down as BaseSequenceNumber
.
Host defines RoundNumber
as 1.
Host defines FundingTime
as the most recent ledger timestamp.
Host constructs and sends a ChannelProposeMsg message to Guest, conveying the information about the channel.
Host can send this message to Guest’s agent’s URL,
which is specified as STARLIGHT_SERVER
in the
stellar.toml
of Guest’s home domain.
Host finds Guest’s home domain from Guest’s Stellar account or
Stellar federation address.
Once Host has sent this message, he moves into the ChannelProposed state.
ChannelProposeMsg is the first that Guest hears about the channel. Before this, Guest’s channel is considered to be in the Start state.
When Guest receives the
ChannelProposeMsg,
she validates it.
She defines FundingTime
as ChannelProposeMsg.FundingTime
.
Guest observes EscrowAccount.SequenceNumber
and marks it down as BaseSequenceNumber
.
Guest defines RoundNumber
as 1.
Guest constructs and sends a ChannelAcceptMsg to Host.
Guest now moves into the AwaitingFunding state.
When Host receives ChannelAcceptMsg, he constructs the ratchet and settlement transactions and validates the two signatures.
Host constructs
FundingTx,
signs it with the private key(s)
for HostAccount
as well as HostEscrowPubKey
,
FirstThrowawayPubKey
,
and SecondThrowawayPubKey
,
and publishes it to the network.
In addition to adding funds to EscrowAccount
,
this transaction adds GuestEscrowPubKey
(which is defined to be GuestAccountKey
)
as a co-signer on EscrowAccount
and GuestRatchetAccount
,
sets HostEscrowPubKey
as the sole signer on HostRatchetAccount
and a co-signer on GuestRatchetAccount
,
and adds an additional minimum balance to HostRatchetAccount
and GuestRatchetAccount
.
After publishing this transaction, Host moves into the AwaitingFunding state.
Once Guest and Host see the FundingTx hit the ledger, they transition to the Open state.
This section describes how one party, Sender (who could be either Guest or Host), can send a payment to the other party, Recipient. Sender will only attempt this when in an Open state; otherwise, the agent will reject the user’s request to make a payment.
Sender receives a
ChannelPayCmd
from the user to make a payment of PaymentAmount
over a channel where Recipient is the counterparty.
She chooses either the most recent ledger timestamp,
or the PaymentTime
from the most recent completed payment,
whichever is later,
as PaymentTime
.
Sender updates RoundNumber
to RoundNumber + 1
.
Sender constructs and sends a PaymentProposeMsg to Recipient.
Sender transitions to a PaymentProposed state.
When Recipient receives PaymentProposeMsg, they validate it.
If they are currently in a PaymentProposed state, they need to resolve the conflict.
Otherwise, Recipient accepts the payment as described below.
Recipient increments RoundNumber
to RoundNumber + 1
,
and defines
PendingPaymentAmount
as the PaymentAmount
from the message,
and PendingPaymentTime
as the PaymentTime
from the message.
Recipient constructs a PaymentAcceptMsg.
Recipient sets CounterpartyLatestSettlementTxes
to
PaymentSettleWithGuestTx
and
PaymentSettleWithHostTx,
with the counterparty’s signatures included on them.
Recipient transitions to a PaymentAccepted state.
Recipient sends the PaymentAcceptMsg to Sender.
When Sender receives PaymentAcceptMsg, they validate it.
Sender constructs and sends a PaymentCompleteMsg to Recipient.
After sending this message, Sender considers the payment complete.
Once Recipient receives PaymentCompleteMsg, they validate it, at which point they consider the payment complete.
Before switching back to the Open state, Sender and Recipient each make the following changes:
- Set
GuestBalance
to NewGuestBalance. - Set
HostBalance
to NewHostBalance. - Set
PaymentTime
toPendingPaymentTime
. - Set
CurrentRatchetTx
to the signed SenderRatchet or RecipientRatchet (when agent is the Sender or Recipient, respectively). - Set
CurrentSettlementTxes
andCounterpartyLatestSettlementTxes
to the signed SettleWithGuestTx and SettleWithHostTx - Recipient now considers the payment complete, and transitions to an Open state.
From this point it will be possible for either party to force-close the channel using the ratchet transaction and the two presigned settlement transactions. See Force closing, below.
This process occurs when Host receives a TopUpCmd from the user. (This version of the protocol does not support topping-up by the Guest.)
Host can top up the channel simply by sending a payment to the escrow account from his HostAccount
account.
A transaction including any such payment is considered a
TopUpTx.
Host and Guest are always watching the account for any incoming payments, which they immediately credit to Host’s balance.
It is possible for both parties to attempt to make payments at the same time (i.e., their PaymentProposeMsg messages cross in mid-flight). If that happens, they will need to resolve that conflict before they can consider either payment finalized.
If a party,
Sender,
receives a
PaymentProposeMsg
while in a
PaymentProposed
state,
they compare the PaymentAmount
of the incoming message against the one from the
PaymentProposeMsg
that they had previously sent.
If Sender’s attempted payment was higher
(or if the attempted payments were equal and Sender is the Host),
they create a “merged” payment.
They increment RoundNumber
,
then initiate a new
PaymentProposeMsg
with a PaymentAmount
that is the difference between their attempted payments,
and with the same PaymentTime
as their own attempted payment.
If Sender’s attempted payment was lower,
(or if the attempted payments were equal and Sender is the Guest),
they increment RoundNumber
,
then transition to an
AwaitingPaymentMerge
state and wait for the new
PaymentProposeMsg
from their counterparty.
Once that higher payment is complete,
they consider both payments completed.
This process occurs when either agent receives a CloseChannelCmd from the user.
Either Guest or Host can request a cooperative close of the channel at any time. The party requesting a close is here referred to as the Closer, and the other party is here referred to as the Cooperator.
Closer constructs and sends a CloseMsg to Cooperator.
Closer then transitions to state AwaitingClose.
When Cooperator receives this message, they validate it.
If the message is valid, Cooperator constructs a CooperativeCloseTx, adds the signature from the message to it, adds their own signature from their own escrow public key, and attempts to submit it to the ledger.
Cooperator only cooperates if they are in an
Open,
PaymentProposed,
or
AwaitingClose
state.
If Cooperator is in a
PaymentProposed
state,
they should validate CooperativeCloseTx
with respect to their pre-payment balances.
Since all correct cooperative close transactions at any given time should be the same, there is no need to resolve conflicts between cooperative close attempts.
Once a channel is funded, each party must remain vigilant to protect their funds in the channel.
In all states other than the setup states and force closing states, each party must watch out for either of the following conditions:
- Their counterparty publishes a ratchet transaction from a previous round
(i.e.,
one that bumps
EscrowAccount
’s sequence number to a number lower thanCurrentRatchetTx
does), or - The ledger timestamp approaches
PaymentTime
+MaxRoundDuration
(which is also the maxtime for theCurrentRatchetTx
, minusFinalityDelay
), and the counterparty has not yet cooperated to either create a new payment round or close the channel.
In the first case,
the party must act to prevent their counterparty from settling the channel at an outdated state
(most likely a state in which the counterparty had a higher balance).
In the second case,
the party must act to prevent their latest ratchet transaction from timing out,
which would mean all the money in EscrowAccount
would become unspendable by either party.
In either of these cases (or if the user manually requests a force close by sending a ForceCloseCmd), an honest party can protect themselves by taking the following actions:
First,
they submit CurrentRatchetTx
to the ledger.
They then transition to state
AwaitingRatchet.
Once they see the ratchet transaction hit the ledger,
they transition to state
AwaitingSettlementMintime.
They then wait until the mintime of the CurrentSettlementTxes
,
at which point they publish the CurrentSettlementTxes
and transition to state
AwaitingSettlement.
Once they see the settlement transactions hit the ledger, they transition to state Closed.
In some cases, a party’s counterparty can have a ratchet transaction that supersedes the party’s own latest ratchet transaction. This can occur when there is an uncompleted payment round, such as when a party is in state PaymentAccepted, or when a force close or cooperative close was initiated while it was in that state. Fortunately, in all such cases, the party does have the signed settlement transactions needed to settle that higher round.
If,
at any time,
a party sees a ratchet transaction hit the ledger that bumps the ratchet higher than CurrentRatchetTx
,
they update
CurrentSettlementTxes
to
CounterpartyLatestSettlementTxes.
If they are in state
AwaitingSettlement,
they return to state
AwaitingSettlementMintime.
This is a list of states in which one party can be at a given time, with respect to a particular single channel.
In all states other than Start and Closed, the agent maintains the following additional, immutable, information about each channel:
ChannelID
(the globally unique identifier for the channel; identical toHostEscrowPubKey
)MaxRoundTime
FinalityDuration
CounterpartyURL
(the Starlight URL of the counterparty)EscrowAccount
HostRatchetAccount
(including account ID and a sequence number, unless the agent is still in the SettingUp state and the account has not yet been created)GuestRatchetAccount
(including account ID and a sequence number, unless the agent is still in the SettingUp state and the account has not yet been created)HostEscrowPubKey
GuestEscrowPubKey
Role
(whether the agent is the Host or Guest for that channel)
This is the state of a channel that has not yet been created or proposed.
This is a state that the Host is in while waiting for their transactions creating the HostRatchetAccount
,
GuestRatchetAccount
,
and EscrowAccount
accounts to hit the ledger.
While in this state, the agent maintains the following additional information:
HostAmount
SetupTxs
(the three SetupAccountTxs they are waiting on)
This is a state that the Host is in after sending a
ChannelProposeMsg,
while waiting for Guest to send a ChannelAcceptMsg
that accepts the channel.
While in this state, the agent maintains the following additional information:
HostAmount
FundingTime
RatchetTx
SettleOnlyWithHostTx
This is a state that both parties are in while waiting for the FundingTx to hit the ledger.
While in this state, the agent maintains the following additional information:
HostAmount
FundingTime
RatchetTx
SettleOnlyWithHostTx
CounterpartyRatchetSig
CounterpartySettlementSig
This is a state that the Host is in while waiting for the CleanupTx to hit the ledger.
When a channel is open (i.e., in an open state, a payment state, a cooperative closing state, or a force closing state, the agent tracks the following information:
BaseSequenceNumber
, the sequence number of theEscrowAccount
upon channel opening;RoundNumber
, the number of the current roundHostBalance
, the amount due to Host upon channel closureGuestBalance
, the amount due to Guest upon channel closureCurrentRatchetTx
, the RatchetTx from the latest round, including the counterparty’s signature on it;CurrentSettlementTxes
, the settlement transactions from the latest round, including the counterparty’s signature on them;CounterpartyLatestSettlementTxes
, the latest settlement transactions for which the counterparty has a valid ratchet transaction, including the counterparty’s signature on them;PaymentTime
, the time of the latest completed payment (orFundingTime
if no payment has been made).
Note:
CurrentSettlementTxes
normally has two items,
SettleWithHostTx
and SettleWithGuestTx
.
When GuestBalance
is 0,
(such as when the channel is first opened),
CurrentSettlementTxes
has only one item,
SettleOnlyWithHostTx
.
CounterpartyLatestSettlementTxes
is normally the same as CurrentSettlementTxes
.
They are different only if the party is in a
PaymentAccepted
state,
or if a close was initiated while the party was in that state.
See
Handling later counterparty ratchet transactions.
This is the “default” state of an open channel, in which the channel is open, the round has not yet timed out, and the party is not aware of any pending payments or close attempts.
These states are defined with respect to the Sender (who could be either Host or Guest) and the Recipient (the other party) of a particular attempted payment.
In each of these states, the agent needs to maintain the following information about the most recently proposed payment:
PendingPaymentAmount
, the amount of the pending paymentPendingPaymentTime
, the time of the pending payment
This is the state that Sender is in after sending a PaymentProposeMsg, and while waiting for a PaymentAcceptMsg from Recipient.
This is the state that Recipient is in after responding to a PaymentProposeMsg with a PaymentAcceptMsg, and waiting for a PaymentCompleteMsg.
This is the state that a party is in if they were expecting a PaymentAcceptMsg, but instead received a PaymentProposeMsg with a higher payment amount. During this state, the party awaits a PaymentProposeMsg with a higher round.
During this state, a party needs to maintain the following additional information:
MyPaymentAmount
, the amount of the party’s own attempted paymentTheirPaymentAmount
, the amount of the counterparty’s attempted payment
This is the state that either party is in if they have published a ratchet transaction and are waiting for it to hit the ledger.
This is the state that either party is in if they have successfully published their own latest ratchet transaction to the ledger, and are waiting for the minimum time on the settlement transactions for that round to arrive.
This is the state that either party is in once they have submitted the settlement transactions and are waiting for those to hit the ledger.
This is the state of a channel that has been closed.
This is the state that a party is in while waiting for a CooperativeCloseTx to hit the ledger.
During this time, the party needs to maintain the following additional information:
CooperativeCloseTx
, the transaction the party is waiting on
Each payment channel requires three accounts on the ledger. Additionally, each party to a channel has a single wallet account, which they use as the funding and destination account for all of their channels.
This is the account that stores the money that is held in the payment channel,
and is the source account of the
SettleOnlyWithHostTx,
SettleWithGuestTxs,
SettleWithHostTxs,
and CooperativeCloseTx.
Its master key is HostEscrowPubKey
.
It is created by a
SetupAccountTx.
The
FundingTx
adds an additional signer to this account,
GuestEscrowPubKey
,
with weight 1.
It also sets the thresholds on the account to 2,
meaning that both the master key
(HostEscrowPubKey
)
and GuestEscrowPubKey
are required to sign off on any operations from that account.
The minimum balance on this account after the FundingTx is 1.5 XLM, because it has one additional signer.
This is the account that is the source account for Host
’s
RatchetTx.
Its master key is FirstThrowawayPubKey
.
It is created by a
SetupAccountTx.
The
FundingTx
adds an additional signer to this account,
HostEscrowPubKey
,
with weight 1.
It also sets the master key weight on this account to 0,
meaning that only HostEscrowPubKey
is allowed or required to sign off on any operations from that account.
The minimum balance on this account after the
FundingTx
is 1.5 XLM,
because it has one additional signer,
HostEscrowPubKey
.
This is the account that is the source account for Guest
’s
RatchetTx.
Its master key is SecondThrowawayPubKey
.
It is created by a
SetupAccountTx.
The
FundingTx
adds two additional signers to this account,
HostEscrowPubKey
and GuestEscrowPubKey
,
each with weight 1,
and sets the master key weight to 0.
It also sets the thresholds on the account to 2,
meaning that both HostEscrowPubKey
and GuestEscrowPubKey
are required to sign off on any operations from that account.
The minimum balance on this account after the
FundingTx is 2 XLM because it has two additional signers, GuestEscrowPubKey
and HostEscrowPubKey
.
This account can be used by Host
for all of their payment channels
(including as their
GuestAccount
in other channels in which they are the Guest).
This is the account that is the source account for the SetupAccountTxs, FundingTx, and TopUpTxs (if any).
This is the destination into which funds are paid in the SettleOnlyWithHostTx and SettleWithHostTx.
This account can be used by Guest
for all of their payment channels
(including as their
HostAccount
in other channels in which they are the Host).
This is the destination into which funds are paid in the SettleWithGuestTx.
The following values are defined in the context of a particular payment round.
RoundSequenceNumber
is defined as BaseSequenceNumber + RoundNumber * 4
Sender
is defined as the party that sends the
PaymentProposeMsg
and
PaymentCompleteMsg,
and normally
(except in cases of conflict)
is the party that receives and validates the
PaymentAcceptMsg.
Recipient
is defined as the party that receives and validates the
PaymentProposeMsg
and
PaymentCompleteMsg,
and normally
(except in cases of conflict)
is the party that sends the
PaymentAcceptMsg.
SenderRatchetAccount
is defined as HostRatchetAccount
if Sender
is the party whose role in the channel is Host
,
and is defined as GuestRatchetAccount
otherwise.
RecipientRatchetAccount
is defined as HostRatchetAccount
if Recipient
is the party whose role in the channel is Host
,
and is defined as GuestRatchetAccount
otherwise.
SenderEscrowPubKey
is defined as HostEscrowPubKey
if Sender
is the party whose role in the channel is Host
,
and is defined as GuestEscrowPubKey
otherwise.
RecipientEscrowPubKey
is HostEscrowPubKey
if Recipient
is the party whose role in the channel is Host
,
and is defined as GuestEscrowPubKey
otherwise.
NewGuestBalance
is defined as the Guest’s balance after the payment.
If Sender
is the party whose Role
in the channel is Guest
,
NewGuestBalance
is defined as GuestBalance - PaymentAmount
,
If Sender
is the party whose Role
in the channel is Host
,
NewGuestBalance
is defined as GuestBalance + PaymentAmount
,
NewHostBalance
is defined as the Host’s balance after the payment.
If Sender
is the party whose Role
in the channel is Guest
,
NewHostBalance
is defined as HostBalance + PaymentAmount
,
If Sender
is the party whose Role
in the channel is Host
,
NewHostBalance
is defined as HostBalance - PaymentAmount
,
If
NewGuestBalance is 0,
PaymentSettleWithGuestTx
is not used.
Otherwise, PaymentSettleWithGuestTx
is defined as a
SettleWithGuestTx
constructed with:
PendingPaymentTime
asPaymentTime
- NewGuestBalance as
GuestAmount
If
NewGuestBalance is 0,
PaymentSettleWithHostTx
is defined as a
SettleOnlyWithHostTx
constructed with PendingPaymentTime
as PaymentTime
.
Otherwise, PaymentSettleWithHostTx
is defined as a
SettleWithHostTx
constructed with PendingPaymentTime
as PaymentTime
.
SenderRatchetTx
is defined as a
RatchetTx
constructed with:
_ SenderRatchetAccount as SourceAccount
_ PendingPaymentTime
as PaymentTime
RecipientRatchetTx
is defined as a
RatchetTx
constructed with:
_ RecipientRatchetAccount as SourceAccount
_ PendingPaymentTime
as PaymentTime
Every message has three top-level fields:
Body
Version
MessageSignature
The message-type-specific fields
(specified in the following sections)
go in the Body
.
Version
is a number, representing the protocol version.
This number will be incremented with each incompatible change to the protocol.
Later versions of the software may be able to understand messages with older version numbers,
but are not guaranteed to.
The current Version
number is 2.
When any message is sent,
the agent sets the Version
number to its current protocol version number.
When any message is received,
the agent looks at the Version
.
If it is a version number that it does not support,
it ignores the message.
MessageSignature
is how a party authenticates that the message was sent by their channel counterparty.
It is a signature on the serialized message (with the MessageSignature
field excluded).
It should be a signature from the public key of the message's sender.
Specifically, that public key is HostAccountKey
if the message's sender is the Host
on that channel,
and GuestAccountKey
if the sender is the Guest
on that channel.
When any message is received,
before taking the message-specific validation steps described below,
the recipient looks up the channel using the ChannelID
from the message Body
and looks up their counterparty's public key,
which is either HostAccountKey
or GuestAccountKey
,
depending on their counterparty's role in the channel.
(If the message is a
ChannelProposeMsg,
no channel will exist yet.
They should instead determine HostAccountKey
from the HostAccount
in the message itself.)
The recipient validates that MessageSignature
is a valid signature from that public key for the message.
If it is not,
or if the message is not a
ChannelProposeMsg
and the channel lookup fails,
the recipient ignores the message.
ChannelID
(the same asHostEscrowPubKey
and the account ID ofEscrowAccount
)GuestEscrowPubKey
HostRatchetAccount
GuestRatchetAccount
MaxRoundDuration
FinalityDelay
Feerate
HostAmount
FundingTime
HostAccount
If valid,
this message causes the agent
(who is the Guest in the channel)
to look up the current sequence number for
EscrowAccount,
and store it as BaseSequenceNumber
.
The agent looks up the current sequence number for
GuestRatchetAccount
and
HostRatchetAccount,
and stores them as GuestRatchetAccount.SequenceNumber
and HostRatchetAccount.SequenceNumber
,
respectively.
The agent then constructs and sends a ChannelAcceptMsg and transitions the channel to an AwaitingFunding state.
To validate this message, the agent who receives it checks that the following conditions are true:
- the agent does not already have a channel with that ChannelID (i.e., the channel must be in a Start state).
- the agent does not already have a channel where
HostAccount
is the counterparty. GuestEscrowPubKey
is the agent’s own public key.- There exist accounts on the ledger with account IDs:
ChannelID
HostRatchetAccount
GuestRatchetAccount
.
- The values for
MaxRoundDuration
,FinalityDelay
, andFeerate
, are within the agent’s accepted bounds. HostAmount
is greater than 0.- The latest ledger timestamp is later than
FundingTime - MaxRoundDuration
and earlier thanFundingTime + MaxRoundDuration
.
ChannelID
GuestRatchetRound1Sig
GuestSettleOnlyWithHostSig
This message is constructed by Guest as part of the process of accepting a channel.
The agent creates
two transactions:
SettleOnlyWithHostTx
and
RatchetRound1Tx, which is a RatchetTx constructed with FundingTime
as PaymentTime
and HostRatchetAccount
as SourceAccount
.
SettleOnlyWithHostTx
returns the funds in the EscrowAccount
and GuestRatchetAccount
to the Host
(who is the only one who’s put anything in,
so far).
RatchetTx
bumps the sequence number of the EscrowAccount
in order to make SettleOnlyWithHostTx
valid
(after a timeout).
Guest signs both of these transactions
(with the private key for GuestEscrowPubKey
)
to get GuestRatchetRound1Sig
and GuestSettleOnlyWithHostSig
,
respectively.
To validate this message, the agent who receives it checks that the following conditions are true:
- the agent has a channel with ID
ChannelID
. - that channel is in state ChannelProposed.
GuestRatchetRound1Sig
is a valid signature onRatchetRound1Tx
, whereRatchetRound1Tx
is a RatchetTx constructed with:FundingTime
asPaymentTime
,HostRatchetAccount
asSourceAccount
.
GuestSettleOnlyWithHostSig
is a valid signature on SettleOnlyWithHostTx.
If valid, this message causes the agent to submit a FundingTx and transition to an AwaitingFunding state.
ChannelID
RoundNumber
PaymentTime
PaymentAmount
SenderSettleWithGuestSig
(or empty)SenderSettleWithHostSig
This message is constructed by Sender as part of the process of proposing a payment.
To construct this message,
Sender’s agent creates a pair of transactions,
PaymentSettleWithGuestTx
and
PaymentSettleWithHostTx.
If
NewGuestBalance
is 0,
this is only one transaction,
which is a
SettleOnlyWithHostTx,
in which case SenderSettleWithGuestSig
is empty.
Sender signs the settlement transactions with the private key for
SenderEscrowPubKey
to get SenderSettleWithGuestSig
and SenderSettleWithHostSig
,
respectively.
To validate this message, the agent first checks that the following conditions are true:
- the agent has a channel with ID
ChannelID
. - that channel is in state Open, PaymentProposed, or AwaitingPaymentMerge.
PaymentAmount
is greater than 0PaymentAmount
is less than the counterparty’s balance (HostBalance
if counterparty is theHost
,GuestBalance
otherwise).
The remaining checks depend on which state the channel is in.
If the channel is in state PaymentProposed,
the agent only checks that the RoundNumber
in the message is equal to the RoundNumber
in its own state.
If the channel is in state Open or AwaitingPaymentMerge, the agent checks that the following conditions are true:
- the
RoundNumber
in the message is one higher than theRoundNumber
in its own state. PaymentTime - MaxRoundDuration
is earlier than the most recent ledger timestamp.PaymentTime + MaxRoundDuration
is later than the most recent ledger timestamp.PaymentTime
is later than or equal to thePaymentTime
of the most recent completed payment.
Then, for these next checks, all of the
computed values
used are computed using the RoundNumber
from the message,
the PaymentAmount
from the message as PendingPaymentAmount
,
and PaymentTime
from the message as PendingPaymentTime
,
rather than using any values from the channel’s own state.
NewGuestBalance
is greater than or equal to 0- If
SenderSettleWithGuestSig
is empty, thenNewGuestBalance
is 0 - If
SenderSettleWithGuestSig
is not empty, thenNewGuestBalance
is greater than 0, andSenderSettleWithGuestSig
is a valid signature fromSenderEscrowPubKey
on PaymentSettleWithGuestTx SenderSettleWithHostSig
is a valid signature fromSenderEscrowPubKey
on PaymentSettleWithHostTx.
If the channel is in state AwaitingPaymentMerge, the agent also checks that the following additional condition is true:
PaymentAmount
is equal toTheirPaymentAmount - MyPaymentAmount
If this message is valid, then the next step depends on the current state of the channel.
If the channel is in an Open or AwaitingPaymentMerge state, this message causes the agent to send a PaymentAcceptMsg and transition to a PaymentAccepted state.
If the channel is in a
PaymentProposed
state,
then the next step depends on whether the PaymentAmount
in the message is lower than the PendingAmount
,
as described in more detail in the
conflict resolution procedure.
If it is lower (or the amounts are equal and the party is the Host of the channel), this message causes the agent to send a new PaymentProposeMsg and update its state to reflect new proposed payment.
If it is higher,
this message causes the agent to transition to an
AwaitingPaymentMerge
state,
with MyPaymentAmount
equal to the PendingPaymentAmount
from the previous state,
and TheirPaymentAmount
equal to the PaymentAmount
from the received
PaymentProposeMsg.
ChannelID
RoundNumber
RecipientRatchetSig
RecipientSettleWithGuestSig
RecipientSettleWithHostSig
This message is constructed by Recipient as part of the process of accepting a payment.
The agent creates SenderRatchetTx, Note that this is a ratchet transaction for Recipient’s counterparty.
The agent signs this ratchet transaction with the private key for
RecipientEscrowPubKey
to get RecipientRatchetSig
.
Recipient uses the same key to sign
PaymentSettleWithGuestTx
and
PaymentSettleWithHostTx
to get RecipientSettleWithGuestSig
and RecipientSettleWithHostSig
.
(If
NewGuestBalance
is 0,
RecipientSettleWithGuestSig
is empty instead.)
To validate this message, the agent first checks that the following conditions are true:
- the agent has a channel with ID
ChannelID
. - that channel is in state PaymentProposed.
RoundNumber
is equal to theRoundNumber
in its own stateRecipientRatchetSig
is a valid signature fromRecipientEscrowPubKey
on SenderRatchetTx- If
NewGuestBalance
is 0,
RecipientSettleWithGuestSig
is empty; otherwise, it is a valid signature fromRecipientEscrowPubKey
on PaymentSettleWithGuestTx RecipientSettleWithHostSig
is a valid signature fromRecipientEscrowPubKey
on PaymentSettleWithHostTx
If valid, this message causes the agent to send a PaymentCompleteMsg and transition the channel to an Open state.
ChannelID
RoundNumber
SenderRatchetSig
This message is constructed by Sender as part of the process of completing a payment.
Sender constructs
RecipientRatchetTx,
and signs it with the private key for
SenderEscrowPubKey
to get SenderRatchetSig
.
To validate this message, the agent checks that the following conditions are true:
- the agent has a channel with ID
ChannelID
. - that channel is in state PaymentProposed.
RoundNumber
is equal to theRoundNumber
in its own state.SenderRatchetSig
is a valid signature fromSenderEscrowPubKey
on RecipientRatchetTx.
When an agent receives this, it ignores it if the channel does not exist, or if the channel is not in a PaymentAccepted state.
If valid, this message causes the agent to transition the channel to an Open state.
ChannelID
CooperativeCloseSig
This message is constructed as part of the process of cooperatively closing a channel.
Closer constructs a CooperativeCloseTx.
Closer signs this transaction with their own escrow private key
(either GuestEscrowPrivKey
or HostEscrowPrivKey
)
to get CloserSig
.
To validate this message, the agent first checks that the following conditions are true:
- the agent has a channel with ID
ChannelID
. - that channel is in state Open PaymentProposed, or AwaitingClose.
CooperativeCloseSig
is a valid signature on a CooperativeCloseTx.
If valid, this message causes the agent to submit a CooperativeCloseTx and transition to a AwaitingClose state.
As described
below
in greater detail,
transaction fees for transactions from the
EscrowAccount
and ratchet accounts
are computed using a channel-specific Feerate
,
while transaction fees for transactions from the
HostAccount
are left to the implementation.
The implementation is also responsible for managing sequence numbers for transactions from the HostAccount. If a transaction submitted from the HostAccount is invalid (likely due to some inconsistency in how the agent tracks the account’s balance or sequence numbers), the solution is left to the implementation, though it could attempt to address the problem with HostAccount.
- Source account:
HostAccount
- Sequence number:
HostAccount.SequenceNumber + 1
- Operations:
- Create account
TargetAccount
with starting balance 1 XLM
- Create account
An agent watches for this transaction while it is in a SettingUp state for a channel in which it is the Host.
It does not matter if this transaction succeeds or fails, since the only way it can fail is if the account it is attempting to create already exists.
If the Host agent sees this transaction succeed or fail,
then the next step depends on the TargetAccount
of the transaction.
If the transaction’s TargetAccount
is not EscrowAccount
,
the agent continues waiting.
If the transaction’s TargetAccount
is EscrowAccount
,
meaning it is the last of the
SetupAccountTxs
for that account,
the agent
(who is the Host in the channel)
looks up the sequence number for
EscrowAccount,
and stores it as BaseSequenceNumber
.
It also looks up the sequence number for
HostRatchetAccount
and
GuestRatchetAccount,
and stores them as HostRatchetAccount.SequenceNumber
and GuestRatchetAccount.SequenceNumber
,
respectively.
It then constructs and sends a ChannelProposeMsg and transitions the channel to a ChannelProposed state.
- Source account:
HostAccount
- Sequence number:
HostAccount.SequenceNumber + 1
- Maxtime:
FundingTime + MaxRoundDuration + FinalityDelay
- Operations:
- Pay
HostAmount + .5 + 8·Feerate
XLM fromHostAccount
toEscrowAccount
- Set options on
EscrowAccount
:- Low threshold: 2
- Medium threshold: 2
- High threshold: 2
- Signer:
- Public key:
GuestEscrowPubKey
- Weight: 1
- Public key:
- Pay
1 + 1·Feerate
XLM fromHostAccount
toGuestRatchetAccount
- Set options on
GuestRatchetAccount
:- Master weight: 0
- Low threshold: 2
- Medium threshold: 2
- High threshold: 2
- Signer:
- Public key:
GuestEscrowPubKey
- Weight: 1
- Public key:
- Set options on
GuestRatchetAccount
:- Signer:
- Public key:
HostEscrowPubKey
- Weight: 1
- Public key:
- Signer:
- Pay
.5 + 1·Feerate
XLM fromHostAccount
toHostRatchetAccount
- Set options on
HostRatchetAccount
:- Master weight: 0
- Signer:
- Public key:
HostEscrowPubKey
- Weight: 1
- Public key:
- Pay
An agent watches for this transaction for its channels that are in an AwaitingFunding state.
If it sees this transaction succeed, it transitions the channel to an Open state.
If the agent’s role in that channel is
Guest,
it does a lookup on the ledger to confirm that the sequence number of
EscrowAccount
is still equal to its stored BaseSequenceNumber
,
and that the sequence number of
GuestRatchetAccount
is still the same as the one stored locally.
It also checks that the current balance of
EscrowAccount
is equal to or greater than HostAmount + 1.5 + 8·Feerate
XLM.
Finally,
it checks that there are no additional signers on
EscrowAccount
or
GuestAccount,
other than those added by the
FundingTx.
If any of the values are different,
it transitions the channel to a
Closed
state.
This transaction is not expected to fail.
If an agent is attempting to submit this transaction and it becomes invalid due to its mintime expiring,
then the agent’s
PrefundTimeout
will cause it to clean up the channel.
No other action is necessary with respect to that channel.
(Depending on how the implementation manages the HostAccount
’s sequence number,
the agent may need to take some other action to ensure that later transactions can still complete,
but this is left to the implementation.)
- Source account:
HostAccount
- Sequence number:
HostAccount.SequenceNumber + 1
- Operations:
- Merge account
EscrowAccount
toHostAccount
- Merge account
HostRatchet
toHostAccount
- Merge account
GuestRatchet
toHostAccount
- Merge account
An agent watches for this transaction for its channels that are in an AwaitingCleanup state.
If it sees this transaction succeed, it transitions the channel to a Closed state.
This transaction is not expected to fail.
- Source account:
RatchetAccount
- Sequence number:
RatchetAccount.SequenceNumber + 1
- Maxtime:
PaymentTime + FinalityDelay + MaxRoundDuration
- Operations:
- Bump sequence of
EscrowAccount
to RoundSequenceNumber + 1
- Bump sequence of
An agent watches for CurrentRatchetTx
(the most recent ratchet transaction where RatchetAccount
is its own ratchet account)
for channels that are in an
AwaitingRatchet
state
(meaning when it has attempted to submit CurrentRatchetTx
).
If it sees this transaction succeed, it transitions the channel to an AwaitingSettlementMintime state.
This transaction is not expected to fail. If the agent is attempting to submit this transaction and it becomes invalid due to an expired maxtime, this means that the timing assumptions of the channel were violated, which means the money in the channel may be locked up permanently. Dealing with this failure is left to the implementation.
An agent watches for any transaction of this form where RatchetAccount
is its counterparty’s ratchet account for channels that are in any state,
other than a
setup state
or
Closed.
If it sees this transaction succeed,
it checks the new sequence number of EscrowAccount
.
If it is lower than the sequence number to which the channel’s CurrentRatchetTx
would bump the EscrowAccount
’s sequence number,
the agent submits the channel’s CurrentRatchetTx
and transitions the channel to an
AwaitingRatchet
state.
- Source account:
EscrowAccount
- Sequence number: RoundSequenceNumber + 2
- Fees:
3·Feerate
- Mintime:
FundingTime + 2·FinalityDelay + MaxRoundDuration
- Operations:
- Merge account
EscrowAccount
toHostAccount
- Merge account
GuestRatchetAccount
toHostAccount
- Merge account
HostRatchetAccount
toHostAccount
- Merge account
An agent watches for any transaction of this form for channels that are in an AwaitingSettlement state.
If it sees this transaction succeed or fail, it moves the channel to a Closed state.
- Source account:
Escrow
- Sequence number: RoundSequenceNumber + 2
- Mintime:
PaymentTime + 2·FinalityDelay + MaxRoundDuration
- Operations:
- Pay
GuestAmount
fromEscrowAccount
toGuestAccount
- Pay
An agent watches for any transaction of this form for channels that are in an AwaitingSettlement state.
If it sees this transaction succeed or fail, it stays in an AwaitingSettlement state until the SettleWithHostTx is published.
- Source account:
Escrow
- Sequence number: RoundSequenceNumber + 3
- Mintime:
PaymentTime + 2·FinalityDelay + MaxRoundDuration
- Operations:
- Merge account
EscrowAccount
toHostAccount
- Merge account
GuestRatchetAccount
toHostAccount
- Merge account
HostRatchetAccount
toHostAccount
- Merge account
An agent watches for any transaction of this form for channels that are in an AwaitingSettlement state.
If it sees this transaction succeed or fail, it moves the channel to a Closed state.
If GuestAmount
is equal to 0, the first operation is skipped.
- Source account:
EscrowAccount
- Sequence number:
EscrowAccount.SequenceNumber + 1
- Fees:
4·Feerate
- Operations:
- Pay
GuestAmount
fromEscrowAccount
toGuestAccount
(only ifGuestAmount
is greater than 0) - Merge account
EscrowAccount
toHostAccount
- Merge account
GuestRatchetAccount
toHostAccount
- Merge account
HostRatchetAccount
toHostAccount
- Pay
An agent watches for any transaction of this form for channels that are in an AwaitingClose or AwaitingRatchet state.
If it sees this transaction succeed, it moves the channel to a Closed state.
If it is attempting to submit this transaction and sees it fail
(presumably due to either GuestAccount
or HostAccount
having been deleted),
it attempts to submit CurrentRatchetTx
and transitions the channel to an
AwaitingRatchet
state.
If this transaction becomes invalid due to too low a sequence number, it means that one or both parties has submitted a ratchet transaction. No further action with respect to the channel is necessary.
- Source account:
HostAccount
- Sequence number:
HostAccount.SequenceNumber + 1
- Operations:
- Pay
TopUpAmount
fromHostAmount
toEscrowAccount
- Pay
An agent watches for any transaction that makes a payment into EscrowAccount
for channels that are in any state other than
Start
or
Closed.
If it sees such a transaction succeed,
it increments HostAmount
by TopUpAmount
.
If it sees such a transaction fail, the most likely reason is that the channel was closed (probably cooperatively) before the transaction was successfully submitted. This does not require any action with respect to that channel.
This is a list of the RPC commands that the user can send to the agent.
This initiates an attempted channel creation with a specified counterparty.
Counterparty
, the counterparty’s Stellar account or Stellar federation addressMaxRoundDuration
FinalityDelay
HostAmount
This command fails if the party already has a channel (or pending channel) with the specified counterparty (i.e., if the channel is not in a Start state).
If valid, this command causes the agent to submit three SetupAccountTxs, and transitions the channel into a SettingUp state.
This initiates an off-ledger payment using one of the user’s channels.
ChannelID
Amount
This command fails if the channel does not exist,
if that channel is not in an
Open
state,
or if Amount
is higher than the party’s balance in that channel.
If valid, this command causes the agent to send a PaymentProposeMsg and transitions the channel into a PaymentProposed state.
This initiates a top-up from the user’s wallet to one of their channels in which they are the Host.
ChannelID
Amount
This command fails if the channel does not exist, if the user is not the Host of the specified channel, or if that channel is not in an Open state.
If valid, this command causes the channel to return to an Open state.
This initiates an attempted cooperative close of one of the user’s channels.
ChannelID
This command fails if the channel does not exist, or if that channel is not in an Open state.
If valid, this command causes the agent to send a CloseMsg, and transition the channel into an AwaitingClose state.
This initiates an attempted force close of one of the user’s channels.
ChannelID
This command fails if the channel does not exist, or if the channel is already in any force closing state.
If valid, this command causes the agent to submit a ratchettx and transition into an AwaitingRatchet state.
This initiates an attempted cleanup of one of a user’s channels in which they are the Host, and which they have proposed to a counterparty, but for which the counterparty has not yet responded.
ChannelID
This command fails if the pending channel does not exist, or if the channel is not in a ChannelProposed state.
If valid, this causes the agent to submit a CleanupTx and transition into an AwaitingCleanup state.
This is a list of the timers that an agent must watch out for.
When an agent is watching for a particular timer to expire, it is watching for the ledger time to pass that timer, rather than watching its own system time.
An agent will only have one timer pending for each of its channels at any one time.
When a channel is in an
AwaitingFunding
state,
the agent has a timer that expires at FundingTime + MaxRoundDuration + FinalityDelay
.
When that timer expires,
the next step depends on whether the agent is the Host
or Guest
in that channel.
If the agent is the Guest
,
it transitions the channel to a
Closed
state.
If the agent is the Host
,
it submits the
CleanUpTx
and transitions the channel to an
AwaitingCleanup
state.
When a Host
channel is in a
ChannelProposed
state,
the agent has a timer that expires at FundingTime + MaxRoundDuration
.
When that timer expires, the agent submits the CleanUpTx and transitions the channel to an AwaitingCleanup state.
When a channel is in an
open,
payment,
or
cooperative closing
state,
the agent has a timer that expires at PaymentTime + MaxRoundDuration
.
When that timer expires,
the agent submits the CurrentRatchetTx
and transitions the channel to an
AwaitingRatchet
state.
When a channel is in an
AwaitingSettlementMintime
state,
the agent has a timer that expires at the mintime of the CurrentSettlementTxes
.
When that timer expires,
the agent submits the CurrentSettlementTxes
and transitions the channel to an
AwaitingSettlement
state.
Transaction fees are specified in the header of each transaction. The protocol computes them by multiplying a defined feerate by the number of operations in the transaction.
The feerate used in transactions from the HostAccount is left up to the implementation. If these transactions are delayed, it does not threaten the safety or liveness of an open channel (though it may delay a top-up, or prevent a channel from being created in time.) (There are no transactions from the GuestAccount in the protocol.)
For transactions from the
EscrowAccount,
HostRatchetAccount,
and
GuestRatchetAccount,
the feerate used depends on the channel,
which is parameterized with a Feerate
value.
This feerate should be set higher than the minimum feerate, because it is critical that ratchet transactions be included within a certain period of time, to protect the safety of funds in the channel. Future Stellar protocol changes such as this should make it possible to safely set even higher feerates for these transactions.
Each ratchet account has an extra starting balance of 1·Feerate
XLM to pay for the fees on the
RatchetTx
from that account,
if it is needed.
Each escrow account has an extra starting balance of 8·Feerate
XLM to pay for the
CooperativeCloseTx
(which has four operations),
SettleWithGuestTx
(which has one operation),
and
SettleWithHostTx
(which has three operations)
from that account,
since in the worst case,
it is possible for all three to occur from the same account
(if a cooperative close transaction fails due to HostAccount
or GuestAccount
being deleted).
These are timing parameters that are set on a per-channel basis. There is a tradeoff to selecting these parameters— if they are set too low, the liveness and (in the case of FinalityDelay) safety of the channel could be put at risk. If they are set unnecessarily high, then a party may be forced to wait unnecessarily long to exit their lumens from the channel.
This is a parameter chosen by Host when he proposes the channel. Guest should ignore the channel proposal if this parameter is higher or lower than the bounds considered acceptable by Guest.
This parameter, informally speaking, specifies how frequently Host and Guest must interact to keep a channel open (i.e., to produce new ratchet transactions before the latest ones expire).
More precisely,
this parameter should be set sufficiently high that if one party initiates a payment round R
when its view of the ledger timestamp is T
,
then the parties will be able to complete the next round, R + 1
,
before the ledger timestamp is greater than T + MaxRoundDuration
.
Unlike FinalityDelay, if this parameter is set to too low a value, it does not threaten the safety of the channel (only its liveness).
This is a parameter chosen by Host when he proposes the channel. Guest should ignore the channel proposal if this parameter is higher or lower than the bounds considered acceptable by Guest.
This parameter should be chosen so that both parties are confident that,
if an event
occurs on the ledger at time T
,
then that party will be able to observe that it occurred,
submit a transaction,
and have that transaction included on the ledger before time T + FinalityDelay
.
The event could be either a transaction being included,
or the ledger timestamp itself passing time T
.
The party can assume that the transaction they are submitting has a feerate of
the
Feerate
for that channel.
They can also assume that the transaction they are submitting is valid for the entire period of T
to T + FinalityDelay
.
This is a brief overview of the Stellar features used by the Starlight payment channel protocol. For more information on how Stellar works, you can see the Stellar Development Guides.
The Stellar network tracks the state of individual “accounts,” each of which can store some balance of lumens, the Stellar network’s native currency. (These accounts can also store balances of other issued assets on Stellar, though this capability is not used in this specification, which only covers payment channels that use lumens.) Each account starts out with a single public-key signer, known as a “master key,” and has a minimum balance of 1 lumen. This master key is also used as the account ID.
Transactions can add additional signers to an account, which gives those signers partial control of the account. (Stellar supports types of signers other than just public keys, but they are not used in this protocol.) With each additional signer, the minimum balance requirement on the account is increased by 0.5 lumens.
Each signer (including the master key) has a “weight.” For a transaction or operation from an account (see below) to be authorized, it must be signed by a set of signers whose total weight is greater than the relevant threshold on the account. (Stellar accounts have low, medium, and high thresholds, corresponding to different tiers of operations, though in this protocol, we will ignore the differences between these thresholds, and keep them at the same values at all times.)
Each account also has a sequence number. The initial sequence number of an account is a large number that is unpredictable before the account is created. The sequence number is incremented by each transaction from that account.
A Stellar transaction has a single source account. That account is the account responsible for paying the fees on the account.
The transaction also increments the source account’s sequence number. Specifically, the transaction itself has a sequence number. Sequence numbers are used to prevent replaying transactions, and also play an important role in the Starlight protocol.
A Stellar transaction can have a maxtime and/or a mintime, which determine the time bounds for when it can be included on the ledger.
Each transaction includes one or more operations. Each operation has its own source account, which can be different from the source account of the transaction itself.
A transaction must be authorized by the source account of each of its operations, as well as the source account of the transaction itself.
The following Stellar operations are used in the Starlight payment channel protocol. (“Source account” here refers to the source account of the operation, which is not necessarily the source account of the transaction.)
CreateAccount
: create an account with a particular account ID, funded by a starting balance of lumens from the source account. This operation fails if the destination account already exists, if the starting balance is less than 1, or if the source account does not have sufficient lumens to make the payment without going below its own minimum balance.Payment
make a payment of an asset (for our purposes, always lumens) from the source account to some destination account. This operation fails if the destination account does not exist, or if the source account does not have sufficient funds to make the payment without going below its own minimum balance.BumpSequnce
: bump the sequence number of the source account to some higher number. This operation fails if the target sequence number is higher than the current maximum allowable sequence number (a very large number which, in practice, our protocol puts us at no risk of hitting).SetOptions
: add or remove a signer from, or change the threshold on, the source account. This operation fails if it would increase the minimum balance on the account higher than its current balance.AccountMerge
: delete an account, sending all of its lumens to some destination account. This operation fails if the destination account does not exist.
There are two ways that a transaction can fail. (This apparently technical detail has significant implications for the Starlight protocol.)
If a transaction is not signed by all necessary signers, the source account has insufficient balance to pay the fees, the source account’s sequence number is incorrect, or the current time is outside the mintime and maxtime bounds of the transaction, the transaction is invalid, and is not included on the ledger.
If one of the operations in a transaction fails, then the transaction fails, and none of the operations have any effect. However, the transaction is still included on the ledger, the source account’s sequence number is still incremented, and the source account still pays fees for that transaction.
The word “fail” is exclusively used in this document to refer to the latter kind of failure.
We assume that an agent is able to detect the invalidity or failure of transactions that it is attempting to submit. We do not assume that an agent is able to detect either failed or invalid transactions submitted by another party.