Alice and Bob bring amounts of some asset into a channel and periodically update the distribution of assets. Distribution is a signed predicate that re-locks the funds under a relative timeout. Within the timeout, a newer signed distribution could be applied to replace the stale one.
This could be one-way channel (Alice->Bob, only Alice brings funds), or it could be a multi-asset channel (Alice brings USD, Bob brings EUR), or a set of multiple currencies that FX traders move around between each other.
The system trivially extends to N-party channel with N-of-N signatures updating the balance distribution.
Setup
Alice and Bob exchange pubkeys A and B used in the operation of the channel and establish a joint MuSig key AB = A & B.
Alice and Bob sign a balance update with seq=1 reflecting initial balances.
Now, it's safe to lock funds. They compute the initial predicate P_init that transiently re-formats the contract in a format compatible with P_exit predicate used to manage exits:
P_init = AB + program {
// transient contract:
contract(payload {
assets,
seq=0,
timeout=MAX_INT,
"" (empty prog),
tag
) -> P_exit
}
P_exit = AB + program {
verify(tx.mintime > self.timeout)
eval(self.redistribution_program)
}
Balance update
Alice and Bob agree to a new distribution of funds and sign a new predicate that can be used to override any prior state of the contract. The program is signed with P_exit multikey.
Both parties sign a tx that re-distributes funds bypassing the contract logic.
The resulting tx looks like a simple transfer w/o any details leaking.
Initiate forced settlement
Alice wants to close the channel because Bob does not cooperate.
Alice forms a transaction Tx1 that opens P_init into programmatic branch that re-locks assets under a transient contract with necessary parameters under P_exit.
Within the same Tx1 Alice spends P_exit with a signed program performing the latest balance update. Tx1 leaves assets locked in the last-agreed proportion under the same predicate P_exit, but withdrawable after timeout.
After timeout, Alice signs Tx2 that pays up-to-date fee and opens up P_exit's programmatic branch that releases the assets to pre-arranged destinations to Alice and Bob.
Contest forced settlement
If Bob observes that Alice initiate channel close with a stale signed predicate (seq number less than the latest they agreed upon).
Privacy considerations
Financial values (asset types and quantities) remain encrypted at all times, in cooperative and non-cooperative cases.
In case of cooperation, there are two transactions: funding and settlement. Both look like regular single-key spends.
In case of non-cooperation, channel contract is published. But the sequence numbers and timeout durations are kept encrypted, so the channel software or participants can't be fingerprinted or estimate their transaction volume.
Simplicity of the scheme allows outsourcing monitoring of the blockchain to semi-trusted servers that will bump the state if they notice a stale exit. Confidentiality of the transfers protects against extorsion and permits using flat-fee to support such watch servers.
2. Multi-hop channels (lightning)
If a simple channel has unconditional balance updates (extra checks are only for replacing stale versions), multi-hop channel needs to update the balances conditionally: "Bob sends $5 to Carol only if he gets $5 from Alice".
The conditional updates use HTLCs (hash-timelocked contracts) to provide reversal after an absolute timeout (relative won't do). To keep channels open indenfinitely, HTLC-locked balances must be replaced with non-HTLC updates as in simple payment channels when the payment is guaranteed (preimage for HTLC is provided).
This means that HTLC condition is wrapped into a payment channel condition:
first, we establish which version of agreement is the latest (payment channel protocol),
then we deal with its extra conditions (if HTLC is on), or just stay put (if it's unconditional update after HTLC is opened).
This works same as above, but intermediate HTLC-locked conditions looks like this:
$new_distribution = program {
self.timeout = tx.maxtime
lock with taproot {
branch1(): {
verify(tx.mintime >= (self.timeout + delta));
output($90 -> Alice)
output($10 -> Bob)
},
branch2($preimage): {
verify(sha256($preimage) == self.htlc);
output($85 -> Alice)
output($15 -> Bob) // Alice is sending $5 through Bob
},
}
}
1. Payment channel
Simple bilateral payment channel.
Alice and Bob bring amounts of some asset into a channel and periodically update the distribution of assets. Distribution is a signed predicate that re-locks the funds under a relative timeout. Within the timeout, a newer signed distribution could be applied to replace the stale one.
This could be one-way channel (Alice->Bob, only Alice brings funds), or it could be a multi-asset channel (Alice brings USD, Bob brings EUR), or a set of multiple currencies that FX traders move around between each other.
The system trivially extends to N-party channel with N-of-N signatures updating the balance distribution.
Setup
Alice and Bob exchange pubkeys A and B used in the operation of the channel and establish a joint MuSig key
AB = A & B
.Alice and Bob sign a balance update with
seq=1
reflecting initial balances.Now, it's safe to lock funds. They compute the initial predicate
P_init
that transiently re-formats the contract in a format compatible withP_exit
predicate used to manage exits:Balance update
Alice and Bob agree to a new distribution of funds and sign a new predicate that can be used to override any prior state of the contract. The program is signed with
P_exit
multikey.Example of a re-distribution program:
Settlement
Both parties sign a tx that re-distributes funds bypassing the contract logic. The resulting tx looks like a simple transfer w/o any details leaking.
Initiate forced settlement
Alice wants to close the channel because Bob does not cooperate.
Alice forms a transaction
Tx1
that opensP_init
into programmatic branch that re-locks assets under a transient contract with necessary parameters underP_exit
.Within the same
Tx1
Alice spendsP_exit
with a signed program performing the latest balance update.Tx1
leaves assets locked in the last-agreed proportion under the same predicateP_exit
, but withdrawable after timeout.After timeout, Alice signs
Tx2
that pays up-to-date fee and opens upP_exit
's programmatic branch that releases the assets to pre-arranged destinations to Alice and Bob.Contest forced settlement
If Bob observes that Alice initiate channel close with a stale signed predicate (seq number less than the latest they agreed upon).
Privacy considerations
Financial values (asset types and quantities) remain encrypted at all times, in cooperative and non-cooperative cases.
In case of cooperation, there are two transactions: funding and settlement. Both look like regular single-key spends.
In case of non-cooperation, channel contract is published. But the sequence numbers and timeout durations are kept encrypted, so the channel software or participants can't be fingerprinted or estimate their transaction volume.
Simplicity of the scheme allows outsourcing monitoring of the blockchain to semi-trusted servers that will bump the state if they notice a stale exit. Confidentiality of the transfers protects against extorsion and permits using flat-fee to support such watch servers.
2. Multi-hop channels (lightning)
If a simple channel has unconditional balance updates (extra checks are only for replacing stale versions), multi-hop channel needs to update the balances conditionally: "Bob sends $5 to Carol only if he gets $5 from Alice".
The conditional updates use HTLCs (hash-timelocked contracts) to provide reversal after an absolute timeout (relative won't do). To keep channels open indenfinitely, HTLC-locked balances must be replaced with non-HTLC updates as in simple payment channels when the payment is guaranteed (preimage for HTLC is provided).
This means that HTLC condition is wrapped into a payment channel condition:
This works same as above, but intermediate HTLC-locked conditions looks like this: