dcrd/blockchain/validate_test.go
Dave Collins 0536ed4db6
blockchain: Combine ErrDoubleSpend & ErrMissingTx.
This replaces the ErrDoubleSpend and ErrMissingTx error codes with a
single error code named ErrMissingTxOut and updates the relevant errors
and expected test results accordingly.

Once upon a time, the code relied on a transaction index, so it was able
to definitively differentiate between a transaction output that
legitimately did not exist and one that had already been spent.

However, since the code now uses a pruned utxoset, it is no longer
possible to reliably differentiate since once all outputs of a
transaction are spent, it is removed from the utxoset completely.
Consequently, a missing transaction could be either because the
transaction never existed or because it is fully spent.

Also, while here, consistently use the LookupEntry function on the
UtxoView instead of directly accessing the internal map as intended.
2018-02-21 14:01:05 -06:00

2209 lines
79 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"bytes"
"compress/bzip2"
"encoding/binary"
"encoding/gob"
"encoding/hex"
"os"
"path/filepath"
"testing"
"time"
"github.com/decred/dcrd/blockchain"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/dcrutil"
"github.com/decred/dcrd/txscript"
"github.com/decred/dcrd/wire"
)
// recalculateMsgBlockMerkleRootsSize recalculates the merkle roots for a msgBlock,
// then stores them in the msgBlock's header. It also updates the block size.
func recalculateMsgBlockMerkleRootsSize(msgBlock *wire.MsgBlock) {
tempBlock := dcrutil.NewBlock(msgBlock)
merkles := blockchain.BuildMerkleTreeStore(tempBlock.Transactions())
merklesStake := blockchain.BuildMerkleTreeStore(tempBlock.STransactions())
msgBlock.Header.MerkleRoot = *merkles[len(merkles)-1]
msgBlock.Header.StakeRoot = *merklesStake[len(merklesStake)-1]
msgBlock.Header.Size = uint32(msgBlock.SerializeSize())
}
// updateVoteCommitments updates all of the votes in the passed block to commit
// to the previous block and height specified by the header.
func updateVoteCommitments(msgBlock *wire.MsgBlock) {
for _, stx := range msgBlock.STransactions {
if !stake.IsSSGen(stx) {
continue
}
// Generate and set the commitment.
var commitment [36]byte
copy(commitment[:], msgBlock.Header.PrevBlock[:])
binary.LittleEndian.PutUint32(commitment[32:], msgBlock.Header.Height-1)
pkScript, _ := txscript.GenerateProvablyPruneableOut(commitment[:])
stx.TxOut[0].PkScript = pkScript
}
}
// TestBlockValidationRules unit tests various block validation rules.
// It checks the following:
// 1. ProcessBlock
// 2. CheckWorklessBlockSanity
// 3. CheckConnectBlock (checkBlockSanity and checkBlockContext as well)
//
// The tests are done with a pregenerated simnet blockchain with two wallets
// running on it:
//
// 1: erase exodus rhythm paragraph cleanup company quiver opulent crusade
// Ohio merit recipe spheroid Pandora stairway disbelief framework component
// newborn monument tumor supportive wallet sensation standard frequency accrue
// customer stapler Burlington klaxon Medusa retouch
//
// 2: indulge hazardous bombast tobacco tunnel Pandora hockey whimsical choking
// Wilmington jawbone revival beaming Capricorn gazelle armistice beaming company
// scenic pedigree quadrant hamburger Algol Yucatan erase impetus seabird
// hemisphere drunken vacancy uncut caretaker Dupont
func TestBlockValidationRules(t *testing.T) {
// Update simnet parameters to reflect what is expected by the legacy
// data.
params := cloneParams(&chaincfg.SimNetParams)
params.GenesisBlock.Header.MerkleRoot = *mustParseHash("a216ea043f0d481a072424af646787794c32bcefd3ed181a090319bbf8a37105")
genesisHash := params.GenesisBlock.BlockHash()
params.GenesisHash = &genesisHash
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := chainSetup("validateunittests", params)
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
// The genesis block should fail to connect since it's already inserted.
err = chain.CheckConnectBlock(dcrutil.NewBlock(params.GenesisBlock),
blockchain.BFNone)
if err == nil {
t.Errorf("CheckConnectBlock: Did not receive expected error")
}
// Load up the rest of the blocks up to HEAD~1.
filename := filepath.Join("testdata/", "blocks0to168.bz2")
fi, err := os.Open(filename)
if err != nil {
t.Errorf("Failed to open %s: %v", filename, err)
}
bcStream := bzip2.NewReader(fi)
defer fi.Close()
// Create a buffer of the read file
bcBuf := new(bytes.Buffer)
bcBuf.ReadFrom(bcStream)
// Create decoder from the buffer and a map to store the data
bcDecoder := gob.NewDecoder(bcBuf)
blockChain := make(map[int64][]byte)
// Decode the blockchain into the map
if err := bcDecoder.Decode(&blockChain); err != nil {
t.Errorf("error decoding test blockchain: %v", err.Error())
}
// Insert blocks 1 to 142 and perform various test. Block 1 has
// special properties, so make sure those validate correctly first.
block1Bytes := blockChain[int64(1)]
timeSource := blockchain.NewMedianTime()
// ----------------------------------------------------------------------------
// ErrBlockOneOutputs 1
// No coinbase outputs check can't trigger because it throws an error
// elsewhere.
// ----------------------------------------------------------------------------
// ErrBlockOneOutputs 2
// Remove one of the premine outputs and make sure it fails.
noCoinbaseOuts1 := new(wire.MsgBlock)
noCoinbaseOuts1.FromBytes(block1Bytes)
noCoinbaseOuts1.Transactions[0].TxOut =
noCoinbaseOuts1.Transactions[0].TxOut[:2]
recalculateMsgBlockMerkleRootsSize(noCoinbaseOuts1)
b1test := dcrutil.NewBlock(noCoinbaseOuts1)
err = blockchain.CheckWorklessBlockSanity(b1test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrBlockOneOutputs test 2: %v", err)
}
err = chain.CheckConnectBlock(b1test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBlockOneOutputs {
t.Errorf("Got no error or unexpected error for ErrBlockOneOutputs "+
"test 2: %v", err)
}
// ----------------------------------------------------------------------------
// ErrBlockOneOutputs 3
// Bad pay to hash.
noCoinbaseOuts1 = new(wire.MsgBlock)
noCoinbaseOuts1.FromBytes(block1Bytes)
noCoinbaseOuts1.Transactions[0].TxOut[0].PkScript[8] ^= 0x01
recalculateMsgBlockMerkleRootsSize(noCoinbaseOuts1)
b1test = dcrutil.NewBlock(noCoinbaseOuts1)
err = blockchain.CheckWorklessBlockSanity(b1test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrBlockOneOutputs test 3: %v", err)
}
err = chain.CheckConnectBlock(b1test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBlockOneOutputs {
t.Errorf("Got no error or unexpected error for ErrBlockOneOutputs "+
"test 3: %v", err)
}
// ----------------------------------------------------------------------------
// ErrBlockOneOutputs 4
// Bad pay to amount.
noCoinbaseOuts1 = new(wire.MsgBlock)
noCoinbaseOuts1.FromBytes(block1Bytes)
noCoinbaseOuts1.Transactions[0].TxOut[0].Value--
recalculateMsgBlockMerkleRootsSize(noCoinbaseOuts1)
b1test = dcrutil.NewBlock(noCoinbaseOuts1)
err = blockchain.CheckWorklessBlockSanity(b1test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrBlockOneOutputs test 4: %v", err)
}
err = chain.CheckConnectBlock(b1test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBlockOneOutputs {
t.Errorf("Got no error or unexpected error for ErrBlockOneOutputs "+
"test 4: %v", err)
}
// ----------------------------------------------------------------------------
// Add the rest of the blocks up to the stake early test block.
stakeEarlyTest := 142
for i := 1; i < stakeEarlyTest; i++ {
bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)])
if err != nil {
t.Errorf("NewBlockFromBytes error: %v", err.Error())
}
_, _, err = chain.ProcessBlock(bl, blockchain.BFNoPoWCheck)
if err != nil {
t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error())
}
}
// ----------------------------------------------------------------------------
// ErrInvalidEarlyStakeTx
// There are multiple paths to this error, but here we try an early SSGen.
block142Bytes := blockChain[int64(stakeEarlyTest)]
earlySSGen142 := new(wire.MsgBlock)
earlySSGen142.FromBytes(block142Bytes)
ssgenTx, _ := hex.DecodeString("01000000020000000000000000000000000000000" +
"000000000000000000000000000000000ffffffff00ffffffff228896eaaeba3e57d" +
"5e2b7d9e38ed1ce9918db5dd8159620f34c0f1fff860f590000000001ffffffff030" +
"0000000000000000000266a2494f455952f88b4ff019c6673f3f01541b76e700e0c8" +
"a2ab9da000000000000009f86010000000000000000000000046a02010066cf98a80" +
"000000000001abb76a914efb4777e0c06c73883d6104a49739302db8058af88ac000" +
"00000000000000221b683090000000000000000ffffffff04deadbeef4519159f000" +
"000008e6b0100160000006a47304402202cc7a2938643d2e42e4363faa948bd19bfa" +
"d7a4f46ad040c34af9cb5d71b23d20220268189f6fd7ed2a0be573293e8814daac53" +
"01a01787c8d8d346c8b681ed91d4f0121031d90b4e6394841780c7292e385d7161cc" +
"3cacc46c894cbc7b82a2fbcae12e773")
mtxFromB := new(wire.MsgTx)
mtxFromB.FromBytes(ssgenTx)
earlySSGen142.AddSTransaction(mtxFromB)
earlySSGen142.Header.Voters++
recalculateMsgBlockMerkleRootsSize(earlySSGen142)
b142test := dcrutil.NewBlock(earlySSGen142)
err = blockchain.CheckWorklessBlockSanity(b142test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrInvalidEarlyStakeTx {
t.Errorf("Got unexpected no error or wrong error for "+
"ErrInvalidEarlyStakeTx test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrInvalidEarlyVoteBits
earlyBadVoteBits42 := new(wire.MsgBlock)
earlyBadVoteBits42.FromBytes(block142Bytes)
earlyBadVoteBits42.Header.VoteBits ^= 0x80
b142test = dcrutil.NewBlock(earlyBadVoteBits42)
err = blockchain.CheckWorklessBlockSanity(b142test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrInvalidEarlyVoteBits {
t.Errorf("Got unexpected no error or wrong error for "+
"ErrInvalidEarlyVoteBits test: %v", err)
}
err = chain.CheckConnectBlock(b142test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrInvalidEarlyVoteBits {
t.Errorf("Got unexpected no error or wrong error for "+
"ErrInvalidEarlyVoteBits test: %v", err)
}
// ----------------------------------------------------------------------------
// Add blocks up to the first stage of testing.
testsIdx1 := 153
testsIdx2 := 154
testsIdx3 := 166
for i := stakeEarlyTest; i < testsIdx1; i++ {
bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)])
if err != nil {
t.Errorf("NewBlockFromBytes error: %v", err.Error())
}
_, _, err = chain.ProcessBlock(bl, blockchain.BFNoPoWCheck)
if err != nil {
t.Errorf("ProcessBlock error at height %v: %v", i,
err.Error())
}
}
// Make sure the last block validates.
block153, err := dcrutil.NewBlockFromBytes(blockChain[int64(testsIdx1)])
if err != nil {
t.Errorf("NewBlockFromBytes error: %v", err.Error())
}
err = chain.CheckConnectBlock(block153, blockchain.BFNoPoWCheck)
if err != nil {
t.Errorf("CheckConnectBlock error: %v", err.Error())
}
block153Bytes := blockChain[int64(testsIdx1)]
// ----------------------------------------------------------------------------
// ErrBadMerkleRoot 1
// Corrupt the merkle root in tx tree regular
badMerkleRoot153 := new(wire.MsgBlock)
badMerkleRoot153.FromBytes(block153Bytes)
badMerkleRoot153.Header.MerkleRoot[0] ^= 0x01
b153test := dcrutil.NewBlock(badMerkleRoot153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadMerkleRoot {
t.Errorf("Failed to get error or correct error for ErrBadMerkleRoot 1"+
"test (err: %v)", err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadMerkleRoot {
t.Errorf("Failed to get error or correct error for ErrBadMerkleRoot 1"+
"test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrBadMerkleRoot 2
// Corrupt the merkle root in tx tree stake
badMerkleRoot153 = new(wire.MsgBlock)
badMerkleRoot153.FromBytes(block153Bytes)
badMerkleRoot153.Header.StakeRoot[0] ^= 0x01
b153test = dcrutil.NewBlock(badMerkleRoot153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadMerkleRoot {
t.Errorf("Failed to get error or correct error for ErrBadMerkleRoot 2"+
"test (err: %v)", err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadMerkleRoot {
t.Errorf("Failed to get error or correct error for ErrBadMerkleRoot 2"+
"test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrUnexpectedDifficulty
badDifficulty153 := new(wire.MsgBlock)
badDifficulty153.FromBytes(block153Bytes)
badDifficulty153.Header.Bits = 0x207ffffe
b153test = dcrutil.NewBlock(badDifficulty153)
_, _, err = chain.ProcessBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrUnexpectedDifficulty {
t.Errorf("Failed to get error or correct error for "+
"ErrUnexpectedDifficulty test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrWrongBlockSize
badBlockSize153 := new(wire.MsgBlock)
badBlockSize153.FromBytes(block153Bytes)
badBlockSize153.Header.Size = 0x20ffff71
b153test = dcrutil.NewBlock(badBlockSize153)
_, _, err = chain.ProcessBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrWrongBlockSize {
t.Errorf("Failed to get error or correct error for "+
"ErrWrongBlockSize test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrHighHash
badHash153 := new(wire.MsgBlock)
badHash153.FromBytes(block153Bytes)
badHash153.Header.Size = 0x20ffff70
b153test = dcrutil.NewBlock(badHash153)
_, _, err = chain.ProcessBlock(b153test, blockchain.BFNone)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrHighHash {
t.Errorf("Failed to get error or correct error for "+
"ErrHighHash test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrMissingParent
missingParent153 := new(wire.MsgBlock)
missingParent153.FromBytes(block153Bytes)
missingParent153.Header.PrevBlock[8] ^= 0x01
updateVoteCommitments(missingParent153)
recalculateMsgBlockMerkleRootsSize(missingParent153)
b153test = dcrutil.NewBlock(missingParent153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected sanity error for ErrMissingParent test: %v",
err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingParent {
t.Errorf("Got no or unexpected error for ErrMissingParent test %v", err)
}
// ----------------------------------------------------------------------------
// ErrBadCoinbaseValue
badSubsidy153 := new(wire.MsgBlock)
badSubsidy153.FromBytes(block153Bytes)
badSubsidy153.Transactions[0].TxOut[2].Value++
recalculateMsgBlockMerkleRootsSize(badSubsidy153)
b153test = dcrutil.NewBlock(badSubsidy153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected sanity error for ErrBadCoinbaseValue test: %v",
err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadCoinbaseValue {
t.Errorf("Got no or unexpected error for ErrBadCoinbaseValue test %v",
err)
}
// ----------------------------------------------------------------------------
// ErrBadCoinbaseOutpoint/ErrFirstTxNotCoinbase
// Seems impossible to hit this because ErrFirstTxNotCoinbase is hit first.
badCBOutpoint153 := new(wire.MsgBlock)
badCBOutpoint153.FromBytes(block153Bytes)
badCBOutpoint153.Transactions[0].TxIn[0].PreviousOutPoint.Hash[0] ^= 0x01
recalculateMsgBlockMerkleRootsSize(badCBOutpoint153)
b153test = dcrutil.NewBlock(badCBOutpoint153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFirstTxNotCoinbase {
t.Errorf("Got no or unexpected sanity error for "+
"ErrBadCoinbaseOutpoint test: %v", err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil {
t.Errorf("Got unexpected no error for ErrBadCoinbaseOutpoint test")
}
// ----------------------------------------------------------------------------
// ErrBadCoinbaseFraudProof
badCBFraudProof153 := new(wire.MsgBlock)
badCBFraudProof153.FromBytes(block153Bytes)
badCBFraudProof153.Transactions[0].TxIn[0].BlockHeight = 0x12345678
recalculateMsgBlockMerkleRootsSize(badCBFraudProof153)
b153test = dcrutil.NewBlock(badCBFraudProof153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadCoinbaseFraudProof {
t.Errorf("Got no or unexpected sanity error for "+
"ErrBadCoinbaseFraudProof test: %v", err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadCoinbaseFraudProof {
t.Errorf("Got no or unexpected sanity error for "+
"ErrBadCoinbaseFraudProof test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrBadCoinbaseAmountIn
badCBAmountIn153 := new(wire.MsgBlock)
badCBAmountIn153.FromBytes(block153Bytes)
badCBAmountIn153.Transactions[0].TxIn[0].ValueIn = 0x1234567890123456
recalculateMsgBlockMerkleRootsSize(badCBAmountIn153)
b153test = dcrutil.NewBlock(badCBAmountIn153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrBadCoinbaseFraudProof test: %v",
err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadCoinbaseAmountIn {
t.Errorf("Got no or unexpected sanity error for "+
"ErrBadCoinbaseAmountIn test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrBadStakebaseAmountIn
badSBAmountIn153 := new(wire.MsgBlock)
badSBAmountIn153.FromBytes(block153Bytes)
badSBAmountIn153.STransactions[0].TxIn[0].ValueIn = 0x1234567890123456
recalculateMsgBlockMerkleRootsSize(badSBAmountIn153)
b153test = dcrutil.NewBlock(badSBAmountIn153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrBadCoinbaseFraudProof test: %v",
err)
}
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadStakebaseAmountIn {
t.Errorf("Got no or unexpected sanity error for "+
"ErrBadCoinbaseAmountIn test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrRegTxInStakeTree
// Break an SSGen by giving it a non-null outpoint.
badStakebaseOutpoint153 := new(wire.MsgBlock)
badStakebaseOutpoint153.FromBytes(block153Bytes)
badOPHash, _ := chainhash.NewHash(bytes.Repeat([]byte{0x01}, 32))
badStakebaseOutpoint153.STransactions[0].TxIn[0].PreviousOutPoint.Hash =
*badOPHash
recalculateMsgBlockMerkleRootsSize(badStakebaseOutpoint153)
badStakebaseOutpoint153.Header.Voters--
b153test = dcrutil.NewBlock(badStakebaseOutpoint153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrRegTxInStakeTree {
t.Errorf("Failed to get error or correct error for ErrRegTxInStakeTree "+
"test (err: %v)", err)
}
// It hits another error on checkConnectBlock.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil {
t.Errorf("Got unexpected no error for ErrRegTxInStakeTree test")
}
// ----------------------------------------------------------------------------
// ErrStakeTxInRegularTree
// Stick an SSGen in TxTreeRegular.
ssgenInRegular153 := new(wire.MsgBlock)
ssgenInRegular153.FromBytes(block153Bytes)
ssgenInRegular153.AddTransaction(ssgenInRegular153.STransactions[4])
ssgenInRegular153.STransactions = ssgenInRegular153.STransactions[0:3]
ssgenInRegular153.Header.Voters -= 2
recalculateMsgBlockMerkleRootsSize(ssgenInRegular153)
b153test = dcrutil.NewBlock(ssgenInRegular153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrStakeTxInRegularTree {
t.Errorf("Failed to get error or correct error for ErrRegTxInStakeTree "+
"test (err: %v)", err)
}
// Throws bad subsidy error too.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil {
t.Errorf("Got unexpected no error for ErrStakeTxInRegularTree test")
}
// ----------------------------------------------------------------------------
// ErrBadStakebaseScriptLen
badStakebaseSS153 := new(wire.MsgBlock)
badStakebaseSS153.FromBytes(block153Bytes)
badStakebaseSS := bytes.Repeat([]byte{0x01}, 256)
badStakebaseSS153.STransactions[0].TxIn[0].SignatureScript =
badStakebaseSS
recalculateMsgBlockMerkleRootsSize(badStakebaseSS153)
b153test = dcrutil.NewBlock(badStakebaseSS153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadStakebaseScriptLen {
t.Errorf("Failed to get error or correct error for bad stakebase "+
"script len test (err: %v)", err)
}
// This otherwise passes the checks.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadStakebaseScriptLen {
t.Errorf("Failed to get error or correct error for bad stakebase "+
"script len test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrBadStakebaseScrVal
badStakebaseScr153 := new(wire.MsgBlock)
badStakebaseScr153.FromBytes(block153Bytes)
badStakebaseScr153.STransactions[0].TxIn[0].SignatureScript[0] ^= 0x01
recalculateMsgBlockMerkleRootsSize(badStakebaseScr153)
b153test = dcrutil.NewBlock(badStakebaseScr153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadStakebaseScrVal {
t.Errorf("Failed to get error or correct error for bad stakebase "+
"script test (err: %v)", err)
}
// This otherwise passes the checks.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadStakebaseScrVal {
t.Errorf("Failed to get error or correct error for bad stakebase "+
"script test (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrInvalidRevocations
badSSRtxNum153 := new(wire.MsgBlock)
badSSRtxNum153.FromBytes(block153Bytes)
badSSRtxNum153.Header.Revocations = 2
b153test = dcrutil.NewBlock(badSSRtxNum153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrRevocationsMismatch {
t.Errorf("got unexpected no error or other error for "+
"ErrInvalidRevocations sanity check: %v", err)
}
// Fails and hits ErrInvalidRevocations.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrRevocationsMismatch {
t.Errorf("got unexpected no error or other error for "+
"ErrInvalidRevocations sanity check: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSSRtxPayeesMismatch
// Add an extra txout to the revocation.
ssrtxPayeesMismatch153 := new(wire.MsgBlock)
ssrtxPayeesMismatch153.FromBytes(block153Bytes)
ssrtxPayeesMismatch153.STransactions[5].TxOut = append(
ssrtxPayeesMismatch153.STransactions[5].TxOut,
ssrtxPayeesMismatch153.STransactions[5].TxOut[0])
recalculateMsgBlockMerkleRootsSize(ssrtxPayeesMismatch153)
b153test = dcrutil.NewBlock(ssrtxPayeesMismatch153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSSRtxPayeesMismatch sanity "+
"check: %v", err)
}
// Fails and hits ErrSSRtxPayeesMismatch.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSSRtxPayeesMismatch {
t.Errorf("Unexpected no or wrong error for ErrSSRtxPayeesMismatch "+
"test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSSRtxPayees 1
// Corrupt the PKH it pays out to.
badSSRtxPayee153 := new(wire.MsgBlock)
badSSRtxPayee153.FromBytes(block153Bytes)
badSSRtxPayee153.STransactions[5].TxOut[0].PkScript[8] ^= 0x01
recalculateMsgBlockMerkleRootsSize(badSSRtxPayee153)
b153test = dcrutil.NewBlock(badSSRtxPayee153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSSRtxPayees sanity "+
"check 1: %v", err)
}
// Fails and hits ErrSSRtxPayees.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSSRtxPayees {
t.Errorf("Unexpected no or wrong error for ErrSSRtxPayees "+
"test 1: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSSRtxPayees 2
// Corrupt the amount. The transaction can pay (0 ... 20000) and still be
// valid because with the sstxOut.Version set to 0x5400 we can have fees up
// to 2^20 for any SSRtx output.
badSSRtxPayee153 = new(wire.MsgBlock)
badSSRtxPayee153.FromBytes(block153Bytes)
badSSRtxPayee153.STransactions[5].TxOut[0].Value = 20001
recalculateMsgBlockMerkleRootsSize(badSSRtxPayee153)
b153test = dcrutil.NewBlock(badSSRtxPayee153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSSRtxPayees sanity "+
"check 2: %v", err)
}
// Fails and hits ErrSSRtxPayees.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSSRtxPayees {
t.Errorf("Unexpected no or wrong error for ErrSSRtxPayees "+
"test 2: %v", err)
}
// ----------------------------------------------------------------------------
// ErrInvalidSSRtx
invalidSSRtxFor153, _ := hex.DecodeString("0100000001e081ca7481ed46de39e528" +
"8a45b6a3f86c478a6ebc60a4b701c75c1bc900ea8a0000000001ffffffff01db040100" +
"0000000000001abc76a914a495e69ddfe8b9770b823314ba66d4ca0620131088ac0000" +
"00000000000001542c79000000000076000000010000006b483045022100d5b06e2f35" +
"b73eeed8331a482c0b45ab3dc1bd98574ae79afbb80853bdac4735022012ea4ce6177c" +
"76e4d7e9aca0d06978cdbcbed163a89d7fffa5297968227914e90121033147afc0d065" +
"9798f602c92aef634aaffc0a82759b9d0654a5d04c28f3451f76")
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(invalidSSRtxFor153)
badSSRtx153 := new(wire.MsgBlock)
badSSRtx153.FromBytes(block153Bytes)
badSSRtx153.AddSTransaction(mtxFromB)
badSSRtx153.Header.Revocations = 2
recalculateMsgBlockMerkleRootsSize(badSSRtx153)
b153test = dcrutil.NewBlock(badSSRtx153)
err = blockchain.CheckWorklessBlockSanity(b153test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrInvalidSSRtx sanity check: %v",
err)
}
// Fails and hits ErrInvalidSSRtx.
err = chain.CheckConnectBlock(b153test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrInvalidSSRtx {
t.Errorf("Unexpected no or wrong error for ErrInvalidSSRtx test: %v",
err)
}
// ----------------------------------------------------------------------------
// Insert block 154 and continue testing
block153MsgBlock := new(wire.MsgBlock)
block153MsgBlock.FromBytes(block153Bytes)
b153test = dcrutil.NewBlock(block153MsgBlock)
_, _, err = chain.ProcessBlock(b153test, blockchain.BFNoPoWCheck)
if err != nil {
t.Errorf("Got unexpected error processing block 153 %v", err)
}
block154Bytes := blockChain[int64(testsIdx2)]
block154MsgBlock := new(wire.MsgBlock)
block154MsgBlock.FromBytes(block154Bytes)
b154test := dcrutil.NewBlock(block154MsgBlock)
// The incoming block should pass fine.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("Unexpected error for check block 154 sanity: %v", err.Error())
}
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err != nil {
t.Errorf("Unexpected error for check block 154 connect: %v", err.Error())
}
// ----------------------------------------------------------------------------
// ErrNotEnoughStake
notEnoughStake154 := new(wire.MsgBlock)
notEnoughStake154.FromBytes(block154Bytes)
notEnoughStake154.STransactions[5].TxOut[0].Value--
notEnoughStake154.AddSTransaction(mtxFromB)
recalculateMsgBlockMerkleRootsSize(notEnoughStake154)
b154test = dcrutil.NewBlock(notEnoughStake154)
// This fails both checks.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrNotEnoughStake {
t.Errorf("Failed to get error or correct error for low stake amt "+
"test (err: %v)", err)
}
// Throws an error in stake consensus.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil {
t.Errorf("Unexpected error for low stake amt test: %v", err.Error())
}
// ----------------------------------------------------------------------------
// ErrFreshStakeMismatch
badFreshStake154 := new(wire.MsgBlock)
badFreshStake154.FromBytes(block154Bytes)
badFreshStake154.Header.FreshStake++
recalculateMsgBlockMerkleRootsSize(badFreshStake154)
b154test = dcrutil.NewBlock(badFreshStake154)
// Throws an error in stake consensus.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFreshStakeMismatch {
t.Errorf("Unexpected no or wrong error for ErrFreshStakeMismatch "+
"sanity check test: %v", err.Error())
}
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFreshStakeMismatch {
t.Errorf("Unexpected no or wrong error for ErrFreshStakeMismatch "+
"sanity check test: %v", err.Error())
}
// ----------------------------------------------------------------------------
// ErrStakeBelowMinimum still needs to be tested, can't on this blockchain
// because it's above minimum and it'll always trigger failure on that
// condition first.
// ----------------------------------------------------------------------------
// ErrNotEnoughVotes
notEnoughVotes154 := new(wire.MsgBlock)
notEnoughVotes154.FromBytes(block154Bytes)
notEnoughVotes154.STransactions = notEnoughVotes154.STransactions[0:2]
notEnoughVotes154.Header.FreshStake = 0
notEnoughVotes154.Header.Voters = 2
recalculateMsgBlockMerkleRootsSize(notEnoughVotes154)
b154test = dcrutil.NewBlock(notEnoughVotes154)
// Fails and hits ErrNotEnoughVotes.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrNotEnoughVotes {
t.Errorf("Got no or unexpected block sanity err for "+
"not enough votes (err: %v)", err)
}
// ----------------------------------------------------------------------------
// ErrTooManyVotes
invalidSSGenFor154, _ := hex.DecodeString("0100000002000000000000000000000" +
"0000000000000000000000000000000000000000000ffffffff00ffffffff9a4fc238" +
"0060cd86a65620f43af5d641a15c11cba8a3b41cb0f87c2e5795ef590000000001fff" +
"fffff0300000000000000000000266a241cf1d119f9443cd651ef6ff263b561d77b27" +
"426e6767f3a853a2370d588ccf119800000000000000000000000000046a02ffffe57" +
"00bb10000000000001abb76a914e9c66c96902aa5ea1dae549e8bdc01ebc8ff7ae488" +
"ac000000000000000002c5220bb10000000000000000ffffffff04deadbeef204e000" +
"00000000037000000020000006a4730440220329517d0216a0825843e41030f40167e" +
"1a71f7b23986eedab83ad6eaa9aec07f022029c6c808dc18ad59454985108dfeef1c1" +
"a1f1753d07bc5041bb133d0400d294e0121032e1e80b402627c3d60789e8b52d20ae6" +
"c05768c9c8d0a296b4ae6043a1e6a0c1")
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(invalidSSGenFor154)
tooManyVotes154 := new(wire.MsgBlock)
tooManyVotes154.FromBytes(block154Bytes)
tooManyVotes154.AddSTransaction(mtxFromB)
tooManyVotes154.Header.Voters = 6
recalculateMsgBlockMerkleRootsSize(tooManyVotes154)
b154test = dcrutil.NewBlock(tooManyVotes154)
// Fails and hits ErrTooManyVotes.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil {
t.Errorf("got unexpected no error for ErrTooManyVotes sanity check")
}
// ----------------------------------------------------------------------------
// ErrTicketUnavailable
nonChosenTicket154 := new(wire.MsgBlock)
nonChosenTicket154.FromBytes(block154Bytes)
nonChosenTicket154.STransactions[4] = mtxFromB
updateVoteCommitments(nonChosenTicket154)
recalculateMsgBlockMerkleRootsSize(nonChosenTicket154)
b154test = dcrutil.NewBlock(nonChosenTicket154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrTicketUnavailable sanity check"+
": %v",
err)
}
// Fails and hits ErrTooManyVotes.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrTicketUnavailable {
t.Errorf("Unexpected no or wrong error for ErrTicketUnavailable test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrVotesOnWrongBlock
wrongBlockVote154 := new(wire.MsgBlock)
wrongBlockVote154.FromBytes(block154Bytes)
wrongBlockScript, _ := hex.DecodeString("6a24008e029f92ae880d45ae61a5366b" +
"b81d9903c5e61045c5b17f1bc97260f8e54497000000")
wrongBlockVote154.STransactions[0].TxOut[0].PkScript = wrongBlockScript
recalculateMsgBlockMerkleRootsSize(wrongBlockVote154)
b154test = dcrutil.NewBlock(wrongBlockVote154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrVotesOnWrongBlock {
t.Errorf("Unexpected no or wrong error for ErrVotesOnWrongBlock test: %v",
err)
}
// Fails and hits ErrTooManyVotes.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrVotesOnWrongBlock {
t.Errorf("Unexpected no or wrong error for ErrVotesOnWrongBlock test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrVotesMismatch
votesMismatch154 := new(wire.MsgBlock)
votesMismatch154.FromBytes(block154Bytes)
sstxsIn154 := votesMismatch154.STransactions[5:]
votesMismatch154.STransactions = votesMismatch154.STransactions[0:4] // 4 Votes
votesMismatch154.STransactions = append(votesMismatch154.STransactions,
sstxsIn154...)
recalculateMsgBlockMerkleRootsSize(votesMismatch154)
b154test = dcrutil.NewBlock(votesMismatch154)
// Fails and hits ErrVotesMismatch.
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrVotesMismatch {
t.Errorf("got unexpected no or wrong error for ErrVotesMismatch "+
"sanity check: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 1
// Everyone votes Yea, but block header says Nay
badVoteBit154 := new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.Header.VoteBits &= 0xFFFE // Zero critical voteBit
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 1: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 1: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 2
// Everyone votes Nay, but block header says Yea
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.Header.VoteBits = 0x0001
for i, stx := range badVoteBit154.STransactions {
if i < 5 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 2: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 2: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 3
// 3x Nay 2x Yea, but block header says Yea
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.Header.VoteBits = 0x0001
for i, stx := range badVoteBit154.STransactions {
if i < 3 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 3: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 3: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 4
// 2x Nay 3x Yea, but block header says Nay
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.Header.VoteBits = 0x0000
for i, stx := range badVoteBit154.STransactions {
if i < 2 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 4: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 4: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 5
// 4x Voters
// 2x Nay 2x Yea, but block header says Yea
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.STransactions = badVoteBit154.STransactions[0:4] // 4 Votes
badVoteBit154.Header.FreshStake = 0
badVoteBit154.Header.VoteBits = 0x0001
badVoteBit154.Header.Voters = 4
badVoteBit154.Transactions[0].TxOut[0].Value = 3960396039
for i, stx := range badVoteBit154.STransactions {
if i < 2 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 5: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 5: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 6
// 3x Voters
// 2x Nay 1x Yea, but block header says Yea
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.STransactions = badVoteBit154.STransactions[0:3]
badVoteBit154.Header.FreshStake = 0
badVoteBit154.Header.VoteBits = 0x0001
badVoteBit154.Header.Voters = 3
badVoteBit154.Transactions[0].TxOut[0].Value = 2970297029
for i, stx := range badVoteBit154.STransactions {
if i < 2 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 6: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 6: %v", err)
}
// ----------------------------------------------------------------------------
// ErrIncongruentVotebit 7
// 3x Voters
// 1x Nay 2x Yea, but block header says Nay
badVoteBit154 = new(wire.MsgBlock)
badVoteBit154.FromBytes(block154Bytes)
badVoteBit154.STransactions = badVoteBit154.STransactions[0:3]
badVoteBit154.Header.FreshStake = 0
badVoteBit154.Header.VoteBits = 0x0000
badVoteBit154.Header.Voters = 3
badVoteBit154.Transactions[0].TxOut[0].Value = 2970297029
for i, stx := range badVoteBit154.STransactions {
if i < 1 {
// VoteBits is encoded little endian.
stx.TxOut[1].PkScript[2] = 0x00
}
}
recalculateMsgBlockMerkleRootsSize(badVoteBit154)
b154test = dcrutil.NewBlock(badVoteBit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 7: %v", err)
}
// Fails and hits ErrIncongruentVotebit.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrIncongruentVotebit {
t.Errorf("Unexpected no or wrong error for ErrIncongruentVotebit "+
"test 7: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSStxCommitment
badCommitScrB, _ := hex.DecodeString("6a1ea495e69ddfe8b9770b823314ba66d4ca0" +
"6201310540cce08000000001234")
badSStxCommit154 := new(wire.MsgBlock)
badSStxCommit154.FromBytes(block154Bytes)
badSStxCommit154.STransactions[5].TxOut[1].PkScript = badCommitScrB
recalculateMsgBlockMerkleRootsSize(badSStxCommit154)
b154test = dcrutil.NewBlock(badSStxCommit154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSStxCommitment sanity check: %v",
err)
}
// Fails and hits ErrSStxCommitment.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSStxCommitment {
t.Errorf("Unexpected no or wrong error for ErrSStxCommitment test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrInvalidSSGenInput
// It doesn't look like this one can actually be hit since checking if
// IsSSGen should fail first.
// ----------------------------------------------------------------------------
// ErrSSGenPayeeOuts 1
// Corrupt the payee
badSSGenPayee154 := new(wire.MsgBlock)
badSSGenPayee154.FromBytes(block154Bytes)
badSSGenPayee154.STransactions[0].TxOut[2].PkScript[8] ^= 0x01
recalculateMsgBlockMerkleRootsSize(badSSGenPayee154)
b154test = dcrutil.NewBlock(badSSGenPayee154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSSGenPayeeOuts sanity "+
"check: %v", err)
}
// Fails and hits ErrSSGenPayeeOuts.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSSGenPayeeOuts {
t.Errorf("Unexpected no or wrong error for ErrSSGenPayeeOuts "+
"test: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSSGenPayeeOuts 2
// Corrupt the amount
badSSGenPayee154 = new(wire.MsgBlock)
badSSGenPayee154.FromBytes(block154Bytes)
badSSGenPayee154.STransactions[0].TxOut[2].Value += 1
recalculateMsgBlockMerkleRootsSize(badSSGenPayee154)
b154test = dcrutil.NewBlock(badSSGenPayee154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrSSGenPayeeOuts sanity "+
"check2 : %v", err)
}
// Fails and hits ErrSSGenPayeeOuts.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrSSGenPayeeOuts {
t.Errorf("Unexpected no or wrong error for ErrSSGenPayeeOuts "+
"test 2: %v", err)
}
// ----------------------------------------------------------------------------
// ErrSSGenSubsidy
// It appears that ErrSSGenSubsidy is impossible to hit due to the
// check above that returns ErrSSGenPayeeOuts.
// ----------------------------------------------------------------------------
// ErrSStxInImmature
// This is impossible to hit from a block's perspective because the
// ticket isn't in the ticket database. So it fails prematurely.
// ----------------------------------------------------------------------------
// ErrSStxInScrType
// The testbed blockchain doesn't have any non-P2PKH or non-P2SH outputs
// so we can't test this. Independently tested and verified, but should
// eventually get its own unit test.
// ----------------------------------------------------------------------------
// ErrInvalidSSRtxInput
// It seems impossible to hit this from a block test because it fails when
// it can't detect the relevant tickets in the missed ticket database
// bucket.
// ----------------------------------------------------------------------------
// ErrTxSStxOutSpend
// Try to spend a ticket output as a regular transaction.
spendTaggedIn154 := new(wire.MsgBlock)
spendTaggedIn154.FromBytes(block154Bytes)
regularTx154, _ := spendTaggedIn154.Transactions[11].Bytes()
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
sstxTaggedInH, _ := chainhash.NewHashFromStr("83a562e29aad50b8aacb816914da" +
"92a3fa46bea9e8f30b69efc6e64b455f0436")
sstxTaggedIn := new(wire.TxIn)
sstxTaggedIn.BlockHeight = 71
sstxTaggedIn.BlockIndex = 1
sstxTaggedIn.ValueIn = 20000
sstxTaggedIn.SignatureScript = []byte{0x51, 0x51}
sstxTaggedIn.Sequence = 0xffffffff
sstxTaggedIn.PreviousOutPoint.Hash = *sstxTaggedInH
sstxTaggedIn.PreviousOutPoint.Index = 0
sstxTaggedIn.PreviousOutPoint.Tree = 1
mtxFromB.AddTxIn(sstxTaggedIn)
spendTaggedIn154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(spendTaggedIn154)
b154test = dcrutil.NewBlock(spendTaggedIn154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrTxSStxOutSpend sanity check: %v",
err)
}
// Fails and hits ErrTxSStxOutSpend.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrTxSStxOutSpend {
t.Errorf("Unexpected no or wrong error for ErrTxSStxOutSpend test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrRegTxSpendStakeOut
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
scrWithStakeOPCode, _ := hex.DecodeString("ba76a9149fe1d1f7ed3b1d0be66c4b3c" +
"4981ca48b810e9bb88ac")
mtxFromB.TxOut[0].PkScript = scrWithStakeOPCode
spendTaggedOut154 := new(wire.MsgBlock)
spendTaggedOut154.FromBytes(block154Bytes)
spendTaggedOut154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(spendTaggedOut154)
b154test = dcrutil.NewBlock(spendTaggedOut154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrRegTxSpendStakeOut sanity check: %v",
err)
}
// Fails and hits ErrRegTxSpendStakeOut.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrRegTxSpendStakeOut {
t.Errorf("Unexpected no or wrong error for ErrRegTxSpendStakeOut test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrInvalidFinalState
badFinalState154 := new(wire.MsgBlock)
badFinalState154.FromBytes(block154Bytes)
badFinalState154.Header.FinalState[0] ^= 0x01
b154test = dcrutil.NewBlock(badFinalState154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrInvalidFinalState sanity check: %v",
err)
}
// Fails and hits ErrInvalidFinalState.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrInvalidFinalState {
t.Errorf("Unexpected no or wrong error for ErrInvalidFinalState test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrPoolSize
badPoolSize154 := new(wire.MsgBlock)
badPoolSize154.FromBytes(block154Bytes)
badPoolSize154.Header.PoolSize++
b154test = dcrutil.NewBlock(badPoolSize154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrPoolSize sanity check: %v",
err)
}
// Fails and hits ErrPoolSize.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrPoolSize {
t.Errorf("Unexpected no or wrong error for ErrPoolSize test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrBadStakebaseValue doesn't seem be be able to be hit because
// ErrSSGenPayeeOuts is hit first. The code should be kept in in case
// the first check somehow fails to catch inflation.
// ----------------------------------------------------------------------------
// ErrDiscordantTxTree
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxIn[0].PreviousOutPoint.Tree = wire.TxTreeStake
errTxTreeIn154 := new(wire.MsgBlock)
errTxTreeIn154.FromBytes(block154Bytes)
errTxTreeIn154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(errTxTreeIn154)
b154test = dcrutil.NewBlock(errTxTreeIn154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrDiscordantTxTree sanity check: %v",
err)
}
// Fails and hits ErrDiscordantTxTree.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrDiscordantTxTree {
t.Errorf("Unexpected no or wrong error for ErrDiscordantTxTree test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrStakeFees
// It should be impossible for this to ever be triggered because of the
// paranoid around transaction inflation, but leave it in anyway just
// in case there is database corruption etc.
// ----------------------------------------------------------------------------
// ErrBadBlockHeight
badBlockHeight154 := new(wire.MsgBlock)
badBlockHeight154.FromBytes(block154Bytes)
badBlockHeight154.Header.Height++
updateVoteCommitments(badBlockHeight154)
recalculateMsgBlockMerkleRootsSize(badBlockHeight154)
b154test = dcrutil.NewBlock(badBlockHeight154)
// Throws ProcessBlock error through checkBlockContext.
_, _, err = chain.ProcessBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrBadBlockHeight {
t.Errorf("ProcessBlock ErrBadBlockHeight test no or unexpected "+
"error: %v", err)
}
// ----------------------------------------------------------------------------
// ErrNoTax 1
// Tax output missing
taxMissing154 := new(wire.MsgBlock)
taxMissing154.FromBytes(block154Bytes)
taxMissing154.Transactions[0].TxOut[0] = taxMissing154.Transactions[0].TxOut[1]
recalculateMsgBlockMerkleRootsSize(taxMissing154)
b154test = dcrutil.NewBlock(taxMissing154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrNoTax "+
"test 1: %v", err)
}
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrNoTax {
t.Errorf("Got no error or unexpected error for ErrNoTax "+
"test 1: %v", err)
}
// ErrNoTax 2
// Wrong hash paid to
taxMissing154 = new(wire.MsgBlock)
taxMissing154.FromBytes(block154Bytes)
taxMissing154.Transactions[0].TxOut[0].PkScript[8] ^= 0x01
recalculateMsgBlockMerkleRootsSize(taxMissing154)
b154test = dcrutil.NewBlock(taxMissing154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrNoTax test 2: %v", err)
}
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrNoTax {
t.Errorf("Got no error or unexpected error for ErrNoTax "+
"test 2: %v", err)
}
// ErrNoTax 3
// Wrong amount paid
taxMissing154 = new(wire.MsgBlock)
taxMissing154.FromBytes(block154Bytes)
taxMissing154.Transactions[0].TxOut[0].Value--
recalculateMsgBlockMerkleRootsSize(taxMissing154)
b154test = dcrutil.NewBlock(taxMissing154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("Got unexpected error for ErrNoTax test 3: %v", err)
}
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrNoTax {
t.Errorf("Got no error or unexpected error for ErrNoTax "+
"test 3: %v", err)
}
// ----------------------------------------------------------------------------
// ErrExpiredTx
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.Expiry = 154
expiredTx154 := new(wire.MsgBlock)
expiredTx154.FromBytes(block154Bytes)
expiredTx154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(expiredTx154)
b154test = dcrutil.NewBlock(expiredTx154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrExpiredTx sanity check: %v",
err)
}
// Fails and hits ErrExpiredTx.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrExpiredTx {
t.Errorf("Unexpected no or wrong error for ErrExpiredTx test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrFraudAmountIn
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxIn[0].ValueIn--
badValueIn154 := new(wire.MsgBlock)
badValueIn154.FromBytes(block154Bytes)
badValueIn154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(badValueIn154)
b154test = dcrutil.NewBlock(badValueIn154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrFraudAmountIn sanity check: %v",
err)
}
// Fails and hits ErrFraudAmountIn.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFraudAmountIn {
t.Errorf("Unexpected no or wrong error for ErrFraudAmountIn test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrFraudBlockHeight
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxIn[0].BlockHeight++
badHeightProof154 := new(wire.MsgBlock)
badHeightProof154.FromBytes(block154Bytes)
badHeightProof154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(badHeightProof154)
b154test = dcrutil.NewBlock(badHeightProof154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrFraudBlockHeight sanity check: %v",
err)
}
// Fails and hits ErrFraudBlockHeight.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFraudBlockHeight {
t.Errorf("Unexpected no or wrong error for ErrFraudBlockHeight test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrFraudBlockIndex
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxIn[0].BlockIndex++
badIndexProof154 := new(wire.MsgBlock)
badIndexProof154.FromBytes(block154Bytes)
badIndexProof154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(badIndexProof154)
b154test = dcrutil.NewBlock(badIndexProof154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrFraudBlockIndex sanity check: %v",
err)
}
// Fails and hits ErrFraudBlockIndex.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrFraudBlockIndex {
t.Errorf("Unexpected no or wrong error for ErrFraudBlockIndex test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrScriptValidation Reg Tree
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxOut[0].Value--
badScrVal154 := new(wire.MsgBlock)
badScrVal154.FromBytes(block154Bytes)
badScrVal154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(badScrVal154)
b154test = dcrutil.NewBlock(badScrVal154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrScriptValidation sanity check: %v",
err)
}
// Fails and hits ErrScriptValidation.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrScriptValidation {
t.Errorf("Unexpected no or wrong error for ErrScriptValidation test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrScriptValidation Stake Tree
badScrValS154 := new(wire.MsgBlock)
badScrValS154.FromBytes(block154Bytes)
badScrValS154.STransactions[5].TxIn[0].SignatureScript[6] ^= 0x01
recalculateMsgBlockMerkleRootsSize(badScrValS154)
b154test = dcrutil.NewBlock(badScrValS154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrScriptValidation sanity check: %v",
err)
}
// Fails and hits ErrScriptValidation.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrScriptValidation {
t.Errorf("Unexpected no or wrong error for ErrScriptValidation test: %v",
err)
}
// ----------------------------------------------------------------------------
// Invalidate the previous block transaction tree. All the tickets in
// this block reference the previous transaction tree regular, and so
// all should be invalid by missing the tx if the header invalidates the
// previous block.
invalMissingInsS154 := new(wire.MsgBlock)
invalMissingInsS154.FromBytes(block154Bytes)
for i := 0; i < int(invalMissingInsS154.Header.Voters); i++ {
invalMissingInsS154.STransactions[i].TxOut[1].PkScript[2] = 0x00
}
invalMissingInsS154.Header.VoteBits = 0x0000
recalculateMsgBlockMerkleRootsSize(invalMissingInsS154)
b154test = dcrutil.NewBlock(invalMissingInsS154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for invalMissingInsS154 sanity check: %v",
err)
}
// Fails and hits ErrMissingTxOut.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for invalMissingInsS154 test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrScriptMalformed
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
mtxFromB.TxOut[0].PkScript = []byte{0x01, 0x02, 0x03, 0x04}
malformedScr154 := new(wire.MsgBlock)
malformedScr154.FromBytes(block154Bytes)
malformedScr154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(malformedScr154)
b154test = dcrutil.NewBlock(malformedScr154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrScriptValidation sanity check: %v",
err)
}
// Fails and hits ErrScriptMalformed.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrScriptMalformed {
t.Errorf("Unexpected no or wrong error for ErrScriptMalformed test: %v",
err)
}
// ----------------------------------------------------------------------------
// ErrMissingTxOut (formerly ErrZeroValueOutputSpend). In the latest version of
// the database, zero value outputs are automatically pruned, so the output
// is simply missing.
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(regularTx154)
zeroValueTxH, _ := chainhash.NewHashFromStr("9432be62a2c664ad021fc3567c" +
"700239067cfaa59be5b67b5808b158dfaed060")
zvi := new(wire.TxIn)
zvi.BlockHeight = 83
zvi.BlockIndex = 0
zvi.ValueIn = 0
zvi.SignatureScript = []byte{0x51}
zvi.Sequence = 0xffffffff
zvi.PreviousOutPoint.Hash = *zeroValueTxH
zvi.PreviousOutPoint.Index = 1
zvi.PreviousOutPoint.Tree = 1
mtxFromB.AddTxIn(zvi)
spendZeroValueIn154 := new(wire.MsgBlock)
spendZeroValueIn154.FromBytes(block154Bytes)
spendZeroValueIn154.Transactions[11] = mtxFromB
recalculateMsgBlockMerkleRootsSize(spendZeroValueIn154)
b154test = dcrutil.NewBlock(spendZeroValueIn154)
err = blockchain.CheckWorklessBlockSanity(b154test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrZeroValueOutputSpend sanity "+
"check: %v", err)
}
// Fails and hits ErrZeroValueOutputSpend.
err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for "+
"ErrMissingTxOut test: %v", err)
}
// ----------------------------------------------------------------------------
// DoubleSpend/TxTree invalidation edge case testing
//
// Load up to block 166. 165 invalidates its previous tx tree, making
// it good for testing.
for i := testsIdx2; i < testsIdx3; i++ {
bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)])
if err != nil {
t.Errorf("NewBlockFromBytes error: %v", err.Error())
}
// Double check and ensure there's no cross tree spending in
// block 164.
if i == 164 {
for _, stx := range bl.MsgBlock().STransactions {
for j, sTxIn := range stx.TxIn {
for _, tx := range bl.MsgBlock().Transactions {
h := tx.TxHash()
if h == sTxIn.PreviousOutPoint.Hash {
t.Errorf("Illegal cross tree reference ("+
"stx %v references tx %v in input %v)",
stx.TxHash(), h, j)
}
}
}
}
}
_, _, err = chain.ProcessBlock(bl, blockchain.BFNoPoWCheck)
if err != nil {
t.Errorf("ProcessBlock error: %v", err.Error())
}
}
block166Bytes := blockChain[int64(testsIdx3)]
// ----------------------------------------------------------------------------
// Attempt to spend from TxTreeRegular of block 164, which should never
// have existed.
spendFrom164RegB, _ := hex.DecodeString("01000000016a7a4928f20fbdeca6c0dd534" +
"8110d26e7abb91549d846638db6379ecae300f70500000000ffffffff01c095a9" +
"050000000000001976a91487bd9a1466619fa8253baa37ffca87bb5b1892da88a" +
"c000000000000000001ffffffffffffffff00000000ffffffff00")
mtxFromB = new(wire.MsgTx)
mtxFromB.FromBytes(spendFrom164RegB)
spendInvalid166 := new(wire.MsgBlock)
spendInvalid166.FromBytes(block166Bytes)
spendInvalid166.AddTransaction(mtxFromB)
recalculateMsgBlockMerkleRootsSize(spendInvalid166)
b166test := dcrutil.NewBlock(spendInvalid166)
err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrMissingTxOut test 1 sanity "+
"check: %v", err)
}
// Fails and hits ErrMissingTxOut.
err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for "+
"ErrMissingTxOut test 1: %v", err)
}
// ----------------------------------------------------------------------------
// Try to buy a ticket with this block's coinbase transaction, which
// should not be allowed because it doesn't yet exist.
sstxSpendInvalid166 := new(wire.MsgBlock)
sstxSpendInvalid166.FromBytes(block166Bytes)
sstxToUse166 := sstxSpendInvalid166.STransactions[5]
// Craft an otherwise valid sstx.
coinbaseHash := spendInvalid166.Transactions[0].TxHash()
sstxCBIn := new(wire.TxIn)
sstxCBIn.ValueIn = 29702992297
sstxCBIn.PreviousOutPoint.Hash = coinbaseHash
sstxCBIn.PreviousOutPoint.Index = 2
sstxCBIn.PreviousOutPoint.Tree = 0
sstxCBIn.BlockHeight = 166
sstxCBIn.BlockIndex = 0
sstxCBIn.Sequence = 4294967295
sstxCBIn.SignatureScript = []byte{0x51, 0x51}
sstxToUse166.AddTxIn(sstxCBIn)
orgAddr, _ := dcrutil.DecodeAddress("ScuQxvveKGfpG1ypt6u27F99Anf7EW3cqhq")
pkScript, _ := txscript.GenerateSStxAddrPush(orgAddr,
dcrutil.Amount(29702992297), 0x0000)
txOut := wire.NewTxOut(int64(0), pkScript)
sstxToUse166.AddTxOut(txOut)
pkScript, _ = txscript.PayToSStxChange(orgAddr)
txOut = wire.NewTxOut(0, pkScript)
sstxToUse166.AddTxOut(txOut)
recalculateMsgBlockMerkleRootsSize(sstxSpendInvalid166)
b166test = dcrutil.NewBlock(sstxSpendInvalid166)
err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrMissingTxOut test 2 sanity "+
"check: %v", err)
}
// Fails and hits ErrMissingTxOut.
err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for "+
"ErrMissingTxOut test 2: %v", err)
}
// ----------------------------------------------------------------------------
// Try to spend immature change from one SStx in another SStx, hitting
// ErrImmatureSpend.
sstxSpend2Invalid166 := new(wire.MsgBlock)
sstxSpend2Invalid166.FromBytes(block166Bytes)
sstxToUse166 = sstxSpend2Invalid166.STransactions[6]
sstxChangeHash := spendInvalid166.STransactions[5].TxHash()
sstxChangeIn := new(wire.TxIn)
sstxChangeIn.ValueIn = 2345438298
sstxChangeIn.PreviousOutPoint.Hash = sstxChangeHash
sstxChangeIn.PreviousOutPoint.Index = 2
sstxChangeIn.PreviousOutPoint.Tree = 1
sstxChangeIn.BlockHeight = 166
sstxChangeIn.BlockIndex = 5
sstxChangeIn.Sequence = 4294967295
sstxChangeIn.SignatureScript = []byte{0x51, 0x51}
sstxToUse166.AddTxIn(sstxChangeIn)
pkScript, _ = txscript.GenerateSStxAddrPush(orgAddr,
dcrutil.Amount(2345438298), 0x0000)
txOut = wire.NewTxOut(int64(0), pkScript)
sstxToUse166.AddTxOut(txOut)
pkScript, _ = txscript.PayToSStxChange(orgAddr)
txOut = wire.NewTxOut(0, pkScript)
sstxToUse166.AddTxOut(txOut)
recalculateMsgBlockMerkleRootsSize(sstxSpend2Invalid166)
b166test = dcrutil.NewBlock(sstxSpend2Invalid166)
err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for ErrImmatureSpend test sanity "+
"check: %v", err)
}
// Fails and hits ErrMissingTxOut. It may not be immediately clear
// why this happens, but in the case of the stake transaction
// tree, because you can't spend in chains, the txlookup code
// doesn't even bother to populate the spent list in the txlookup
// and instead just writes the transaction hash as being missing.
// This output doesn't become legal to spend until the next block.
err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrImmatureSpend {
t.Errorf("Unexpected no or wrong error for "+
"ErrImmatureSpend test: %v", err)
}
// ----------------------------------------------------------------------------
// Try to double spend the same input in the stake transaction tree.
sstxSpend3Invalid166 := new(wire.MsgBlock)
sstxSpend3Invalid166.FromBytes(block166Bytes)
sstxToUse166 = sstxSpend3Invalid166.STransactions[6]
sstxToUse166.AddTxIn(sstxSpend3Invalid166.STransactions[5].TxIn[0])
sstxToUse166.AddTxOut(sstxSpend3Invalid166.STransactions[5].TxOut[1])
sstxToUse166.AddTxOut(sstxSpend3Invalid166.STransactions[5].TxOut[2])
recalculateMsgBlockMerkleRootsSize(sstxSpend3Invalid166)
b166test = dcrutil.NewBlock(sstxSpend3Invalid166)
err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for double spend test 1 sanity "+
"check: %v", err)
}
// Fails and hits ErrMissingTxOut.
err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for "+
"double spend test 1: %v", err)
}
// ----------------------------------------------------------------------------
// Try to double spend an input in the unconfirmed tx tree regular
// that's already spent in the stake tree.
regTxSpendStakeIn166 := new(wire.MsgBlock)
regTxSpendStakeIn166.FromBytes(block166Bytes)
sstxIn := regTxSpendStakeIn166.STransactions[5].TxIn[0]
regTxSpendStakeIn166.Transactions[2].AddTxIn(sstxIn)
recalculateMsgBlockMerkleRootsSize(regTxSpendStakeIn166)
b166test = dcrutil.NewBlock(regTxSpendStakeIn166)
err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params)
if err != nil {
t.Errorf("got unexpected error for deouble spend test 2 sanity "+
"check: %v", err)
}
// Fails and hits ErrMissingTxOut.
err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck)
if err == nil || err.(blockchain.RuleError).ErrorCode !=
blockchain.ErrMissingTxOut {
t.Errorf("Unexpected no or wrong error for "+
"double spend test 2: %v", err)
}
}
// TestBlockchainSpendJournal tests for whether or not the spend journal is being
// written to disk correctly on a live blockchain.
func TestBlockchainSpendJournal(t *testing.T) {
// Create a new database and chain instance to run tests against.
params := &chaincfg.SimNetParams
chain, teardownFunc, err := chainSetup("spendjournalunittest", params)
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
}
defer teardownFunc()
// The genesis block should fail to connect since it's already
// inserted.
err = chain.CheckConnectBlock(dcrutil.NewBlock(params.GenesisBlock),
blockchain.BFNone)
if err == nil {
t.Errorf("CheckConnectBlock: Did not receive expected error")
}
// Load up the rest of the blocks up to HEAD.
filename := filepath.Join("testdata/", "reorgto179.bz2")
fi, err := os.Open(filename)
if err != nil {
t.Errorf("Failed to open %s: %v", filename, err)
}
bcStream := bzip2.NewReader(fi)
defer fi.Close()
// Create a buffer of the read file
bcBuf := new(bytes.Buffer)
bcBuf.ReadFrom(bcStream)
// Create decoder from the buffer and a map to store the data
bcDecoder := gob.NewDecoder(bcBuf)
blockChain := make(map[int64][]byte)
// Decode the blockchain into the map
if err := bcDecoder.Decode(&blockChain); err != nil {
t.Errorf("error decoding test blockchain: %v", err.Error())
}
// Load up the short chain
finalIdx1 := 179
for i := 1; i < finalIdx1+1; i++ {
bl, err := dcrutil.NewBlockFromBytes(blockChain[int64(i)])
if err != nil {
t.Fatalf("NewBlockFromBytes error: %v", err.Error())
}
_, _, err = chain.ProcessBlock(bl, blockchain.BFNone)
if err != nil {
t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error())
}
}
err = chain.DoStxoTest()
if err != nil {
t.Errorf(err.Error())
}
}
// TestSequenceLocksActive ensure the sequence locks are detected as active or
// not as expected in all possible scenarios.
func TestSequenceLocksActive(t *testing.T) {
now := time.Now().Unix()
tests := []struct {
name string
seqLockHeight int64
seqLockTime int64
blockHeight int64
medianTime int64
want bool
}{
{
// Block based sequence lock with height at min
// required.
name: "min active block height",
seqLockHeight: 1000,
seqLockTime: -1,
blockHeight: 1001,
medianTime: now + 31,
want: true,
},
{
// Time based sequence lock with relative time at min
// required.
name: "min active median time",
seqLockHeight: -1,
seqLockTime: now + 30,
blockHeight: 1001,
medianTime: now + 31,
want: true,
},
{
// Block based sequence lock at same height.
name: "same height",
seqLockHeight: 1000,
seqLockTime: -1,
blockHeight: 1000,
medianTime: now + 31,
want: false,
},
{
// Time based sequence lock with relative time equal to
// lock time.
name: "same median time",
seqLockHeight: -1,
seqLockTime: now + 30,
blockHeight: 1001,
medianTime: now + 30,
want: false,
},
{
// Block based sequence lock with relative height below
// required.
name: "height below required",
seqLockHeight: 1000,
seqLockTime: -1,
blockHeight: 999,
medianTime: now + 31,
want: false,
},
{
// Time based sequence lock with relative time before
// required.
name: "median time before required",
seqLockHeight: -1,
seqLockTime: now + 30,
blockHeight: 1001,
medianTime: now + 29,
want: false,
},
}
for _, test := range tests {
seqLock := blockchain.SequenceLock{
MinHeight: test.seqLockHeight,
MinTime: test.seqLockTime,
}
got := blockchain.SequenceLockActive(&seqLock, test.blockHeight,
time.Unix(test.medianTime, 0))
if got != test.want {
t.Errorf("%s: mismatched seqence lock status - got %v, "+
"want %v", test.name, got, test.want)
continue
}
}
}
// TestCheckBlockSanity tests the context free block sanity checks with blocks
// not on a chain.
func TestCheckBlockSanity(t *testing.T) {
params := &chaincfg.SimNetParams
timeSource := blockchain.NewMedianTime()
block := dcrutil.NewBlock(&badBlock)
err := blockchain.CheckBlockSanity(block, timeSource, params)
if err == nil {
t.Fatalf("block should fail.\n")
}
}
// TestCheckWorklessBlockSanity tests the context free workless block sanity
// checks with blocks not on a chain.
func TestCheckWorklessBlockSanity(t *testing.T) {
params := &chaincfg.SimNetParams
timeSource := blockchain.NewMedianTime()
block := dcrutil.NewBlock(&badBlock)
err := blockchain.CheckWorklessBlockSanity(block, timeSource, params)
if err == nil {
t.Fatalf("block should fail.\n")
}
}
// TestCheckBlockHeaderContext tests that genesis block passes context headers
// because its parent is nil.
func TestCheckBlockHeaderContext(t *testing.T) {
// Create a new database for the blocks.
params := &chaincfg.SimNetParams
dbPath := filepath.Join(os.TempDir(), "examplecheckheadercontext")
_ = os.RemoveAll(dbPath)
db, err := database.Create("ffldb", dbPath, params.Net)
if err != nil {
t.Fatalf("Failed to create database: %v\n", err)
return
}
defer os.RemoveAll(dbPath)
defer db.Close()
// Create a new BlockChain instance using the underlying database for
// the simnet network.
chain, err := blockchain.New(&blockchain.Config{
DB: db,
ChainParams: params,
TimeSource: blockchain.NewMedianTime(),
})
if err != nil {
t.Fatalf("Failed to create chain instance: %v\n", err)
return
}
err = chain.TstCheckBlockHeaderContext(&params.GenesisBlock.Header, nil, blockchain.BFNone)
if err != nil {
t.Fatalf("genesisblock should pass just by definition: %v\n", err)
return
}
// Test failing checkBlockHeaderContext when calcNextRequiredDifficulty
// fails.
block := dcrutil.NewBlock(&badBlock)
newNode := blockchain.TstNewBlockNode(&block.MsgBlock().Header, nil)
err = chain.TstCheckBlockHeaderContext(&block.MsgBlock().Header, newNode, blockchain.BFNone)
if err == nil {
t.Fatalf("Should fail due to bad diff in newNode\n")
return
}
}
// TestTxValidationErrors ensures certain malformed freestanding transactions
// are rejected as as expected.
func TestTxValidationErrors(t *testing.T) {
// Create a transaction that is too large
tx := wire.NewMsgTx()
prevOut := wire.NewOutPoint(&chainhash.Hash{0x01}, 0, wire.TxTreeRegular)
tx.AddTxIn(wire.NewTxIn(prevOut, nil))
pkScript := bytes.Repeat([]byte{0x00}, wire.MaxBlockPayload)
tx.AddTxOut(wire.NewTxOut(0, pkScript))
// Assert the transaction is larger than the max allowed size.
txSize := tx.SerializeSize()
if txSize <= wire.MaxBlockPayload {
t.Fatalf("generated transaction is not large enough -- got "+
"%d, want > %d", txSize, wire.MaxBlockPayload)
}
// Ensure transaction is rejected due to being too large.
err := blockchain.CheckTransactionSanity(tx, &chaincfg.MainNetParams)
rerr, ok := err.(blockchain.RuleError)
if !ok {
t.Fatalf("CheckTransactionSanity: unexpected error type for "+
"transaction that is too large -- got %T", err)
}
if rerr.ErrorCode != blockchain.ErrTxTooBig {
t.Fatalf("CheckTransactionSanity: unexpected error code for "+
"transaction that is too large -- got %v, want %v",
rerr.ErrorCode, blockchain.ErrTxTooBig)
}
}
// badBlock is an intentionally bad block that should fail the context-less
// sanity checks.
var badBlock = wire.MsgBlock{
Header: wire.BlockHeader{
Version: 1,
MerkleRoot: chaincfg.SimNetParams.GenesisBlock.Header.MerkleRoot,
VoteBits: uint16(0x0000),
FinalState: [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Voters: uint16(0x0000),
FreshStake: uint8(0x00),
Revocations: uint8(0x00),
Timestamp: time.Unix(1401292357, 0), // 2009-01-08 20:54:25 -0600 CST
PoolSize: uint32(0),
Bits: 0x207fffff, // 545259519
SBits: int64(0x0000000000000000),
Nonce: 0x37580963,
StakeVersion: uint32(0),
Height: uint32(0),
},
Transactions: []*wire.MsgTx{},
STransactions: []*wire.MsgTx{},
}