Repository navigation
blockchain: move block validation rule tests into fullblocktests (1 of x). - #1060
Conversation
df11353 to
68a4456
Compare
davecgh
left a comment
There was a problem hiding this comment.
Before I dig into the particulars, I noticed the copyright dates on the touched files need to be updated for 2018.
davecgh
left a comment
There was a problem hiding this comment.
I still have to go through each condition more thoroughly, but here is some initial feedback for the first couple of tests.
There was a problem hiding this comment.
Please avoid hard coding things like this since it makes the tests more fragile in the face of code changes. It also would technically allow an extremely remote possibility that the generated block actually has the hard coded hash. Instead, it's better to flip a bit in the script to mirror what might happen with an alpha particle.
There was a problem hiding this comment.
I would suggest using |= here to avoid changing any other bits that might later be used for other purposes and thus could invalidate the block for the wrong reason.
There was a problem hiding this comment.
Rather than repeating the pattern over and over, please create a function similar to ReplaceVotes from chaingen which accepts a specific vote number, version, and bits. I would suggest the following:
// ReplaceVoteBitsN returns a function that itself takes a block and modifies
// it by replacing the vote bits of the vote located at the provided index. It
// will panic if the stake transaction at the provided index is not already a
// vote.
//
// NOTE: This must only be used as a munger to the 'NextBlock' function or it
// will lead to an invalid live ticket pool.
func ReplaceVoteBitsN(voteNum int, voteBits uint16) func(*wire.MsgBlock) {
return func(b *wire.MsgBlock) {
// Attempt to prevent misuse of this function by ensuring the
// provided stake transaction number is actually a vote.
stx := b.STransactions[voteNum]
if !isVoteTx(stx) {
panic(fmt.Sprintf("attempt to replace non-vote "+
"transaction #%d for for block %s", voteNum,
b.BlockHash()))
}
// Extract the existing vote version.
existingScript := stx.TxOut[1].PkScript
var voteVersion uint32
if len(existingScript) >= 8 {
voteVersion = binary.LittleEndian.Uint32(existingScript[4:8])
}
stx.TxOut[1].PkScript = voteBitsScript(voteBits, voteVersion)
}
}That way it uses the existing infrastructure to properly recreate the entire vote script and will panic if it's used improperly to help ensure the tests stay valid.
Then this function will read more like:
b.Header.VoteBits |= 0x0001
const voteBitsNo = 0x000
for i := 0; i < 4; i++ {
chaingen.ReplaceVoteBitsN(i, voteBitsNo)
}There was a problem hiding this comment.
Then the same deal here with reusing the function from above.
a4fafa1 to
b1c604c
Compare
There was a problem hiding this comment.
This is not reliable because the block being voted on could already have a zero there in which case the test would fail with a false positive.
You need to ensure some bits are flipped. So, I would suggest using xor. You could do b.STransactions[0].TxOut[0].PkScript[2] ^= 0x55 to flip every other bit in the byte.
There was a problem hiding this comment.
Total nitpick, but please add periods to these so they're consistent with the rest of the comments.
There was a problem hiding this comment.
I'd personally leave these inner comments off. The intro comment already describes it. Also, the naming is inconsistent since it says "yea" while the variables are named yes. I prefer the yes/no naming.
There was a problem hiding this comment.
Rather than setting a specific number, I would suggest doing b.Header.Revocations++ so that the test will remain valid even if the actual number of revocations somehow ends up being 2 which would create a false positive.
There was a problem hiding this comment.
Rather than setting a specific number, I would suggest doing b.STransactions[0].TxIn[0].ValueIn++ so that the test can't possibly create any false positives if the actual value in somehow ultimately ends up being the magic number.
There was a problem hiding this comment.
Rather than setting a specific number, I would suggest doing b.Header.Size++ so that the test can't possibly create any false positives if the actual value in somehow ultimately ends up being the magic number.
There was a problem hiding this comment.
Rather than setting a specific number, I would suggest doing b.STransactions[0].TxIn[0].ValueIn++ so that the test can't possibly create any false positives if the actual value in somehow ultimately ends up being the magic number.
There was a problem hiding this comment.
There is already a block b56 below that this will collide with.
There was a problem hiding this comment.
The header comment already says this. Not needed.
There was a problem hiding this comment.
b52 can't be changed like this or it will cause the b52a test to not test the intended thing. More generally, you can't reuse any of the same names. I think I'll add an assertion to NextBlock to causes duplicates to fail.
07f122a to
4b3abad
Compare
|
Some additional details: |
There was a problem hiding this comment.
s/with a revocations count mismatch/with a header that commits to more revocations than the block actually contains./
There was a problem hiding this comment.
This works, but I think a better test would be to actually replace the entire script with a completely valid script that votes on bs14 (the block before the one it should be voting on which is bs14).
That way it will ensure it doesn't allow voting on the wrong block even when the block it's voting on is actually valid.
There was a problem hiding this comment.
Technically there is no need to do this. Blocks coming from the generator already vote yes by default. However, I guess it can't hurt to be explicit.
There was a problem hiding this comment.
for i := 0; i < 3; i++ {
g.ReplaceVoteBitsN(i, voteBitNo)(b)
}
for i := 3; i < 5; i++ {
g.ReplaceVoteBitsN(i, voteBitYes)(b)
}The second for loop isn't even needed since they're already yes.
There was a problem hiding this comment.
for i := 0; i < 2; i++ {
g.ReplaceVoteBitsN(i, voteBitNo)(b)
}
for i := 2; i < 5; i++ {
g.ReplaceVoteBitsN(i, voteBitYes)(b)
}The second for loop isn't even needed since they're already yes.
There was a problem hiding this comment.
The arrows on the next line should start after the parens like so:
// ... -> bf1(0) -> bf2(1) -> bf5(2) -> bf6(3)
// \-> bpw1(4)The reason is because then it can be copied and extended like so with the arrows still lining up:
// ... -> bf1(0) -> bf2(1) -> bf5(2) -> bf6(3) -> somethingelse....
// \-> bpw1(4)4b3abad to
7fd2c6f
Compare
There was a problem hiding this comment.
space just before (bpw4 added last)
12d9c29 to
550ebee
Compare
There was a problem hiding this comment.
Exceeds 80.
// Create block with a header that commits to more revocations than the
// block actually contains..There was a problem hiding this comment.
This isn't what I meant. It's a valid vote, but it's consuming a a ticket that is already spent and this is dependent on the order of the checks in the code. Go ahead and revert it to ^= 0x55 for now. I'll have to write some code to expose creating a vote script on an invalid block.
550ebee to
6da8225
Compare
|
Some additional details: |
This PR adds
AssertTipBlockStakeRoothelper and moves the following block rejection cases into fullblocktests.ErrVotesOnWrongBlockErrIncongruentVotebitErrBadMerkleRootErrWrongBlockSizeErrBadCoinbaseAmountInErrBadStakebaseAmountInErrRevocationsMismatchWork towards #1030.