MultiPath Payments in the Lightning Network

MPP (Multipath Payments) is a payment split into multiple smaller payments. This allows larger payments or payments that use the balance from multiple channels. Success rates increase as smaller payments are more likely to succeed than larger ones.

Simplified MPP is a receiver-generated preimage that accepts multiple smaller payments to the same payment_hash and settles them when the total amount is reached (or exceeded). Simplified MPP can settle with fewer shares, too (unlike in AMP). This implementation is supported in all Lightning Network clients.

AMP (Atomic Multipath Payments) is a newer protocol that combines multiple payments into a single atomic transaction, making it more efficient and faster than MPP. In AMP, the sender generates the preimages and sends multiple keysend payments to the receiver, who can settle them all (atomically) once all shares are received. Receiver cannot settle fewer shares, unlike with MPP. Currently, only LND supports AMP.

High AMP is a future implementation that could use PTLCs to send an atomic multipath payment where the sender generates the preimage, but each share can only be settled when they have all been reassembled.

Note that for atomic multipath payments, all routes must be successful for the payment to go through. If any route fails, the payment will fail, and none of the routes will be completed.

AMP actually came first, but Simplified MPP were much easier to implement, so it got implemented first.

A General Overview of how to perform Multipath Payments.

  • Set up a Lightning Node

  • Connect to the Lightning Network

  • Generate an Invoice

  • Split the Payment

  • Find Payment Routes

  • Send Payment

  • Handle Payment Failures

Simple Example of Multipath Payment with Lnd in NodeJs

  • Install the necessary packages

      npm install lnd-grpc, grpc, bolt11
    
  • Connect to LND's gRpc server using the grpc library and set up multipath options and send the payment .

      const grpc = require('grpc');
      const lnrpc = grpc.load('proto/rpc.proto').lnrpc;
    
      // Connect to LND's gRPC server
      const lndCert = fs.readFileSync('tls.cert');
      const sslCreds = grpc.credentials.createSsl(lndCert);
      const lndGrpc = new lnrpc.Lightning('localhost:10009', sslCreds);
    
      // Set up payment request details
      const paymentRequest = 'lnbc100...'; // Payment request string
      const numPaths = 3; // Number of paths to use
      const maxAmt = 500000; // Maximum amount to send per path (in satoshis)
    
      // Prepare payment request
      const payReq = new lnrpc.SendRequest();
      payReq.payment_request = paymentRequest;
    
      // Set up multipath options
      const mpOpts = new lnrpc.MultiPathPayment();
      mpOpts.payment_request = payReq;
      mpOpts.num_paths = numPaths;
      mpOpts.max_total_amt_msat = maxAmt * 1000; // Convert to milli-satoshis
    
      // Make non-atomic multipath payment
      lndGrpc.sendMultipathPayment(mpOpts, (err, response) => {
        if (err) {
          console.error('Payment failed:', err);
        } else {
          console.log('Payment successful:', response);
        }
      });
    

In this example, we first connect to LND's gRPC server using the grpc library and load the rpc.proto file. We then set up the payment request details, including the payment request string, the number of paths to use, and the maximum amount to send per path in satoshis.

Next, we prepare the payment request using the SendRequest object and set up the multipath options using the MultiPathPayment object. We then make the non-atomic multipath payment using the sendMultipathPayment function, passing in the multipath options object and a callback function to handle the payment response.

Note that this example assumes you have a valid payment request string and LND node set up and running on localhost:10009. You will also need to have the rpc.proto file available in the proto directory.

MPP and AMP payment paths are attempted independently, but you are right in AMP a failure in one path does effectively affect the others (they all have to fail too).

Simple Example of Atomic Multipath Payment with Lnd in NodeJs

  • Install the necessary packages.

      npm install lnd-grpc, grpc, bolt11
    
  • Connect to LND's gRpc server using the grpc library and set up atomic multipath options, and send the payment.

      onst grpc = require('grpc');
      const { lnrpc } = grpc.load('path/to/lnrpc.proto');
      const { lightningAddress } = require('bolt11');
    
      const client = new lnrpc.Lightning('localhost:10009', grpc.credentials.createInsecure());
    
      const paymentHash = Buffer.from('payment_hash_goes_here', 'hex');
      const paymentRequest = 'payment_request_goes_here';
    
      // Decode the payment request to get the details of the payment.
      const decoded = lightningAddress(paymentRequest);
      const finalAmount = decoded.mtokens;
      const finalExpiry = decoded.expiry;
      const finalRoute = decoded.data.hops.map(hop => ({
        pubkey: Buffer.from(hop.publicKey, 'hex'),
        amtToForward: parseInt(hop.channelCapacity, 10),
        fee: parseInt(hop.fee, 10),
        expiry: parseInt(hop.cltvExpiryDelta, 10),
      }));
    
      const sendPayment = async (route, amount, hash) => {
        return new Promise((resolve, reject) => {
          const call = client.sendPayment();
    
          call.on('data', data => {
            // Payment was successful
            resolve(data);
          });
    
          call.on('error', err => {
            reject(err);
          });
    
          call.write({
            paymentHash: hash,
            dest: route[route.length - 1].pubkey,
            amt: amount,
            finalCltvDelta: finalExpiry,
            paymentRequest: paymentRequest,
            route: route,
          });
    
          call.end();
        });
      };
    
      // Create an array of routes for the multipath payment.
      const routes = [
        [{ pubkey: Buffer.from('node1_pubkey', 'hex'), amtToForward: 1000, fee: 1, expiry: 40 },
        { pubkey: Buffer.from('node2_pubkey', 'hex'), amtToForward: 800, fee: 2, expiry: 35 },
        { pubkey: Buffer.from('node3_pubkey', 'hex'), amtToForward: 600, fee: 3, expiry: 30 }],
        [{ pubkey: Buffer.from('node4_pubkey', 'hex'), amtToForward: 500, fee: 2, expiry: 25 },
        { pubkey: Buffer.from('node5_pubkey', 'hex'), amtToForward: 400, fee: 1, expiry: 20 }]
      ];
    
      // Call the sendPayment function for each route in the array.
      for (let i = 0; i < routes.length; i++) {
        await sendPayment(routes[i], finalAmount, paymentHash);
      }
    

In this example, we first decode the payment request to get the payment details, including the final amount, expiry, and route. We then define a sendPayment function that takes a route, amount, and payment hash as arguments and sends a payment through that route using the sendPayment gRPC method of the lnd client. Finally, we create an array of routes and call the sendPayment function for each route in the array to make the atomic multipath payment.

In summary, There is no strict superiority between MPP and AMP; the choice will depend on the specific use case. MPP is useful for scenarios where it is acceptable for payments to be partially delivered, as it increases the chances of success. Atomic MPP is more suitable for scenarios where it is essential to ensure that payments are either fully delivered or not delivered at all at the cost of lower payment success rates. Switching to --key_send in LND uses MPP, and switching to --amp uses AMP.