Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug]: asset amount received off by 1 #1391

Open
ZZiigguurraatt opened this issue Feb 17, 2025 · 9 comments
Open

[bug]: asset amount received off by 1 #1391

ZZiigguurraatt opened this issue Feb 17, 2025 · 9 comments
Labels
bug Something isn't working needs triage

Comments

@ZZiigguurraatt
Copy link

I have the following network configuration:

       SAT           TA
dave <----> edward <----> frank

Frank is trying to receive 36000 asset. Frank creates an invoice and then dave pays it, but only 35999 asset are received by frank. Here's the output of my test script:

=======================================================================================
dave sending sats and frank receiving 36000 Asset1_base (360.0 Asset1_friendly) via edward
MaxAllowableFee: 11
accepted_buy_quote {
  peer: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
  id: "I!\317\337\326YT\306\316c\204\220\\\027W\275\025Ra\212\322\262Z\330\364yt\212\027~*n"
  scid: 17616239553884858990
  asset_max_amount: 36000
  ask_asset_rate {
    coefficient: "98522167480000000000"
    scale: 10
  }
  expiry: 1739790310
  min_transportable_units: 34876
}
invoice_result {
  r_hash: "\272\336\007\243\2747\334G\363V\247\"\031\243n8\352h?J\034\362\025\013\'\334\252%\024\346A\010"
  payment_request: "lnbcrt3654n1pnmx97rpp5ht0q0gauxlwy0u6k5u3pngmw8r4xs062rnep2ze8mj4z298xgyyqdqqcqzzsxqyz5vqrzjqd3j3r7qxuzfcvffc70f6rl9lculmx5pppks227hugnqrp8e8hqtlarewj9pwl32dcqqqqlgqqqqqqgq2qsp57hdvujmvs96knjm8yky24emrxx7sd8dfx3zcpw3eva8c7fc5tffq9qxpqysgqerguxmewt0fwl338zhp7rxhmdzwgpwuqc3f73q0uf5hu2wr2p6ax4lg6q79ga44l9wjsmx9lfhhm6lh0ar7aw4edn3k59g5ysyavzqcqr0wtpw"
  add_index: 1
  payment_addr: "\365\332\316Kl\201ui\313g%\210\252\347c1\275\006\235\2514E\200\2729gO\217\'\024ZR"
}

r_hash: bade07a3bc37dc47f356a72219a36e38ea683f4a1cf2150b27dcaa2514e64108

invoice num_satoshis: 365

invoice route_hints: [hop_hints {
  node_id: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
  chan_id: 17616239553884858990
  fee_base_msat: 1000
  fee_proportional_millionths: 1
  cltv_expiry_delta: 80
}
]

expected route: ['dave', 'edward', 'frank']
actual channel capacities:
active: true
remote_pubkey: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
channel_point: "ce67b84f7c2dca9088681ab3ee54ef0ba24d989af39831b3a0fa04a09f4bfc01:0"
chan_id: 387028093042688
capacity: 1000000
local_balance: 946530
remote_balance: 50000
commit_fee: 2810
commit_weight: 1116
fee_per_kw: 2500
csv_delay: 144
initiator: true
chan_status_flags: "ChanStatusDefault"
local_chan_reserve_sat: 10000
remote_chan_reserve_sat: 10000
lifetime: 80
uptime: 80
commitment_type: ANCHORS
push_amount_sat: 50000
local_constraints {
  csv_delay: 144
  chan_reserve_sat: 10000
  dust_limit_sat: 354
  max_pending_amt_msat: 990000000
  min_htlc_msat: 1
  max_accepted_htlcs: 483
}
remote_constraints {
  csv_delay: 144
  chan_reserve_sat: 10000
  dust_limit_sat: 354
  max_pending_amt_msat: 990000000
  min_htlc_msat: 1
  max_accepted_htlcs: 483
}
alias_scids: 17592186044416000001
peer_scid_alias: 17592186044416000000

(✔) (A) cap: 1000000 sat, dave->[bal: 946530 sat|res: 10000 sat|spend: 932000 sat], edward->[bal: 50000 sat|res: 10000 sat|spend: 35470 sat], commit_fee: 2810
active: true
remote_pubkey: "03e18a034b585fd7596c95845d25f555a815c46afb191fb0bc7d5107c84b27f046"
channel_point: "20f5b113f3fb116d3eadf3b7aa4273464b8c6575be22586146ddd7b803c2374c:0"
chan_id: 485984139542528
capacity: 100000
local_balance: 46920
remote_balance: 50000
commit_fee: 2420
commit_weight: 958
fee_per_kw: 2500
csv_delay: 144
private: true
initiator: true
chan_status_flags: "ChanStatusDefault"
local_chan_reserve_sat: 1000
remote_chan_reserve_sat: 1062
lifetime: 36
uptime: 36
commitment_type: SIMPLE_TAPROOT_OVERLAY
push_amount_sat: 50000
local_constraints {
  csv_delay: 144
  chan_reserve_sat: 1000
  dust_limit_sat: 354
  max_pending_amt_msat: 99000000
  min_htlc_msat: 1
  max_accepted_htlcs: 83
}
remote_constraints {
  csv_delay: 144
  chan_reserve_sat: 1062
  dust_limit_sat: 354
  max_pending_amt_msat: 99000000
  min_htlc_msat: 1
  max_accepted_htlcs: 83
}
alias_scids: 17592186044416000001
alias_scids: 17616239553884858990
peer_scid_alias: 17592186044416000000
custom_channel_data: "{\"assets\":[{\"asset_utxo\":{\"version\":1,\"asset_genesis\":{\"genesis_point\":\"e7a9d5de366f27522d4ba930e6a609a3510ddb2094a4440d6dbfee1f0f8fabcb:1\",\"name\":\"Asset1\",\"meta_hash\":\"009dc0aa29bd1c320fa9b33e15227169f23b77ab6ca845fabcdab2249b95568c\",\"asset_id\":\"37b7a42f889d90f46f6208da31997ffd15d8b06dfa2e56ee0f3c367de0b08089\"},\"amount\":200000000,\"script_key\":\"0250aaeb166f4234650d84a2d8a130987aeaf6950206e0905401ee74ff3f8d18e6\",\"decimal_display\":2},\"capacity\":200000000,\"local_balance\":200000000,\"remote_balance\":0}]}"

custom_channel_data:
{'assets': [{'asset_utxo': {'amount': 200000000,
                            'asset_genesis': {'asset_id': '37b7a42f889d90f46f6208da31997ffd15d8b06dfa2e56ee0f3c367de0b08089',
                                              'genesis_point': 'e7a9d5de366f27522d4ba930e6a609a3510ddb2094a4440d6dbfee1f0f8fabcb:1',
                                              'meta_hash': '009dc0aa29bd1c320fa9b33e15227169f23b77ab6ca845fabcdab2249b95568c',
                                              'name': 'Asset1'},
                            'decimal_display': 2,
                            'script_key': '0250aaeb166f4234650d84a2d8a130987aeaf6950206e0905401ee74ff3f8d18e6',
                            'version': 1},
             'capacity': 200000000,
             'local_balance': 200000000,
             'remote_balance': 0}]}
(✔) (A) cap: 100000 sat, edward->[bal: 46920 sat|res: 1000 sat|spend: 41390 sat], frank->[bal: 50000 sat|res: 1062 sat|spend: 44408 sat], commit_fee: 2420
(✔) (A) cap: 200000000 Asset1, edward->[bal: 200000000 Asset1], frank->[bal: 0 Asset1]

payment_hash: "bade07a3bc37dc47f356a72219a36e38ea683f4a1cf2150b27dcaa2514e64108"
value: 365
creation_date: 1739790275
payment_preimage: "0000000000000000000000000000000000000000000000000000000000000000"
value_sat: 365
value_msat: 365400
payment_request: "lnbcrt3654n1pnmx97rpp5ht0q0gauxlwy0u6k5u3pngmw8r4xs062rnep2ze8mj4z298xgyyqdqqcqzzsxqyz5vqrzjqd3j3r7qxuzfcvffc70f6rl9lculmx5pppks227hugnqrp8e8hqtlarewj9pwl32dcqqqqlgqqqqqqgq2qsp57hdvujmvs96knjm8yky24emrxx7sd8dfx3zcpw3eva8c7fc5tffq9qxpqysgqerguxmewt0fwl338zhp7rxhmdzwgpwuqc3f73q0uf5hu2wr2p6ax4lg6q79ga44l9wjsmx9lfhhm6lh0ar7aw4edn3k59g5ysyavzqcqr0wtpw"
status: IN_FLIGHT
creation_time_ns: 1739790275326967912
payment_index: 1

payment_hash: "bade07a3bc37dc47f356a72219a36e38ea683f4a1cf2150b27dcaa2514e64108"
value: 365
creation_date: 1739790275
payment_preimage: "0000000000000000000000000000000000000000000000000000000000000000"
value_sat: 365
value_msat: 365400
payment_request: "lnbcrt3654n1pnmx97rpp5ht0q0gauxlwy0u6k5u3pngmw8r4xs062rnep2ze8mj4z298xgyyqdqqcqzzsxqyz5vqrzjqd3j3r7qxuzfcvffc70f6rl9lculmx5pppks227hugnqrp8e8hqtlarewj9pwl32dcqqqqlgqqqqqqgq2qsp57hdvujmvs96knjm8yky24emrxx7sd8dfx3zcpw3eva8c7fc5tffq9qxpqysgqerguxmewt0fwl338zhp7rxhmdzwgpwuqc3f73q0uf5hu2wr2p6ax4lg6q79ga44l9wjsmx9lfhhm6lh0ar7aw4edn3k59g5ysyavzqcqr0wtpw"
status: IN_FLIGHT
creation_time_ns: 1739790275326967912
htlcs {
  route {
    total_time_lock: 814
    total_fees: 1
    total_amt: 366
    hops {
      chan_id: 387028093042688
      chan_capacity: 1000000
      amt_to_forward: 365
      fee: 1
      expiry: 734
      amt_to_forward_msat: 365400
      fee_msat: 1000
      pub_key: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
      tlv_payload: true
    }
    hops {
      chan_id: 17616239553884858990
      chan_capacity: 365
      amt_to_forward: 365
      expiry: 734
      amt_to_forward_msat: 365400
      pub_key: "03e18a034b585fd7596c95845d25f555a815c46afb191fb0bc7d5107c84b27f046"
      tlv_payload: true
      mpp_record {
        total_amt_msat: 365400
        payment_addr: "\365\332\316Kl\201ui\313g%\210\252\347c1\275\006\235\2514E\200\2729gO\217\'\024ZR"
      }
    }
    total_fees_msat: 1000
    total_amt_msat: 366400
    first_hop_amount_msat: 366400
  }
  attempt_time_ns: 1739790275341403191
  attempt_id: 1
}
payment_index: 1

payment_hash: "bade07a3bc37dc47f356a72219a36e38ea683f4a1cf2150b27dcaa2514e64108"
value: 365
creation_date: 1739790275
fee: 1
payment_preimage: "336a8fd91c848fbdf373a8902f7fc765a678faec207185d10277a18ad8db45d0"
value_sat: 365
value_msat: 365400
payment_request: "lnbcrt3654n1pnmx97rpp5ht0q0gauxlwy0u6k5u3pngmw8r4xs062rnep2ze8mj4z298xgyyqdqqcqzzsxqyz5vqrzjqd3j3r7qxuzfcvffc70f6rl9lculmx5pppks227hugnqrp8e8hqtlarewj9pwl32dcqqqqlgqqqqqqgq2qsp57hdvujmvs96knjm8yky24emrxx7sd8dfx3zcpw3eva8c7fc5tffq9qxpqysgqerguxmewt0fwl338zhp7rxhmdzwgpwuqc3f73q0uf5hu2wr2p6ax4lg6q79ga44l9wjsmx9lfhhm6lh0ar7aw4edn3k59g5ysyavzqcqr0wtpw"
status: SUCCEEDED
fee_sat: 1
fee_msat: 1000
creation_time_ns: 1739790275326967912
htlcs {
  status: SUCCEEDED
  route {
    total_time_lock: 814
    total_fees: 1
    total_amt: 366
    hops {
      chan_id: 387028093042688
      chan_capacity: 1000000
      amt_to_forward: 365
      fee: 1
      expiry: 734
      amt_to_forward_msat: 365400
      fee_msat: 1000
      pub_key: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
      tlv_payload: true
    }
    hops {
      chan_id: 17616239553884858990
      chan_capacity: 365
      amt_to_forward: 365
      expiry: 734
      amt_to_forward_msat: 365400
      pub_key: "03e18a034b585fd7596c95845d25f555a815c46afb191fb0bc7d5107c84b27f046"
      tlv_payload: true
      mpp_record {
        total_amt_msat: 365400
        payment_addr: "\365\332\316Kl\201ui\313g%\210\252\347c1\275\006\235\2514E\200\2729gO\217\'\024ZR"
      }
    }
    total_fees_msat: 1000
    total_amt_msat: 366400
    first_hop_amount_msat: 366400
  }
  attempt_time_ns: 1739790275341403191
  resolve_time_ns: 1739790275600402040
  preimage: "3j\217\331\034\204\217\275\363s\250\220/\177\307e\246x\372\354 q\205\321\002w\241\212\330\333E\320"
  attempt_id: 1
}
payment_index: 1

status: SUCCEEDED
expected route: ['dave', 'edward', 'frank']
actual channel capacities:
active: true
remote_pubkey: "0363288fc037049c3129c79e9d0fe5fe39fd9a81086d052bd7e2260184f93dc0bf"
channel_point: "ce67b84f7c2dca9088681ab3ee54ef0ba24d989af39831b3a0fa04a09f4bfc01:0"
chan_id: 387028093042688
capacity: 1000000
local_balance: 946163
remote_balance: 50366
commit_fee: 2811
commit_weight: 1116
fee_per_kw: 2500
total_satoshis_sent: 366
num_updates: 2
csv_delay: 144
initiator: true
chan_status_flags: "ChanStatusDefault"
local_chan_reserve_sat: 10000
remote_chan_reserve_sat: 10000
lifetime: 82
uptime: 82
commitment_type: ANCHORS
push_amount_sat: 50000
local_constraints {
  csv_delay: 144
  chan_reserve_sat: 10000
  dust_limit_sat: 354
  max_pending_amt_msat: 990000000
  min_htlc_msat: 1
  max_accepted_htlcs: 483
}
remote_constraints {
  csv_delay: 144
  chan_reserve_sat: 10000
  dust_limit_sat: 354
  max_pending_amt_msat: 990000000
  min_htlc_msat: 1
  max_accepted_htlcs: 483
}
alias_scids: 17592186044416000001
peer_scid_alias: 17592186044416000000

(A) cap: 1000000 sat, dave->[bal: 946163 sat|res: 10000 sat|spend: 931633 sat], edward->[bal: 50366 sat|res: 10000 sat|spend: 35836 sat], commit_fee: 2811
active: true
remote_pubkey: "03e18a034b585fd7596c95845d25f555a815c46afb191fb0bc7d5107c84b27f046"
channel_point: "20f5b113f3fb116d3eadf3b7aa4273464b8c6575be22586146ddd7b803c2374c:0"
chan_id: 485984139542528
capacity: 100000
local_balance: 46566
remote_balance: 50354
commit_fee: 2420
commit_weight: 958
fee_per_kw: 2500
total_satoshis_sent: 354
num_updates: 2
csv_delay: 144
private: true
initiator: true
chan_status_flags: "ChanStatusDefault"
local_chan_reserve_sat: 1000
remote_chan_reserve_sat: 1062
lifetime: 38
uptime: 38
commitment_type: SIMPLE_TAPROOT_OVERLAY
push_amount_sat: 50000
local_constraints {
  csv_delay: 144
  chan_reserve_sat: 1000
  dust_limit_sat: 354
  max_pending_amt_msat: 99000000
  min_htlc_msat: 1
  max_accepted_htlcs: 83
}
remote_constraints {
  csv_delay: 144
  chan_reserve_sat: 1062
  dust_limit_sat: 354
  max_pending_amt_msat: 99000000
  min_htlc_msat: 1
  max_accepted_htlcs: 83
}
alias_scids: 17592186044416000001
alias_scids: 17616239553884858990
peer_scid_alias: 17592186044416000000
custom_channel_data: "{\"assets\":[{\"asset_utxo\":{\"version\":1,\"asset_genesis\":{\"genesis_point\":\"e7a9d5de366f27522d4ba930e6a609a3510ddb2094a4440d6dbfee1f0f8fabcb:1\",\"name\":\"Asset1\",\"meta_hash\":\"009dc0aa29bd1c320fa9b33e15227169f23b77ab6ca845fabcdab2249b95568c\",\"asset_id\":\"37b7a42f889d90f46f6208da31997ffd15d8b06dfa2e56ee0f3c367de0b08089\"},\"amount\":200000000,\"script_key\":\"0250aaeb166f4234650d84a2d8a130987aeaf6950206e0905401ee74ff3f8d18e6\",\"decimal_display\":2},\"capacity\":200000000,\"local_balance\":199964001,\"remote_balance\":35999}]}"

custom_channel_data:
{'assets': [{'asset_utxo': {'amount': 200000000,
                            'asset_genesis': {'asset_id': '37b7a42f889d90f46f6208da31997ffd15d8b06dfa2e56ee0f3c367de0b08089',
                                              'genesis_point': 'e7a9d5de366f27522d4ba930e6a609a3510ddb2094a4440d6dbfee1f0f8fabcb:1',
                                              'meta_hash': '009dc0aa29bd1c320fa9b33e15227169f23b77ab6ca845fabcdab2249b95568c',
                                              'name': 'Asset1'},
                            'decimal_display': 2,
                            'script_key': '0250aaeb166f4234650d84a2d8a130987aeaf6950206e0905401ee74ff3f8d18e6',
                            'version': 1},
             'capacity': 200000000,
             'local_balance': 199964001,
             'remote_balance': 35999}]}
(A) cap: 100000 sat, edward->[bal: 46566 sat|res: 1000 sat|spend: 41036 sat], frank->[bal: 50354 sat|res: 1062 sat|spend: 44762 sat], commit_fee: 2420
(A) cap: 200000000 Asset1, edward->[bal: 199964001 Asset1], frank->[bal: 35999 Asset1]

=======================================================================================
@ZZiigguurraatt
Copy link
Author

OK, so it seems you don't work in sat

In [1]: AssetsPerSat=(98522167480000000000/10**10)/100e6

In [2]: AssetsPerSat
Out[2]: 98.52216748

In [3]: Sats=36000/AssetsPerSat

In [4]: Sats
Out[4]: 365.4000000285012

In [5]: int(Sats)
Out[5]: 365

In [6]: int(Sats)*AssetsPerSat
Out[6]: 35960.591130199995

but do work in msat

In [1]: AssetsPerMSat=(98522167480000000000/10**10)/100e9

In [2]: AssetsPerMSat
Out[2]: 0.09852216748

In [3]: MSat=36000/AssetsPerMSat

In [4]: MSat
Out[4]: 365400.0000285012

In [5]: int(MSat)
Out[5]: 365400

In [6]: int(MSat)*AssetsPerMSat
Out[6]: 35999.999997192004

In [7]: int(int(MSat)*AssetsPerMSat)
Out[7]: 35999

?

@guggero
Copy link
Member

guggero commented Feb 17, 2025

Yes, the fundamental unit of the LN is milli-satoshi.

@ZZiigguurraatt
Copy link
Author

OK, so the problem here is that internally asset units are always derived units and so we loose precision when going back and forth?

So, this is basically a won't fix?

@guggero
Copy link
Member

guggero commented Feb 17, 2025

Yes, it's a fundamental issue of not having true fractional units and exchange rate calculations.
If your calculation for something is 35999.99999 and need to represent that as an integer, you have two options: Either you round down (e.g. sender sends one unit less) or you round up (sender potentially ends one unit too much).

We've decided to go with "allow underpayment by at most 1 unit by sender" instead of the other possibility which would be "always overpay by at most 1 unit on sender".
These are the two possibilities we have. So IMO discussion should be: Which one is preferred?

@ZZiigguurraatt
Copy link
Author

So IMO discussion should be: Which one is preferred?

So, it seems you are only giving the option of ceil vs floor as a rounding technique, but why not also allow rounding up if greater or equal to 1/2 fractional assets and rounding down if less than 1/2 fractional assets? That case would make more sense to me if we have a FixedPoint type that has many more digits that can capture fractional assets in it's arithmetic before converting back to an integer.

@Roasbeef
Copy link
Member

So, it seems you are only giving the option of ceil vs floor as a rounding technique, but why not also allow rounding up if greater or equal to 1/2 fractional assets and rounding down if less than 1/2 fractional assets?

How does that differ from ceil vs floor?

Or is it that you suggest a piece-wise rounding scheme? I think we'd need to carefully analyze the proposal.

If we're always slowly rounding up anytime something is above 1/2 fractional cents, then I would be worried about the consistent upward drift that could include.

@ZZiigguurraatt
Copy link
Author

So, it seems you are only giving the option of ceil vs floor as a rounding technique, but why not also allow rounding up if greater or equal to 1/2 fractional assets and rounding down if less than 1/2 fractional assets?

How does that differ from ceil vs floor?

Or is it that you suggest a piece-wise rounding scheme? I think we'd need to carefully analyze the proposal.

If we're always slowly rounding up anytime something is above 1/2 fractional cents, then I would be worried about the consistent upward drift that could include.

ceil and floor are off by up to 1, whereas rounding at 1/2, you are off by at most 1/2.

If there is already a standard throughout all LL products to use floor, then maybe it is best with that to be consistent though.

@ZZiigguurraatt
Copy link
Author

@Liongrass , one thing that may not be as heavily emphasized in the docs as it should be is that sats are the base of everything, even if we think we are working in assets. Even if we are doing a direct asset only channel, assets are still a derived unit so we have other weird stuff that comes up like #1392 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs triage
Projects
Status: 🆕 New
Development

No branches or pull requests

3 participants