mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-15 12:50:42 +02:00
The claim of a returning self-payment matched payment_hash and payment_secret but not the HTLC amount. Answering the hook with resolve settles the HTLC at once, so lightningd's own final_incorrect_htlc_amount check is skipped. The last-hop peer relays our onion (and thus the secret) intact but chooses the offered amount, so it could settle a reduced HTLC, learn the preimage, and claim the full amount upstream. Record the intended amount at Claimer::generate() time and resolve only an exact match; a mismatch is left to normal handling, which fails the HTLC for lack of an invoice. The same issue was recently fixed in sling (daywalker90/sling@835f36e8). Fixes #322.
112 lines
2.9 KiB
C++
112 lines
2.9 KiB
C++
#include"Boss/Mod/FundsMover/Claimer.hpp"
|
|
#include"Boss/Msg/ProvideHtlcAcceptedDeferrer.hpp"
|
|
#include"Boss/Msg/ReleaseHtlcAccepted.hpp"
|
|
#include"Boss/Msg/SolicitHtlcAcceptedDeferrer.hpp"
|
|
#include"Boss/Msg/TimerRandomHourly.hpp"
|
|
#include"Boss/concurrent.hpp"
|
|
#include"Boss/log.hpp"
|
|
#include"Ev/Io.hpp"
|
|
#include"Ev/now.hpp"
|
|
#include"Ln/HtlcAccepted.hpp"
|
|
#include"S/Bus.hpp"
|
|
|
|
namespace {
|
|
|
|
auto const timeout = double(3600 * 24);
|
|
|
|
}
|
|
|
|
namespace Boss { namespace Mod { namespace FundsMover {
|
|
|
|
void Claimer::start() {
|
|
bus.subscribe<Msg::SolicitHtlcAcceptedDeferrer
|
|
>([this](Msg::SolicitHtlcAcceptedDeferrer const&) {
|
|
auto f = [this](Ln::HtlcAccepted::Request const& r) {
|
|
auto const& h = r.payment_hash;
|
|
auto it = entries.find(h);
|
|
if (it == entries.end())
|
|
return Ev::lift(false);
|
|
auto const& payment_secret = it->second.payment_secret;
|
|
|
|
if (r.payment_secret != payment_secret)
|
|
return Ev::lift(false);
|
|
|
|
/* Resolve only an exact-amount HTLC: the
|
|
* last-hop peer relays our onion intact but
|
|
* chooses the offered amount, and a short
|
|
* HTLC would still buy the preimage that
|
|
* claims the full amount upstream. */
|
|
if (r.incoming_amount != it->second.expected_amount)
|
|
return Boss::log( bus, Warn
|
|
, "FundsMover::Claimer: HTLC "
|
|
"for known payment hash has "
|
|
"amount %s, expected %s; "
|
|
"not resolving."
|
|
, std::string(r.incoming_amount)
|
|
.c_str()
|
|
, std::string(it->second
|
|
.expected_amount)
|
|
.c_str()
|
|
).then([]() {
|
|
return Ev::lift(false);
|
|
});
|
|
|
|
/* Extract data. */
|
|
auto id = r.id;
|
|
auto preimage = std::move(it->second.preimage);
|
|
/* Delete from entries. */
|
|
entries.erase(it);
|
|
|
|
/* Prepare background action. */
|
|
auto act = bus.raise(Msg::ReleaseHtlcAccepted{
|
|
Ln::HtlcAccepted::Response::resolve(
|
|
id, std::move(preimage)
|
|
)
|
|
});
|
|
|
|
/* Launch background action and return. */
|
|
return Boss::concurrent(act).then([]() {
|
|
return Ev::lift(true);
|
|
});
|
|
};
|
|
return bus.raise(Msg::ProvideHtlcAcceptedDeferrer{
|
|
std::move(f)
|
|
});
|
|
});
|
|
|
|
bus.subscribe<Msg::TimerRandomHourly
|
|
>([this](Msg::TimerRandomHourly const&) {
|
|
auto now = Ev::now();
|
|
/* Scan all entries and erase those that have gone
|
|
* past timeout. */
|
|
for ( auto it = entries.begin(), next = entries.begin()
|
|
; it != entries.end()
|
|
; it = next
|
|
) {
|
|
/* Save next entry. */
|
|
next = it;
|
|
++next;
|
|
|
|
if (it->second.timeout < now)
|
|
entries.erase(it);
|
|
}
|
|
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
|
|
std::pair<Ln::Preimage, Ln::Preimage> Claimer::generate(Ln::Amount expected_amount) {
|
|
auto pre = Ln::Preimage(rand);
|
|
auto sec = Ln::Preimage(rand);
|
|
|
|
auto h = pre.sha256();
|
|
auto& entry = entries[h];
|
|
entry.timeout = Ev::now() + timeout;
|
|
entry.preimage = pre;
|
|
entry.payment_secret = sec;
|
|
entry.expected_amount = expected_amount;
|
|
|
|
return std::make_pair(std::move(pre), std::move(sec));
|
|
}
|
|
|
|
}}}
|