Lightning Network (c-lightning )

Lightning Network

Lightning Network(LN)은 Bitcoin(혹은 Litecoin) 블럭체인 위에서 동작하는 Layer 2 솔류션으로서 주로 소액을 처리하는 Micro-transaction을 위해 사용된다.

Lightning Network의 BOLT (Basis of Lightning Technology) 스펙은 2016년 초안이 작성되었고, 이후 여러 구현들이 만들어졌다. 이 중 다음과 같은 구현체들이 널리 사용되고 있다.

  • c-lightning: BlockStream에 의해 C로 구현되었으며, 2018년 초부터 Bitcoin Mainnet에서 사용
  • lnd: Lightning Labs에 의해 Go로 구현되었으며, 2018년 초부터 Mainnet에서 사용
  • Eclair: ACINQ 에 의해 Scala 로 구현되었으며, 특히 Eclair Mobile은 Lightning Network에 대한 Native Mobile 구현체를 제공

c-lightning 설치

c-lightning은 https://github.com/ElementsProject/lightning.git 에 소스코드가 있으며, 이를 빌드하여 설치할 수 있다. 또한, 소스코드 빌드 없이 Ubuntu PPA를 이용하여 다음과 같이 설치할 수 있다.

    $ sudo apt-get install -y software-properties-common
    $ sudo add-apt-repository -u ppa:lightningnetwork/ppa
    $ sudo apt-get install lightningd

c-lightning 데몬 실행

c-lightning의 Lightning Network 데몬(lightningd)은 아래와 같이 실행한다. lightningd은 먼저 bitcoind를 실행한 후에 lightningd를 실행하여야 한다. lightningd를 처음 실행하면 ~/.lightning 폴더가 생성된다.

    // Mainnet
    $ lightningd

    // Testnet
    $ lightningd --testnet

c-lightning 클라이언트 실행

클라이언트 프로그램은 lightning-cli 이며, 테스트넷에서 실행하기 위해서는 --testnet 아규먼트를 지정한다.

    // Mainnet
    $ lightning-cli 

    // Testnet
    $ lightning-cli --testnet

lightning-cli 에서 다음과 같은 명령으로 기초적인 정보를 얻을 수 있다.

    $ lightning-cli --testnet getinfo
    $ lightning-cli --testnet listconfigs
    $ lightning-cli --testnet listfunds
    $ lightning-cli --testnet listtransactions
    $ lightning-cli --testnet listinvoices
    $ lightning-cli --testnet listnodes

c-lightning 오픈 채널

Lightning Network 을 사용하기 위해서는 먼저 Lightning Network Peer와 채널을 오픈해야 한다. 먼저 Lightning Network 월렛의 주소를 만들고, Lightning Network Peer와 연결한 후, 초기 펀드를 보내 채널을 오픈한다.

// (1) LN 새 주소 얻기
$ lightning-cli --testnet newaddr
{
   "bech32": "tb1qzrvad53f9hgf4nj7c4rp8md338ng74ynddwcjr"
}

// (2) Bitcoin network에서 LN 주소로 코인 보내기 
$ bitcoin-cli -testnet sendtoaddress "tb1qzrvad53f9hgf4nj7c4rp8md338ng74ynddwcjr" 0.011

// (3) LN 주소 코인이 보내졌는지 확인 (1 confirm 후)
$ lightning-cli --testnet listfunds

// (4) LN Peer와 연결 (https://1ml.com/testnet 에서 Peer 검색)
$ lightning-cli --testnet connect 03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431@34.192.102.161:9735

// (5) 채널에 펀드를 보내면서 채널 오픈
$ lightning-cli --testnet fundchannel 03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431 1100000
{
   "tx": "02000000000102b00d20e18e590b21e06ae4173c77e8f673408784ecaebb3c527ac32ad4556dd90100000000fdffffff1031d3bafbbae36bb7001cd74a9bfde4df8e31b1b71ca274f2ec6939ea9080610100000000fdffffff02e0c8100000000000220020f0edf4a9eb4c91f71f25fdb7e507232882abc60aa2e5436be7a4dd0537767d6e029304000000000016001489b41ee1efc5d684072534d8407774e290d646fc0247304402206d46e2ca17e893aaa744ea52eec829573201f308008f6a22d2cfaa8566b13ac5022062955f05f9a53658bed530c3163065310321537c717de1a03554d73f20352979012103aaf3db5eccd095a9c5ce283016f584f7e929b381aab66f37c52b98b46c75151d0247304402200e52a24a917d31f851da5ac4170fc83d57a5332f9e39bada44b66329e3b4781302202a49a86b2f4620afb681748391ba536db6b261e292adc0deed2463583d6fbe48012103aaf3db5eccd095a9c5ce283016f584f7e929b381aab66f37c52b98b46c75151d826c2100",
   "txid": "6569f30b6e41f58c0c5e66fbf38a1849958380dd2a409f28c27e04919a634509",
   "channel_id": "0945639a91047ec2289f402add80839549188af3fb665e0c8cf5416e0bf36965",
   "outnum": 0
}

fundchannel 명령으로 채널을 오픈한 후에는 listfunds 명령으로 채널 오픈 상태를 체크할 수 있다. 아래 출력 결과에서 channels.state가 CHANNELD_AWAITING_LOCKIN 으로 되어 있는데, 이는 펀드 락인을 대기중인 경우로, 조금 기다리면 CHANNELD_NORMAL 상태가 된다.

$ lightning-cli --testnet listfunds
{
   "outputs": [
      ... 
   ],
   "channels": [
      {
         "peer_id": "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431",
         "connected": true,
         "state": "CHANNELD_AWAITING_LOCKIN",   
         "channel_sat": 1100000,
         "our_amount_msat": "1100000000msat",
         "channel_total_sat": 1100000,
         "amount_msat": "1100000000msat",
         "funding_txid": "6569f30b6e41f58c0c5e66fbf38a1849958380dd2a409f28c27e04919a634509",
         "funding_output": 0
      }
   ]
}

채널 상태가 정상(CHANNELD_NORMAL)이 되면, short_channel_id가 표시된다. short_channel_id는 {block}x{txid}x{vout} 형식으로 되어 있는데, 아래 "2190478x3x0"은 2190478 blockheight, 3 txid, 0 vout을 의미한다.

   "channels": [
      {
         "peer_id": "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431",
         "connected": true,
         "state": "CHANNELD_NORMAL",
         "short_channel_id": "2190478x3x0",
         "channel_sat": 1100000,
         "our_amount_msat": "1100000000msat",
         "channel_total_sat": 1100000,
         "amount_msat": "1100000000msat",
         "funding_txid": "6569f30b6e41f58c0c5e66fbf38a1849958380dd2a409f28c27e04919a634509",
         "funding_output": 0
      }
   ]

c-lightning Invoice 생성

Lightning Network에서 Payment를 받기 위해서는 "Invoice"를 생성해서 전달하고, Sender는 이 Invoice를 "Pay"하게 된다. lightning-cli에서 Invoice를 생성하기 위해서는 "invoice" 명령을 사용하고, 기본적인 아규먼트로 얼마를(msatoshi) 보내야 하는지, Invoice 내부 관리를 위한 간단한 레이블(label), 그리고 외부에 표시할 Invoice 내용을(description) 간략히 적게 된다. 이밖에, Invoice가 언제 Expire하는지 등의 추가적인 사항들을 적을 수도 있다.

    // 기본 파라미터들
    $ lightning-cli invoice msatoshi "label" "description" 

예를 들어, 아래는 커피한잔에 대한 Invoice를 생성한 것이다. 여기서 100000은 milli-sats(1 msats = 0.001 sat) 단위를 사용하는데, 따라서 이 송장의 금액은 100 sats가 된다. 레이블은 order123 으로 이는 외부에 보내지지 않고 나중에 내부 검색용으로 사용될 수 있다. 마지막 "A cup of coffee"은 description으로 이는 외부에 보내지는 주문에 대한 간략한 내용이다.

$ lightning-cli --testnet invoice 100000 order123 "A cup of coffee"
{
    "payment_hash": "0a96ac443e16bc3013bad97a0b71b2659d69f7420b77c0c7197fb9bc7ad9a3d9",
    "expires_at": 1648337886,
    "bolt11": "lntb1u1p3rv627pp5p2t2c3p7z67rqya6m9aqkudjvkwkna6zpdmup3ce07umc7ke50vsdqcgysxxatsyphkvgrrdanxvet9xqyjw5qcqp2sp54gxw4jselmwqw47gsrwwrrwpvacm2ur8fqp32uhh05fagw99p7ls9qyyssqxnuvsnumez88fyec32g3w9y4wm78m5xj8t5krz7yw08d5mkkgq4jg82x6xpqmanyfyrc28fntsyd0g4wwvkqnwsrgej9xa0meuz70ucq9du0zd",
    "payment_secret": "aa0ceaca19fedc0757c880dce18dc16771b5706748031572f77d13d438a50fbf",
    "warning_capacity": "Insufficient incoming channel capacity to pay invoice"
}

Invoice의 출력 중에 bolt11은 일종의 송장번호와 같은 역활을 하는데, 돈을 보내는 사람은 이 bolt11 정보를 통해 돈을 보내게 된다. 사실 bolt11는 단순히 '번호'라기 보다는 여러 정보를 포함하고 있다. 처음의 ln은 Lightning Network을 의미하고, tb는 testnet을 의미한다. (Mainnet의 경우는 bc, regtest의 경우는 bcrt를 사용한다).

다음 1u는 Invoice 금액을 가리키는데, 여기서 u는 0.000001 (micro multiplier)를 가리키는 것으로 1u = 1 BTC * 0.000001 = 100 sats = 100,000 msats를 가리킨다. 이 multiplier에는 m (0.001, milli), u (0.000001, micro), n (0.000000001, nano), p (0.000000000001, pico) 등 4종류가 있다.

1u 다음에 있는 것은 Invoice 데이타로서 이를 해독하기 위해서 decodepay 명령을 사용할 수 있다. description은 송장이 어떤 것을 위한 것인지를 알려주며, payee는 누구에게 지급되는 지, exiry는 이 송장이 언제 효력을 잃는 지 등을 표시한다.

$ lightning-cli --testnet decodepay "lntb1u1p3rv627pp5p2t2c3p7z67rqya6m9aqkudjvkwkna6zpdmup3ce07umc7ke50vsdqcgysxxatsyphkvgrrdanxvet9xqyjw5qcqp2sp54gxw4jselmwqw47gsrwwrrwpvacm2ur8fqp32uhh05fagw99p7ls9qyyssqxnuvsnumez88fyec32g3w9y4wm78m5xj8t5krz7yw08d5mkkgq4jg82x6xpqmanyfyrc28fntsyd0g4wwvkqnwsrgej9xa0meuz70ucq9du0zd"
{
    "currency": "tb",
    "created_at": 1647733086,
    "expiry": 604800,
    "payee": "033a02486b4da51ba523f1f2f6663c203b81c53075c59a0ff4f10e45685946045e",
    "msatoshi": 100000,
    "amount_msat": "100000msat",
    "description": "A cup of coffee",
    "min_final_cltv_expiry": 10,
    "payment_secret": "aa0ceaca19fedc0757c880dce18dc16771b5706748031572f77d13d438a50fbf",
    "features": "024200",
    "payment_hash": "0a96ac443e16bc3013bad97a0b71b2659d69f7420b77c0c7197fb9bc7ad9a3d9",
    "signature": "3044022034f8c84f9bc88e7493388a9117149576fc7dd0d23ae9618bc473ceda6ed6402b0220241d46d1820df6644907851d335c08d7a2ae732c09ba0346645375fbcf05e7f3"
}

LN Invoice 지급(payment)

"ln***" 으로 시작되는 Invoice에 대해 Sender는 코인을 보내게 된다. Invoice 지급은 lightning-cli에서 "pay" 명령을 사용하면 된다. 보통 decodepay를 통해 Invoice의 내용이 맞는지를 확인한 후, pay 명령으로 코인을 보내게 된다.

한가지 주의할 점은 자신이 자신에 pay하는 것은 현재 지원되지 않고 있다. 따라서, 다른 사람의 Invoice 를 얻은 후 pay 명령으로 지불하여야 한다. Payment가 완료되면, 아래와 같이 status가 complete로 표시되고, listfunds에서 Channel의 금액(channel_sat 필드)이 줄어 들게 된다.

$ lightning-cli --testnet pay lntb10u1p3rvfh4pp574acajq3gw4vrfd7x58xq6aywjtrxrj58wpzqgug55k7u03pjdmqdq5xysyymr0ddskxcmfdehsxqrrsscqp79qy9qsqsp50ustczgfrgcvcquw58v352ruy89nf5kura5uxyqqn0jghrwpse4smyvx77c34u6qxx365w0k9lv7pk70kyyfnunw9r00s67zassrlxd8d46zv3auevxdy36vax8nezgzep2qupcjvufzrg2akeyhakat8qqq5gugw2
{
    "destination": "0348cc1a9479697cd52db445ea74149ad40bb01bb2045a3e8acba21b70f94ab7cf",
    "payment_hash": "f57b8ec81143aac1a5be350e606ba47496330e543b82202388a52dee3e219376",
    "created_at": 1647716816.855,
    "parts": 1,
    "msatoshi": 1000000,
    "amount_msat": "1000000msat",
    "msatoshi_sent": 1002502,
    "amount_sent_msat": "1002502msat",
    "payment_preimage": "4003bef61f29fc67e6afcab842365a3bf539bf1e9e36847579c84f449611f2a7",
    "status": "complete"
}

Lightning Network은 2개의 노드간에 채널을 만들게 된다. 예를 들어, A와 B가 채널을 만들었고, B와 C가 채널을 만들었다고 가정할 때, A는 C에게 지불할 수 있다. A가 C에게 100 sats를 지불할 때, 실제로 A는 B에게 먼저 100 sats를 지불하고, 다시 B는 C에게 100 sats를 지불한다. 즉, 100 sats가 B를 거쳐 가게 되는데, 이때 B에게 약간의 Fee를 지불하게 된다. 채널이 오픈된 상태이므로 이러한 차감이 오프체인(off-chain)에서 수행되며, 나중에 채널이 Close를 최종 결산 결과만 온체인(on-chain)에 기록하게 된다.

채널 닫기 (Close Channel)

Lightning Network은 온체인에 2번 기록하는데, 처음 채널을 오픈할 때와 마지막 채널을 닫을 때이다. 보통 채널을 오픈한 후 Lightning Network 상에서 여러 번의 트랜잭션들이 오고 간 후에, 마지막에 채널을 종료하게 되는데, 이때 채널 잔액이 Lightning Network 월렛 주소로 옮겨지게 된다.

채널을 닫기 위해서는 c-lightning에서 close 명령을 사용한다. close 명령 뒤에는 채널의 리모트 노드 peer_id를 지정해야 하고, 그 다음에 unilateraltimeout 값을 지정한다.

채널의 상대 노드의 peer_id는 listfunds 명령의 channels 섹션에서 구할 수 있다.

$ lightning-cli --testnet listfunds
{
    "outputs": [
    ]
    "channels": [
    {
       "peer_id": "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431",
       "connected": true,
       "state": "CHANNELD_NORMAL",
       ... 생략 ...

Lightning Network에서 채널을 닫을 것은 상호간의 합의(mutual)에 의하거나 혹은 일방적으로(unilateral) 진행할 수 있다. unilateraltimeout 파라미터는 채널 상대 노드가 합의하도록 얼마만큼의 시간(초)을 기다려야 하는지를 표시하는 것이다. 예를 들어, 60이면 60초 동안 합의를 기다리고 연락이 없으면 일방적으로 채널을 Close하게 된다. Lightning Network에서 상대 채널이 오프라인인 상태일 수 있는데, 이 경우 unilateral close가 진행될 것이다. unilateraltimeout = 0 인 경우, 즉시 Close 하지만 이때에도 상호 합의를 먼저 시도한 후 그 다음 일방적 Close를 진행한다.

아래는 채널을 Close한 예인데, Close 후에 온체인 트랜잭션이 txid = 30efaacbc0b74e1a6be1c19f11d1ac0b899cb06173bd69428ecd2a7df9198645 에 쓰여 졌고, mutual 합의에 의해 Close 되었음을 알 수 있다.

$ lightning-cli --testnet close "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431" 0
{
    "tx": "02000000010945639a91047ec2289f402add80839549188af3fb665e0c8cf5416e0bf369650000000000ffffffff02ea03000000000000160014a54248995d5910cbbd6a64eccd4d28bc2bf8c26a45c4100000000000160014b4fa21318f4ac68d7cb70c756f3e71ac02d0727f00000000",
    "txid": "30efaacbc0b74e1a6be1c19f11d1ac0b899cb06173bd69428ecd2a7df9198645",
    "type": "mutual"
}    

채널이 닫힌 후, listfunds를 통해 잔액 및 채널 상태를 확인할 수 있다. 아래 출력 결과를 보면 채널 state는 "ONCHAIN" 으로 되었고, LN 월렛의 outputs (UTXO)에는 1098821 sats가 입금된 것을 확인할 수 있다. 채널을 Close하는 쪽에서 Fee를 지불하게 되는데, 아래 채널의 잔액에서 트랜잭션 Fee를 제외한 금액이 LN 월렛에 입금된 것을 볼 수 있다.

$ lightning-cli --testnet listfunds
{
    "outputs": [
    {
        "txid": "30efaacbc0b74e1a6be1c19f11d1ac0b899cb06173bd69428ecd2a7df9198645",
        "output": 1,
        "value": 1098821,
        "amount_msat": "1098821000msat",
        "scriptpubkey": "0014b4fa21318f4ac68d7cb70c756f3e71ac02d0727f",
        "address": "tb1qknazzvv0ftrg6l9hp36k70n34spdqunleszec0",
        "status": "confirmed",
        "blockheight": 2190643,
        "reserved": false
     }
    ],
    "channels": [
     {
        "peer_id": "03bae2db4b57738c1ec1ffa1c5e5a4423968cc592b3b39cddf7d495e72919d6431",
        "connected": false,
        "state": "ONCHAIN",
        "short_channel_id": "2190478x3x0",
        "channel_sat": 1098997,
        ... 생략 ...
본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.