gitlab.com/jokerrs1/Sia@v1.3.2/modules/consensus/accept_txntypes_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/NebulousLabs/Sia/crypto"
     7  	"github.com/NebulousLabs/Sia/types"
     8  	"github.com/NebulousLabs/fastrand"
     9  )
    10  
    11  // testBlockSuite tests a wide variety of blocks.
    12  func (cst *consensusSetTester) testBlockSuite() {
    13  	cst.testSimpleBlock()
    14  	cst.testSpendSiacoinsBlock()
    15  	cst.testValidStorageProofBlocks()
    16  	cst.testMissedStorageProofBlocks()
    17  	cst.testFileContractRevision()
    18  	cst.testSpendSiafunds()
    19  }
    20  
    21  // testSimpleBlock mines a simple block (no transactions except those
    22  // automatically added by the miner) and adds it to the consnesus set.
    23  func (cst *consensusSetTester) testSimpleBlock() {
    24  	// Get the starting hash of the consenesus set.
    25  	initialChecksum := cst.cs.dbConsensusChecksum()
    26  	initialHeight := cst.cs.dbBlockHeight()
    27  	initialBlockID := cst.cs.dbCurrentBlockID()
    28  
    29  	// Mine and submit a block
    30  	block, err := cst.miner.AddBlock()
    31  	if err != nil {
    32  		panic(err)
    33  	}
    34  
    35  	// Check that the consensus info functions changed as expected.
    36  	resultingChecksum := cst.cs.dbConsensusChecksum()
    37  	if initialChecksum == resultingChecksum {
    38  		panic("checksum is unchanged after mining a block")
    39  	}
    40  	resultingHeight := cst.cs.dbBlockHeight()
    41  	if resultingHeight != initialHeight+1 {
    42  		panic("height of consensus set did not increase as expected")
    43  	}
    44  	currentPB := cst.cs.dbCurrentProcessedBlock()
    45  	if currentPB.Block.ParentID != initialBlockID {
    46  		panic("new processed block does not have correct information")
    47  	}
    48  	if currentPB.Block.ID() != block.ID() {
    49  		panic("the state's current block is not reporting as the recently mined block.")
    50  	}
    51  	if currentPB.Height != initialHeight+1 {
    52  		panic("the processed block is not reporting the correct height")
    53  	}
    54  	pathID, err := cst.cs.dbGetPath(currentPB.Height)
    55  	if err != nil {
    56  		panic(err)
    57  	}
    58  	if pathID != block.ID() {
    59  		panic("current path does not point to the correct block")
    60  	}
    61  
    62  	// Revert the block that was just added to the consensus set and check for
    63  	// parity with the original state of consensus.
    64  	parent, err := cst.cs.dbGetBlockMap(currentPB.Block.ParentID)
    65  	if err != nil {
    66  		panic(err)
    67  	}
    68  	_, _, err = cst.cs.dbForkBlockchain(parent)
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  	if cst.cs.dbConsensusChecksum() != initialChecksum {
    73  		panic("adding and reverting a block changed the consensus set")
    74  	}
    75  	// Re-add the block and check for parity with the first time it was added.
    76  	// This test is useful because a different codepath is followed if the
    77  	// diffs have already been generated.
    78  	_, _, err = cst.cs.dbForkBlockchain(currentPB)
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  	if cst.cs.dbConsensusChecksum() != resultingChecksum {
    83  		panic("adding, reverting, and reading a block was inconsistent with just adding the block")
    84  	}
    85  }
    86  
    87  // TestIntegrationSimpleBlock creates a consensus set tester and uses it to
    88  // call testSimpleBlock.
    89  func TestIntegrationSimpleBlock(t *testing.T) {
    90  	if testing.Short() {
    91  		t.SkipNow()
    92  	}
    93  	t.Parallel()
    94  	cst, err := createConsensusSetTester(t.Name())
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	defer cst.Close()
    99  	cst.testSimpleBlock()
   100  }
   101  
   102  // testSpendSiacoinsBlock mines a block with a transaction spending siacoins
   103  // and adds it to the consensus set.
   104  func (cst *consensusSetTester) testSpendSiacoinsBlock() {
   105  	// Create a random destination address for the output in the transaction.
   106  	destAddr := randAddress()
   107  
   108  	// Create a block containing a transaction with a valid siacoin output.
   109  	txnValue := types.NewCurrency64(1200)
   110  	txnBuilder := cst.wallet.StartTransaction()
   111  	err := txnBuilder.FundSiacoins(txnValue)
   112  	if err != nil {
   113  		panic(err)
   114  	}
   115  	outputIndex := txnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: txnValue, UnlockHash: destAddr})
   116  	txnSet, err := txnBuilder.Sign(true)
   117  	if err != nil {
   118  		panic(err)
   119  	}
   120  	err = cst.tpool.AcceptTransactionSet(txnSet)
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  
   125  	// Mine and apply the block to the consensus set.
   126  	_, err = cst.miner.AddBlock()
   127  	if err != nil {
   128  		panic(err)
   129  	}
   130  
   131  	// See that the destination output was created.
   132  	outputID := txnSet[len(txnSet)-1].SiacoinOutputID(outputIndex)
   133  	sco, err := cst.cs.dbGetSiacoinOutput(outputID)
   134  	if err != nil {
   135  		panic(err)
   136  	}
   137  	if !sco.Value.Equals(txnValue) {
   138  		panic("output added with wrong value")
   139  	}
   140  	if sco.UnlockHash != destAddr {
   141  		panic("output sent to the wrong address")
   142  	}
   143  }
   144  
   145  // TestIntegrationSpendSiacoinsBlock creates a consensus set tester and uses it
   146  // to call testSpendSiacoinsBlock.
   147  func TestIntegrationSpendSiacoinsBlock(t *testing.T) {
   148  	if testing.Short() {
   149  		t.SkipNow()
   150  	}
   151  	t.Parallel()
   152  	cst, err := createConsensusSetTester(t.Name())
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	defer cst.Close()
   157  	cst.testSpendSiacoinsBlock()
   158  }
   159  
   160  // testValidStorageProofBlocks adds a block with a file contract, and then
   161  // submits a storage proof for that file contract.
   162  func (cst *consensusSetTester) testValidStorageProofBlocks() {
   163  	// COMPATv0.4.0 - Step the block height up past the hardfork amount. This
   164  	// code stops nondeterministic failures when producing storage proofs that
   165  	// is related to buggy old code.
   166  	for cst.cs.dbBlockHeight() <= 10 {
   167  		_, err := cst.miner.AddBlock()
   168  		if err != nil {
   169  			panic(err)
   170  		}
   171  	}
   172  
   173  	// Create a file (as a bytes.Buffer) that will be used for the file
   174  	// contract.
   175  	filesize := uint64(4e3)
   176  	file := fastrand.Bytes(int(filesize))
   177  	merkleRoot := crypto.MerkleRoot(file)
   178  
   179  	// Create a file contract that will be successful.
   180  	validProofDest := randAddress()
   181  	payout := types.NewCurrency64(400e6)
   182  	fc := types.FileContract{
   183  		FileSize:       filesize,
   184  		FileMerkleRoot: merkleRoot,
   185  		WindowStart:    cst.cs.dbBlockHeight() + 1,
   186  		WindowEnd:      cst.cs.dbBlockHeight() + 2,
   187  		Payout:         payout,
   188  		ValidProofOutputs: []types.SiacoinOutput{{
   189  			UnlockHash: validProofDest,
   190  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   191  		}},
   192  		MissedProofOutputs: []types.SiacoinOutput{{
   193  			UnlockHash: types.UnlockHash{},
   194  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   195  		}},
   196  	}
   197  
   198  	// Submit a transaction with the file contract.
   199  	oldSiafundPool := cst.cs.dbGetSiafundPool()
   200  	txnBuilder := cst.wallet.StartTransaction()
   201  	err := txnBuilder.FundSiacoins(payout)
   202  	if err != nil {
   203  		panic(err)
   204  	}
   205  	fcIndex := txnBuilder.AddFileContract(fc)
   206  	txnSet, err := txnBuilder.Sign(true)
   207  	if err != nil {
   208  		panic(err)
   209  	}
   210  	err = cst.tpool.AcceptTransactionSet(txnSet)
   211  	if err != nil {
   212  		panic(err)
   213  	}
   214  	_, err = cst.miner.AddBlock()
   215  	if err != nil {
   216  		panic(err)
   217  	}
   218  
   219  	// Check that the siafund pool was increased by the tax on the payout.
   220  	siafundPool := cst.cs.dbGetSiafundPool()
   221  	if !siafundPool.Equals(oldSiafundPool.Add(types.Tax(cst.cs.dbBlockHeight()-1, payout))) {
   222  		panic("siafund pool was not increased correctly")
   223  	}
   224  
   225  	// Check that the file contract made it into the database.
   226  	ti := len(txnSet) - 1
   227  	fcid := txnSet[ti].FileContractID(fcIndex)
   228  	_, err = cst.cs.dbGetFileContract(fcid)
   229  	if err != nil {
   230  		panic(err)
   231  	}
   232  
   233  	// Create and submit a storage proof for the file contract.
   234  	segmentIndex, err := cst.cs.StorageProofSegment(fcid)
   235  	if err != nil {
   236  		panic(err)
   237  	}
   238  	segment, hashSet := crypto.MerkleProof(file, segmentIndex)
   239  	sp := types.StorageProof{
   240  		ParentID: fcid,
   241  		HashSet:  hashSet,
   242  	}
   243  	copy(sp.Segment[:], segment)
   244  	txnBuilder = cst.wallet.StartTransaction()
   245  	txnBuilder.AddStorageProof(sp)
   246  	txnSet, err = txnBuilder.Sign(true)
   247  	if err != nil {
   248  		panic(err)
   249  	}
   250  	err = cst.tpool.AcceptTransactionSet(txnSet)
   251  	if err != nil {
   252  		panic(err)
   253  	}
   254  	_, err = cst.miner.AddBlock()
   255  	if err != nil {
   256  		panic(err)
   257  	}
   258  
   259  	// Check that the file contract has been removed.
   260  	_, err = cst.cs.dbGetFileContract(fcid)
   261  	if err != errNilItem {
   262  		panic("file contract should not exist in the database")
   263  	}
   264  
   265  	// Check that the siafund pool has not changed.
   266  	postProofPool := cst.cs.dbGetSiafundPool()
   267  	if !postProofPool.Equals(siafundPool) {
   268  		panic("siafund pool should not change after submitting a storage proof")
   269  	}
   270  
   271  	// Check that a delayed output was created for the valid proof.
   272  	spoid := fcid.StorageProofOutputID(types.ProofValid, 0)
   273  	dsco, err := cst.cs.dbGetDSCO(cst.cs.dbBlockHeight()+types.MaturityDelay, spoid)
   274  	if err != nil {
   275  		panic(err)
   276  	}
   277  	if dsco.UnlockHash != fc.ValidProofOutputs[0].UnlockHash {
   278  		panic("wrong unlock hash in dsco")
   279  	}
   280  	if !dsco.Value.Equals(fc.ValidProofOutputs[0].Value) {
   281  		panic("wrong sco value in dsco")
   282  	}
   283  }
   284  
   285  // TestIntegrationValidStorageProofBlocks creates a consensus set tester and
   286  // uses it to call testValidStorageProofBlocks.
   287  func TestIntegrationValidStorageProofBlocks(t *testing.T) {
   288  	if testing.Short() {
   289  		t.SkipNow()
   290  	}
   291  	t.Parallel()
   292  	cst, err := createConsensusSetTester(t.Name())
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	defer cst.Close()
   297  	cst.testValidStorageProofBlocks()
   298  }
   299  
   300  // testMissedStorageProofBlocks adds a block with a file contract, and then
   301  // fails to submit a storage proof before expiration.
   302  func (cst *consensusSetTester) testMissedStorageProofBlocks() {
   303  	// Create a file contract that will be successful.
   304  	filesize := uint64(4e3)
   305  	payout := types.NewCurrency64(400e6)
   306  	missedProofDest := randAddress()
   307  	fc := types.FileContract{
   308  		FileSize:       filesize,
   309  		FileMerkleRoot: crypto.Hash{},
   310  		WindowStart:    cst.cs.dbBlockHeight() + 1,
   311  		WindowEnd:      cst.cs.dbBlockHeight() + 2,
   312  		Payout:         payout,
   313  		ValidProofOutputs: []types.SiacoinOutput{{
   314  			UnlockHash: types.UnlockHash{},
   315  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   316  		}},
   317  		MissedProofOutputs: []types.SiacoinOutput{{
   318  			UnlockHash: missedProofDest,
   319  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   320  		}},
   321  	}
   322  
   323  	// Submit a transaction with the file contract.
   324  	oldSiafundPool := cst.cs.dbGetSiafundPool()
   325  	txnBuilder := cst.wallet.StartTransaction()
   326  	err := txnBuilder.FundSiacoins(payout)
   327  	if err != nil {
   328  		panic(err)
   329  	}
   330  	fcIndex := txnBuilder.AddFileContract(fc)
   331  	txnSet, err := txnBuilder.Sign(true)
   332  	if err != nil {
   333  		panic(err)
   334  	}
   335  	err = cst.tpool.AcceptTransactionSet(txnSet)
   336  	if err != nil {
   337  		panic(err)
   338  	}
   339  	_, err = cst.miner.AddBlock()
   340  	if err != nil {
   341  		panic(err)
   342  	}
   343  
   344  	// Check that the siafund pool was increased by the tax on the payout.
   345  	siafundPool := cst.cs.dbGetSiafundPool()
   346  	if !siafundPool.Equals(oldSiafundPool.Add(types.Tax(cst.cs.dbBlockHeight()-1, payout))) {
   347  		panic("siafund pool was not increased correctly")
   348  	}
   349  
   350  	// Check that the file contract made it into the database.
   351  	ti := len(txnSet) - 1
   352  	fcid := txnSet[ti].FileContractID(fcIndex)
   353  	_, err = cst.cs.dbGetFileContract(fcid)
   354  	if err != nil {
   355  		panic(err)
   356  	}
   357  
   358  	// Mine a block to close the storage proof window.
   359  	_, err = cst.miner.AddBlock()
   360  	if err != nil {
   361  		panic(err)
   362  	}
   363  
   364  	// Check that the file contract has been removed.
   365  	_, err = cst.cs.dbGetFileContract(fcid)
   366  	if err != errNilItem {
   367  		panic("file contract should not exist in the database")
   368  	}
   369  
   370  	// Check that the siafund pool has not changed.
   371  	postProofPool := cst.cs.dbGetSiafundPool()
   372  	if !postProofPool.Equals(siafundPool) {
   373  		panic("siafund pool should not change after submitting a storage proof")
   374  	}
   375  
   376  	// Check that a delayed output was created for the missed proof.
   377  	spoid := fcid.StorageProofOutputID(types.ProofMissed, 0)
   378  	dsco, err := cst.cs.dbGetDSCO(cst.cs.dbBlockHeight()+types.MaturityDelay, spoid)
   379  	if err != nil {
   380  		panic(err)
   381  	}
   382  	if dsco.UnlockHash != fc.MissedProofOutputs[0].UnlockHash {
   383  		panic("wrong unlock hash in dsco")
   384  	}
   385  	if !dsco.Value.Equals(fc.MissedProofOutputs[0].Value) {
   386  		panic("wrong sco value in dsco")
   387  	}
   388  }
   389  
   390  // TestIntegrationMissedStorageProofBlocks creates a consensus set tester and
   391  // uses it to call testMissedStorageProofBlocks.
   392  func TestIntegrationMissedStorageProofBlocks(t *testing.T) {
   393  	if testing.Short() {
   394  		t.SkipNow()
   395  	}
   396  	t.Parallel()
   397  	cst, err := createConsensusSetTester(t.Name())
   398  	if err != nil {
   399  		t.Fatal(err)
   400  	}
   401  	defer cst.Close()
   402  	cst.testMissedStorageProofBlocks()
   403  }
   404  
   405  // testFileContractRevision creates and revises a file contract on the
   406  // blockchain.
   407  func (cst *consensusSetTester) testFileContractRevision() {
   408  	// COMPATv0.4.0 - Step the block height up past the hardfork amount. This
   409  	// code stops nondeterministic failures when producing storage proofs that
   410  	// is related to buggy old code.
   411  	for cst.cs.dbBlockHeight() <= 10 {
   412  		_, err := cst.miner.AddBlock()
   413  		if err != nil {
   414  			panic(err)
   415  		}
   416  	}
   417  
   418  	// Create a file (as a bytes.Buffer) that will be used for the file
   419  	// contract.
   420  	filesize := uint64(4e3)
   421  	file := fastrand.Bytes(int(filesize))
   422  	merkleRoot := crypto.MerkleRoot(file)
   423  
   424  	// Create a spendable unlock hash for the file contract.
   425  	sk, pk := crypto.GenerateKeyPair()
   426  	uc := types.UnlockConditions{
   427  		PublicKeys: []types.SiaPublicKey{{
   428  			Algorithm: types.SignatureEd25519,
   429  			Key:       pk[:],
   430  		}},
   431  		SignaturesRequired: 1,
   432  	}
   433  
   434  	// Create a file contract that will be revised.
   435  	validProofDest := randAddress()
   436  	payout := types.NewCurrency64(400e6)
   437  	fc := types.FileContract{
   438  		FileSize:       filesize,
   439  		FileMerkleRoot: crypto.Hash{},
   440  		WindowStart:    cst.cs.dbBlockHeight() + 2,
   441  		WindowEnd:      cst.cs.dbBlockHeight() + 3,
   442  		Payout:         payout,
   443  		ValidProofOutputs: []types.SiacoinOutput{{
   444  			UnlockHash: validProofDest,
   445  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   446  		}},
   447  		MissedProofOutputs: []types.SiacoinOutput{{
   448  			UnlockHash: types.UnlockHash{},
   449  			Value:      types.PostTax(cst.cs.dbBlockHeight(), payout),
   450  		}},
   451  		UnlockHash: uc.UnlockHash(),
   452  	}
   453  
   454  	// Submit a transaction with the file contract.
   455  	txnBuilder := cst.wallet.StartTransaction()
   456  	err := txnBuilder.FundSiacoins(payout)
   457  	if err != nil {
   458  		panic(err)
   459  	}
   460  	fcIndex := txnBuilder.AddFileContract(fc)
   461  	txnSet, err := txnBuilder.Sign(true)
   462  	if err != nil {
   463  		panic(err)
   464  	}
   465  	err = cst.tpool.AcceptTransactionSet(txnSet)
   466  	if err != nil {
   467  		panic(err)
   468  	}
   469  	_, err = cst.miner.AddBlock()
   470  	if err != nil {
   471  		panic(err)
   472  	}
   473  
   474  	// Submit a revision for the file contract.
   475  	ti := len(txnSet) - 1
   476  	fcid := txnSet[ti].FileContractID(fcIndex)
   477  	fcr := types.FileContractRevision{
   478  		ParentID:          fcid,
   479  		UnlockConditions:  uc,
   480  		NewRevisionNumber: 69292,
   481  
   482  		NewFileSize:           filesize,
   483  		NewFileMerkleRoot:     merkleRoot,
   484  		NewWindowStart:        cst.cs.dbBlockHeight() + 1,
   485  		NewWindowEnd:          cst.cs.dbBlockHeight() + 2,
   486  		NewValidProofOutputs:  fc.ValidProofOutputs,
   487  		NewMissedProofOutputs: fc.MissedProofOutputs,
   488  		NewUnlockHash:         uc.UnlockHash(),
   489  	}
   490  	ts := types.TransactionSignature{
   491  		ParentID:       crypto.Hash(fcid),
   492  		CoveredFields:  types.CoveredFields{WholeTransaction: true},
   493  		PublicKeyIndex: 0,
   494  	}
   495  	txn := types.Transaction{
   496  		FileContractRevisions: []types.FileContractRevision{fcr},
   497  		TransactionSignatures: []types.TransactionSignature{ts},
   498  	}
   499  	encodedSig := crypto.SignHash(txn.SigHash(0), sk)
   500  	txn.TransactionSignatures[0].Signature = encodedSig[:]
   501  	err = cst.tpool.AcceptTransactionSet([]types.Transaction{txn})
   502  	if err != nil {
   503  		panic(err)
   504  	}
   505  	_, err = cst.miner.AddBlock()
   506  	if err != nil {
   507  		panic(err)
   508  	}
   509  
   510  	// Create and submit a storage proof for the file contract.
   511  	segmentIndex, err := cst.cs.StorageProofSegment(fcid)
   512  	if err != nil {
   513  		panic(err)
   514  	}
   515  	segment, hashSet := crypto.MerkleProof(file, segmentIndex)
   516  	sp := types.StorageProof{
   517  		ParentID: fcid,
   518  		HashSet:  hashSet,
   519  	}
   520  	copy(sp.Segment[:], segment)
   521  	txnBuilder = cst.wallet.StartTransaction()
   522  	txnBuilder.AddStorageProof(sp)
   523  	txnSet, err = txnBuilder.Sign(true)
   524  	if err != nil {
   525  		panic(err)
   526  	}
   527  	err = cst.tpool.AcceptTransactionSet(txnSet)
   528  	if err != nil {
   529  		panic(err)
   530  	}
   531  	_, err = cst.miner.AddBlock()
   532  	if err != nil {
   533  		panic(err)
   534  	}
   535  
   536  	// Check that the file contract has been removed.
   537  	_, err = cst.cs.dbGetFileContract(fcid)
   538  	if err != errNilItem {
   539  		panic("file contract should not exist in the database")
   540  	}
   541  }
   542  
   543  // TestIntegrationFileContractRevision creates a consensus set tester and uses
   544  // it to call testFileContractRevision.
   545  func TestIntegrationFileContractRevision(t *testing.T) {
   546  	if testing.Short() {
   547  		t.SkipNow()
   548  	}
   549  	t.Parallel()
   550  	cst, err := createConsensusSetTester(t.Name())
   551  	if err != nil {
   552  		t.Fatal(err)
   553  	}
   554  	defer cst.Close()
   555  	cst.testFileContractRevision()
   556  }
   557  
   558  // testSpendSiafunds spends siafunds on the blockchain.
   559  func (cst *consensusSetTester) testSpendSiafunds() {
   560  	// Create a random destination address for the output in the transaction.
   561  	destAddr := randAddress()
   562  
   563  	// Create a block containing a transaction with a valid siafund output.
   564  	txnValue := types.NewCurrency64(3)
   565  	txnBuilder := cst.wallet.StartTransaction()
   566  	err := txnBuilder.FundSiafunds(txnValue)
   567  	if err != nil {
   568  		panic(err)
   569  	}
   570  	outputIndex := txnBuilder.AddSiafundOutput(types.SiafundOutput{Value: txnValue, UnlockHash: destAddr})
   571  	txnSet, err := txnBuilder.Sign(true)
   572  	if err != nil {
   573  		panic(err)
   574  	}
   575  	err = cst.tpool.AcceptTransactionSet(txnSet)
   576  	if err != nil {
   577  		panic(err)
   578  	}
   579  
   580  	// Find the siafund inputs used in the txn set.
   581  	var claimValues []types.Currency
   582  	var claimIDs []types.SiacoinOutputID
   583  	for _, txn := range txnSet {
   584  		for _, sfi := range txn.SiafundInputs {
   585  			sfo, err := cst.cs.dbGetSiafundOutput(sfi.ParentID)
   586  			if err != nil {
   587  				// It's not in the database because it's in an earlier
   588  				// transaction: disregard it - testing the first layer of
   589  				// dependencies is sufficient.
   590  				continue
   591  			}
   592  			poolDiff := cst.cs.dbGetSiafundPool().Sub(sfo.ClaimStart)
   593  			value := poolDiff.Div(types.SiafundCount).Mul(sfo.Value)
   594  			claimValues = append(claimValues, value)
   595  			claimIDs = append(claimIDs, sfi.ParentID.SiaClaimOutputID())
   596  		}
   597  	}
   598  	if len(claimValues) == 0 {
   599  		panic("no siafund outputs created?")
   600  	}
   601  
   602  	// Mine and apply the block to the consensus set.
   603  	_, err = cst.miner.AddBlock()
   604  	if err != nil {
   605  		panic(err)
   606  	}
   607  
   608  	// See that the destination output was created.
   609  	outputID := txnSet[len(txnSet)-1].SiafundOutputID(outputIndex)
   610  	sfo, err := cst.cs.dbGetSiafundOutput(outputID)
   611  	if err != nil {
   612  		panic(err)
   613  	}
   614  	if !sfo.Value.Equals(txnValue) {
   615  		panic("output added with wrong value")
   616  	}
   617  	if sfo.UnlockHash != destAddr {
   618  		panic("output sent to the wrong address")
   619  	}
   620  	if !sfo.ClaimStart.Equals(cst.cs.dbGetSiafundPool()) {
   621  		panic("ClaimStart is not being set correctly")
   622  	}
   623  
   624  	// Verify that all expected claims were created and added to the set of
   625  	// delayed siacoin outputs.
   626  	for i, id := range claimIDs {
   627  		dsco, err := cst.cs.dbGetDSCO(cst.cs.dbBlockHeight()+types.MaturityDelay, id)
   628  		if err != nil {
   629  			panic(err)
   630  		}
   631  		if !dsco.Value.Equals(claimValues[i]) {
   632  			panic("expected a different claim value on the siaclaim")
   633  		}
   634  	}
   635  }
   636  
   637  // TestIntegrationSpendSiafunds creates a consensus set tester and uses it
   638  // to call testSpendSiafunds.
   639  func (cst *consensusSetTester) TestIntegrationSpendSiafunds(t *testing.T) {
   640  	if testing.Short() {
   641  		t.SkipNow()
   642  	}
   643  	t.Parallel()
   644  	cst, err := createConsensusSetTester(t.Name())
   645  	if err != nil {
   646  		t.Fatal(err)
   647  	}
   648  	defer cst.Close()
   649  	cst.testSpendSiafunds()
   650  }
   651  
   652  // testDelayedOutputMaturity adds blocks that result in many delayed outputs
   653  // maturing at the same time, verifying that bulk maturity is handled
   654  // correctly.
   655  
   656  // TestRegressionDelayedOutputMaturity creates a consensus set tester and uses
   657  // it to call testDelayedOutputMaturity. In the past, bolt's ForEach function
   658  // had been used incorrectly resulting in the incorrect processing of bulk
   659  // delayed outputs.
   660  
   661  // testFileContractMaturity adds blocks that result in many file contracts
   662  // being closed at the same time.
   663  
   664  // TestRegressionFileContractMaturity creates a consensus set tester and uses
   665  // it to call testFileContractMaturity. In the past, bolt's ForEach function
   666  // had been used incorrectly, resulting in the incorrect processing of bulk
   667  // file contracts.
   668  
   669  /*
   670  // testPaymentChannelBlocks submits blocks to set up, use, and close a payment
   671  // channel.
   672  func (cst *consensusSetTester) testPaymentChannelBlocks() error {
   673  	// The current method of doing payment channels is gimped because public
   674  	// keys do not have timelocks. We will be hardforking to include timelocks
   675  	// in public keys in 0.4.0, but in the meantime we need an alternate
   676  	// method.
   677  
   678  	// Gimped payment channels: 2-of-2 multisig where one key is controlled by
   679  	// the funding entity, and one key is controlled by the receiving entity. An
   680  	// address is created containing both keys, and then the funding entity
   681  	// creates, but does not sign, a transaction sending coins to the channel
   682  	// address. A second transaction is created that sends all the coins in the
   683  	// funding output back to the funding entity. The receiving entity signs the
   684  	// transaction with a timelocked signature. The funding entity will get the
   685  	// refund after T blocks as long as the output is not double spent. The
   686  	// funding entity then signs the first transaction and opens the channel.
   687  	//
   688  	// Creating the channel:
   689  	//	1. Create a 2-of-2 unlock conditions, one key held by each entity.
   690  	//	2. Funding entity creates, but does not sign, a transaction sending
   691  	//		money to the payment channel address. (txn A)
   692  	//	3. Funding entity creates and signs a transaction spending the output
   693  	//		created in txn A that sends all the money back as a refund. (txn B)
   694  	//	4. Receiving entity signs txn B with a timelocked signature, so that the
   695  	//		funding entity cannot get the refund for several days. The funding entity
   696  	//		is given a fully signed and eventually-spendable txn B.
   697  	//	5. The funding entity signs and broadcasts txn A.
   698  	//
   699  	// Using the channel:
   700  	//	Each the receiving entity and the funding entity keeps a record of how
   701  	//	much has been sent down the unclosed channel, and watches the
   702  	//	blockchain for a channel closing transaction. To send more money down
   703  	//	the channel, the funding entity creates and signs a transaction sending
   704  	//	X+y coins to the receiving entity from the channel address. The
   705  	//	transaction is sent to the receiving entity, who will keep it and
   706  	//	potentially sign and broadcast it later. The funding entity will only
   707  	//	send money down the channel if 'work' or some other sort of event has
   708  	//	completed that indicates the receiving entity should get more money.
   709  	//
   710  	// Closing the channel:
   711  	//	The receiving entity will sign the transaction that pays them the most
   712  	//	money and then broadcast that transaction. This will spend the output
   713  	//	and close the channel, invalidating txn B and preventing any future
   714  	//	transactions from being made over the channel. The channel must be
   715  	//	closed before the timelock expires on the second signature in txn B,
   716  	//	otherwise the funding entity will be able to get a full refund.
   717  	//
   718  	//	The funding entity should be waiting until either the receiving entity
   719  	//	closes the channel or the timelock expires. If the receiving entity
   720  	//	closes the channel, all is good. If not, then the funding entity can
   721  	//	close the channel and get a full refund.
   722  
   723  	// Create a 2-of-2 unlock conditions, 1 key for each the sender and the
   724  	// receiver in the payment channel.
   725  	sk1, pk1, err := crypto.StdKeyGen.Generate() // Funding entity.
   726  	if err != nil {
   727  		return err
   728  	}
   729  	sk2, pk2, err := crypto.StdKeyGen.Generate() // Receiving entity.
   730  	if err != nil {
   731  		return err
   732  	}
   733  	uc := types.UnlockConditions{
   734  		PublicKeys: []types.SiaPublicKey{
   735  			{
   736  				Algorithm: types.SignatureEd25519,
   737  				Key:       pk1[:],
   738  			},
   739  			{
   740  				Algorithm: types.SignatureEd25519,
   741  				Key:       pk2[:],
   742  			},
   743  		},
   744  		SignaturesRequired: 2,
   745  	}
   746  	channelAddress := uc.UnlockHash()
   747  
   748  	// Funding entity creates but does not sign a transaction that funds the
   749  	// channel address. Because the wallet is not very flexible, the channel
   750  	// txn needs to be fully custom. To get a custom txn, manually create an
   751  	// address and then use the wallet to fund that address.
   752  	channelSize := types.NewCurrency64(10e3)
   753  	channelFundingSK, channelFundingPK, err := crypto.StdKeyGen.Generate()
   754  	if err != nil {
   755  		return err
   756  	}
   757  	channelFundingUC := types.UnlockConditions{
   758  		PublicKeys: []types.SiaPublicKey{{
   759  			Algorithm: types.SignatureEd25519,
   760  			Key:       channelFundingPK[:],
   761  		}},
   762  		SignaturesRequired: 1,
   763  	}
   764  	channelFundingAddr := channelFundingUC.UnlockHash()
   765  	fundTxnBuilder := cst.wallet.StartTransaction()
   766  	if err != nil {
   767  		return err
   768  	}
   769  	err = fundTxnBuilder.FundSiacoins(channelSize)
   770  	if err != nil {
   771  		return err
   772  	}
   773  	scoFundIndex := fundTxnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: channelSize, UnlockHash: channelFundingAddr})
   774  	fundTxnSet, err := fundTxnBuilder.Sign(true)
   775  	if err != nil {
   776  		return err
   777  	}
   778  	fundOutputID := fundTxnSet[len(fundTxnSet)-1].SiacoinOutputID(int(scoFundIndex))
   779  	channelTxn := types.Transaction{
   780  		SiacoinInputs: []types.SiacoinInput{{
   781  			ParentID:         fundOutputID,
   782  			UnlockConditions: channelFundingUC,
   783  		}},
   784  		SiacoinOutputs: []types.SiacoinOutput{{
   785  			Value:      channelSize,
   786  			UnlockHash: channelAddress,
   787  		}},
   788  		TransactionSignatures: []types.TransactionSignature{{
   789  			ParentID:       crypto.Hash(fundOutputID),
   790  			PublicKeyIndex: 0,
   791  			CoveredFields:  types.CoveredFields{WholeTransaction: true},
   792  		}},
   793  	}
   794  
   795  	// Funding entity creates and signs a transaction that spends the full
   796  	// channel output.
   797  	channelOutputID := channelTxn.SiacoinOutputID(0)
   798  	refundUC, err := cst.wallet.NextAddress()
   799  	refundAddr := refundUC.UnlockHash()
   800  	if err != nil {
   801  		return err
   802  	}
   803  	refundTxn := types.Transaction{
   804  		SiacoinInputs: []types.SiacoinInput{{
   805  			ParentID:         channelOutputID,
   806  			UnlockConditions: uc,
   807  		}},
   808  		SiacoinOutputs: []types.SiacoinOutput{{
   809  			Value:      channelSize,
   810  			UnlockHash: refundAddr,
   811  		}},
   812  		TransactionSignatures: []types.TransactionSignature{{
   813  			ParentID:       crypto.Hash(channelOutputID),
   814  			PublicKeyIndex: 0,
   815  			CoveredFields:  types.CoveredFields{WholeTransaction: true},
   816  		}},
   817  	}
   818  	sigHash := refundTxn.SigHash(0)
   819  	cryptoSig1, err := crypto.SignHash(sigHash, sk1)
   820  	if err != nil {
   821  		return err
   822  	}
   823  	refundTxn.TransactionSignatures[0].Signature = cryptoSig1[:]
   824  
   825  	// Receiving entity signs the transaction that spends the full channel
   826  	// output, but with a timelock.
   827  	refundTxn.TransactionSignatures = append(refundTxn.TransactionSignatures, types.TransactionSignature{
   828  		ParentID:       crypto.Hash(channelOutputID),
   829  		PublicKeyIndex: 1,
   830  		Timelock:       cst.cs.dbBlockHeight() + 2,
   831  		CoveredFields:  types.CoveredFields{WholeTransaction: true},
   832  	})
   833  	sigHash = refundTxn.SigHash(1)
   834  	cryptoSig2, err := crypto.SignHash(sigHash, sk2)
   835  	if err != nil {
   836  		return err
   837  	}
   838  	refundTxn.TransactionSignatures[1].Signature = cryptoSig2[:]
   839  
   840  	// Funding entity will now sign and broadcast the funding transaction.
   841  	sigHash = channelTxn.SigHash(0)
   842  	cryptoSig0, err := crypto.SignHash(sigHash, channelFundingSK)
   843  	if err != nil {
   844  		return err
   845  	}
   846  	channelTxn.TransactionSignatures[0].Signature = cryptoSig0[:]
   847  	err = cst.tpool.AcceptTransactionSet(append(fundTxnSet, channelTxn))
   848  	if err != nil {
   849  		return err
   850  	}
   851  	// Put the txn in a block.
   852  	_, err = cst.miner.AddBlock()
   853  	if err != nil {
   854  		return err
   855  	}
   856  
   857  	// Try to submit the refund transaction before the timelock has expired.
   858  	err = cst.tpool.AcceptTransactionSet([]types.Transaction{refundTxn})
   859  	if err != types.ErrPrematureSignature {
   860  		return err
   861  	}
   862  
   863  	// Create a transaction that has partially used the channel, and submit it
   864  	// to the blockchain to close the channel.
   865  	closeTxn := types.Transaction{
   866  		SiacoinInputs: []types.SiacoinInput{{
   867  			ParentID:         channelOutputID,
   868  			UnlockConditions: uc,
   869  		}},
   870  		SiacoinOutputs: []types.SiacoinOutput{
   871  			{
   872  				Value:      channelSize.Sub(types.NewCurrency64(5)),
   873  				UnlockHash: refundAddr,
   874  			},
   875  			{
   876  				Value: types.NewCurrency64(5),
   877  			},
   878  		},
   879  		TransactionSignatures: []types.TransactionSignature{
   880  			{
   881  				ParentID:       crypto.Hash(channelOutputID),
   882  				PublicKeyIndex: 0,
   883  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
   884  			},
   885  			{
   886  				ParentID:       crypto.Hash(channelOutputID),
   887  				PublicKeyIndex: 1,
   888  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
   889  			},
   890  		},
   891  	}
   892  	sigHash = closeTxn.SigHash(0)
   893  	cryptoSig3, err := crypto.SignHash(sigHash, sk1)
   894  	if err != nil {
   895  		return err
   896  	}
   897  	closeTxn.TransactionSignatures[0].Signature = cryptoSig3[:]
   898  	sigHash = closeTxn.SigHash(1)
   899  	cryptoSig4, err := crypto.SignHash(sigHash, sk2)
   900  	if err != nil {
   901  		return err
   902  	}
   903  	closeTxn.TransactionSignatures[1].Signature = cryptoSig4[:]
   904  	err = cst.tpool.AcceptTransactionSet([]types.Transaction{closeTxn})
   905  	if err != nil {
   906  		return err
   907  	}
   908  
   909  	// Mine the block with the transaction.
   910  	_, err = cst.miner.AddBlock()
   911  	if err != nil {
   912  		return err
   913  	}
   914  	closeRefundID := closeTxn.SiacoinOutputID(0)
   915  	closePaymentID := closeTxn.SiacoinOutputID(1)
   916  	exists := cst.cs.db.inSiacoinOutputs(closeRefundID)
   917  	if !exists {
   918  		return errors.New("close txn refund output doesn't exist")
   919  	}
   920  	exists = cst.cs.db.inSiacoinOutputs(closePaymentID)
   921  	if !exists {
   922  		return errors.New("close txn payment output doesn't exist")
   923  	}
   924  
   925  	// Create a payment channel where the receiving entity never responds to
   926  	// the initial transaction.
   927  	{
   928  		// Funding entity creates but does not sign a transaction that funds the
   929  		// channel address. Because the wallet is not very flexible, the channel
   930  		// txn needs to be fully custom. To get a custom txn, manually create an
   931  		// address and then use the wallet to fund that address.
   932  		channelSize := types.NewCurrency64(10e3)
   933  		channelFundingSK, channelFundingPK, err := crypto.StdKeyGen.Generate()
   934  		if err != nil {
   935  			return err
   936  		}
   937  		channelFundingUC := types.UnlockConditions{
   938  			PublicKeys: []types.SiaPublicKey{{
   939  				Algorithm: types.SignatureEd25519,
   940  				Key:       channelFundingPK[:],
   941  			}},
   942  			SignaturesRequired: 1,
   943  		}
   944  		channelFundingAddr := channelFundingUC.UnlockHash()
   945  		fundTxnBuilder := cst.wallet.StartTransaction()
   946  		err = fundTxnBuilder.FundSiacoins(channelSize)
   947  		if err != nil {
   948  			return err
   949  		}
   950  		scoFundIndex := fundTxnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: channelSize, UnlockHash: channelFundingAddr})
   951  		fundTxnSet, err := fundTxnBuilder.Sign(true)
   952  		if err != nil {
   953  			return err
   954  		}
   955  		fundOutputID := fundTxnSet[len(fundTxnSet)-1].SiacoinOutputID(int(scoFundIndex))
   956  		channelTxn := types.Transaction{
   957  			SiacoinInputs: []types.SiacoinInput{{
   958  				ParentID:         fundOutputID,
   959  				UnlockConditions: channelFundingUC,
   960  			}},
   961  			SiacoinOutputs: []types.SiacoinOutput{{
   962  				Value:      channelSize,
   963  				UnlockHash: channelAddress,
   964  			}},
   965  			TransactionSignatures: []types.TransactionSignature{{
   966  				ParentID:       crypto.Hash(fundOutputID),
   967  				PublicKeyIndex: 0,
   968  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
   969  			}},
   970  		}
   971  
   972  		// Funding entity creates and signs a transaction that spends the full
   973  		// channel output.
   974  		channelOutputID := channelTxn.SiacoinOutputID(0)
   975  		refundUC, err := cst.wallet.NextAddress()
   976  		refundAddr := refundUC.UnlockHash()
   977  		if err != nil {
   978  			return err
   979  		}
   980  		refundTxn := types.Transaction{
   981  			SiacoinInputs: []types.SiacoinInput{{
   982  				ParentID:         channelOutputID,
   983  				UnlockConditions: uc,
   984  			}},
   985  			SiacoinOutputs: []types.SiacoinOutput{{
   986  				Value:      channelSize,
   987  				UnlockHash: refundAddr,
   988  			}},
   989  			TransactionSignatures: []types.TransactionSignature{{
   990  				ParentID:       crypto.Hash(channelOutputID),
   991  				PublicKeyIndex: 0,
   992  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
   993  			}},
   994  		}
   995  		sigHash := refundTxn.SigHash(0)
   996  		cryptoSig1, err := crypto.SignHash(sigHash, sk1)
   997  		if err != nil {
   998  			return err
   999  		}
  1000  		refundTxn.TransactionSignatures[0].Signature = cryptoSig1[:]
  1001  
  1002  		// Receiving entity never communitcates, funding entity must reclaim
  1003  		// the 'channelSize' coins that were intended to go to the channel.
  1004  		reclaimUC, err := cst.wallet.NextAddress()
  1005  		reclaimAddr := reclaimUC.UnlockHash()
  1006  		if err != nil {
  1007  			return err
  1008  		}
  1009  		reclaimTxn := types.Transaction{
  1010  			SiacoinInputs: []types.SiacoinInput{{
  1011  				ParentID:         fundOutputID,
  1012  				UnlockConditions: channelFundingUC,
  1013  			}},
  1014  			SiacoinOutputs: []types.SiacoinOutput{{
  1015  				Value:      channelSize,
  1016  				UnlockHash: reclaimAddr,
  1017  			}},
  1018  			TransactionSignatures: []types.TransactionSignature{{
  1019  				ParentID:       crypto.Hash(fundOutputID),
  1020  				PublicKeyIndex: 0,
  1021  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
  1022  			}},
  1023  		}
  1024  		sigHash = reclaimTxn.SigHash(0)
  1025  		cryptoSig, err := crypto.SignHash(sigHash, channelFundingSK)
  1026  		if err != nil {
  1027  			return err
  1028  		}
  1029  		reclaimTxn.TransactionSignatures[0].Signature = cryptoSig[:]
  1030  		err = cst.tpool.AcceptTransactionSet(append(fundTxnSet, reclaimTxn))
  1031  		if err != nil {
  1032  			return err
  1033  		}
  1034  		block, _ := cst.miner.FindBlock()
  1035  		err = cst.cs.AcceptBlock(block)
  1036  		if err != nil {
  1037  			return err
  1038  		}
  1039  		reclaimOutputID := reclaimTxn.SiacoinOutputID(0)
  1040  		exists := cst.cs.db.inSiacoinOutputs(reclaimOutputID)
  1041  		if !exists {
  1042  			return errors.New("failed to reclaim an output that belongs to the funding entity")
  1043  		}
  1044  	}
  1045  
  1046  	// Create a channel and the open the channel, but close the channel using
  1047  	// the timelocked signature.
  1048  	{
  1049  		// Funding entity creates but does not sign a transaction that funds the
  1050  		// channel address. Because the wallet is not very flexible, the channel
  1051  		// txn needs to be fully custom. To get a custom txn, manually create an
  1052  		// address and then use the wallet to fund that address.
  1053  		channelSize := types.NewCurrency64(10e3)
  1054  		channelFundingSK, channelFundingPK, err := crypto.StdKeyGen.Generate()
  1055  		if err != nil {
  1056  			return err
  1057  		}
  1058  		channelFundingUC := types.UnlockConditions{
  1059  			PublicKeys: []types.SiaPublicKey{{
  1060  				Algorithm: types.SignatureEd25519,
  1061  				Key:       channelFundingPK[:],
  1062  			}},
  1063  			SignaturesRequired: 1,
  1064  		}
  1065  		channelFundingAddr := channelFundingUC.UnlockHash()
  1066  		fundTxnBuilder := cst.wallet.StartTransaction()
  1067  		err = fundTxnBuilder.FundSiacoins(channelSize)
  1068  		if err != nil {
  1069  			return err
  1070  		}
  1071  		scoFundIndex := fundTxnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: channelSize, UnlockHash: channelFundingAddr})
  1072  		fundTxnSet, err := fundTxnBuilder.Sign(true)
  1073  		if err != nil {
  1074  			return err
  1075  		}
  1076  		fundOutputID := fundTxnSet[len(fundTxnSet)-1].SiacoinOutputID(int(scoFundIndex))
  1077  		channelTxn := types.Transaction{
  1078  			SiacoinInputs: []types.SiacoinInput{{
  1079  				ParentID:         fundOutputID,
  1080  				UnlockConditions: channelFundingUC,
  1081  			}},
  1082  			SiacoinOutputs: []types.SiacoinOutput{{
  1083  				Value:      channelSize,
  1084  				UnlockHash: channelAddress,
  1085  			}},
  1086  			TransactionSignatures: []types.TransactionSignature{{
  1087  				ParentID:       crypto.Hash(fundOutputID),
  1088  				PublicKeyIndex: 0,
  1089  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
  1090  			}},
  1091  		}
  1092  
  1093  		// Funding entity creates and signs a transaction that spends the full
  1094  		// channel output.
  1095  		channelOutputID := channelTxn.SiacoinOutputID(0)
  1096  		refundUC, err := cst.wallet.NextAddress()
  1097  		refundAddr := refundUC.UnlockHash()
  1098  		if err != nil {
  1099  			return err
  1100  		}
  1101  		refundTxn := types.Transaction{
  1102  			SiacoinInputs: []types.SiacoinInput{{
  1103  				ParentID:         channelOutputID,
  1104  				UnlockConditions: uc,
  1105  			}},
  1106  			SiacoinOutputs: []types.SiacoinOutput{{
  1107  				Value:      channelSize,
  1108  				UnlockHash: refundAddr,
  1109  			}},
  1110  			TransactionSignatures: []types.TransactionSignature{{
  1111  				ParentID:       crypto.Hash(channelOutputID),
  1112  				PublicKeyIndex: 0,
  1113  				CoveredFields:  types.CoveredFields{WholeTransaction: true},
  1114  			}},
  1115  		}
  1116  		sigHash := refundTxn.SigHash(0)
  1117  		cryptoSig1, err := crypto.SignHash(sigHash, sk1)
  1118  		if err != nil {
  1119  			return err
  1120  		}
  1121  		refundTxn.TransactionSignatures[0].Signature = cryptoSig1[:]
  1122  
  1123  		// Receiving entity signs the transaction that spends the full channel
  1124  		// output, but with a timelock.
  1125  		refundTxn.TransactionSignatures = append(refundTxn.TransactionSignatures, types.TransactionSignature{
  1126  			ParentID:       crypto.Hash(channelOutputID),
  1127  			PublicKeyIndex: 1,
  1128  			Timelock:       cst.cs.dbBlockHeight() + 2,
  1129  			CoveredFields:  types.CoveredFields{WholeTransaction: true},
  1130  		})
  1131  		sigHash = refundTxn.SigHash(1)
  1132  		cryptoSig2, err := crypto.SignHash(sigHash, sk2)
  1133  		if err != nil {
  1134  			return err
  1135  		}
  1136  		refundTxn.TransactionSignatures[1].Signature = cryptoSig2[:]
  1137  
  1138  		// Funding entity will now sign and broadcast the funding transaction.
  1139  		sigHash = channelTxn.SigHash(0)
  1140  		cryptoSig0, err := crypto.SignHash(sigHash, channelFundingSK)
  1141  		if err != nil {
  1142  			return err
  1143  		}
  1144  		channelTxn.TransactionSignatures[0].Signature = cryptoSig0[:]
  1145  		err = cst.tpool.AcceptTransactionSet(append(fundTxnSet, channelTxn))
  1146  		if err != nil {
  1147  			return err
  1148  		}
  1149  		// Put the txn in a block.
  1150  		block, _ := cst.miner.FindBlock()
  1151  		err = cst.cs.AcceptBlock(block)
  1152  		if err != nil {
  1153  			return err
  1154  		}
  1155  
  1156  		// Receiving entity never signs another transaction, so the funding
  1157  		// entity waits until the timelock is complete, and then submits the
  1158  		// refundTxn.
  1159  		for i := 0; i < 3; i++ {
  1160  			block, _ := cst.miner.FindBlock()
  1161  			err = cst.cs.AcceptBlock(block)
  1162  			if err != nil {
  1163  				return err
  1164  			}
  1165  		}
  1166  		err = cst.tpool.AcceptTransactionSet([]types.Transaction{refundTxn})
  1167  		if err != nil {
  1168  			return err
  1169  		}
  1170  		block, _ = cst.miner.FindBlock()
  1171  		err = cst.cs.AcceptBlock(block)
  1172  		if err != nil {
  1173  			return err
  1174  		}
  1175  		refundOutputID := refundTxn.SiacoinOutputID(0)
  1176  		exists := cst.cs.db.inSiacoinOutputs(refundOutputID)
  1177  		if !exists {
  1178  			return errors.New("timelocked refund transaction did not get spent correctly")
  1179  		}
  1180  	}
  1181  
  1182  	return nil
  1183  }
  1184  */
  1185  
  1186  /*
  1187  // TestPaymentChannelBlocks creates a consensus set tester and uses it to call
  1188  // testPaymentChannelBlocks.
  1189  func TestPaymentChannelBlocks(t *testing.T) {
  1190  	if testing.Short() {
  1191  		t.SkipNow()
  1192  	}
  1193  	cst, err := createConsensusSetTester(t.Name())
  1194  	if err != nil {
  1195  		t.Fatal(err)
  1196  	}
  1197  	defer cst.closeCst()
  1198  	err = cst.testPaymentChannelBlocks()
  1199  	if err != nil {
  1200  		t.Fatal(err)
  1201  	}
  1202  }
  1203  */