Channel establishment is initiated by the customer in zkChannels.
The merchant has completed the setup phase, and the customer and merchant have established a communication session.
The customer has obtained the merchant’s setup information out of band. The customer must verify the merchant's public parameters are well-formed and valid:
- The merchant blind signing public key
merchant_zkabacus_public_key
must consist of a valid Pointcheval Sanders public key of the expected length with components in the BLS12-381 pairing subgroups G1 and G2. - The range constraint parameters
range_constraint_parameters
must consist of a valid Pointcheval Sanders key of the expected length with components in the BLS12-381 pairing subgroup G1, and valid signatures on the appropriate integer range. - The revocation lock commitment parameters
revocation_commitment_parameters
must be well-formed Pedersen parameters of the expected length, and consist of elements in the BLS12-381 pairing subgroup G1. - The merchant EdDSA public key
merchant_public_key
must be a valid EdDSA key for the curve specified bytezos-client
and the merchant addressmerchant_address
must be a Tezos tz1 address correctly derived frommerchant_public_key
.
The customer should ensure they have a Tezos implicit account with balance sufficient to both contribute the desired amount to the zkChannel and pay the operations fees needed to originate, fund, and call the appropriate entry points of the corresponding smart contract. We recommend 2 tez based on our contract benchmarks on testnet.
Channel establishment is a three-round protocol between the customer and the merchant.
The establishment protocol uses zkAbacus
as a subprotocol. Details for zkAbacus
may be found in Chapter 3.3.3 of the zkChannels Protocol document. Each party also interacts with the Tezos blockchain to open and verify the channel's escrow account. Details of on-chain operations are provided here.
The protocol flows between the customer and the merchant are given in the following diagram:
+-------+ +-------+
| |--------- open_c ------->| |
| |<-------- open_m --------| |
| | | |
| |--------- init_c ------->| |
| Cust |<-------- init_m --------| Merch |
| |------- contract_ID ------>| |
| |---- funding_confirmed --->| |
| |<------- activate ---------| |
| | | |
+-------+ +-------+
The protocol proceeds as follows:
-
The customer sends the
open_c
message, which contains information about the initial state of the proposed channel. -
The merchant verifies the received message and either accepts or rejects the proposed channel. They reply with the
open_m
message, which contains the merchant's contribution to the channel identifier. -
The customer and merchant each compute the channel identifer
channel_id
, which acts as the unique channel identifier for the on-chain Tezos escrow account and off-chainzkAbacus
channel. The identifierchannel_id
is computed asSHA3-256(customer_randomness, merchant_randomness, customer_public_key, merchant_public_key, merchant_zkabacus_public_key)
converted to a BLS12-381 scalar. -
They then initialize the
zkAbacus
channel by runningzkAbacus.Initialize()
on the previously established public parameters. In this subroutine:a. The customer sends the
init_c
message to the merchant. This message consists of a (hiding) commitment to the intial state and a zero-knowledge proof of correctness.b. The merchant verifies the received message and sends the
init_m
message, which contains an initial closing authorization signature, to the customer. -
The customer originates the zkChannels contract on chain:
a. They forge and sign the origination operation with the following channel-specific arguments:
channel_id
: The channel identifier, a BLS12-381 scalar.customer_address
: The customer's Tezos tz1 address.init_customer_balance
: The customer's initial balance.customer_public_key
: The customer's Tezos public key.merchant_address
: The merchant's Tezos tz1 address.init_merchant_balance
: The merchant's initial balance.merchant_public_key
: The merchant's Tezos public key.merchant_zkabacus_public_key
: The merchant's zkAbacus public key.
b. They inject the origination operation. They compute the contract identifier
contract_ID
and initialize the chain watcher to track the zkchannels contract with identifiercontract_ID
. When the chain watcher indicates that the origination operation is confirmed to depthrequired_confirmations
, they update the channel status toOriginated
.c. They send the contract identifer
contract_ID
to the merchant. -
The merchant checks the corresponding contract and initial storage for the expected values and initializes the chain watcher to track the zkChannels contract specified by
contract_ID
. If the values are not as expected, the merchant aborts. -
The customer funds the zkChannels contract by calling the
addCustFunding
entrypoint of the contract. The source of this transfer operation must be thecustomer_address
specified in the contract's initial storage and the transfer amount must be equal toinit_customer_balance
. When the chain watcher indicates that theaddCustFunding
operation group is confirmed to the depthrequired_confirmations
, they update the channel status toCustomerFunded
. The customer then sends a success message, namely thefunding_confirmed
message to the merchant. -
The merchant then funds their side of the smart contract, if the channel is dual-funded, by calling the
AddMerchFunding
entrypoint of the contract with identifiercontract_id
. When the chain watcher indicates this contract has statusOPEN
for a depth ofrequired_confirmations
blocks, the merchant runszkAbacus.Activate()
to generate the initial payment tag and sends the customer the messageactivate
, which contains this payment tag. -
Upon completion of
zkAbacus.Activate()
, the zkChannel is open and ready for payments.
Balances in zkAbacus are represented as BLS12-381 scalars. We support integer values in the range (-2^63, 2^63).
The open_c
message is sent from the customer to the merchant and is formed as follows:
- type:
open_c
- data:
- [
string
:customer_randomness
]: Customer randomness contribution to the channel identifer. - [
int
:init_customer_balance
]: The proposed initial customer balance. - [
int
:init_merchant_balance
]: The proposed initial merchant balance. - [
address
:customer_address
]: The customer's Tezos tz1 account address. - [
key
:customer_public_key
]: The customer's Tezos EdDSA public key. - [
string
:merch_pp_hash
]: A hash of merchant public parameters as specified in Merchant Setup.
- [
The customer, before sending:
- Retrieves the merchant public parameters and checks these parameters are well-formed and valid as specified above.
- Generates
customer_randomness
uniformly at random using a secure RNG.
Upon receipt, the merchant checks that the following are true. If any are false, the merchant aborts:
- Checks
customer_randomness
is the correct length. - Checks
init_customer_balance
≥ 0 andinit_merchant_balance
≥ 0 are positive integers. - Checks
customer_public_key
is a valid Tezos EdDSA public key for the curve specified bytezos-client
and thatcustomer_address
is a valid Tezos tz1 address that is correctly derived fromcustomer_public_key
. - Checks
merch_pp_hash
is the SHA3-256 hash of(merchant_zkabacus_public_key, merchant_address, merchant_public_key)
. - Checks
customer_address
is an implicit Tezos account (tz1 address), and not a smart contract address (KT1 address).
The merchant may choose to either accept or reject the channel establishment request. The implementation should provide a customizable approver mechanism in order to realize channel establishment approvals and rejections. If the merchant accepts, they should ensure their implicit Tezos account with address merchant_address
has a balance sufficient to both contribute the desired amount to the zkChannel and pay the operations fees needed to fund and call the appropriate entry points of the corresponding smart contract. We recommend 0.009 tez based on our contract benchmarks on testnet.
The merchant sends the open_m
message to the customer; this message is formed as follows:
- type:
open_m
- data: [
string
:merchant_randomness
]. This is the merchant randomness contribution to the channel identifier.
Upon receipt, the customer checks the that merchant_randomness
is the correct length. If so, the customer sets the channel identifier channel_id
to: SHA3-256(customer_randomness, merchant_randomness, customer_public_key, merchant_public_key, merchant_zkabacus_public_key)
converted to a BLS12-381 scalar, where:
customer_randomness
is the customer's contribution to the channel identifier sent to the merchant in theopen_c
message.merchant_randomness
is the merchant's contribution to the channel identifier received in theopen_m
message.customer_public_key
is the customer Tezos account public key.merchant_public_key
is the merchant Tezos account public key.merchant_zkabacus_public_key
is the merchant's zkAbacus Pointcheval Sanders public key.
If not, the customer aborts.
Before sending, the merchant:
- Generates
merchant_randomness
uniformly at random using a secure RNG. - Sets the channel identifier
channel_id
to:SHA3-256(customer_randomness, merchant_randomness, customer_public_key, merchant_public_key, merchant_zkabacus_public_key)
converted to a BLS12-381 scalar, where:customer_randomness
is the customer's contribution to the channel identifier sent to the merchant in theopen_c
message.merchant_randomness
is the merchant's contribution to the channel identifier received in theopen_m
message.customer_public_key
is the customer Tezos account public key.merchant_public_key
is the merchant Tezos account public key.merchant_zkabacus_public_key
is the merchant's zkAbacus Pointcheval Sanders public key.
The customer sends an init_c
message to the merchant.
- type:
init_c
- data:
- [
string
:channel_id
] - [
bls12_381_g1
:close_state_commitment
]: A commitment to the initial closing state. - [
bls12_381_g1
:state_commitment
]: A commitment to the initial state. - [
(bls12_381_g1, bls12_381_g1, Vec<bls12_381_fr>): establish_proof
]: A zero-knowledge proof of correctness of the commitments to the initial state and initial closing state.
- [
The customer runs the zkAbacus.Initialize()
on inputs channel_id
, init_customer_balance
, and init_merchant_balance
to generate the init_c
message.
Upon receipt, the merchant:
- Checks that
channel_id
matches the channel identifier previously computed. - Continues as specified in
zkAbacus.Initialize()
.
If channel_id
is incorrect, the merchant aborts.
The merchant sends an init_m
message to the customer.
- type:
init_m
- data: [
(bls12_381_g1, bls12_381_g1):closing_signature
]. A closing authorization signature on the initial closing state.
Upon receipt, the customer verifies closing_signature
is a valid signature with respect to the merchant zkAbacus Pointcheval Sanders public key. If the signature is valid, the customer continues as specified in zkAbacus.Initialize()
. If the signature is invalid, the customer aborts.
The merchant runs zkAbacus.Initialize
on inputs channel_id
, init_customer_balance
, and init_merchant_balance
. If successful, the merchant sends the resulting message init_m
. Otherwise, the merchant aborts.
The customer sends the contract_ID
message to the merchant.
- type:
contract_identifier
- data:
- [
address
:contract-id
]: The contract identifier for the zkChannels smart contract. - [
int
:originated-block-height
]: The block height of the zkChannels smart contract.
- [
The customer:
- Originates the contract as specified in the Tezos zkEscrowAgent Realization document and computes the contract identifier
contract_ID
. - Initializes the chain watcher to track the contract with identifier
contract_ID
. - Receives a notification from the chain watcher that the origination operation for the contract specified by
contract_ID
is confirmed on chain for at leastrequired_confirmations
blocks. - Updates the channel status to
Originated
. - Sends the
contract_identifier
message to the merchant.
Upon receipt of the contract_identifier
message, the merchant:
-
Initializes the chain watcher to track the contract with identifier
contract_ID
. -
Checks that the originated contract
contract-id
contains the expected zkchannels contract with respect to the channel identifierchannel_id
, the customer Tezos public keycustomer_public_key
, the customer's tezos tz1 addresscustomer_address
, the merchant public parameters, and the initial balancesinit_customer_balance
andinit_merchant_balance
. -
Checks that the on-chain storage of
contract-id
atoriginated-block-height
is exactly as expected for channelchannel_id
:- The contract storage contains
merchant_zkabacus_public_key
in the expected field(s). - The customer's tezos tz1 address and public key match the fields
customer_address
andcustomer_public_key
, respectively. - The merchant's tezos tz1 address and public key match the fields
merchant_address
andmerchant_public_key
, respectively. - The
self_delay
field in the contract matches the value specified in the global defaults. - The
close
field in the contract matches the merchant'sclose
flag defined as defined in the global defaults. Theclose
flag represents a fixed scalar used by the merchant to differentiate closing state and state. - The fields
customer_balance
andmerchant_balance
are initialized toinit_customer_balance
andinit_merchant_balance
, respectively. - The
status
field of the contract is initialized toAWAITING_CUST_FUNDING
. - The
context-string
is set to"zkChannels mutual close"
, as defined in the global defaults.
- The contract storage contains
-
Checks that the originated contract is confirmed on chain for at least
required_confirmations
blocks. -
Updates the channel status to
Originated
.
The customer sends the funding_confirmed
message to the merchant.
- type:
funding_confirmed
- data: none.
The customer:
- Funds the contract by calling the
addCustomerFunding
entrypoint. - Receives a notification from the chain watcher that the funding operation is confirmed on chain for at least
required_confirmations
blocks. - Updates the channel status to
CustomerFunded
. - Sends the
funding_confirmed
message to the merchant.
Upon receipt of the funding_confirmed
message, the merchant:
- Checks that the customer has funded the contract for at least
required_confirmations
blocks. This requires checking that the customer's operation to add their funds is the last operation to have interacted with the smart contract, and that in the most recent blocks of the blockchain (up torequired_confirmations
blocks in the past) there have been no further operations interacting with the contract. - In the dual-funded case, funds their side of the contract by calling the
addMerchantFunding
entrypoint. The source of this transfer operation must be themerchant_address
specified in the contract's initial storage and the transfer amount must be equal toinit_merchant_balance
. - When the chain watcher indicates that the
addMerchantFunding
operation is confirmed on chain and the contract storagestatus
isOPEN
for at leastrequired_confirmations
blocks, updates the channel status toMerchantFunded
.
The merchant sends the activate
message to the customer.
- type:
activate
- data: [(bls12_381_g1, bls12_381_g1):
payment_tag
]
Upon receipt, the customer:
- In the dual-funded case, when the chain watcher indicates the contract storage status is
OPEN
at a confirmation depth ofrequired_confirmations
, updates the channel status toMerchantFunded
. - If the customer does not see a confirmed
addMerchantFunding
operation from the merchant within a specified timeout period, they call thereclaimFunding
entrypoint. - Checks that
payment_tag
is a valid signature with respect to the merchant's zkAbacus Pointcheval Sanders public key. If not, aborts by initiating a unilateral close. - Updates the channel status to
Ready
.
Before sending, the merchant:
- The chain watcher must indicate that the contract storage status has been set to
OPEN
forrequired_confirmations
blocks. - Generates the
activate
message by runningzkAbacus.Activate()
on the initial state commitmentstate_commitment
provided in the customer'sinit_c
message, the channel identifierchannel_id
, and their zkAbacus Pointcheval Sanders public key. - Updates the channel status to
Active
.