Page MenuHomePhabricator

Calculated nBits by UpdateTime() will be replaced by following code
AbandonedPublic

Authored by qshuai on Apr 24 2018, 07:44.

Details

Reviewers
deadalnix
jasonbcox
Group Reviewers
Restricted Owners Package(Owns No Changed Paths)
Restricted Project
Test Plan

On testnet, the function GetNextWorkRequired() will exec twice and gets the same result. On the other hand, UpdateTime() function should remain single role for updating timestamp item of block.

Diff Detail

Repository
rABC Bitcoin ABC
Branch
master
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 2376
Build 2882: Bitcoin ABC Buildbot (legacy)
Build 2881: arc lint + arc unit

Event Timeline

Owners added a reviewer: Restricted Owners Package.Apr 24 2018, 07:44

There are two reasons to remove the code(as you see):
The calculated nBits will be replaced by the following GetNextWorkRequired(.) function. So calculating difficulty(nBits) in UpdateTime() function is meaningless. The main chain does not matter. But testnet will exec twice.
UpdateTime() function should remain single role for updating timestamp item of block.

I've already reviewed this and it looks good to me, but I want to get a second pair of eyes on it since I'm not as familiar with the miner code. @deadalnix @schancel

deadalnix requested changes to this revision.Apr 25 2018, 01:10

This is breaking mining on testnet.

This revision now requires changes to proceed.Apr 25 2018, 01:10

If updating the time has an impact on something, then it is correct for the function updating time to also update that thing. The alternative is for the function to leave the block template in an invalid state, which bring zero benefit - or if it does, it needs to be justified.

jasonbcox requested changes to this revision.Apr 25 2018, 02:47

So I don't think it breaks mining on testnet. I'm not seeing the codepath that would break it. I do agree with Amaury that conceptual correctness of the UpdateTime() function is essential, especially if there will be new callers of this function in the future. However, the fact that GetNextWorkRequired() is being called twice with no benefit is a bad code smell. At this time, I'm not sure what refactor would be required to make it clean.

@deadalnix @jasonb The following picture shows UpdateTime() function calling.

WX20180425-114504.png (315×470 px, 21 KB)
I think my diff do not break testnet. As to testnet, if CreateNewBlock() function not be called, the block's nBits item will not be updated, and it's nBits item using the previous pblocktemplate's. I mean the pblocktemplate's nBits item should not be updated until receiving a new block from network.
The following code is the condition of calling CreateNewBlock() function :

if (pindexPrev != chainActive.Tip() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) {
    ...
    pblocktemplate = BlockAssembler(config, Params()).CreateNewBlock(scriptDummy);
    ...
}

Ok, so I was traveling but now I'm back and I can expand a bit more on the reasoning here.

First, you are correct pointing out that the nBits is computed twice in that specific case and that the computation is somewhat redundant. This shows that there is a probably design problem somewhere. You are correct so far.

On the other hand, the reasoning you make to justify the change assume that the caller of UpdateTime behaves in a specific way. This is really bad as it makes the code fragile. The reasoning made is that it is okay to put a giant bear trap somewhere in the garden because nobody is walking that path anyway. Eventually, someone is going to change something and fall into the trap. In that specific case, the trap is that the UpdateTime function can render invalid some valid template that is passed to it. It is not acceptable.

So lesson n°1 here: Do not make assumption about the caller behavior in some piece of code with a public API. If it is possible to use that code wrong, it *WILL* be used wrong, it's just a matter of time.

Or to quote John Carmack:

I noticed that each time PVS-Studio was updated, it found something in our codebase with the new rules. This seems to imply that if you have a large enough codebase, any class of error that is syntactically legal probably exists there. In a large project, code quality is every bit as statistical as physical material properties – flaws exist all over the place, you can only hope to minimize the impact they have on your users.

See http://www.gamasutra.com/view/news/128836/InDepth_Static_Code_Analysis.php for more.

So what's the problem here ? The problem is that updating the time and updating the nBits fields do not seem to be orthogonal operations. One has impact on the other. So rather than trying to make them orthogonal - as you propose to do, you should embrace the fact they aren't. A way to do this is to change the UpdateTime function into something like UpdateTimeAndTarget and do both in there.Call site can then be cleaned up, nBits and timestamp can be made private fields in the template structure, with a getter for each, and UpdateTimeAndTarget can become a member function that set both. This would create an API such as it become very difficult to make a mistake there, as well as make it clear who's responsibility it is to compute the difficulty. That's what comes to mind right now, I'm not sure of the implications of such an approach, it may have downside, but this is the general direction such a fix should go into.