Skip to content

Conversation

jamescowens
Copy link
Member

@jamescowens jamescowens commented Aug 3, 2018

This PR implements three conf file parameters:

minstakesplitvalue
enablestakesplit
stakingefficiency

enablestakesplit=1 will enable the automatic splitting of UTXO's in the coinstake transaction (stake outputs). Zero is the default (disabled).

stakingefficiency=xx is an integer that specifies the desired staking efficiency. This is constrained by the code to be between 75% and 98%, in case someone puts some nonsense value in there.

minstakesplitvalue=xxx is an integer that specifies the minimum UTXO size desired post split to provide a secondary control on UTXO size. If difficulty drops and a high efficiency is specified, the efficiency alone may split UTXO's into amounts smaller than the user desires. This will prevent that from occurring. If a user specifies less than 800 GRC, then the code uses 800 GRC. Note that the stake splitter uses a 160 block averaging interval for calculating the difficulty to smooth out the difficulty swings.

The code is implemented as a modification to the UTXO push at the end of the CreateCoinStake, and the addition of the Gridcoin reward to the UTXO(s) at the end of CreateGridcoinReward. Note that the code can handle an n-way UTXO split in the case of giant UTXO's that need to be split more than 2-way, but there is a current limit placed of 2-way because of the limitation of the number of Coinstake outputs in AcceptBlock. This may be changed in the future.

This addresses issue #1240.

This PR is already staking successfully on testnet with my testnet Windows node.

@jamescowens jamescowens force-pushed the StakeUTXOSplit branch 4 times, most recently from aa26c36 to 4e0134c Compare August 3, 2018 22:04
@jamescowens
Copy link
Member Author

Here is the stake output...

08/03/18 22:46:26 CreateCoinStake: Found Kernel;
08/03/18 22:46:26 CreateCoinStake: nMinUTXOPostSplitValue = 800.000000
08/03/18 22:46:26 CreateCoinStake: dEfficiency = 0.950000
08/03/18 22:46:26 CreateCoinStake: nDesiredStakeUTXOValue = 800.000000
08/03/18 22:46:26 CreateCoinStake: nUTXOCount = 2
08/03/18 22:46:26 CreateCoinStake: nActualStakeUTXOValue = 1027.658856
08/03/18 22:46:26 CreateCoinStake: create stake UTXO 1 value 1027.658856
08/03/18 22:46:26 CreateCoinStake: create stake UTXO 2 value 1027.658856
08/03/18 22:46:26 CreateCoinStake: added kernel type=1 credit=2055.317712
08/03/18 22:46:26 StakeMiner: created rest of the block
08/03/18 22:46:26 CreateGridcoinReward: for bc0621a4ac4610ffa400a0d298c02e23 mint 35.986201 {RSAWeight 2920.000000} Research 35.986296, Interest 0.000000
08/03/18 22:46:26 CreateGridcoinReward: nUTXOCount = 2
08/03/18 22:46:26 CreateGridcoinReward: nActualStakeUTXOReward = 17.993101
08/03/18 22:46:26 CreateGridcoinReward: added reward to stake UTXO 1 value 17.993101
08/03/18 22:46:26 CreateGridcoinReward: added reward to stake UTXO 2 value 17.993101
08/03/18 22:46:26 StakeMiner: added gridcoin reward to coinstake
08/03/18 22:46:27 AddNeuralContractOrVote: not Needed
08/03/18 22:46:27 StakeMiner: signed boinchash, coinstake, wholeblock
08/03/18 22:46:27 {SBC} new best {4a5d4c796bbfcd00672992f706a62b0490b798434cd87fa7ea6813b130678caa 631293} ;
08/03/18 22:46:27 {PB}: ACC;
08/03/18 22:46:27 StakeMiner: block processed

Copy link
Member

@tomasbrod tomasbrod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split into multiple functions
one that calculates how many outputs to split to
second that does the actual splitting
called from the main miner function

@jamescowens
Copy link
Member Author

That makes it a lot harder Tomas and what does it get you? Those functions would be captive and never used by anything but the miner.

@jamescowens
Copy link
Member Author

jamescowens commented Aug 4, 2018

It will make it prettier... :). Before I go to the trouble to split off into separate functions, do you buy into my implementation concept?

@jamescowens
Copy link
Member Author

Tomas. Looking at this more carefully. I agree with putting the number of UTXO's calculation into a helper funtion. It seems wrong to me to actually create another function to "split" the UTXO's, because they are not really being split. The calculation determines the number to "create" and then push is done to create them. This properly belongs in CreateCoinStake I think... Further, we add the reward to the same UTXO's later in CreateGridcoinReward.

@jamescowens jamescowens force-pushed the StakeUTXOSplit branch 2 times, most recently from 5517a32 to 36aee75 Compare August 4, 2018 22:05
@jamescowens
Copy link
Member Author

Implemented unsigned int GetNumberofStakeOutputs(int64_t nValue). Also changed upper efficiency limit to 98%. 99% is too aggressive and unnecessary.

@tomasbrod
Copy link
Member

This properly belongs in CreateCoinStake I think... Further, we add the reward to the same UTXO's later in CreateGridcoinReward.

This is the reason why i want it split. CreateGridcoinReward is called after CreateCoinStake and would only add reward to the 1nd output.

You are duplicating (not fully) the splitting code both in CreateCoinStake and CreateGridcoinReward. If the splitting was done after all reward manipulation, only once.

@jamescowens
Copy link
Member Author

It adds rewards to both outputs because I put a loop in there. I agree it duplicates the loop. The problem is what it CreateCoinStake doing anymore if it doesn't actually create the UTXO's. I will discuss with you on Slack/Rocket.

@jamescowens
Copy link
Member Author

Ok @tomasbrod. I created void SplitCoinStakeOutput(CBlock &blocknew). Please take a look again. I think this does it...

@@ -600,7 +600,6 @@ bool CreateCoinStake( CBlock &blocknew, CKey &key,

txnew.vout.push_back(CTxOut(0, CScript())); // First Must be empty
txnew.vout.push_back(CTxOut(nCredit, scriptPubKeyOut));
//txnew.vout.push_back(CTxOut(0, scriptPubKeyOut));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You left that code in there commented out, probably from the original write of V8. I figured it should go away.

void SplitCoinStakeOutput(CBlock &blocknew)
{
// When this function is called, CreateCoinStake and CreateGridcoinReward have already been called
// and there will be a single coinstake output (besides the empty one) that has the combined stake + research
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great candidate for assert()ion

Copy link
Member Author

@jamescowens jamescowens Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you suggesting to assert? There is no condition in any of that code that is expected to fail. It is a no-op if the number of UTXO's is 1, and GetNumberofStakeOutputs is clamped. If CreateCoinStake and CreateGridcoinReward have succeeded (because we have arrived at the call to SplitCoinStakeOutput), then CBlock StakeBlock already exists and is in the proper form.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert(blockNew.vtx.size() == 2)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomasbrod I think you actually meant assert(blockNew.vtx[1].vout.size()==2)...

src/kernel.h Outdated
@@ -69,4 +69,6 @@ int64_t CalculateStakeWeightV8(
const CTransaction &CoinTx, unsigned CoinTxN,
const MiningCPID &BoincData);

unsigned int GetNumberofStakeOutputs(int64_t nValue);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definition in miner but declaration in kernel.h? Make it consistent, please.

Copy link
Member Author

@jamescowens jamescowens Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't even have a CreateCoinStake declaration in a header file @tomasbrod ... And for that matter, I didn't make a declaration for SplitCoinStakeOutput... so I need to fix that. I don't even know why the compiler is not barking about it, or about your original CreateCoinStake function that has no declaration. I will move the GetNumberOfStakeOutputs and also add SplitCoinStakeOutput to miner.h.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have declaration, because the function is not used nowhere, but the file it is defined in. Yes, correctly is should have a declaration or be made static. I am commenting on the fact that you put the declaration to kernel.cpp

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do not need declaration if the function is used below it's definition (definition is a declaration)

Copy link
Member

@denravonska denravonska Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually good to leave it out so the user API becomes more clear. It does require to extern declare it in the test suite, so don't make it static. You could argue that you should test through the public API. While true, in this case, I suspect the actual unit test will become too hard to both read and write.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Really hard to unit test this. I moved the declarations to miner.h. Are you guys ok with that?

src/miner.cpp Outdated
// Pull Minimum Post Stake UTXO Split Value from config or command line parameter.
// Do not allow it to be specified below 800 GRC.
nMinUTXOPostSplitValue = GetArg("-minimumstakeutxovalueforsplit", 800) * COIN;
nMinUTXOPostSplitValue = (nMinUTXOPostSplitValue < 800 * COIN) ? 800 * COIN : nMinUTXOPostSplitValue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use std::max

Copy link
Member Author

@jamescowens jamescowens Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything doesn't have to be done with methods @tomasbrod. I will do it, but the request to change is silly. You are lucky. std::max is defined in C++11. Its use here requires a cast of the 800 to int64_t because std::max does not like to compare with different integer types.

if (dEfficiency > 0.98)
dEfficiency = 0.98;
else if (dEfficiency < 0.75)
dEfficiency = 0.75;
Copy link
Member

@tomasbrod tomasbrod Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use std::clamp( val, low, high )
include <algorithm> if not already declared

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can return fast if nValue < nMinUTXOPostSplitValue

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't use std:;clamp. It is C++17 and is not available in many compilers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the fast return and implemented it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate that it took them this long to implement a clamp instead of the ugly std::max(lower, std::min(n, upper)); construct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That construct is uglier to me than doing the clamp with straight if statements... I agree with you!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to change line 715 to a std::max. I will do it in the morning.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

src/miner.cpp Outdated
else if (dEfficiency < 0.75)
dEfficiency = 0.75;

LogPrintf("GetNumberofStakeOutputs: dEfficiency = %f", dEfficiency);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove prints like this, or at least condition under fDebug2/fDebug

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you should remove the original prints from CreateCoinStake, which are similar.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are going to be consistent with the LogPrintf's in CreateCoinStake (at 605 for example), then the ones at 663 and 671 should remain non-conditional. I have conditioned all others on fDebug2.

src/miner.cpp Outdated
// Set Desired UTXO size post stake based on efficiency and difficulty, but do not allow to go below MinUTXOPostSplitValue.
// Note that we use GetAverageDifficulty over a 4 hour (160 block period) rather than StakeKernelDiff, because the block
// to block difficulty has too much scatter.
nDesiredStakeUTXOValue = 9942.2056 * GetAverageDifficulty(160) * (3.0 / 2.0) * (1 / dEfficiency - 1) * COIN;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please link to explanation of the constants (file under doc/ or steemit)
move 9942.2056 constant somewhere up

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point. I can do that, since I did all of these calculations... the problem is that the final blue paper is not done yet, only the initial draft. I will put a link to the initial draft paper.

@tomasbrod
Copy link
Member

I should have done the requested changes instead of talking about them.

Anyway. Let's talk about the config options.
minimumstakeutxovalueforsplit is too long,
calling the other efficiency in config can evoke need for 100%, idk which name would be non biased
The enable switch could be merged with min output tunable:
0-> disabled, non zero -> enabled, >800 -> increases min output
@denravonska what do you think about the option names?

@jamescowens
Copy link
Member Author

jamescowens commented Aug 6, 2018

I don't like combining the tunables. It makes it more cryptic. We have ENOUGH cryptic code in this codebase. Option names should be descriptive of what they do, just like variable names/functions, and they should not be overloaded unless absolutely necessary. I will be happy to shorten them and will take suggestions as long as they are descriptive.

@jamescowens
Copy link
Member Author

I should have done the requested changes instead of talking about them.

@tomasbrod That is insulting to me. Sorry to have wasted your time.

src/miner.cpp Outdated

// Pull Minimum Post Stake UTXO Split Value from config or command line parameter.
// Default to 800 and do not allow it to be specified below 800 GRC.
nMinUTXOPostSplitValue = GetArg("-minimumstakeutxovalueforsplit", (int64_t)800) * COIN;
Copy link
Member

@denravonska denravonska Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small bug here where the lower cap is 800 Halfords, not coins, if min stake value is set to 0. You could also extract the constant to avoid someone changing one but not the other.

namespace
{
    const uint64_t MIN_STAKE_SPLIT_VALUE_GRC = 800;
}

// ...
const int64_t = std::max(
        GetArg("-minimumstakeutxovalueforsplit", MIN_STAKE_SPLIT_VALUE_GRC),
        MIN_STAKE_SPLIT_VALUE_GRC) * COIN;

Edit: MIN_STAKE_SPLIT_VALUE_GRC was a poor name as it's not in coins. Ignore my std::max construct as it made things less readable :D But the bug remains.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u are right. Good catch! I could fix it equivalently by changing line 692 to nMinUTXOPostSplitValue = max(nMinUTXOPostSplitValue, (int64_t)800 * COIN);

which do you think is better?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the small change and let's assume that the next author is a sensible adult.

Copy link
Member Author

@jamescowens jamescowens Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should define a system constant for the 800. Might as well do it now. And the 800 is effectively in GRC so I think your constant name is good! Now... where to put it....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer your approach @denravonska. I am putting the constant in miner.h as

// Note the below constant controls the minimum value allowed for post
// split UTXO size. It is int64_t but in GRC so that it matches the entry in the config file.
// It will be converted to Halfords in GetNumberOfStakeOutputs by multiplying by COIN.
static const int64_t MIN_STAKE_SPLIT_VALUE_GRC = 800;

I have to do it this way to avoid a template error, because the GetArg is int64_t.

@tomasbrod
Copy link
Member

I should have done the requested changes instead of talking about them.

@tomasbrod That is insulting to me. Sorry to have wasted your time.

I tried to applologise for wasting your time by ordering you around to change this and that. Sorry
i need to improve my language skills

@jamescowens
Copy link
Member Author

Then I owe you an apology. I mistook your comment. Just know that I am the type person that likes to finish what I started...

@jamescowens
Copy link
Member Author

Ok. I think everything is in there. The only thing left is to shorten the config params if you both don't like the longer names... :)

@denravonska
Copy link
Member

I would prefer a shorter and more to the point name but I cannot come up with anything better.

@jamescowens
Copy link
Member Author

Me either...

@jamescowens
Copy link
Member Author

I think it is ready for general testnet testing... I have been running in testnet already in the previous guises, and it appears to work nicely.

@denravonska denravonska added this to the Camilla milestone Aug 7, 2018
Copy link
Member

@iFoggz iFoggz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK agree for testing if everyone comes to consensus on moving forward

@jamescowens
Copy link
Member Author

Ok! @barton2526 gave me some inspiration... I have changed the config option names to...

minstakesplitvalue
enablestakesplit
stakingefficiency

Much better!

I think this is ready for testing now!

This modifies the miner to split stake outputs if necessary to optimize UTXO size.
@denravonska
Copy link
Member

Closing in favor of #1265

denravonska added a commit that referenced this pull request Sep 25, 2018
Side staking (extends stake splitting PR #1244)
@tomasbrod
Copy link
Member

merging this as well (no-op code-wise) would grant James bit of stats.

@jamescowens
Copy link
Member Author

I didn't even think about that! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants