Payment Channels

Published on
May 17, 2023
Published by

A payment channel in terms of Bitcoin is a mechanism used to exchange and update transactions and messages off-chain, in a way that this exchange will suddenly consolidated to the blockchain at the end of the exchange. Basically, it a technique in which a transaction is modified upon the malleability property, which provides the ability to modify a transaction without invalidating it. A payment channel technique can be explained and implemented through three main processes: opening a payment channel, updating it up to n iterations (with a pair of accept and propose messages) and finally, closing the payment channel, action that invokes the final settlement of the payment.

The following diagram describes all the iterations that happen during a payment channel operation between the involved parties: payer (party who pays), payee (party who earns) and of course, the blockchain. Each interaction will be explained next.

Opening a payment channel

To open a payment channel using the G2C Suite, the payer needs to initialize the channel first using g2ccient_walletPaymentChannelInit() function, passing identification of the involved parties and setting the maximum amount of satoshis to reserve and lock into the channel sending a transaction to the blockchain. At this point no funds are locked. Once initialized, the payer can call g2cclient_walletPaymentChannelOpen() to open the payment channel using the channel ID obtained from the previous call. At this point, the maximum amount of satoshis will be effectively locked.

channelOpening() {
   // this snippet has to be called the the payer
   return new Promise((resolve, reject) => {
     try {
       // initParameters object contains the following fields:
       // const initParameters = {
       //    tokenid,
       //    tokens1,
       //    tokenc1,
       //    application,
       //    sourcenick,
       //    destinationnick,
       //    maxamount,
       //    commissiontype,
       //    description,
       //}
       // The maxamount must not be exceeded by any future iteration update during the proposal and accept interaction, so keep in mind that must be precalculated in right way  avoid closing this channel and opening a new one
       g2cclient_walletPaymentChannelInit( initParameters, (response) => {
           console.log('Payment channel initialized', response);
           const channelId = response.data.channelId;
           // openParameters object contains the following fields:
           // const openParameters = {
           //    tokenid,
           //    tokens1,
           //    tokenc1,
           //    application,
           //    nick,
           //    channelId,
           // }
           g2cclient_walletPaymentChannelOpen( openParameters, (response) => {
               console.log('Payment channel opened {}', response);
               // as part of the function output, there is the channelId
               resolve(response);
             },
           );
         },
       );
     } catch (error) {
       console.log('Error {}', error);
       reject(new Error(error));
     }
  });
}

Updating a payment channel

Once a payment channel is updated, both parties can exchange several messages in a flow of request-response interactions, always flowing from the payee to the payer. To do so, the G2C Suite makes available two different functions: g2cclient_walletPaymentChannelPropose() and g2cclient_walletPaymentChannelAccept().

First, the payee proposes a payment draft with the current amount to be received (newAmount) using g2cclient_walletPaymentChannelPropose() function. The last signed iteration amount between the payee and the payer will be applied when some party decides to close the payment channel. In case there is no interaction between both parties, the G2C Suite will rollback the amount to the payer when the channel is finally closed.

channelPropose() {
   return new Promise((resolve, reject) => {
     try {
       // proposeParameters object contains the following fields:
       // const proposeParameters = {
       //    tokenid,
       //    tokens1,
       //    application,
       //    nick,
       //    channelId,
       //    newAmount
       // }
       g2cclient_walletPaymentChannelPropose( proposeParameters, (response) => {
             console.log('Payee proposes an updated newAmount {}', response);
             resolve(response);
           },
       );
     } catch (error) {
       console.log('Error {}', error);
       reject(new Error(error));
     }
  });
}

Once propose function is successfully called, the payer must validate the transaction using the g2cclient_walletPaymentChannelAccept() function from the G2C Suite.

channelAccept() {
   return new Promise((resolve, reject) => {
     try {
       // acceptParameters object contains the following fields:
       // const acceptParameters = {
       //    tokenid,
       //    tokens1,
       //    tokenc1,
       //    application,
       //    nick,
       //    channelId,
       // }
       g2cclient_walletPaymentChannelAccept(acceptParameters, (response) => {
           console.log('Payer accepts the newAmount {}', response);
           resolve(response);
         },
       );
     } catch (error) {
       console.log('Error {}', error);
       reject(new Error(error));
     }
  });
}

Closing a payment channel

Finally, both parties are allowed to close the payment channel whenever they want calling g2cclient_walletPaymentChannelClose() function from the G2C Suite. As a consequence, the state of the payment changes in a way that neither party can further update nor claim anymore within the current channel. Also, this function performs the payment from the payer to the payee using the last signed amount between them and returning the remaining locked funds -if any- to the payer.

channelClose() {
   return new Promise((resolve, reject) => {
     try {
       // const closeParameters = {
       //    tokenid,
       //    tokens1,
       //    application,
       //    nick,
       //    channelId,
       //    force,
       //  }
       g2cclient_walletPaymentChannelClose( closeParameters, (response) => {
           console.log('Party closes the payment channel {}', response);
           resolve(result);
         },
       );
     } catch (error) {
       reject(new Error(error));
     }
  });
}

As stated before, once closed, the transaction is settled into the blockchain and the function returns the identifier of the payment transaction.