[bug]: asset amount received off by 1
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]
=======================================================================================
See https://github.com/lightninglabs/taproot-assets/blob/main/docs/rfq-and-decimal-display.md#precision-requirement-for-assets-in-the-lightning-network
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
?
Yes, the fundamental unit of the LN is milli-satoshi.
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?
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?
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.
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.
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.
@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 https://github.com/lightninglabs/taproot-assets/issues/1392 .
Seems like this is a won't fix, gonna close this.