rpc_harness_test.go raw

   1  package rpctest
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"testing"
   7  	"time"
   8  
   9  	"github.com/p9c/p9/pkg/amt"
  10  	"github.com/p9c/p9/pkg/btcaddr"
  11  
  12  	"github.com/p9c/p9/pkg/qu"
  13  
  14  	"github.com/p9c/p9/pkg/chaincfg"
  15  	"github.com/p9c/p9/pkg/chainhash"
  16  	"github.com/p9c/p9/pkg/txscript"
  17  	"github.com/p9c/p9/pkg/util"
  18  	"github.com/p9c/p9/pkg/wire"
  19  )
  20  
  21  func testSendOutputs(r *Harness, t *testing.T) {
  22  	genSpend := func(amt amt.Amount) *chainhash.Hash {
  23  		// Grab a fresh address from the wallet.
  24  		addr, e := r.NewAddress()
  25  		if e != nil {
  26  			t.Fatalf("unable to get new address: %v", e)
  27  		}
  28  		// Next, send amt DUO to this address,
  29  		// spending from one of our mature coinbase outputs.
  30  		addrScript, e := txscript.PayToAddrScript(addr)
  31  		if e != nil {
  32  			t.Fatalf("unable to generate pkscript to addr: %v", e)
  33  		}
  34  		output := wire.NewTxOut(int64(amt), addrScript)
  35  		txid, e := r.SendOutputs([]*wire.TxOut{output}, 10)
  36  		if e != nil {
  37  			t.Fatalf("coinbase spend failed: %v", e)
  38  		}
  39  		return txid
  40  	}
  41  	assertTxMined := func(txid *chainhash.Hash, blockHash *chainhash.Hash) {
  42  		block, e := r.Node.GetBlock(blockHash)
  43  		if e != nil {
  44  			t.Fatalf("unable to get block: %v", e)
  45  		}
  46  		numBlockTxns := len(block.Transactions)
  47  		if numBlockTxns < 2 {
  48  			t.Fatalf(
  49  				"crafted transaction wasn't mined, block should have "+
  50  					"at least %v transactions instead has %v", 2, numBlockTxns,
  51  			)
  52  		}
  53  		minedTx := block.Transactions[1]
  54  		txHash := minedTx.TxHash()
  55  		if txHash != *txid {
  56  			t.Fatalf("txid's don't match, %v vs %v", txHash, txid)
  57  		}
  58  	}
  59  	// First, generate a small spend which will require only a single input.
  60  	txid := genSpend(5 * amt.SatoshiPerBitcoin)
  61  	// Generate a single block, the transaction the wallet created should be found in this block.
  62  	blockHashes, e := r.Node.Generate(1)
  63  	if e != nil {
  64  		t.Fatalf("unable to generate single block: %v", e)
  65  	}
  66  	assertTxMined(txid, blockHashes[0])
  67  	// Next, generate a spend much greater than the block reward. This transaction should also have been mined properly.
  68  	txid = genSpend(500 * amt.SatoshiPerBitcoin)
  69  	blockHashes, e = r.Node.Generate(1)
  70  	if e != nil {
  71  		t.Fatalf("unable to generate single block: %v", e)
  72  	}
  73  	assertTxMined(txid, blockHashes[0])
  74  }
  75  
  76  func assertConnectedTo(t *testing.T, nodeA *Harness, nodeB *Harness) {
  77  	nodeAPeers, e := nodeA.Node.GetPeerInfo()
  78  	if e != nil {
  79  		t.Fatalf("unable to get nodeA's peer info")
  80  	}
  81  	nodeAddr := nodeB.node.config.listen
  82  	addrFound := false
  83  	for _, peerInfo := range nodeAPeers {
  84  		if peerInfo.Addr == nodeAddr {
  85  			addrFound = true
  86  			break
  87  		}
  88  	}
  89  	if !addrFound {
  90  		t.Fatal("nodeA not connected to nodeB")
  91  	}
  92  }
  93  
  94  func testConnectNode(r *Harness, t *testing.T) {
  95  	// Create a fresh test harness.
  96  	harness, e := New(&chaincfg.SimNetParams, nil, nil)
  97  	if e != nil {
  98  		t.Fatal(e)
  99  	}
 100  	if e := harness.SetUp(false, 0); E.Chk(e) {
 101  		t.Fatalf("unable to complete rpctest setup: %v", e)
 102  	}
 103  	defer func() {
 104  		if e := harness.TearDown(); E.Chk(e) {
 105  		}
 106  	}()
 107  	// Establish a p2p connection from our new local harness to the main harness.
 108  	if e := ConnectNode(harness, r); E.Chk(e) {
 109  		t.Fatalf("unable to connect local to main harness: %v", e)
 110  	}
 111  	// The main harness should show up in our local harness' peer's list, and vice verse.
 112  	assertConnectedTo(t, harness, r)
 113  }
 114  
 115  func testTearDownAll(t *testing.T) {
 116  	// Grab a local copy of the currently active harnesses before attempting to tear them all down.
 117  	initialActiveHarnesses := ActiveHarnesses()
 118  	// Tear down all currently active harnesses.
 119  	if e := TearDownAll(); E.Chk(e) {
 120  		t.Fatalf("unable to teardown all harnesses: %v", e)
 121  	}
 122  	// The global testInstances map should now be fully purged with no active test harnesses remaining.
 123  	if len(ActiveHarnesses()) != 0 {
 124  		t.Fatalf("test harnesses still active after TearDownAll")
 125  	}
 126  	for _, harness := range initialActiveHarnesses {
 127  		// Ensure all test directories have been deleted.
 128  		var e error
 129  		if _, e = os.Stat(harness.testNodeDir); e == nil {
 130  			t.Errorf("created test datadir was not deleted.")
 131  		}
 132  	}
 133  }
 134  func testActiveHarnesses(r *Harness, t *testing.T) {
 135  	numInitialHarnesses := len(ActiveHarnesses())
 136  	// Create a single test harness.
 137  	harness1, e := New(&chaincfg.SimNetParams, nil, nil)
 138  	if e != nil {
 139  		t.Fatal(e)
 140  	}
 141  	defer func() {
 142  		if e := harness1.TearDown(); E.Chk(e) {
 143  		}
 144  	}()
 145  	// With the harness created above, a single harness should be detected as active.
 146  	numActiveHarnesses := len(ActiveHarnesses())
 147  	if !(numActiveHarnesses > numInitialHarnesses) {
 148  		t.Fatalf(
 149  			"ActiveHarnesses not updated, should have an " +
 150  				"additional test harness listed.",
 151  		)
 152  	}
 153  }
 154  func testJoinMempools(r *Harness, t *testing.T) {
 155  	// Assert main test harness has no transactions in its mempool.
 156  	pooledHashes, e := r.Node.GetRawMempool()
 157  	if e != nil {
 158  		t.Fatalf("unable to get mempool for main test harness: %v", e)
 159  	}
 160  	if len(pooledHashes) != 0 {
 161  		t.Fatal("main test harness mempool not empty")
 162  	}
 163  	// Create a local test harness with only the genesis block. The nodes will be synced below so the same transaction
 164  	// can be sent to both nodes without it being an orphan.
 165  	var harness *Harness
 166  	harness, e = New(&chaincfg.SimNetParams, nil, nil)
 167  	if e != nil {
 168  		t.Fatal(e)
 169  	}
 170  	if e = harness.SetUp(false, 0); E.Chk(e) {
 171  		t.Fatalf("unable to complete rpctest setup: %v", e)
 172  	}
 173  	defer func() {
 174  		if e = harness.TearDown(); E.Chk(e) {
 175  		}
 176  	}()
 177  	nodeSlice := []*Harness{r, harness}
 178  	// Both mempools should be considered synced as they are empty. Therefore, this should return instantly.
 179  	if e = JoinNodes(nodeSlice, Mempools); E.Chk(e) {
 180  		t.Fatalf("unable to join node on mempools: %v", e)
 181  	}
 182  	// Generate a coinbase spend to a new address within the main harness' mempool.
 183  	var addr btcaddr.Address
 184  	if addr, e = r.NewAddress(); E.Chk(e) {
 185  	}
 186  	var addrScript []byte
 187  	addrScript, e = txscript.PayToAddrScript(addr)
 188  	if e != nil {
 189  		t.Fatalf("unable to generate pkscript to addr: %v", e)
 190  	}
 191  	output := wire.NewTxOut(5e8, addrScript)
 192  	var testTx *wire.MsgTx
 193  	testTx, e = r.CreateTransaction([]*wire.TxOut{output}, 10, true)
 194  	if e != nil {
 195  		t.Fatalf("coinbase spend failed: %v", e)
 196  	}
 197  	if _, e = r.Node.SendRawTransaction(testTx, true); E.Chk(e) {
 198  		t.Fatalf("send transaction failed: %v", e)
 199  	}
 200  	// Wait until the transaction shows up to ensure the two mempools are not the same.
 201  	harnessSynced := qu.T()
 202  	go func() {
 203  		for {
 204  			var poolHashes []*chainhash.Hash
 205  			poolHashes, e = r.Node.GetRawMempool()
 206  			if e != nil {
 207  				t.Fatalf("failed to retrieve harness mempool: %v", e)
 208  			}
 209  			if len(poolHashes) > 0 {
 210  				break
 211  			}
 212  			time.Sleep(time.Millisecond * 100)
 213  		}
 214  		harnessSynced <- struct{}{}
 215  	}()
 216  	select {
 217  	case <-harnessSynced.Wait():
 218  	case <-time.After(time.Minute):
 219  		t.Fatalf("harness node never received transaction")
 220  	}
 221  	// This select case should fall through to the default as the goroutine should be blocked on the JoinNodes call.
 222  	poolsSynced := qu.T()
 223  	go func() {
 224  		if e = JoinNodes(nodeSlice, Mempools); E.Chk(e) {
 225  			t.Fatalf("unable to join node on mempools: %v", e)
 226  		}
 227  		poolsSynced <- struct{}{}
 228  	}()
 229  	select {
 230  	case <-poolsSynced.Wait():
 231  		t.Fatalf("mempools detected as synced yet harness has a new tx")
 232  	default:
 233  	}
 234  	// Establish an outbound connection from the local harness to the main harness and wait for the chains to be synced.
 235  	if e = ConnectNode(harness, r); E.Chk(e) {
 236  		t.Fatalf("unable to connect harnesses: %v", e)
 237  	}
 238  	if e = JoinNodes(nodeSlice, Blocks); E.Chk(e) {
 239  		t.Fatalf("unable to join node on blocks: %v", e)
 240  	}
 241  	// Send the transaction to the local harness which will result in synced mempools.
 242  	if _, e = harness.Node.SendRawTransaction(testTx, true); E.Chk(e) {
 243  		t.Fatalf("send transaction failed: %v", e)
 244  	}
 245  	// Select once again with a special timeout case after 1 minute. The goroutine above should now be blocked on
 246  	// sending into the unbuffered channel. The send should immediately succeed. In order to avoid the test hanging
 247  	// indefinitely, a 1 minute timeout is in place.
 248  	select {
 249  	case <-poolsSynced.Wait():
 250  	case <-time.After(time.Minute):
 251  		t.Fatalf("mempools never detected as synced")
 252  	}
 253  }
 254  func testJoinBlocks(r *Harness, t *testing.T) {
 255  	// Create a second harness with only the genesis block so it is behind the main harness.
 256  	harness, e := New(&chaincfg.SimNetParams, nil, nil)
 257  	if e != nil {
 258  		t.Fatal(e)
 259  	}
 260  	if e := harness.SetUp(false, 0); E.Chk(e) {
 261  		t.Fatalf("unable to complete rpctest setup: %v", e)
 262  	}
 263  	defer func() {
 264  		if e := harness.TearDown(); E.Chk(e) {
 265  		}
 266  	}()
 267  	nodeSlice := []*Harness{r, harness}
 268  	blocksSynced := qu.T()
 269  	go func() {
 270  		if e := JoinNodes(nodeSlice, Blocks); E.Chk(e) {
 271  			t.Fatalf("unable to join node on blocks: %v", e)
 272  		}
 273  		blocksSynced <- struct{}{}
 274  	}()
 275  	// This select case should fall through to the default as the goroutine should be blocked on the JoinNodes calls.
 276  	select {
 277  	case <-blocksSynced.Wait():
 278  		t.Fatalf("blocks detected as synced yet local harness is behind")
 279  	default:
 280  	}
 281  	// Connect the local harness to the main harness which will sync the chains.
 282  	if e := ConnectNode(harness, r); E.Chk(e) {
 283  		t.Fatalf("unable to connect harnesses: %v", e)
 284  	}
 285  	// Select once again with a special timeout case after 1 minute. The goroutine above should now be blocked on
 286  	// sending into the unbuffered channel. The send should immediately succeed. In order to avoid the test hanging
 287  	// indefinitely, a 1 minute timeout is in place.
 288  	select {
 289  	case <-blocksSynced.Wait():
 290  	case <-time.After(time.Minute):
 291  		t.Fatalf("blocks never detected as synced")
 292  	}
 293  }
 294  func testGenerateAndSubmitBlock(r *Harness, t *testing.T) {
 295  	// Generate a few test spend transactions.
 296  	addr, e := r.NewAddress()
 297  	if e != nil {
 298  		t.Fatalf("unable to generate new address: %v", e)
 299  	}
 300  	pkScript, e := txscript.PayToAddrScript(addr)
 301  	if e != nil {
 302  		t.Fatalf("unable to create script: %v", e)
 303  	}
 304  	output := wire.NewTxOut(amt.SatoshiPerBitcoin.Int64(), pkScript)
 305  	const numTxns = 5
 306  	txns := make([]*util.Tx, 0, numTxns)
 307  	var tx *wire.MsgTx
 308  	for i := 0; i < numTxns; i++ {
 309  		tx, e = r.CreateTransaction([]*wire.TxOut{output}, 10, true)
 310  		if e != nil {
 311  			t.Fatalf("unable to create tx: %v", e)
 312  		}
 313  		txns = append(txns, util.NewTx(tx))
 314  	}
 315  	// Now generate a block with the default block version, and a zeroed out time.
 316  	block, e := r.GenerateAndSubmitBlock(txns, ^uint32(0), time.Time{})
 317  	if e != nil {
 318  		t.Fatalf("unable to generate block: %v", e)
 319  	}
 320  	// Ensure that all created transactions were included, and that the block version was properly set to the default.
 321  	numBlocksTxns := len(block.Transactions())
 322  	if numBlocksTxns != numTxns+1 {
 323  		t.Fatalf(
 324  			"block did not include all transactions: "+
 325  				"expected %v, got %v", numTxns+1, numBlocksTxns,
 326  		)
 327  	}
 328  	blockVersion := block.WireBlock().Header.Version
 329  	if blockVersion != BlockVersion {
 330  		t.Fatalf(
 331  			"block version is not default: expected %v, got %v",
 332  			BlockVersion, blockVersion,
 333  		)
 334  	}
 335  	// Next generate a block with a "non-standard" block version along with time stamp a minute after the previous
 336  	// block's timestamp.
 337  	timestamp := block.WireBlock().Header.Timestamp.Add(time.Minute)
 338  	targetBlockVersion := uint32(1337)
 339  	block, e = r.GenerateAndSubmitBlock(nil, targetBlockVersion, timestamp)
 340  	if e != nil {
 341  		t.Fatalf("unable to generate block: %v", e)
 342  	}
 343  	// Finally ensure that the desired block version and timestamp were set properly.
 344  	header := block.WireBlock().Header
 345  	blockVersion = header.Version
 346  	if blockVersion != int32(targetBlockVersion) {
 347  		t.Fatalf(
 348  			"block version mismatch: expected %v, got %v",
 349  			targetBlockVersion, blockVersion,
 350  		)
 351  	}
 352  	if !timestamp.Equal(header.Timestamp) {
 353  		t.Fatalf(
 354  			"header time stamp mismatch: expected %v, got %v",
 355  			timestamp, header.Timestamp,
 356  		)
 357  	}
 358  }
 359  func testGenerateAndSubmitBlockWithCustomCoinbaseOutputs(
 360  	r *Harness,
 361  	t *testing.T,
 362  ) {
 363  	// Generate a few test spend transactions.
 364  	addr, e := r.NewAddress()
 365  	if e != nil {
 366  		t.Fatalf("unable to generate new address: %v", e)
 367  	}
 368  	pkScript, e := txscript.PayToAddrScript(addr)
 369  	if e != nil {
 370  		t.Fatalf("unable to create script: %v", e)
 371  	}
 372  	output := wire.NewTxOut(amt.SatoshiPerBitcoin.Int64(), pkScript)
 373  	const numTxns = 5
 374  	txns := make([]*util.Tx, 0, numTxns)
 375  	for i := 0; i < numTxns; i++ {
 376  		var tx *wire.MsgTx
 377  		tx, e = r.CreateTransaction([]*wire.TxOut{output}, 10, true)
 378  		if e != nil {
 379  			t.Fatalf("unable to create tx: %v", e)
 380  		}
 381  		txns = append(txns, util.NewTx(tx))
 382  	}
 383  	// Now generate a block with the default block version, a zero'd out time, and a burn output.
 384  	block, e := r.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(
 385  		txns,
 386  		^uint32(0), time.Time{}, []wire.TxOut{
 387  			{
 388  				Value:    0,
 389  				PkScript: []byte{},
 390  			},
 391  		},
 392  	)
 393  	if e != nil {
 394  		t.Fatalf("unable to generate block: %v", e)
 395  	}
 396  	// Ensure that all created transactions were included, and that the block version was properly set to the default.
 397  	numBlocksTxns := len(block.Transactions())
 398  	if numBlocksTxns != numTxns+1 {
 399  		t.Fatalf(
 400  			"block did not include all transactions: "+
 401  				"expected %v, got %v", numTxns+1, numBlocksTxns,
 402  		)
 403  	}
 404  	blockVersion := block.WireBlock().Header.Version
 405  	if blockVersion != BlockVersion {
 406  		t.Fatalf(
 407  			"block version is not default: expected %v, got %v",
 408  			BlockVersion, blockVersion,
 409  		)
 410  	}
 411  	// Next generate a block with a "non-standard" block version along with time stamp a minute after the previous
 412  	// block's timestamp.
 413  	timestamp := block.WireBlock().Header.Timestamp.Add(time.Minute)
 414  	targetBlockVersion := uint32(1337)
 415  	block, e = r.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(
 416  		nil,
 417  		targetBlockVersion, timestamp, []wire.TxOut{
 418  			{
 419  				Value:    0,
 420  				PkScript: []byte{},
 421  			},
 422  		},
 423  	)
 424  	if e != nil {
 425  		t.Fatalf("unable to generate block: %v", e)
 426  	}
 427  	// Finally ensure that the desired block version and timestamp were set properly.
 428  	header := block.WireBlock().Header
 429  	blockVersion = header.Version
 430  	if blockVersion != int32(targetBlockVersion) {
 431  		t.Fatalf(
 432  			"block version mismatch: expected %v, got %v",
 433  			targetBlockVersion, blockVersion,
 434  		)
 435  	}
 436  	if !timestamp.Equal(header.Timestamp) {
 437  		t.Fatalf(
 438  			"header time stamp mismatch: expected %v, got %v",
 439  			timestamp, header.Timestamp,
 440  		)
 441  	}
 442  }
 443  func testMemWalletReorg(r *Harness, t *testing.T) {
 444  	// Create a fresh harness, we'll be using the main harness to force a re-org on this local harness.
 445  	harness, e := New(&chaincfg.SimNetParams, nil, nil)
 446  	if e != nil {
 447  		t.Fatal(e)
 448  	}
 449  	if e := harness.SetUp(true, 5); E.Chk(e) {
 450  		t.Fatalf("unable to complete rpctest setup: %v", e)
 451  	}
 452  	defer func() {
 453  		if e := harness.TearDown(); E.Chk(e) {
 454  		}
 455  	}()
 456  	// The internal wallet of this harness should now have 250 DUO.
 457  	expectedBalance := 250 * amt.SatoshiPerBitcoin
 458  	walletBalance := harness.ConfirmedBalance()
 459  	if expectedBalance != walletBalance {
 460  		t.Fatalf(
 461  			"wallet balance incorrect: expected %v, got %v",
 462  			expectedBalance, walletBalance,
 463  		)
 464  	}
 465  	// Now connect this local harness to the main harness then wait for their chains to synchronize.
 466  	if e := ConnectNode(harness, r); E.Chk(e) {
 467  		t.Fatalf("unable to connect harnesses: %v", e)
 468  	}
 469  	nodeSlice := []*Harness{r, harness}
 470  	if e := JoinNodes(nodeSlice, Blocks); E.Chk(e) {
 471  		t.Fatalf("unable to join node on blocks: %v", e)
 472  	}
 473  	// The original wallet should now have a balance of 0 DUO as its entire chain should have been decimated in favor of
 474  	// the main harness' chain.
 475  	expectedBalance = amt.Amount(0)
 476  	walletBalance = harness.ConfirmedBalance()
 477  	if expectedBalance != walletBalance {
 478  		t.Fatalf(
 479  			"wallet balance incorrect: expected %v, got %v",
 480  			expectedBalance, walletBalance,
 481  		)
 482  	}
 483  }
 484  func testMemWalletLockedOutputs(r *Harness, t *testing.T) {
 485  	// Obtain the initial balance of the wallet at this point.
 486  	startingBalance := r.ConfirmedBalance()
 487  	// First, create a signed transaction spending some outputs.
 488  	addr, e := r.NewAddress()
 489  	if e != nil {
 490  		t.Fatalf("unable to generate new address: %v", e)
 491  	}
 492  	pkScript, e := txscript.PayToAddrScript(addr)
 493  	if e != nil {
 494  		t.Fatalf("unable to create script: %v", e)
 495  	}
 496  	outputAmt := 50 * amt.SatoshiPerBitcoin
 497  	output := wire.NewTxOut(int64(outputAmt), pkScript)
 498  	tx, e := r.CreateTransaction([]*wire.TxOut{output}, 10, true)
 499  	if e != nil {
 500  		t.Fatalf("unable to create transaction: %v", e)
 501  	}
 502  	// The current wallet balance should now be at least 50 DUO less (accounting for fees) than the period balance
 503  	currentBalance := r.ConfirmedBalance()
 504  	if !(currentBalance <= startingBalance-outputAmt) {
 505  		t.Fatalf(
 506  			"spent outputs not locked: previous balance %v, "+
 507  				"current balance %v", startingBalance, currentBalance,
 508  		)
 509  	}
 510  	// Now unlocked all the spent inputs within the unbroadcast signed transaction. The current balance should now be
 511  	// exactly that of the starting balance.
 512  	r.UnlockOutputs(tx.TxIn)
 513  	currentBalance = r.ConfirmedBalance()
 514  	if currentBalance != startingBalance {
 515  		t.Fatalf(
 516  			"current and starting balance should now match: "+
 517  				"expected %v, got %v", startingBalance, currentBalance,
 518  		)
 519  	}
 520  }
 521  
 522  var harnessTestCases = []HarnessTestCase{
 523  	testSendOutputs,
 524  	testConnectNode,
 525  	testActiveHarnesses,
 526  	testJoinBlocks,
 527  	testJoinMempools, // Depends on results of testJoinBlocks
 528  	testGenerateAndSubmitBlock,
 529  	testGenerateAndSubmitBlockWithCustomCoinbaseOutputs,
 530  	testMemWalletReorg,
 531  	testMemWalletLockedOutputs,
 532  }
 533  
 534  var mainHarness *Harness
 535  
 536  const (
 537  	numMatureOutputs = 25
 538  )
 539  
 540  func TestMain(m *testing.M) {
 541  	var e error
 542  	mainHarness, e = New(&chaincfg.SimNetParams, nil, nil)
 543  	if e != nil {
 544  		fmt.Println("unable to create main harness: ", e)
 545  		os.Exit(1)
 546  	}
 547  	// Initialize the main mining node with a chain of length 125, providing 25 mature coinbases to allow spending from
 548  	// for testing purposes.
 549  	if e = mainHarness.SetUp(true, numMatureOutputs); E.Chk(e) {
 550  		fmt.Println("unable to setup test chain: ", e)
 551  		// Even though the harness was not fully setup, it still needs to be torn down to ensure all resources such as
 552  		// temp directories are cleaned up. The error is intentionally ignored since this is already an error path and
 553  		// nothing else could be done about it anyways.
 554  		_ = mainHarness.TearDown()
 555  		os.Exit(1)
 556  	}
 557  	exitCode := m.Run()
 558  	// Clean up any active harnesses that are still currently running.
 559  	if len(ActiveHarnesses()) > 0 {
 560  		if e := TearDownAll(); E.Chk(e) {
 561  			fmt.Println("unable to tear down chain: ", e)
 562  			os.Exit(1)
 563  		}
 564  	}
 565  	os.Exit(exitCode)
 566  }
 567  func TestHarness(t *testing.T) {
 568  	// We should have (numMatureOutputs * 50 DUO) of mature unspendable
 569  	// outputs.
 570  	expectedBalance := numMatureOutputs * 50 * amt.SatoshiPerBitcoin
 571  	harnessBalance := mainHarness.ConfirmedBalance()
 572  	if harnessBalance != expectedBalance {
 573  		t.Fatalf(
 574  			"expected wallet balance of %v instead have %v",
 575  			expectedBalance, harnessBalance,
 576  		)
 577  	}
 578  	// Current tip should be at a height of numMatureOutputs plus the required number of blocks for coinbase maturity.
 579  	nodeInfo, e := mainHarness.Node.GetInfo()
 580  	if e != nil {
 581  		t.Fatalf("unable to execute getinfo on node: %v", e)
 582  	}
 583  	expectedChainHeight := numMatureOutputs + uint32(mainHarness.ActiveNet.CoinbaseMaturity)
 584  	if uint32(nodeInfo.Blocks) != expectedChainHeight {
 585  		t.Errorf(
 586  			"Chain height is %v, should be %v",
 587  			nodeInfo.Blocks, expectedChainHeight,
 588  		)
 589  	}
 590  	for _, testCase := range harnessTestCases {
 591  		testCase(mainHarness, t)
 592  	}
 593  	testTearDownAll(t)
 594  }
 595