-
Notifications
You must be signed in to change notification settings - Fork 901
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
Planning for SPV backend #3858
Comments
For now, a minimal scan of the
Unfortunately, BIP158 does not include entire transactions: it only includes spends from and spends to specific However, transactions-being-tracked is only used in two places:
So I think we can modify the
That way, our only interface absolutely necesary is a Of note as well, is that |
Okay, I think Instead, I propose a new
DESCRIPTION
RETURN VALUEThis returns a result object with the following fields:
This command returns The By using the above, we can use just an Electrum Stratum connection. Electrum Stratum does not look like it can support downloading entire blocks, which is what Thoughts? @darosior in particular? |
Hmmm so I was looking at our code as well, and it seems we have an existing As best as I can determine, this Electrum has a specific Hmmm. Now, in order to prevent too much activity from interacting with However, the above will not work well with Electrum, since we cannot simply download a block. Further, the spentness of P2WSH outpoints depends on us processing every transaction in a block. We cannot just depend on data that involves only our own UTXOs and addresses! Hmmmm. I suppose this needs more design thinking. |
All right, let us add a new A backend plugin may choose not to implement The backend plugin We also need to create a new db table, The Then, in
Then, on normal synching at the block tip:
Thoughts? @cdecker in particular since this is a massive change in chaintopology and wallet db. Sigh. The complication is in short-channel-ids. |
Supporting protocol that expose a less natural (scriptPubkey based instead of txid / txout based) interface would be great as it would allow to use more different backends than just This comes down to the reason I did not go for this initially: efficiency. We moved from a There is still BIP158 which would be nice to have though. So this is trading (a lot of) complexity for BIP158 support: I have no strong opinion on this. |
BIP158 does not have any specific support we can get for the What we can do with BIP158 would be to continue with the current system, which is that But caching at the plugin level is probably needed indeed, especially for Electrum and BIP158 backends. Sigh. Then a backend plugin could support a
Maybe the block-synching command can be allowed to return multiple options. It gets a set of UTXOs and addresses the
Thus, a plugin need not parse block data. However, the array of transactions return would be used by an Electrum backend, and presumably the Electrum server is the one that parsed the block for us, so the new Elements parsing thing need not be understood by the plugin either. BIP158 backends would just match the addresses to the BIP158 filters, and download the entire block as well too. If the block-synch command returns:
Then But if the block-synch command returns:
Then So I think a new table of blocks that we have full knowledge of SCID events in that block should be maintained by us (i.e. the If
The upshot of this is that with a BIP158 backend, we can "spontaneously catch" some of the short-channel-id updates if it happens that a block matches the filter we request. Aargh. The hard problem is the SCID lookup! |
I think it's cleaner for us to make a (backward compatible) breaking change at this point. Bitcoin backend, revived
|
We need to be aware of reorgs, especially those that take, say, 3 or 4 blocks, and which might have conflicting transactions affecting our wallet or channels, so I think this is mildly undesirable to just depend on a count rather than a header chain. |
Yes, sure (I meant Also, an even simpler upgrade mechanism: if a plugin registers |
We also expose a Now, for scid tracking, I believe what we are now planning is to let the plugin handle this by implementing a However, for Then Alternately we can just remove the |
@darosior @cdecker @niftynei @rustyrussell I have updated the original post with the current plan I have. |
Hmmmm.. So I went digging about in Another is the (but the above risks reorg-related not noticing that a channel has been closed due to races between |
Okay, so I started digging into actual code, and it looks like we cannot remove My plan was to remove this in favor of The reason why we want to remove However, there are a few places where we use
The first, we could fudge: if it has an The second and third, not so much, sigh. Again, we cannot just keep the Perhaps we can add a For For Electrum backend, we group the UTXOs by address and query the server for each address, then figure out spendedness. For BIP158 backends, this is a lot more uncomfortable! Starting with the chain tip, we check if any UTXOs match in the Neutrino filter for the block. If it does, oh no, download the block and see if any of the UTXOs in the parameter input set is spent in that block. If one is spent, remove it from the parameter input set and mark it as "spent" and put it in our results set. Keep going backwards through the blockchain until the set of all UTXOs is known to be spent (i.e. the parameter inputs set is empty) OR we reach Sigh. EDIT I realized that In addition, at startup, EDIT2: The comment on |
Another complication is that This is actually a further complication on our current reservation system with RBF (#3867 #3845 et al)!! In particular, suppose a transaction is completely made valid via In particular, that is how it would behave if the (Nowhere do we explicitly say that we do not support So if we are going to go RBF, we need to start being a lot more aware of the mempool as well, and aware that the mempool is not synchronized with miner mempools. Maintaining a mempool greatly complicates a BIP158 backend (Electrum is fine since you can query the mempool of the Stratum server). A BIP158 backend cannot maintain a complete mempool since the point of an SPV wallet is to not maintain a complete UTXO set (by not downloading every block). This means that a BIP158 backend has to know the UTXO set of at least our onchain wallet, so that it can filter out txes from the mempool that do not involve our UTXOs. So it looks to me that the backend plugin has to do UTXO set management itself, instead of Okay, so let us just have BIP158 maintain all transactions broadcasted by the |
Fwiw we do not support blocksonly as we require fee estimates.
Bitcoind has a quirk though where you could have outdated estimates if started in blocksonly... (i'e a pr for that)
…-------- Original Message --------
On Jul 29, 2020, 06:11, ZmnSCPxj, ZmnSCPxj jxPCSmnZ wrote:
Another complication is that getutxout calls into gettxout. gettxout has an optional third argument, include_mempool, which defaults to true if not specified, and bcli does not specify it. This means that getutxout also considers the mempool.
This is actually a further complication on our current reservation system with RBF ([#3867](#3867) [#3845](#3845) et al)!! In particular, suppose a transaction is completely made valid via signpsbt. Then it is broadcast directly (or another node broadcasts it via sendpsbt, but not our own). The outputs reserved by the original fundpsbt/utxopsbt remain reserved and you can utxopsbt again with allow_reserved to RBF it. However, if the node is restarted and the transaction is still unconfirmed, the outputs will now be marked as spent and you cannot utxopsbt again with allow_reserved.
In particular, that is how it would behave if the bitcoind we are talking to is not set to blocksonly. In blocksonly mode the bitcoind does not maintain a mempool and on restart we would still see the coins as unspent.
(Nowhere do we explicitly say that we do not support blocksonly!)
So if we are going to go RBF, we need to start being a lot more aware of the mempool as well, and aware that the mempool is not synchronized with miner mempools.
Maintaining a mempool greatly complicates a BIP158 backend (Electrum is fine since you can query the mempool of the Stratum server). A BIP158 backend cannot maintain a complete mempool since the point of an SPV wallet is to not maintain a complete UTXO set (by not downloading every block).
This means that a BIP158 backend has to know the UTXO set of at least our onchain wallet, so that it can filter out txes from the mempool that do not involve our UTXOs. So it looks to me that the backend plugin has to do UTXO set management itself, instead of lightningd... aargh.
Okay, so let us just have BIP158 maintain all transactions broadcasted by the lightningd as the "mempool". This probably means requiring that sendrawtransaction be replaced with a PSBT-using sendpsbttomempool instead, so that the backend plugin can go access the scriptPubKey of spent coins (so that a BIP158 "light mempool" can contain all the transactions broadcast by this lightningd, and then at least remove transactions from the "light mempool" that spend coins that are also spent in an incoming block). This still prevents a BIP158 backend plugin from reporting that a PSBT-based transaction spending our own funds was broadcasted by somebody else, though, at least until confirmation. Sigh.
—
You are receiving this because you were mentioned.
Reply to this email directly, [view it on GitHub](#3858 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AFLK3F4KT5BBTUHMQFMKMVLR56ONZANCNFSM4PBZ2OVA).
|
Hmmm but you can replace plugins, right? You could have a plugin which runsh Naaaa that is dumb. We should explicitly document that we do not support |
Hmm even better: we could make a getnetworkinfo call at startup to check if we relay txs, and display a nice error if not. I'm going to PR that.
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
Le mercredi, juillet 29, 2020 1:54 PM, ZmnSCPxj, ZmnSCPxj jxPCSmnZ <[email protected]> a écrit :
… Hmmm but you can replace plugins, right? You could have a plugin which runsh bcli in background and forwards all methods except estimatefees, which instead downloads from johoe.
Naaaa that is dumb. We should explicitly document that we do not support blocksonly.
—
You are receiving this because you were mentioned.
Reply to this email directly, [view it on GitHub](#3858 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AFLK3F3UKINVNMLU2LJES23R6AEVVANCNFSM4PBZ2OVA).
|
Now that you mention mempools and feerates.... how do BIP158 SPV nodes guess reasonable feerates?? Sigh. Because if the BIP158 SPV node is receiving arbitrary transactions into its mempools, that is actually much of the same bandwidth as a fullnode (after IBD, at least), since modern fullnodes use compact blocks.... whut. How would a BIP158 SPV backend get fee estimates? (I am tempted to just say johoe...). |
Now it gets interesting, i am always from the quantum aspect of reality in favor of pure randomness. To show a decent will in the absence of determined knowledge is often enough to push things forward in the desired direction. Just dice a bit. And RBF the rest? |
RBF is difficult for us to handle in And until we fully implement anchor commitments, we need a decent feerate estimation for |
yup, we are with CPFP ,RBF or good estimation in the hand of the miners and Its a bit like programming Grover like oracles. That's my overall reasoning to dice at some point and let s** happen, instead of central point of johoe..., anchor commitments, hmm I process mempool message please wait.., please wait.., please wait .. ? |
Okay, here is a horrible idea for fee estimation with a BIP158 backend:
|
FWIW you could also use the |
SPV is certainly not something I would consider as "safe to ship by default", even if we had a perfectly trustless method of fee estimation. FWIW even a tx-broadcasting fullnode will be asking all its peers for all txes in the peer mempool anyway, and thus trusting them to not lie about their mempools; we are simply reducing the bandwidth use by doing a random sampling that we assume to be representative of the entire data set, which is good enough for scientific papers anyway so should be good for us here in engineering-land (yeah right). And if we can fully move to anchor commitments, then we can get away with a lousy lowball initial estimate and just CPFP+RBF later. For example, a |
We currently get away with this because we always get every transaction in every block, so even if our filters are schizophrenic it does not matter. Anyway, the changes needed are:
Another thing to consider is to add a The issue is back-compatibility to the v1 backend interface. Ideally the backend plugin should just implement |
Add pruning label. The reason we cannot work well with pruning is that we need historical information on channel opens, especially on a fresh install which knows nothing about older channels. Without pruning we just look up the old blocks and check the channel exists. With pruning we cannot validate historically long-lived channels, and cannot send it as gossip and cannot rely on it for routing. A possible solution for pruning would be for us to process blocks at the block tip as usual from the fullnode, but for historical channels below the pruning cutoff, ask a server (e.g. Electrum servers have a decent interface for querying historical channels by SCID). This is still less desirable than running a non-pruning fullnode, since you are trusting that the server does not lie to you for map data, and if you are e.g. evaluating map data for where best to make channels (i.e. autopilot) then the server can lie to you and bias your selection towards surveilling nodes. |
The best a server can do is to not return a block, if we verify the Merkle Root and the blockhash against the local pruned node, which still maintains a list of all valid blockheaders in the past. Refusing to return a block is a pretty obvious failure, so I feel pretty good about anchoring our trust in a pruned node, and using the server to get the details in a tamper proof manner 🙂 |
I was thinking in terms of Electrum However, with the Electrum |
First off: SPV is worse security than fullnodes. Please use fullnodes if you can.
On the other hand, if you trust a particular cloud service provider, you can run a fullnode on a cloud service, and thus not spend a lot of bandwidth in your own local connection. You might then think to run
lightningd
locally and point the--bitcoin
parameters at the cloud service fullnode, but this actually downloads every block in full and gets you as much bandwidth use as a locally-runbitcoind
withblocksonly
option. You could instead run an SPV server of some kind on your remote cloud service fullnode, and as long as the cloud service does not secretly replace yourbitcoind
withminercoin2xd-unlimitedvision
, you should be fine. Then we would want support for SPV in a locally-runlightningd
to limit the bandwidth needed, and point it only at your trusted cloud service fullnode.Here is my current plan:
When
lightningd/bitcoind
object is initialized, it checks for the following two sets of APIs:getrawblockbyheight
,getutxout
,estimatefees
,getchaininfo
,sendrawtransaction
- current 0.9.0 set.gettxesbyheight
,getutxobyscid
,estimatefees
,getchaininfo
,sendpsbttomempool
,checkspent
- next version of the backend plugin API.gettxesbyheight
is given aheight
to fetch, plus areceive_scriptpubkeys
an array of hex stringscriptPubKey
s to check, and aspend_utxos
an array of objects withtxid
,vout
, andscriptPubKey
fields. It returns a fieldfound
asfalse
if not yet found, or if the block is found it returnsfound
field astrue
and in addition returns theblockid
the hash of the block header,header
the hex string dump of the header, andtxes
an array of hex string transactions in the block that match the givenreceive_scriptpubkeys
orspend_utxos
.getutxobyscid
is given anscid
of BOLT-standard formBBBxTTxOO
. It returns a fieldfound
, which isfalse
if the given SCID does not match an unspent SegWit v0 32-byte witness (P2WSH), or a SegWit v1 32-byte witness (Taproot). Iftrue
it returns thescriptPubKey
of the output as well as theamount
.sendpsbttomempool
is given a Base64 PSBT that contains a completely signed transaction, and aallowhighfees
parameter, and broadcasts it. We have to switchbitcoind_sendrawtx
to usestruct bitcoin_tx
which conveniently contains a PSBT.checkspent
is given an array of objects. Each object represents a txo with at leasttxid
andvout
andscriptPubKey
fields, and optionalblockheight
, which is the known confirmation height of the txo. For each txo, it checks if the txo is spent, and if so, adds it to thespent
result, which is an array of objects withtxid
,vout
, andspendheight
fields, with'spendheight': 0
for TXOs that are spent in unconfirmed transactions.Internally, we also have these changes to
lightningd/bitcoind
functions:bitcoind_can_getutxobyscid
. This accepts aconst struct bitcoind *
and returnstrue
if the bitcoin backend implementsgetutxobyscid
.bitcoind_getutxobyscid
andbitcoind_checkspent
, but not safe to callbitcoind_getfilteredblock
orbitcoind_getutxout
.false
, it is not safe to callbitcoind_getutxobyscid
orbitcoind_checkspent
but it is safe to callbitcoind_getfilteredblock
andbitcoind_getutxout
.bitcoind_gettxesbyheight
. This is always callable.chaintopology
should use this API instead ofbitcoind_getrawblockbyheight
, since the latter is removed.bitcoind_getrawblockheight
is thatbitcoind_gettxesbyheight
requires filter arguments. It still returns astruct block
albeit one with a possibly-incomplete set oftx
.getrawblockbyheight
and ignores the filter arguments.bitcoind_sendrawtx
should acceptstruct bitcoin_tx
instead ofconst char *hextx
.Finally, we also add a new
lightningd
command,checknewblock
, which triggerschaintopology
into checking for a new block. The backend plugin should call this when the block tip height changes. Normalbitcoin-cli
and BIP158 plugins will poll, though a BIP158 plugin could have a peer push a new block at it (not likely unless the peer is a miner). An Electrum plugin would subscribe toblockchain.headers.subscribe
. For now we also do polling ourselves inlightningd
to support older plugins that do not perform this call.After we change
lightningd
, we can then updatebcli
to use the new interface. In order to check the new interface,gettxesbyheight
will also apply filtering to transactions in the block, and will not provide txes that do not match the given filters.getutxobyscid
could have a simple on-disk cache: a mapping of SCID to either a tuple oftxid
andvout
, or Nothing. If an SCID exists in the on-disk cache, and it maps to nothing, it is known-invalid SCID, else if it maps to atxid
vout
thebcli
will go querybitcoin-cli getutxout
to see if it is still unspent. If the SCID does not exist in the on-disk cache, then we scan the block and update the cache, and we also iterate over all possible SCIDs (e.g. even absurd ones like BBBx16777215x65535) and map those that do not match a P2WSH or Taproot output to Nothing.Original text:
First off: SPV is worse security than fullnodes. Please use fullnodes if you can.
Currently, the interface to
bcli
inherently assumes the backend is a fullnode of some kind. This fullnode is always fully trusted to not lie, and we always download every block from the backend.When doing block synchronization, what we can do is to have a
txfilter
containing:Now, it is important that we keep track of UTXOs that we want to track spendedness of channels. However, each such UTXO has its own address as well.
Most SPV protocols (eg BIP158, Electrum) are address-centric. So, we should provide just addresses to the backend. The backend should inform us
So I propose for the
bcli
getrawblockbyheight
:txfilter
, which is an array ofscriptPubKey
hexdumps.block
field (but must still returnblockhash
). This is used to informlightningd
that the block at the given height does not involve any of the addresses in thetxfilter
.blockheader
field regardless. This is needed so that chaintopology can recognize reorgs.Then:
blockchain.scripthash.get_history
on every listedscriptPubKey
, and if any of them has a transaction whose confirmedheight
equals the height being queried, then we should download that block (elsewhere) and provide it to thelightningd
.blockchain.block.header
and save the header, thenblockchain.scripthash.get_history
for a single scriptpubkey, then ablockchain.block.header
and compare if it is the same as previous; if not the same, we should restart the querying again, since the alternate block might now contain the previousscriptpubkey
. This is not resilient against very fast ABA problems but hopefully those are rare.0x00
Neutrino filter for the block header hash at the requested height. Then we check if anything in ourtxfilter
matches the Neutrino filter.Ideally, our fullnode-trusting
bcli
should also apply thetxfilter
to blocks it returns. This is to ensure that any bugs in maintaining thelightningd
-sidetxfilter
can be caught by our test suite without having to add SPV plugins of our own.Unfortunately, to determine if an address is spent from, we would require a
txindex
, or have the plugin maintain its own transaction index. Both are obviously undesirable.However, in practice:
So let me propose instead passing in two arguments to
getrawblockbyheight
ofbcli
:txfilter_receive_addr
, an array ofscriptpubkey
s, which, if any of them receive funds in the given block, we should return the block.txfilter_spend_utxo
, an array of{'txid': 'xx', outnum: 42, scriptpubkey: 'xx'}
, which, if any of the given UTXOs are spent, we should return the block.Then:
bcli
only needs to scan the requested block and every transaction:prevout
matching atxfilter_spend_utxo
entry, return the block.scriptPubKey
matching atxfilter_receive_addr
entry, return the block.scriptpubkey
from thetxfilter_spend_utxo
and append them to thetxfilter_receive_addr
.This allows us to check in our test suite that
lightningd
is maintaining itstxfilter
correctly, using only thebcli
plugin, while allowing address-based SPV interfaces to be used.The text was updated successfully, but these errors were encountered: