fullblocks_test.go raw

   1  package blockchain_test
   2  
   3  import (
   4  	"os"
   5  	
   6  	"github.com/p9c/p9/pkg/database"
   7  	_ "github.com/p9c/p9/pkg/database/ffldb"
   8  	"github.com/p9c/p9/pkg/wire"
   9  )
  10  
  11  const (
  12  	// testDbType is the database backend type to use for the tests.
  13  	testDbType = "ffldb"
  14  	// testDbRoot is the root directory used to create all test databases.
  15  	testDbRoot = "testdbs"
  16  	// blockDataNet is the expected network in the test block data.
  17  	blockDataNet = wire.MainNet
  18  )
  19  
  20  // filesExists returns whether or not the named file or directory exists.
  21  func fileExists(name string) bool {
  22  	var e error
  23  	if _, e = os.Stat(name); E.Chk(e) {
  24  		if os.IsNotExist(e) {
  25  			return false
  26  		}
  27  	}
  28  	return true
  29  }
  30  
  31  // isSupportedDbType returns whether or not the passed database type is currently supported.
  32  func isSupportedDbType(dbType string) bool {
  33  	supportedDrivers := database.SupportedDrivers()
  34  	for _, driver := range supportedDrivers {
  35  		if dbType == driver {
  36  			return true
  37  		}
  38  	}
  39  	return false
  40  }
  41  
  42  // // chainSetup is used to create a new db and chain instance with the genesis block already inserted.  In addition to the new chain instance, it returns a teardown function the caller should invoke when done testing to clean up.
  43  // func chainSetup(	dbName string, netparams *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
  44  // 	if !isSupportedDbType(testDbType) {
  45  // 		return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
  46  // 	}
  47  // 	// Handle memory database specially since it doesn't need the disk specific handling.
  48  // 	var db database.DB
  49  // 	var teardown func()
  50  // 	if testDbType == "memdb" {
  51  // 		ndb, e := database.Create(testDbType)
  52  // 		if e != nil  {
  53  // 			return nil, nil, fmt.Errorf("error creating db: %v", err)
  54  // 		}
  55  // 		db = ndb
  56  // 		// Setup a teardown function for cleaning up.  This function is returned to the caller to be invoked when it is done testing.
  57  // 		teardown = func() {
  58  // 			db.Close()
  59  // 		}
  60  // 	} else {
  61  // 		// Create the root directory for test databases.
  62  // 		if !fileExists(testDbRoot) {
  63  // 			if e := os.MkdirAll(testDbRoot, 0700); E.Chk(e) {
  64  // 				e := fmt.Errorf("unable to create test db "+
  65  // 					"root: %v", err)
  66  // 				return nil, nil, e
  67  // 			}
  68  // 		}
  69  // 		// Create a new database to store the accepted blocks into.
  70  // 		dbPath := filepath.Join(testDbRoot, dbName)
  71  // 		_ = os.RemoveAll(dbPath)
  72  // 		ndb, e := database.Create(testDbType, dbPath, blockDataNet)
  73  // 		if e != nil  {
  74  // 			return nil, nil, fmt.Errorf("error creating db: %v", err)
  75  // 		}
  76  // 		db = ndb
  77  // 		// Setup a teardown function for cleaning up.  This function is returned to the caller to be invoked when it is done testing.
  78  // 		teardown = func() {
  79  // 			db.Close()
  80  // 			os.RemoveAll(dbPath)
  81  // 			os.RemoveAll(testDbRoot)
  82  // 		}
  83  // 	}
  84  // 	// Copy the chain netparams to ensure any modifications the tests do to the chain parameters do not affect the global instance.
  85  // 	paramsCopy := *netparams
  86  // 	// Create the main chain instance.
  87  // 	chain, e := blockchain.New(&blockchain.Config{
  88  // 		DB:          db,
  89  // 		ChainParams: &paramsCopy,
  90  // 		Checkpoints: nil,
  91  // 		TimeSource:  blockchain.NewMedianTime(),
  92  // 		SigCache:    txscript.NewSigCache(1000),
  93  // 	})
  94  // 	if e != nil  {
  95  // 		teardown()
  96  // 		e := fmt.Errorf("failed to create chain instance: %v", err)
  97  // 		return nil, nil, e
  98  // 	}
  99  // 	return chain, teardown, nil
 100  // }
 101  
 102  // // TestFullBlocks ensures all tests generated by the fullblocktests package have the expected result when processed via ProcessBlock.
 103  // func TestFullBlocks(// 	t *testing.T) {
 104  // 	tests, e := fullblocktests.Generate(false)
 105  // 	if e != nil  {
 106  // 		t.Fatalf("failed to generate tests: %v", err)
 107  // 	}
 108  // 	// Create a new database and chain instance to run tests against.
 109  // 	chain, teardownFunc, e := chainSetup("fullblocktest",
 110  // 		&chaincfg.RegressionTestParams)
 111  // 	if e != nil  {
 112  // 		t.Errorf("Failed to setup chain instance: %v", err)
 113  // 		return
 114  // 	}
 115  // 	defer teardownFunc()
 116  // 	// testAcceptedBlock attempts to process the block in the provided test instance and ensures that it was accepted according to the flags specified in the test.
 117  // 	testAcceptedBlock := func(item fullblocktests.AcceptedBlock) {
 118  // 		blockHeight := item.Height
 119  // 		block := util.NewBlock(item.Block)
 120  // 		block.SetHeight(blockHeight)
 121  // 		t.Logf("Testing block %s (hash %s, height %d)",
 122  // 			item.Name, block.Hash(), blockHeight)
 123  // 		isMainChain, isOrphan, e := chain.ProcessBlock(block,
 124  // 			blockchain.BFNone, block.Height())
 125  // 		if e != nil  {
 126  // 			t.Fatalf("block %q (hash %s, height %d) should "+
 127  // 				"have been accepted: %v", item.Name,
 128  // 				block.Hash(), blockHeight, err)
 129  // 		}
 130  // 		// Ensure the main chain and orphan flags match the values specified in the test.
 131  // 		if isMainChain != item.IsMainChain {
 132  // 			t.Fatalf("block %q (hash %s, height %d) unexpected main "+
 133  // 				"chain flag -- got %v, want %v", item.Name,
 134  // 				block.Hash(), blockHeight, isMainChain,
 135  // 				item.IsMainChain)
 136  // 		}
 137  // 		if isOrphan != item.IsOrphan {
 138  // 			t.Fatalf("block %q (hash %s, height %d) unexpected "+
 139  // 				"orphan flag -- got %v, want %v", item.Name,
 140  // 				block.Hash(), blockHeight, isOrphan,
 141  // 				item.IsOrphan)
 142  // 		}
 143  // 	}
 144  // 	// testRejectedBlock attempts to process the block in the provided test instance and ensures that it was rejected with the reject code specified in the test.
 145  // 	testRejectedBlock := func(item fullblocktests.RejectedBlock) {
 146  // 		blockHeight := item.Height
 147  // 		block := util.NewBlock(item.Block)
 148  // 		block.SetHeight(blockHeight)
 149  // 		t.Logf("Testing block %s (hash %s, height %d)",
 150  // 			item.Name, block.Hash(), blockHeight)
 151  // 		_, _, e = chain.ProcessBlock(
 152  // 			block, blockchain.BFNone, block.Height())
 153  // 		if e ==  nil {
 154  // 			t.Fatalf("block %q (hash %s, height %d) should not "+
 155  // 				"have been accepted", item.Name, block.Hash(),
 156  // 				blockHeight)
 157  // 		}
 158  // 		// Ensure the error code is of the expected type and the reject code matches the value specified in the test instance.
 159  // 		rerr, ok := err.(blockchain.RuleError)
 160  // 		if !ok {
 161  // 			t.Fatalf("block %q (hash %s, height %d) returned "+
 162  // 				"unexpected error type -- got %T, want "+
 163  // 				"blockchain.RuleError", item.Name, block.Hash(),
 164  // 				blockHeight, err)
 165  // 		}
 166  // 		if rerr.ErrorCode != item.RejectCode {
 167  // 			t.Fatalf("block %q (hash %s, height %d) does not have "+
 168  // 				"expected reject code -- got %v, want %v",
 169  // 				item.Name, block.Hash(), blockHeight,
 170  // 				rerr.ErrorCode, item.RejectCode)
 171  // 		}
 172  // 	}
 173  // 	// testRejectedNonCanonicalBlock attempts to decode the block in the provided test instance and ensures that it failed to decode with a message error.
 174  // 	testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) {
 175  // 		headerLen := len(item.RawBlock)
 176  // 		if headerLen > 80 {
 177  // 			headerLen = 80
 178  // 		}
 179  // 		blockHash := chainhash.DoubleHashH(item.RawBlock[0:headerLen])
 180  // 		blockHeight := item.Height
 181  // 		t.Logf("Testing block %s (hash %s, height %d)", item.Name,
 182  // 			blockHash, blockHeight)
 183  // 		// Ensure there is an error due to deserializing the block.
 184  // 		var msgBlock wire.Block
 185  // 		e := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding)
 186  // 		if _, ok := err.(*wire.MessageError); !ok {
 187  // 			t.Fatalf("block %q (hash %s, height %d) should have "+
 188  // 				"failed to decode", item.Name, blockHash,
 189  // 				blockHeight)
 190  // 		}
 191  // 	}
 192  // 	// testOrphanOrRejectedBlock attempts to process the block in the provided test instance and ensures that it was either accepted as an orphan or rejected with a rule violation.
 193  // 	testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) {
 194  // 		blockHeight := item.Height
 195  // 		block := util.NewBlock(item.Block)
 196  // 		block.SetHeight(blockHeight)
 197  // 		t.Logf("Testing block %s (hash %s, height %d)",
 198  // 			item.Name, block.Hash(), blockHeight)
 199  // 		_, isOrphan, e := chain.ProcessBlock(
 200  // 			block, blockchain.BFNone, block.Height())
 201  // 		if e != nil  {
 202  // 			// Ensure the error code is of the expected type.
 203  // 			if _, ok := err.(blockchain.RuleError); !ok {
 204  // 				t.Fatalf("block %q (hash %s, height %d) "+
 205  // 					"returned unexpected error type -- "+
 206  // 					"got %T, want blockchain.RuleError",
 207  // 					item.Name, block.Hash(), blockHeight,
 208  // 					err)
 209  // 			}
 210  // 		}
 211  // 		if !isOrphan {
 212  // 			t.Fatalf("block %q (hash %s, height %d) was accepted, "+
 213  // 				"but is not considered an orphan", item.Name,
 214  // 				block.Hash(), blockHeight)
 215  // 		}
 216  // 	}
 217  // 	// testExpectedTip ensures the current tip of the blockchain is the block specified in the provided test instance.
 218  // 	testExpectedTip := func(item fullblocktests.ExpectedTip) {
 219  // 		blockHeight := item.Height
 220  // 		block := util.NewBlock(item.Block)
 221  // 		block.SetHeight(blockHeight)
 222  // 		t.Logf("Testing tip for block %s (hash %s, height %d)",
 223  // 			item.Name, block.Hash(), blockHeight)
 224  // 		// Ensure hash and height match.
 225  // 		best := chain.BestSnapshot()
 226  // 		if best.Hash != item.Block.BlockHash() ||
 227  // 			best.Height != blockHeight {
 228  // 			t.Fatalf("block %q (hash %s, height %d) should be "+
 229  // 				"the current tip -- got (hash %s, height %d)",
 230  // 				item.Name, block.Hash(), blockHeight, best.Hash,
 231  // 				best.Height)
 232  // 		}
 233  // 	}
 234  // 	for testNum, test := range tests {
 235  // 		for itemNum, item := range test {
 236  // 			switch item := item.(type) {
 237  // 			case fullblocktests.AcceptedBlock:
 238  // 				testAcceptedBlock(item)
 239  // 			case fullblocktests.RejectedBlock:
 240  // 				testRejectedBlock(item)
 241  // 			case fullblocktests.RejectedNonCanonicalBlock:
 242  // 				testRejectedNonCanonicalBlock(item)
 243  // 			case fullblocktests.OrphanOrRejectedBlock:
 244  // 				testOrphanOrRejectedBlock(item)
 245  // 			case fullblocktests.ExpectedTip:
 246  // 				testExpectedTip(item)
 247  // 			default:
 248  // 				t.Fatalf("test #%d, item #%d is not one of "+
 249  // 					"the supported test instance types -- "+
 250  // 					"got type: %T", testNum, itemNum, item)
 251  // 			}
 252  // 		}
 253  // 	}
 254  // }
 255