Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Complete hybrid full block SPV mode #9483
Conversation
jonasschnelli
added GUI Wallet
labels
Jan 6, 2017
|
(Prefer if we don't propagate the misuse of "SPV" for things that don't support fraud proofs) |
|
I kinda want to use an open lock icon instead of the likely meaningless to uses SPV in any case. We also should do something about the confirmed counts in this mode. I'm not sure what. The issue is that confirmations mean less when you're not validating. Perhaps displaying transactions like they are unconfirmed until they have 6 blocks might be the thing to do. Or displaying a visible "not-verified" on any transaction with the not validated flag. |
|
Indeed, I would assume any mode like this shouldn't count confirmation at all. |
dabura667
commented
Jan 7, 2017
|
How about a flag for showing confirmations during SPV mode? Default to off. People who understand the implications and just don't want to bother having to search their address on an explorer can enable in the menu / config |
|
@dabura667 Is it sufficient to simply show it in the transaction details dialog, perhaps? |
dabura667
commented
Jan 7, 2017
|
@luke-jr I would think so, yes. |
molxyz
commented
Jan 8, 2017
|
@molxyz I've also noticed the same thing, confirmations in Transactions list don't update, unless I restart Core. Other than that it works great. I used this for testing: |
|
Thanks for reporting. This seems to be a UI update issue. Will fix it in the next overhaul / PR update. |
|
Thought: Can this be made to work with external wallets/software? |
I don't know what you mean by this. |
jonasschnelli
referenced
this pull request
Jan 24, 2017
Closed
[WIP][Experimental] Add Hybrid full block SPV mode #9076
|
Needs rebase. What happens with -autorequestblocks=0 -spv=0? I don't know it seems overly complicated. I thought we would just have a single param spvonly that defaults to 0 (equivalent to this autorequestblocks, and your spv is always =1, ie spvonly=0 equivalent to -spv=1 -autorequestblocks=1, spvonly=1 equivalent to -spv=1 -autorequestblocks=0). Not sure, just thinking out loud. |
jonasschnelli
added some commits
Nov 15, 2016
|
Rebased.
This would result in a mode where no blocks are automatically requested (only headers are fetched). |
jonasschnelli
added some commits
Dec 20, 2016
|
Adapted to work with bumpfee. |
ryanofsky
reviewed
Jan 24, 2017
Just started reviewing this. Plan to review more this week but posting a few comments now, all minor.
| @@ -141,6 +141,9 @@ class CWalletDB : public CDB | ||
| bool WriteBestBlock(const CBlockLocator& locator); | ||
| bool ReadBestBlock(CBlockLocator& locator); | ||
| + bool WriteNonValidationBestBlock(const CBlockLocator& locator); | ||
| + bool ReadNonValidationBestBlock(CBlockLocator& locator); |
ryanofsky
Jan 24, 2017
•
Contributor
It would be nice if you could choose one of the terms "nonvalidation" "nonvalidated" and "nvs" and use it consistently everywhere in the code (personally I like "nonvalidated"). Right now it seems like everything is named randomly and it's hard to remember what things are called.
| @@ -3085,13 +3122,16 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg | ||
| } | ||
| // Not in the mempool anymore? don't bother sending it. |
ryanofsky
Jan 24, 2017
Contributor
Comment is out of date. Could you update it to mention that this now checks the wallet, and also mention why it does this (maybe it should be obvious, but the reason isn't clear to me yet).
| @@ -1165,11 +1175,31 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) | ||
| } | ||
| } | ||
| -void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) | ||
| +void CWallet::UpdatedBlockHeaderTip(bool fInitialDownload, const CBlockIndex *pindexNew) |
ryanofsky
Jan 24, 2017
Contributor
fInitialDownload isn't actually used, though maybe it is worth keeping if you plan to unify with the NotifyHeaderTip signal in the future.
| + const CBlockIndex *pindexFork = headersChainActive.FindFork(pNVSLastKnownBestHeader); | ||
| + if (headersChainActive.Tip() && headersChainActive.Tip() != pindexFork) | ||
| + { | ||
| + pNVSLastKnownBestHeader = const_cast<CBlockIndex *>(pindexFork); |
ryanofsky
Jan 24, 2017
Contributor
I think you can just make pNVSLastKnownBestHeader and pNVSBestBlock into const pointers to avoid these const_casts.
| @@ -1182,6 +1212,14 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, | ||
| } | ||
| } | ||
| +void CWallet::GetNonMempoolTransaction(const uint256 &hash, CTransactionRef &txsp) |
ryanofsky
Jan 24, 2017
•
Contributor
Any reason not to call this FindTransaction like the signal which it binds to? FindTransaction definitely seems like a better name given the implementation because if this method is called, it will return a transaction whether it is in the mempool or not.
| + map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hash); | ||
| + if (mi != mapWallet.end()) | ||
| + { | ||
| + txsp = MakeTransactionRef(mi->second); |
| @@ -45,6 +45,9 @@ class CAuxiliaryBlockRequest : public std::enable_shared_from_this<CAuxiliaryBlo | ||
| /** returns the amount of already loaded/local-stored blocks from this blockrequest */ | ||
| unsigned int amountOfBlocksLoaded(); | ||
| + /** returns true if all blocks have been downloaded & processed */ | ||
| + bool isCompleted(); |
ryanofsky
Feb 9, 2017
Contributor
Should probably squash this commit (Little CAuxiliaryBlockRequest refactor) into commit Add CAuxiliaryBlockRequest, a class to handle auxiliary blocks downloads to avoid code churn in the review.
| /** Constructor of the lock free CAuxiliaryBlockRequest, vBlocksToDownloadIn remains constant */ | ||
| - CAuxiliaryBlockRequest(std::vector<const CBlockIndex*> vBlocksToDownloadIn, int64_t created, const std::function<bool(std::shared_ptr<CAuxiliaryBlockRequest>, const CBlockIndex *pindex)> progressCallbackIn); | ||
| + CAuxiliaryBlockRequest(std::vector<const CBlockIndex*> vBlocksToDownloadIn, int64_t created, bool passThroughSignalsIn, const std::function<bool(std::shared_ptr<CAuxiliaryBlockRequest>, const CBlockIndex *pindex)> progressCallbackIn); |
ryanofsky
Feb 9, 2017
Contributor
Probably should just squash this "CBlockRequest: make SyncTransaction() optional" commit into "Add CAuxiliaryBlockRequest, a class to handle auxiliary blocks downloads" to avoid churn.
| +#include <vector> | ||
| + | ||
| +// "Lock free" auxiliary block request | ||
| +class CAuxiliaryBlockRequest : public std::enable_shared_from_this<CAuxiliaryBlockRequest> { |
ryanofsky
Feb 9, 2017
Contributor
I think it would be better if this class didn't exist, and the logic to download and process these blocks was integrated into the normal network processing logic instead of being segregated. This could mean:
- Getting rid of the new
currentBlockRequestglobal variable. Instead just add a simpledeque<const CBlockIndex*> blocksToDownloadFirstor similar variable innet_processing.cppalongside related variables likemapBlocksInFlight. - Replacing
CAuxiliaryBlockRequest::progressCallbackwith an ordinary validationinterface signal. - Moving logic currently in
CAuxiliaryBlockRequest::fillInNextBlockstoFindNextBlocksToDownload. Instead of having a convoluted control flow whereFindNextBlocksToDownloadcallsfillInNextBlockswhich calls back to aFindNextBlocksToDownloadclosure just to push a fewCBlockIndexpointers onto a vector, you could use a regular for loop to fill the vector. - Moving logic currently in
CAuxiliaryBlockRequest::processWithPossibleBlockintoProcessNewBlockor into an analog ofActivateBestChaincalled byProcessNewBlockthat sends the needed wallet and ui notifications for unvalidated blocks in the same way thatActivateBestChaindoes for validated blocks.
| @@ -231,7 +232,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; | ||
| * @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call |
| + // chain-tip to calculate the block height | ||
| + CChain *chainToUse = &chainActive; | ||
| + if (calcHeightFromHeaders && headersChainActive.Tip()) | ||
| + chainToUse = &headersChainActive; |
ryanofsky
Feb 9, 2017
Contributor
headersChainActive variable doesn't seem to be defined yet. Should reorder this commit ("Allow CheckFinalTx() without validation using the headers chain") after "Add CChain object for headers-only chain."
| + // Try to process all requested blocks that we don't have, but only | ||
| + // process an unrequested block if it's new and has enough work to | ||
| + // advance our tip, and isn't too many blocks ahead. | ||
| + bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; |
ryanofsky
Feb 9, 2017
Contributor
Do you really want to skip this whole section just because blockRequest is not null? E.g. wouldn't it make sense to avoid WriteBlockToDisk below when fAlreadyHave is true like this is doing?
Also it's not clear to me whether BLOCK_FAILED_VALID and setDirtyBlockIndex below should be updated for blockRequest blocks. If you know that they should not be updated, it would be useful to have a comment explaining why.
| @@ -30,7 +30,7 @@ class PeerLogicValidation : public CValidationInterface { | ||
| public: | ||
| PeerLogicValidation(CConnman* connmanIn); | ||
| - virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock); | ||
| + virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock, bool validated); |
ryanofsky
Feb 9, 2017
Contributor
Why change signature of SyncTransaction when the value of the new argument can already be derived from pindex? It seems like it would make handling code clearer if the validated condition were written out there where it is actually used, instead of determined by the networking code and then passed along.
| @@ -27,6 +27,9 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); | ||
| static const bool DEFAULT_AUTOMATIC_BLOCK_REQUESTS = true; | ||
| extern std::atomic<bool> fAutoRequestBlocks; | ||
| +static const bool DEFAULT_FETCH_BLOCKS_WHILE_FETCH_HEADERS = true; | ||
| +extern std::atomic<bool> fFetchBlocksWhileFetchingHeaders; |
ryanofsky
Feb 9, 2017
Contributor
Maybe rename fFetchBlocksWhileFetchingHeaders to fRequestBlocksWhileFetchingHeaders for consistency with fAutoRequestBlocks.
| @@ -1251,9 +1252,12 @@ UniValue getchaintips(const JSONRPCRequest& request) | ||
| } else if (block->nStatus & BLOCK_FAILED_MASK) { | ||
| // This block or one of its ancestors is invalid. | ||
| status = "invalid"; | ||
| - } else if (block->nChainTx == 0) { | ||
| + } else if (headersChainActive.Contains(block)) { |
ryanofsky
Feb 9, 2017
Contributor
Seems like it makes sense to return "headers-only-fork" when block->nChainTx == 0 && !headersChainActive.Contains(block), but I don't see how it makes sense to return "headers-only" when block->nChainTx != 0 && headersChainActive.Contains(block) instead of valid-fork or valid-headers when those conditions apply.
Maybe this should be changed to:
} else if (block->nChainTx == 0) {
// This block cannot be connected because full block data for it or one of its parents is missing.
status = headersChainActive.Contains(block) ? "headers-only-fork" : "headers-only";
} ...
| @@ -1511,6 +1512,30 @@ UniValue requestblocks(const JSONRPCRequest& request) | ||
| throw JSONRPCError(RPC_INVALID_PARAMETER, "Unkown action"); | ||
| } | ||
| +UniValue setautorequestblocks(const JSONRPCRequest& request) | ||
| +{ | ||
| + if (request.fHelp || request.params.size() > 1) |
| @@ -1413,6 +1419,98 @@ UniValue reconsiderblock(const JSONRPCRequest& request) | ||
| return NullUniValue; | ||
| } | ||
| +UniValue requestblocks(const JSONRPCRequest& request) |
ryanofsky
Feb 9, 2017
Contributor
Can you explain what the the use-cases for this api are, and also what the use cases for the cancellation and SyncTransaction-suppressing options are? It doesn't seem good that this RPC can interfere with block download in spv mode and prevent transactions from getting to the wallet, but I can't figure out if this is designed to interact this way intentionally or if it is something that should be fixed.
jonasschnelli
Jul 11, 2017
Member
The use cases for the RPC requestblocks API:
You start your peer with auto-download-blocks = false, you will only sync the headers then. You can selectively download blocks and eventually pass them through the signal (== ZMQ), use cases: experiments, SPV, light-client backend, ideally if you have a full validated node within your network and you want to selectively load blocks from that node (you don't want to validate everything again).
| + ret.pushKV("request_present", (bool)blockRequest); | ||
| + if (blockRequest) { | ||
| + ret.pushKV("created", UniValue(blockRequest->created)); | ||
| + ret.pushKV("is_cancled", UniValue(blockRequest->isCancelled())); |
ryanofsky
Feb 9, 2017
Contributor
Maybe change "is_cancled" to "cancelled" (to be consistent with "created"). Spelling error is also above in rpc documentation.
| + std::vector<const CBlockIndex*> blocksToDownload; | ||
| + { | ||
| + LOCK(cs_main); //mapBlockIndex | ||
| + for (UniValue strHashU : hash_Uarray.getValues()) |
| + if (request.params.size() == 3 && request.params[2].isBool()) | ||
| + passThroughSignals = request.params[2].get_bool(); | ||
| + | ||
| + std::shared_ptr<CAuxiliaryBlockRequest> blockRequest(new CAuxiliaryBlockRequest(blocksToDownload, GetAdjustedTime(), passThroughSignals, [](std::shared_ptr<CAuxiliaryBlockRequest> cb_spvRequest, const CBlockIndex *pindex) -> bool { |
| + if (request.params.size() == 3 && request.params[2].isBool()) | ||
| + passThroughSignals = request.params[2].get_bool(); | ||
| + | ||
| + std::shared_ptr<CAuxiliaryBlockRequest> blockRequest(new CAuxiliaryBlockRequest(blocksToDownload, GetAdjustedTime(), passThroughSignals, [](std::shared_ptr<CAuxiliaryBlockRequest> cb_spvRequest, const CBlockIndex *pindex) -> bool { |
ryanofsky
Feb 9, 2017
Contributor
Could just pass {} or nullptr for progress function instead of writing out the long lambda declaration.
| + std::shared_ptr<CAuxiliaryBlockRequest> blockRequest(new CAuxiliaryBlockRequest(blocksToDownload, GetAdjustedTime(), passThroughSignals, [](std::shared_ptr<CAuxiliaryBlockRequest> cb_spvRequest, const CBlockIndex *pindex) -> bool { | ||
| + return true; | ||
| + })); | ||
| + bool overwrite = (CAuxiliaryBlockRequest::GetCurrentRequest() != nullptr); |
ryanofsky
Feb 9, 2017
Contributor
There's a race condition here if the state changes between the GetCurrentRequest() and setAsCurrentRequest() calls. Could easily be avoided by having setAsCurrentRequest return the overwrite bool, or a pointer to the previous request.
| { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} }, | ||
| + { "blockchain", "requestblocks", &requestblocks, true, {"action", "blockhashes", "pass-internally"} }, |
ryanofsky
Feb 9, 2017
Contributor
Probably should use "pass_internally" (underscore instead of dash) so the argument can be a valid identifier in python and other languages.
| @@ -1169,6 +1169,9 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, | ||
| { | ||
| LOCK2(cs_main, cs_wallet); | ||
| + if (!validated) |
ryanofsky
Feb 9, 2017
Contributor
Would be good to squash this "[Wallet] don't consume non-validated transactions" commit into the "Pass CBlockRequest blocks through SyncTransaction signal" to make commit order less fragile and review more straightforward.
| @@ -1185,6 +1185,14 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, | ||
| } | ||
| } | ||
| +void CWallet::GetNonMempoolTransaction(const uint256 &hash, CTransactionRef &txsp) |
| + if (headersChainActive.Tip() && headersChainActive.Tip() != pindexFork) | ||
| + { | ||
| + // fork detected | ||
| + // TODO |
ryanofsky
Feb 9, 2017
Contributor
Probably should just squash this commit (Keep track of the headers chain tip to detect forks) together with commit Add full working SPV mode to the wallet to avoid the code churn here.
| + } | ||
| + } | ||
| + } | ||
| + pLastKnownBestHeader = (CBlockIndex *)pindexNew; |
ryanofsky
Feb 9, 2017
Contributor
Should just make pLastKnownBestHeader a pointer to const to avoid the need for this cast.
| - // fork detected | ||
| - // TODO | ||
| - } | ||
| + pNVSLastKnownBestHeader = const_cast<CBlockIndex *>(pindexFork); |
ryanofsky
Feb 9, 2017
Contributor
Const casts here should not be necessary if you declare pNVSLastKnownBestHeader and pNVSLastKnownBestHeader as pointers to const CBlockIndex objects.
| @@ -1947,7 +1944,7 @@ CAmount CWallet::GetUnconfirmedBalance() const | ||
| for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | ||
| { | ||
| const CWalletTx* pcoin = &(*it).second; | ||
| - if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) | ||
| + if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && (!pcoin->fValidated || pcoin->InMempool())) |
ryanofsky
Feb 9, 2017
Contributor
!pcoin->fValidated seems like it might be too lax a condition, because the transaction could be from an orphaned block.
And it's unclear why this change would have any desirable effect given that GetDepthInMainChain is updated in this commit to search headersChainActive, so presumably any nonvalidated transaction in headersChainActive would return true for IsTrusted.
| @@ -3897,6 +3914,95 @@ bool CWallet::ParameterInteraction() | ||
| return true; | ||
| } | ||
| +void CWallet::RequestNonValidationScan(int64_t optional_timestamp) |
ryanofsky
Feb 9, 2017
Contributor
Eliminate optional_timestamp argument? I don't see any calls where optional_timestamp is nonzero.
| @@ -3897,6 +3914,95 @@ bool CWallet::ParameterInteraction() | ||
| return true; | ||
| } | ||
| +void CWallet::RequestNonValidationScan(int64_t optional_timestamp) | ||
| +{ | ||
| + if (CAuxiliaryBlockRequest::GetCurrentRequest() && !CAuxiliaryBlockRequest::GetCurrentRequest()->isCompleted()) |
ryanofsky
Feb 9, 2017
Contributor
There's a race condition here where GetCurrentRequest() can return null between the first and second calls.
| + | ||
| + CBlockIndex *pIndex = NULL; | ||
| + CBlockIndex *chainActiveTip = NULL; | ||
| + int64_t oldest_key = std::numeric_limits<int64_t>::max();; |
ryanofsky
Feb 9, 2017
Contributor
double semicolon here, also probably should choose consistently between snake_case and camelCase for local variable names.
| + pIndex = headersChainActive.Tip(); | ||
| + std::map<CKeyID, int64_t> mapKeyBirth; | ||
| + GetKeyBirthTimes(mapKeyBirth); | ||
| + for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { |
| + std::map<CKeyID, int64_t> mapKeyBirth; | ||
| + GetKeyBirthTimes(mapKeyBirth); | ||
| + for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { | ||
| + if ((*it).second < oldest_key) |
| + } | ||
| + } | ||
| + | ||
| + if (optional_timestamp > 0) |
ryanofsky
Feb 9, 2017
Contributor
As noted above, this case never seems to happen. But if it could happen, it would seem to negate all the code right above this, so maybe that code should be moved into an else.
| + | ||
| + // ensure we only request up to nMaxBlocksPerAuxiliaryRequest | ||
| + if (blocksToDownload.size() > nMaxBlocksPerAuxiliaryRequest) | ||
| + blocksToDownload.erase(blocksToDownload.begin()); |
ryanofsky
Feb 9, 2017
Contributor
Could switch to from vector to deque, or treat blocksToDownload as a circular buffer and call std::rotate after the loop to avoid O(n^2) cost of erasing from the beginning of a vector in a loop.
| + // reverse the blocks vector from older->newer | ||
| + std::reverse(blocksToDownload.begin(), blocksToDownload.end()); | ||
| + // create an auxiliary block request | ||
| + std::shared_ptr<CAuxiliaryBlockRequest> auxiliaryRequest(new CAuxiliaryBlockRequest(blocksToDownload, GetAdjustedTime(), true, [this](std::shared_ptr<CAuxiliaryBlockRequest> cb_AuxiliaryBlockRequest, const CBlockIndex *pindex) -> bool { |
| + if (pindex && (!pNVSBestBlock || pindex->nHeight > pNVSBestBlock->nHeight)) | ||
| + { | ||
| + // write non validation best block | ||
| + pNVSBestBlock = const_cast<CBlockIndex *>(pindex); |
ryanofsky
Feb 9, 2017
Contributor
No need for const_cast if you just make pNVSBestBlock a const pointer.
| + if (!EnsureWalletIsAvailable(request.fHelp)) | ||
| + return NullUniValue; | ||
| + | ||
| + if (request.fHelp || request.params.size() > 1) |
| + ); | ||
| + | ||
| + if (request.params.size() == 1) | ||
| + pwalletMain->setSPVEnabled(request.params[0].get_bool()); |
ryanofsky
Feb 9, 2017
Contributor
Seems incongruous that unlike the setting -spv option, enabling spv via RPC does not update fFetchBlocksWhileFetchingHeaders
| @@ -1151,7 +1152,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) | ||
| { | ||
| const CWalletTx& wtx = (*it).second; | ||
| - if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx)) | ||
| + if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx, -1, wtx.fValidated)) |
ryanofsky
Feb 9, 2017
Contributor
Seems to be a bug, this should probably say !wtx.fValidated.
This CheckFinalTx(*wtx.tx, -1, !wtx.fValidated) pattern is repeated enough times that I think it would be better if it were wrapped in a method:
bool CWalletTx::CheckFinal() const {
return CheckFinalTx(*tx, -1, !fValidated);
}
| + | ||
| +void WalletModel::setSpvEnabled(bool state) | ||
| +{ | ||
| + wallet->setSPVEnabled(state); |
ryanofsky
Feb 9, 2017
Contributor
Seems incongruous that unlike setting the -spv option, toggling the spv mode in the GUI does not update fFetchBlocksWhileFetchingHeaders.
| @@ -507,6 +509,10 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con | ||
| return; | ||
| } | ||
| + // don't request any other blocks if we are in non autorequest mode (usefull for non-validation mode) |
| @@ -3187,7 +3188,9 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg | ||
| // Message: getdata (blocks) | ||
| // | ||
| vector<CInv> vGetData; | ||
| - if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | ||
| + if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER | ||
| + && (fFetchBlocksWhileFetchingHeaders || (headersChainActive.Tip() && headersChainActive.Tip()->GetBlockTime() > GetAdjustedTime()-600*24)) |
ryanofsky
Feb 9, 2017
Contributor
I think it would make the logic of this PR (and the code) clearer if both fFetchBlocksWhileFetchingHeaders and fAutoRequestBlocks flags were both handled inside of FindNextBlocksToDownload, instead of one flag handled inside, and one outside.
| @@ -3187,7 +3188,9 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg | ||
| // Message: getdata (blocks) | ||
| // | ||
| vector<CInv> vGetData; | ||
| - if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | ||
| + if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER | ||
| + && (fFetchBlocksWhileFetchingHeaders || (headersChainActive.Tip() && headersChainActive.Tip()->GetBlockTime() > GetAdjustedTime()-600*24)) |
| @@ -435,6 +435,7 @@ std::string HelpMessage(HelpMessageMode mode) | ||
| strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); | ||
| strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); | ||
| strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)"); | ||
| + strUsage += HelpMessageOpt("-autorequestblocks", strprintf("Automatic block request, if disabled, blocks will not be requested in IBD/sync-up (default: %u)", DEFAULT_AUTOMATIC_BLOCK_REQUESTS)); |
ryanofsky
Feb 9, 2017
Contributor
Need to update the man page, too, I believe. Maybe also worth documenting that when -autorequestblocks is disabled, it only prevents blocks downloaded as part of the normal sync. It doesn't prevent downloading of blocks newer than the oldest wallet key in the -spv syncing code.
| @@ -760,10 +760,30 @@ | ||
| </layout> | ||
| </item> | ||
| <item> | ||
| + <widget class="QLabel" name="fallbackFeeWarningLabel"> | ||
| + <property name="toolTip"> | ||
| + <string>Using the fallbackfee can result in sending a transaction that will take serval hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain.</string> |
|
I reviewed all the commits and left many minor comments, but I have two broader concerns about this PR that may be worth some discussion:
Similarly, I don't think it makes sense to have a so-called "SPV mode" in the wallet that just reflects the If changing the wallet SPV mode toggled the Apart from externally visible names, naming in the code should definitely be made more consistent. If there was a global search and replace in this PR to change all occurrences of "nonvalidation," "nvs," "headers only," "spv," "hybrid," and "auxilliary" with just "nonvalidated" it would help a lot with readability, because the seemingly random choices of names make the implementation seem more haphazard than it needs to be. |
|
To summarize my feedback above, here's what I think ideally would be next steps for this PR:
|
This was referenced Mar 22, 2017
|
@ryanofsky: Thanks for your review and sorry for the late response.
Any objections calling this SPV mode? |
jonasschnelli
referenced
this pull request
Jul 11, 2017
Open
Add simple light-client mode (RPC only) #10794
See #10794 (comment)
I don't like it, but I wouldn't object to a useful feature because it has a confusing name, and my complaints above are more about naming inconsistency than about this name in particular. Also, I wish you would respond to my some of my suggestions in detail. I wasn't suggesting renaming "SPV mode" to "non-validating mode" or to "client-mode" (I don't even know where "client-mode" comes from). I suggested renaming the |
|
@ryanofsky In early 2011, there was an incomplete feature in the codebase called "client mode", which probably was intended to be some sort of SPV version. It never got finished, and was eventually removed. |
|
That's interesting. I don't think client is a bad name either (seems pretty innocuous). I just hadn't heard it before. |

jonasschnelli commentedJan 6, 2017
•
edited
This is the complete patch-set for the hybrid full block SPV mode.
If one enables the SPV mode with
-spv=1it does...validated = false(visible inlisttransactionsetc).Pure full block SPV mode is possible by setting
-autorequestblocks=0, in that mode, no blocks for validating the chain will be downloaded, resulting in a SPV only mode.For better testing, this PR also includes a bump to 0.0005 for the default fallback fee.
Including all required GUI changes and RPC tests:
Screenshots: