κ²°μ μ±λ (Payment Channels)
κ²°μ μ±λμ "λΉλκΈ°" XRP κ²°μ λ₯Ό λ§€μ° μμ λ¨μλ‘ λλκ³ λμ€μ κ²°μ ν μ μλ κ³ κΈ κΈ°λ₯μ λλ€.
κ²°μ μ±λμ XRPλ μΌμμ μΌλ‘ μμ½λ©λλ€. μ‘μ μλ μ±λμ λν μ²κ΅¬(Claims)λ₯Ό μμ±νκ³ , μμ μλ XRP μμ₯ κ±°λλ₯Ό 보λ΄κ±°λ μ μμ₯ λ²μ μ΄ ν©μμ μν΄ μΉμΈλκΈ°λ₯Ό κΈ°λ€λ¦¬μ§ μκ³ νμΈν©λλ€. (μ΄κ²μ μΌλ°μ μΈ νΈλμμ μ΄ ν©μμ μν΄ μΉμΈλλ ν¨ν΄κ³Ό λ³κ°λ‘ λ°μνλ―λ‘ λΉλκΈ° κ³Όμ μ λλ€.) μμ μλ μΈμ λ μ§ μ²κ΅¬λ₯Ό μΈμΆ(redeem)νμ¬ κ·Έ μ²κ΅¬μ μν΄ μΉμΈλ XRP κΈμ‘μ λ°μ μ μμ΅λλ€. μ΄λ¬ν μ²κ΅¬λ₯Ό κ²°μ νλ κ²μ νμ€ XRP μμ₯ κ±°λλ₯Ό μ¬μ©νλ©°, μ΄λ μΌλ°μ μΈ ν©μ κ³Όμ μ μΌλΆμ λλ€. μ΄ λ¨μΌ κ±°λλ λ μμ μ²κ΅¬μ μν΄ λ³΄μ₯λ μ¬λ¬ κ±°λλ₯Ό ν¬ν¨ν μ μμ΅λλ€.
μ²κ΅¬λ κ°λ³μ μΌλ‘ νμΈλμ§λ§ λμ€μ μΌκ΄λ‘ κ²°μ λ μ μκΈ° λλ¬Έμ, κ²°μ μ±λμ ν΅ν΄ νΈλμμ μ μ°Έκ°μλ€μ΄ μ΄λ¬ν μ²κ΅¬μ λμ§νΈ μλͺ μ μμ±νκ³ νμΈνλ λ₯λ ₯μλ§ μ νλλ μλλ‘ μ§νν μ μκ² λ©λλ€. μ΄ μ νμ μ£Όλ‘ μ°Έκ°μλ€μ νλμ¨μ΄ μλμ μλͺ μκ³ λ¦¬μ¦μ 볡μ‘μ±μ κΈ°λ°νκ³ μμ΅λλ€. μ΅λ μλλ₯Ό μ»κΈ° μν΄μ , XRP μμ₯μ κΈ°λ³Έμ μΈ secp256k1 ECDSA μλͺ λ³΄λ€ λΉ λ₯Έ Ed25519 μλͺ μ μ¬μ©νμΈμ. μ°κ΅¬λ 2011λ μ μν νλμ¨μ΄μμ μ΄λΉ Ed25519 100,000κ° μ΄μμ μλͺ μ μμ±νκ³ μ΄λΉ 70,000κ° μ΄μμ νμΈν μ μλ λ₯λ ₯μ μ¦λͺ νμ΅λλ€.
μ κ²°μ μ±λμ μ¬μ©ν΄μΌ νλκ°β
κ²°μ μ±λμ μ¬μ©νλ κ³Όμ μ νμ λ λΉμ¬μ, μ¦ μ§λΆμμ μμ·¨μΈμ ν¬ν¨ν©λλ€. μ§λΆμλ XRP μμ₯μ μ¬μ©νλ κ°μΈμ΄λ κΈ°κ΄μΌλ‘, μμ·¨μΈμ κ³ κ°μ λλ€. μμ·¨μΈμ μνμ΄λ μλΉμ€μ λν λκ°λ‘ XRPλ₯Ό λ°λ κ°μΈμ΄λ μ¬μ 체μ λλ€.
κ²°μ μ±λμ λ³Έμ§μ μΌλ‘ μ΄λ€ κ²μ ꡬ맀νκ³ ν맀ν μ μλμ§μ λν΄ μ§μ νμ§ μμ΅λλ€. κ·Έλ¬λ κ²°μ μ±λμ μ ν©ν μνκ³Ό μλΉμ€μ μ νμ λ€μκ³Ό κ°μ΅λλ€:
- λμ§νΈ μμ΄ν μ²λΌ κ±°μ μ¦μ μ μ‘λ μ μλ κ²λ€
- κ±°λ μ²λ¦¬ λΉμ©μ΄ κ°κ²©μ λΉμΌ λΆλΆμ μ°¨μ§νλ μ λ ΄ν κ²λ€
- μ νν μλμ΄ λ―Έλ¦¬ μλ €μ§μ§ μκ³ λλμΌλ‘ ꡬ맀νλ κ²λ€
κ²°μ μ±λμ μμ μ£ΌκΈ°β
λ€μ λ€μ΄μ΄κ·Έλ¨μ κ²°μ μ±λμ μμ μ£ΌκΈ°λ₯Ό μμ½ν κ²μ λλ€.
κ²°μ μ±λμ JSON ννβ
{
"Account": "rBqb89MRQJnMPq8wTwEbtz4kvxrEDfcYvt",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount": "4325800",
"Balance": "2323423",
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
"SettleDelay": 3600,
"Expiration": 536027313,
"CancelAfter": 536891313,
"SourceTag": 0,
"DestinationTag": 1002341,
"DestinationNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "PayChannel",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "F0AB71E777B2DA54B86231E19B82554EF1F8211F92ECA473121C655BFC5329BF",
"PreviousTxnLgrSeq": 14524914,
"index": "96F76F27D8A327FC48753167EC04A46AA0E382E6F57F32FD12274144D00F1797"
}
κ²°μ μ±λ κ΄λ ¨ νΈλμμ λ° λ¦¬νμ€νΈ νμ β
νΈλμμ (Transaction)β
μ§λΆ μ±λμ μμ±νκ³ XRPλ‘ μκΈμ μ 곡ν©λλ€. μ΄ νΈλμμ μ 보λ΄λ μ£Όμκ° μ§λΆ μ±λμ "μΆμ² μ£Όμ"κ° λ©λλ€.
{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "PaymentChannelCreate",
"Amount": "10000",
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"SettleDelay": 86400,
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
"CancelAfter": 533171558,
"DestinationTag": 23480,
"SourceTag": 11747
}XRP μ§λΆ μ±λμμ XRPλ₯Ό μ²κ΅¬νκ±°λ, μ§λΆ μ±λμ λ§λ£μΌμ μ‘°μ νκ±°λ, λ λ€ ν μ μμ΅λλ€. μ΄ κ±°λλ νΈλμμ λ°μ μμ μ§μ λ μ±λμμμ μν μ λ°λΌ λ€λ₯΄κ² μ¬μ©λ μ μμ΅λλ€.
μ±λμ μΆμ² μ£Όμλ λ€μκ³Ό κ°μ μμ μ μνν μ μμ΅λλ€:
- μ±λμμ λͺ©μ μ§λ‘ XRPλ₯Ό μ²κ΅¬μμ ν¨κ» λλ μμ΄ λ³΄λΌ μ μμ΅λλ€.
- μ±λμ SettleDelayκ° μ§λ νμ μ±λμ λ§λ£λ₯Ό μ€μ ν μ μμ΅λλ€.
- 보λ₯ μ€μΈ λ§λ£ μκ°μ μ§μΈ μ μμ΅λλ€.
- μ±λμ μ¦μ λ«μ μ μμΌλ©°, μ΄ κ²½μ° μ²κ΅¬λ₯Ό λ¨Όμ μ²λ¦¬νκ±°λ μ²λ¦¬νμ§ μμ μ μμ΅λλ€. μ±λμ μμ¬ XRPκ° μλ κ²½μ° μΆμ² μ£Όμλ μ±λμ μ¦μ λ«μ μ μμ΅λλ€.
μ±λμ λͺ©μ μ§ μ£Όμλ λ€μκ³Ό κ°μ μμ μ μνν μ μμ΅λλ€:
- μλͺ λ μ²κ΅¬μλ₯Ό μ¬μ©νμ¬ μ±λλ‘λΆν° XRPλ₯Ό μλ Ήν μ μμ΅λλ€.
- μ²κ΅¬μλ₯Ό μ²λ¦¬ν νμ μ±λμ μ¦μ λ«μ μ μμΌλ©°, μ΄ κ²½μ° λ―Έμ²κ΅¬ XRPλ₯Ό μ±λμ μΆμ²λ‘ νλΆν©λλ€.
μ΄ νΈλμμ μ 보λ΄λ μ΄λ€ μ£Όμλ μ§, μ΄μ λ μ μ λ§κ° μκ°λ³΄λ€ λ§λ£ λλ CancelAfter μκ°μ΄ λ μ€λλ μ±λμ λ«μ μ μμ΅λλ€. νΈλμμ μ λ΄μ©κ³Όλ μκ΄μμ΄ μ ν¨νκ² κ΅¬μ±λ PaymentChannelClaim νΈλμμ μ μ΄ ν¨κ³Όλ₯Ό κ°μ§λλ€.
{
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"Balance": "1000000",
"Amount": "1000000",
"Signature": "30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B",
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A"
}μ§λΆ μ±λμ XRPλ₯Ό μΆκ°νκ³ , νμνλ€λ©΄ μ±λμ λ§λ£ μκ°μ μ λ°μ΄νΈν©λλ€. μ§λΆ μ±λμ μΆμ² μ£Όμλ§ μ΄ νΈλμμ μ μ¬μ©ν μ μμ΅λλ€.
{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "PaymentChannelFund",
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"Amount": "200000",
"Expiration": 543171558
}
리νμ€νΈ (Request)β
μ§λΆμλ
channel_authorize
λ©μλλ₯Ό μ¬μ©νμ¬ μμ±ν μλͺ (signature)λ μμ·¨μΈμ΄ XRPλ₯Ό μΈμΆνλλ° νμν κΆνμ λΆμ¬ν©λλ€. μ΄ μλͺ μ μμ·¨μΈμκ² μ μ‘λμ΄μΌ ν©λλ€.νμ§λ§ λ¨μν μλͺ λ§μ 보λ΄λ κ²μ΄ μλλΌ, XRP Ledgerμμμ
Payment Channel Claim
νΈλμμ μ μ μΆνλλ° νμν λͺ κ°μ§ μ 보λ ν¨κ» 보λ΄μΌ ν©λλ€. μ΄λ¬ν μ 보μλ λ€μμ΄ ν¬ν¨λ©λλ€:channel_id
: κ²°μ μ±λμ κ³ μ ν μλ³μμ λλ€.signature
:channel_authorize
λ©μλμ μν΄ μμ±λ μλͺ μ λλ€.public_key
: κ²°μ μ±λμ λ§λ κ³μ μ κ³΅κ° ν€μ λλ€.amount
: μΈμΆνλ €λ XRP κΈμ‘μ λλ€.
μ΄ μ 보λ€μ μμ·¨μΈμκ² μ 곡νλ©΄, μμ·¨μΈμ μ΄λ₯Ό μ¬μ©νμ¬ XRP Ledgerμ
Payment Channel Claim
νΈλμμ μ μ μΆνκ³ , μ§μ λ κΈμ‘μ XRPλ₯Ό μΈμΆν μ μμ΅λλ€.channel_verify
λ©μλλ μμ·¨μΈμ΄ κ²°μ μ±λμμ νΉμ μμ XRPλ₯Ό μΈμΆν μ μλ μλͺ μ μ ν¨μ±μ κ²μ¬ν©λλ€. 보μμ±μ ν보νκ³ , λΆμ νμλ₯Ό λ°©μ§νκΈ° μν λͺ©μ μΌλ‘, μκΈμ μΈμΆμ μ§μ μ μΌλ‘ μν₯μ λΌμΉμ§λ μμ΅λλ€.
κ²°μ μ±λ μ¬μ© μλ리μ€β
import os
from account import XrplAccount
from transaction import XrplTransaction
from request import XrplRequest
from utils import Logger
DIR = os.path.dirname(__file__)
def main():
# 0. μ§λΆμμ μμ·¨μΈμ μ§κ°μ μ°κ²°ν©λλ€.
payer = XrplAccount(
wallet_path=os.path.join(DIR, "wallets", "payer.json"),
create=True,
)
payee = XrplAccount(
wallet_path=os.path.join(DIR, "wallets", "payee.json"),
create=True,
)
# νΈλμμ
κ³Ό 리νμ€νΈλ₯Ό μν κ°μ²΄λ₯Ό μμ±ν©λλ€.
transaction = XrplTransaction()
request = XrplRequest()
# κ²°κ³Ό νμΈμ μν΄ λ‘κ±°λ₯Ό μμ±ν©λλ€. (λ‘κ·Έ νμΌμ logs λλ ν 리μ μ μ₯λ©λλ€.)
logger = Logger(os.path.join(DIR, "logs", "channel_scenario.log"))
# 1. μ§λΆμ: μ±λ μμ±
# μ§λΆμκ° μ±λμ μμ±νλ©΄ μ
λ ₯ν κΈμ‘λ§νΌμ XRPκ° μ§κ°μΌλ‘λΆν° λΉ μ Έλμ μ±λμ μμΉλ©λλ€.
result = transaction.create_payment_channel(
account=payer,
destination_address=payee.address,
amount=500,
settle_delay=86400,
public_key=payee.wallet.public_key,
)
logger.log(result)
# μ§λΆμμ μμ·¨μΈ λͺ¨λ μ±λμ 곡κ°ν€λ₯Ό μ μ₯ν©λλ€. (μ΄ κ³΅κ°ν€λ μμ·¨μΈμ΄ μ²κ΅¬μλ₯Ό κ²μ¦ν λ μ¬μ©ν©λλ€.)
channel_public_key = result["PublicKey"]
# 2. μμ·¨μΈ: μ±λ νμΈ
# μμ·¨μΈμ μ§λΆμκ° μμ±ν μ±λμ νμΈν©λλ€.
# μ±λμ λ¨λ°©ν₯μ΄λ―λ‘ μμ·¨μΈμ μ§λΆμμ κ³μ μ£Όμλ₯Ό ν΅ν΄ μ±λμ νμΈν©λλ€.
result = request.get_account_channels(payee.client, payer.address)
logger.log(result)
channel_id = result["channels"][-1]["channel_id"]
# 3. μ§λΆμ: μ²κ΅¬μμ μλͺ
# μ§λΆμκ° λ³ΈμΈ μ§κ°μ λΉλ°ν€λ‘ μ²κ΅¬μμ μλͺ
ν©λλ€.
result = request.authorize_channel(
client=payer.client,
channel_id=channel_id,
amount=100,
secret=payer.wallet.seed,
)
logger.log(result)
# μλͺ
κ²°κ³Όλ₯Ό μ μ₯ν©λλ€. (μ΄ μλͺ
κ²°κ³Όλ₯Ό μ§λΆμκ° μμ·¨μΈμκ² μ μ‘ν΄μΌ ν©λλ€. Off-ledgerλ‘ μ μ‘ν©λλ€.)
signature = result["signature"]
# 4. μ§λΆμ: μ²κ΅¬μλ₯Ό μμ·¨μΈμκ² μ μ‘
# νμν μ 보λ€μ μ λΆ ν¬ν¨μμΌ μ²κ΅¬μλ₯Ό λ§λλλ€.
# Off-ledgerλ‘ μ²κ΅¬μλ₯Ό μμ·¨μΈμκ² μ μ‘ ν©λλ€. (XRP Ledgerμλ κΈ°λ‘λμ§ μμ΅λλ€.)
invoice = {
"channel_id": channel_id,
"signature": signature,
"amount": 100,
"public_key": channel_public_key,
}
logger.log(invoice)
# 5. μμ·¨μΈ: μ²κ΅¬μ νμΈ
# μμ·¨μΈμ μ²κ΅¬μλ₯Ό νμΈνκ³ , μ²κ΅¬μμ ν¬ν¨λ μλͺ
μ μ²κ΅¬μμ 곡κ°ν€λ‘ κ²μ¦ν©λλ€.
result = request.verify_channel(client=payee.client, **invoice)
logger.log(result)
# 6. μμ·¨μΈ: μν λλ μλΉμ€ μ 곡
# Off-ledger λλ on-ledgerλ‘ μν λλ μλΉμ€λ₯Ό μ 곡ν©λλ€.
# 7. μνλλλ‘ 3-6λ¨κ³ λ°λ³΅ν©λλ€.
# 8. μμ·¨μΈ: XRP μν
# κ±°λκ° μλ£λλ©΄, μμ·¨μΈμ μΈμ¦λ κΈμ‘μ λν μ²κ΅¬λ₯Ό μνν©λλ€.
# μ²κ΅¬μλ λμ λ κΈμ‘μ ν¬ν¨νκ³ μμΌλ―λ‘, 3~6λ¨κ³κ° μ¬λ¬λ² μ§νλμ΄λ λ§μ§λ§ μ²κ΅¬μλ§ claimνλ©΄ λ©λλ€.
# redeem_payment_channelμ λ΄λΆμμ PaymentChannelClaim νΈλμμ
μ μμ±ν©λλ€.
result = transaction.redeem_payment_channel(
account=payee, balance=invoice["amount"], **invoice
)
logger.log(result)
# 9. μ§λΆμ: μ±λ λ«κΈ° μμ²
# μ±λμ λ«μΌλ©΄ μ±λμ λ¨μμλ XRPκ° μ§λΆμμκ² λμκ°λλ€.
# close_payment_channelμ λ΄λΆμμ μ±λμ λ«λ νλκ·Έλ₯Ό ν¬ν¨ν PaymentChannelClaim νΈλμμ
μ μμ±ν©λλ€.
result = transaction.close_payment_channel(account=payer, channel_id=channel_id)
logger.log(result)
# 10. μ§λΆμ(λλ λκ΅°κ°): λ§λ£λ μ±λ λ«κΈ°
# λ§μ½ μ±λμ΄ λ«νμ§ μκ³ λ§λ£λμλ€λ©΄ μ무λ (μ§λΆμλ μμ·¨μΈ λ§κ³ λ μ λ§ μ무λ) μ±λμ λ«μ μ μμ΅λλ€.
# result = transaction.close_payment_channel(account=payee, channel_id=channel_id)
# logger.log(result)
# μ±λ λ«νλμ§ νμΈ
result = request.get_account_channels(payer.client, payer.address)
logger.log(result)
if __name__ == "__main__":
main()