How findstake is determined


#1

[this is part of a developers guide to be shared as I go along and learn the nitty-gritty of peercoins protocol myself. If I had access to the wiki I would have pasted it there]

In short, a blob of data is double sha256-hashed and compared to a target. A stake is found when the hash is lower than the target.

bytes short descr description
0-7 StakeModifier will decribe later
8-11 Block time unix time of block that contains the tx
12-15 Tx previous offset the position of previous Tx within the Block blob size
16-19 Tx previous time timestamp of Tx’s previous Tx
20-23 Tx previous index index of Tx’s previous Tx
24-27 a future time to be varied like a nounce
The target = Stakeage offsetted by 30 days but capped at 60 * systemTarget as calculated from the last added pos block’s difficulty of the chain.

The blob is sha256 hashed twice, then reversed in order which results in a 32 bytes blob and converted back to a biginteger.

A stake is found when the hash is lower than the target. So this explains why having a large coinage is easier to find a stake.

StakeModifier
Each (pos?)block contains a 8 bytes long modifier property. The StakeModifier to be used for find stakes is not the modifier of the block where the transaction resides, but the first Pos block that is 761920 (+1) seconds older later down the chain. I guess this is a security measure to counter the myths about nothing at stake. (ppc version 3)
UPDATE
stakemodifiers changes not per block but per time intervals, so 40-60 blocks together can have the same modifier. To get the stakemodier, not only 761920 seconds must have passed, but also the modifier must have changed.

A Block blob consist of the following:
4 version uint32_t Block version information
32 prev_block char[32] The hash value of the previous block this particular block references
32 merkle_root char[32] The reference to a Merkle tree
4 timestamp uint32_t A Unix timestamp recording when this block was created
4 bits uint32_t The calculated difficulty target being used for this block
4 nonce uint32_t The nonce used to generate this block
1 // TxnCount
? blob of 1 or more tx[]

A Tx blob consist of the following:
4 version uint32_t Transaction data format version
4 tx timestamp
1 tx_in count, number of transaction inputs
? TxIn blob
1 tx_out count, number of transaction outputs
? TxOut blob
4 lock time
1 Signature length

TxIn consists of the following fields:
32 Previous output hash (OutPoint)
4 Prevous output index (OutPoint)
1 length of signature script
? signature script (coinbase)
4 Sequence

The TxOut structure consists of the following fields:
8 Transaction amount
1 Length of the pk_script
? pk_script Usually contains the public key as a Bitcoin script setting up conditions to claim this output.

here is an example:
var blockOneBytes = []byte{
0x01, 0x00, 0x00, 0x00, // Version 1
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44,
0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67,
0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1,
0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot
0x61, 0xbc, 0x66, 0x49, // Timestamp
0xff, 0xff, 0x00, 0x1d, // Bits
0x01, 0xe3, 0x62, 0x99, // Nonce
0x01, // TxnCount
0x01, 0x00, 0x00, 0x00, // Version
0x61, 0xbc, 0x66, 0x49, // Time
0x01, // Varint for number of transaction inputs
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
0xff, 0xff, 0xff, 0xff, // Prevous output index
0x07, // Varint for length of signature script
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script (coinbase)
0xff, 0xff, 0xff, 0xff, // Sequence
0x01, // Varint for number of transaction outputs
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
0xee, // 65-byte uncompressed public key
0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time
0x00, // Varint for Signature length
}

So to sum up, the Offset used in findstake can be calculated as follows: (4+32+32+4+4+4+1) + (all Tx sizes before the Tx in question)
Sizes, modifier and tx sequences can be obtained via:

Variable length integer
Integer can be encoded depending on the represented value to save space. Variable length integers always precede an array/vector of a type of data that may vary in length. Longer numbers are encoded in little endian.

Value Storage length Format
< 0xfd 1 uint8_t
<= 0xffff 3 0xfd followed by the length as uint16_t
<= 0xffffffff 5 0xfe followed by the length as uint32_t

  • 9 0xff followed by the length as uint64_t

So the tx-Offset is calculated as follows: 80+(1 or 3 or 5 or 9) + the sizes of preceding tx’s


To conclude, I think it is entirely possible to finish thefindstakeJS project! :))

What's in 0.3.0 release:

Stake generation protocol upgrade (protocol switch March 20th)
Qt UI support
Fix compatibility with vanitygen (note: private keys dumped in v0.2 is no longer importable into v0.3.0, must dump again from v0.3.0 client)
Miscellaneous bug fixes and improvements

The protocol upgrade in 0.3.0 includes a new algorithm to derive proof-of-stake hash modifier, the entity that scrambles computation for stake owners, which replaces the current proof-of-stake difficulty used as modifier in 0.2 protocol. The design was started late September last year, when I first began to realize the issues with using difficulty as modifier. Honorary mention also goes to Jutarul, who independently discovered and verified an issue with using difficulty as modifier and published on bitcointalk in December last year, while successfully executed a demo attack on the block chain. Other changes in the protocol include starting hash weight from 0 at the 30-day mininum age, and requirement that coinstake timestamp must equal block timestamp. Overall 0.3 protocol should significantly strengthen the proof-of-stake protection and resolve the current known vulnerabilities.


Alex's technical questions
#2

thehuntergames, this is very useful information!!! Thank you so much for sharing this!

Could you explain your methodology to extract these info from the code? I’ve started setting up Eclipse and imported Peercoin’s project in it. I’m also planning to explore Peercoin’s code myself, there are many questions that I have and remain unanswered, such as WHERE in the code it is written that the PoW:PoS ratio is kept at 1:5.5 (c.f. observations here).

I’m looking forward for more. And heavily support your initiative. :wink:
This must be written in a Wiki for sure.


#3

I find reading go script easier than c++, so this iswhere i started:


At the moment I am figuring out a way to get all this data needed for my findstakejs project through web apis. Still thinking about the offset value


As for the ratio, i ve analysed the GetNextTargetRequired function thoroughly and wrote a post about it somewhere during that peak:

unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
if (pindexLast == NULL)
return bnProofOfWorkLimit.GetCompact(); // genesis block

const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
if (pindexPrev->pprev == NULL)
    return GetInitialTarget(fProofOfStake); // first block
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
if (pindexPrevPrev->pprev == NULL)
    return GetInitialTarget(fProofOfStake); // second block

int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();

// peercoin: target change every block
// peercoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
bnNew.SetCompact(pindexPrev->nBits);
int64 nTargetSpacing = fProofOfStake? STAKE_TARGET_SPACING : min(nTargetSpacingWorkMax, (int64) STAKE_TARGET_SPACING * (1 + pindexLast->nHeight - pindexPrev->nHeight));
int64 nInterval = nTargetTimespan / nTargetSpacing;
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
bnNew /= ((nInterval + 1) * nTargetSpacing);

if (bnNew > bnProofOfWorkLimit)
    bnNew = bnProofOfWorkLimit;

return bnNew.GetCompact();

}

My method to understand other peeps code is to fill in all constants with numbers and fill in a spreadsheet for those variables that can vary, draw a chart if necessary and voila.

basically, i concluded that ideally the system tries to have 10 or 11 pos blocks between 2 pow blocks, with pos blocks intervalled at 10 minutes.

I know when statiscally looking at the total number of blocks it is 1:5.5, but if looked at newly created blocks of say the last 250 blocks or 24 hours, the ratio is more what the code tries to archive…


#4

Thank you thehuntergames, this is awesome work. I wish kac- and Mably can join you on this task to document the code.

This is very useful information that you provide here and helps me a lot to understand the code. However I think it’s best to rely on the official C++ implementation, even if it may be hard to understand, and maybe use ppcd written in Go to help like you did.

[quote=“thehuntergames, post:3, topic:3276”]basically, i concluded that ideally the system tries to have 10 or 11 pos blocks between 2 pow blocks, with pos blocks intervalled at 10 minutes.

I know when statiscally looking at the total number of blocks it is 1:5.5, but if looked at newly created blocks of say the last 250 blocks or 24 hours, the ratio is more what the code tries to archive…[/quote]

This is very strange, do you think this is because too few are minting and too many are mining?


#5

[quote=“thehuntergames, post:1, topic:3276”]StakeModifier
Each (pos?)block contains a 8 bytes long modifier property. The StakeModifier to be used for find stakes is not the modifier of the block where the transaction resides, but the first Pos block that is 761920 (+1) seconds older down the chain. I guess this is a security measure to counter the myths about nothing at stake. (ppc version 3)[/quote]

Can you explain more about stake modifier? This is one of the things I only partly understand? It takes some pieces of the block that are closest to 791920 seconds previous?


#6

sure

All block have a modifier property even pow blocks. (See http://ppc.blockr.io/block/info/146895 under extras, displayed as hex value)

To obtain the StakeModifier: of that block 146895 do (or better yet a binary search):
Block 146895 was created at timestamp: 2014-12-05 22:03:16 and currently it is late enough to confidently find its stakemodifier

  • get the next block data and see if the block

    • is a pos block
    • has a blocktime 761920 seconds later than 2014-12-05 22:03:16
  • rinse and repeat the above if not found yet

if found its stakemodifier to used for stake hashing is the modifier of that first block that passes the conditions.

source: https://github.com/mably/btcchain/blob/master/kernel.go#L407


#7

[quote=“thehuntergames, post:6, topic:3276”]sure

All block have a modifier property even pow blocks. (See http://ppc.blockr.io/block/info/146895 under extras, displayed as hex value)

To obtain the StakeModifier: of that block 146895 do (or better yet a binary search):
Block 146895 was created at timestamp: 2014-12-05 22:03:16 and currently it is late enough to confidently find its stakemodifier

  • get the next block data and see if the block

    • is a pos block
    • has a blocktime 761920 seconds later than 2014-12-05 22:03:16
  • rinse and repeat the above if not found yet

if found its stakemodifier to used for stake hashing is the modifier of that first block that passes the conditions.

source: https://github.com/mably/btcchain/blob/master/kernel.go#L407[/quote]

Still hard for me to understand a bit. Am looking through the code to help get a grasp. So does this mean that the coin cannot stake until 761920 seconds have passed?


#8

no you need to wait 30 days after your transaction for to have any coinage

this thread is about how to get the data needed for the hash calculation.

There is a list available of the calculated stakemodifiers up to block 142000

https://onedrive.live.com/redir?resid=DB7D019CE8BD8E40!106

still working on finishing the findstakejs project, be patient…


#9

To be added in the Wiki! wiki.peercointalk.org


#10

For those who think findstakejs will end with the upcoming v05, I have good news.

Upon studying the code changes I think it is still possible to findstake up to 30-9=21 days into the future. :smiley:

To be exact it is up to 2592000-761920=1,830,080 seconds later than last known block time but who is counting right?

Whats needed is to collect all stakemodifiers of past say 23 days.
One way to do this is to use http://ppc.blockr.io/api/v1/block/info/last but requires way too many requests to get all blocks of the past 23 days.

When collecting is done and you have a lookuptable of modifiers along with their times,

EDIT: see also http://oi57.tinypic.com/qxlbn6.jpg
the findstake process can begin. The difference in being using a constant stakemodifier, now you just need to lookup which stakemodifier to use depending on the current future timestamp. This due to the ‘sliding’ modifier v05 will have in place.

so:
21 days from now you would use: converttoepoch(2015-10-24T05:17:08Z) ==> 765edf8004b40874 as the stakemodifer for the function CheckStakeKernelHash.

hope this clarifies the process

cheerios


#11

Good news. I haved findstake to find quite a few blocks. But due to laziness I have forgotten to turn on my minter in time often ::slight_smile: . It has to happen right? Now I have a peerbox running 7/24. Looking into 21 days in the future is pretty good considering that longer prediction comes with biggerr forgetting chance…


#12

Looking at Update #9: Peercoin Team Releases Full Roadmap for 2018-2020 (Infographic Included) I have a sense the staking logic will be a little different as the timestamp of a previous tx is needed in the blob:

16-19 Tx previous time timestamp of Tx’s previous Tx

Am I correct to guess this will be replaced by the blocktime of that tx in version 0.7?


#13

Yes, txns will inherit the block’s timestamp. It is more similar to how bitcoin does it.