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.
2209 lines
79 KiB
Go
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(¶ms.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{},
|
|
}
|