github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/maintenance_test.go (about)

     1  package consensus
     2  
     3  /*
     4  import (
     5  	"testing"
     6  
     7  	"github.com/coreos/bbolt"
     8  
     9  	"github.com/NebulousLabs/Sia/modules"
    10  	"github.com/NebulousLabs/Sia/types"
    11  )
    12  
    13  // TestApplyMinerPayouts probes the applyMinerPayouts method of the consensus
    14  // set.
    15  func TestApplyMinerPayouts(t *testing.T) {
    16  	if testing.Short() {
    17  		t.SkipNow()
    18  	}
    19  	cst, err := createConsensusSetTester(t.Name())
    20  	if err != nil {
    21  		t.Fatal(err)
    22  	}
    23  	defer cst.closeCst()
    24  
    25  	// Create a block node with a single miner payout.
    26  	pb := new(processedBlock)
    27  	pb.Height = cst.cs.dbBlockHeight()
    28  	pb.Block.Timestamp = 2 // MinerPayout id is determined by block id + index; add uniqueness to the block id.
    29  	pb.Block.MinerPayouts = append(pb.Block.MinerPayouts, types.SiacoinOutput{Value: types.NewCurrency64(12)})
    30  	mpid0 := pb.Block.MinerPayoutID(0)
    31  
    32  	// Apply the single miner payout.
    33  	_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
    34  		applyMinerPayouts(tx, pb)
    35  		return nil
    36  	})
    37  	exists := cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid0)
    38  	if !exists {
    39  		t.Error("miner payout was not created in the delayed outputs set")
    40  	}
    41  	dsco, err := cst.cs.dbGetDSCO(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid0)
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  	if dsco.Value.Cmp64(12) != 0 {
    46  		t.Error("miner payout created with wrong currency value")
    47  	}
    48  	exists = cst.cs.db.inSiacoinOutputs(mpid0)
    49  	if exists {
    50  		t.Error("miner payout was added to the siacoin output set")
    51  	}
    52  	if cst.cs.db.lenDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay) != 2 { // 1 for consensus set creation, 1 for the output that just got added.
    53  		t.Error("wrong number of delayed siacoin outputs in consensus set")
    54  	}
    55  	if len(pb.DelayedSiacoinOutputDiffs) != 1 {
    56  		t.Fatal("block node did not get the delayed siacoin output diff")
    57  	}
    58  	if pb.DelayedSiacoinOutputDiffs[0].Direction != modules.DiffApply {
    59  		t.Error("delayed siacoin output diff has the wrong direction")
    60  	}
    61  	if pb.DelayedSiacoinOutputDiffs[0].ID != mpid0 {
    62  		t.Error("delayed siacoin output diff has wrong id")
    63  	}
    64  
    65  	// Apply a processed block with two miner payouts.
    66  	pb2 := new(processedBlock)
    67  	pb2.Height = cst.cs.dbBlockHeight()
    68  	pb2.Block.Timestamp = 5 // MinerPayout id is determined by block id + index; add uniqueness to the block id.
    69  	pb2.Block.MinerPayouts = []types.SiacoinOutput{
    70  		{Value: types.NewCurrency64(5)},
    71  		{Value: types.NewCurrency64(10)},
    72  	}
    73  	mpid1 := pb2.Block.MinerPayoutID(0)
    74  	mpid2 := pb2.Block.MinerPayoutID(1)
    75  	_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
    76  		applyMinerPayouts(tx, pb2)
    77  		return nil
    78  	})
    79  	exists = cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid1)
    80  	if !exists {
    81  		t.Error("delayed siacoin output was not created")
    82  	}
    83  	exists = cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid2)
    84  	if !exists {
    85  		t.Error("delayed siacoin output was not created")
    86  	}
    87  	if len(pb2.DelayedSiacoinOutputDiffs) != 2 {
    88  		t.Error("block node should have 2 delayed outputs")
    89  	}
    90  
    91  	// Trigger a panic where the miner payouts have already been applied.
    92  	defer func() {
    93  		r := recover()
    94  		if r == nil {
    95  			t.Error("expecting error after corrupting database")
    96  		}
    97  	}()
    98  	defer func() {
    99  		r := recover()
   100  		if r == nil {
   101  			t.Error("expecting error after corrupting database")
   102  		}
   103  		cst.cs.db.rmDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, mpid0)
   104  		cst.cs.db.addSiacoinOutputs(mpid0, types.SiacoinOutput{})
   105  		_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
   106  			applyMinerPayouts(tx, pb)
   107  			return nil
   108  		})
   109  	}()
   110  	_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
   111  		applyMinerPayouts(tx, pb)
   112  		return nil
   113  	})
   114  }
   115  
   116  // TestApplyMaturedSiacoinOutputs probes the applyMaturedSiacoinOutputs method
   117  // of the consensus set.
   118  func TestApplyMaturedSiacoinOutputs(t *testing.T) {
   119  	if testing.Short() {
   120  		t.SkipNow()
   121  	}
   122  	cst, err := createConsensusSetTester(t.Name())
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	defer cst.closeCst()
   127  	pb := cst.cs.dbCurrentProcessedBlock()
   128  
   129  	// Trigger the sanity check concerning already-matured outputs.
   130  	defer func() {
   131  		r := recover()
   132  		if r != errOutputAlreadyMature {
   133  			t.Error(r)
   134  		}
   135  	}()
   136  	cst.cs.db.addSiacoinOutputs(types.SiacoinOutputID{}, types.SiacoinOutput{})
   137  	_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
   138  		createDSCOBucket(tx, pb.Height)
   139  		return nil
   140  	})
   141  	cst.cs.db.addDelayedSiacoinOutputsHeight(pb.Height, types.SiacoinOutputID{}, types.SiacoinOutput{})
   142  	_ = cst.cs.db.Update(func(tx *bolt.Tx) error {
   143  		applyMaturedSiacoinOutputs(tx, pb)
   144  		return nil
   145  	})
   146  }
   147  
   148  // TestApplyMissedStorageProof probes the applyMissedStorageProof method of the
   149  // consensus set.
   150  func TestApplyMissedStorageProof(t *testing.T) {
   151  	if testing.Short() {
   152  		t.SkipNow()
   153  	}
   154  	cst, err := createConsensusSetTester(t.Name())
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	defer cst.closeCst()
   159  
   160  	// Create a block node.
   161  	pb := new(processedBlock)
   162  	pb.Height = cst.cs.height()
   163  
   164  	// Create a file contract that's expiring and has 1 missed proof output.
   165  	expiringFC := types.FileContract{
   166  		Payout:             types.NewCurrency64(300e3),
   167  		WindowEnd:          pb.Height,
   168  		MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(290e3)}},
   169  	}
   170  	// Assign the contract a 0-id.
   171  	cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC)
   172  	cst.cs.db.addFCExpirations(pb.Height)
   173  	cst.cs.db.addFCExpirationsHeight(pb.Height, types.FileContractID{})
   174  	cst.cs.applyMissedStorageProof(pb, types.FileContractID{})
   175  	exists := cst.cs.db.inFileContracts(types.FileContractID{})
   176  	if exists {
   177  		t.Error("file contract was not consumed in missed storage proof")
   178  	}
   179  	spoid := types.FileContractID{}.StorageProofOutputID(types.ProofMissed, 0)
   180  	exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid)
   181  	if !exists {
   182  		t.Error("missed proof output was never created")
   183  	}
   184  	exists = cst.cs.db.inSiacoinOutputs(spoid)
   185  	if exists {
   186  		t.Error("storage proof output made it into the siacoin output set")
   187  	}
   188  	exists = cst.cs.db.inFileContracts(types.FileContractID{})
   189  	if exists {
   190  		t.Error("file contract remains after expiration")
   191  	}
   192  
   193  	// Trigger the debug panics.
   194  	// not exist.
   195  	defer func() {
   196  		r := recover()
   197  		if r != errNilItem {
   198  			t.Error(r)
   199  		}
   200  	}()
   201  	defer func() {
   202  		r := recover()
   203  		if r != errNilItem {
   204  			t.Error(r)
   205  		}
   206  		// Trigger errMissingFileContract
   207  		cst.cs.applyMissedStorageProof(pb, types.FileContractID(spoid))
   208  	}()
   209  	defer func() {
   210  		r := recover()
   211  		if r != errNilItem {
   212  			t.Error(r)
   213  		}
   214  
   215  		// Trigger errStorageProofTiming
   216  		expiringFC.WindowEnd = 0
   217  		cst.cs.applyMissedStorageProof(pb, types.FileContractID{})
   218  	}()
   219  	defer func() {
   220  		r := recover()
   221  		if r != errNilItem {
   222  			t.Error(r)
   223  		}
   224  
   225  		// Trigger errPayoutsAlreadyPaid from siacoin outputs.
   226  		cst.cs.db.rmDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid)
   227  		cst.cs.db.addSiacoinOutputs(spoid, types.SiacoinOutput{})
   228  		cst.cs.applyMissedStorageProof(pb, types.FileContractID{})
   229  	}()
   230  	// Trigger errPayoutsAlreadyPaid from delayed outputs.
   231  	cst.cs.db.rmFileContracts(types.FileContractID{})
   232  	cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC)
   233  	cst.cs.db.addDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid, types.SiacoinOutput{})
   234  	cst.cs.applyMissedStorageProof(pb, types.FileContractID{})
   235  }
   236  */
   237  
   238  // TestApplyFileContractMaintenance probes the applyFileContractMaintenance
   239  // method of the consensus set.
   240  /*
   241  func TestApplyFileContractMaintenance(t *testing.T) {
   242  	if testing.Short() {
   243  		t.SkipNow()
   244  	}
   245  	cst, err := createConsensusSetTester(t.Name())
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	defer cst.closeCst()
   250  
   251  	// Create a block node.
   252  	pb := new(processedBlock)
   253  	pb.Height = cst.cs.height()
   254  
   255  	// Create a file contract that's expiring and has 1 missed proof output.
   256  	expiringFC := types.FileContract{
   257  		Payout:             types.NewCurrency64(300e3),
   258  		WindowEnd:          pb.Height,
   259  		MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(290e3)}},
   260  	}
   261  	// Assign the contract a 0-id.
   262  	cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC)
   263  	cst.cs.db.addFCExpirations(pb.Height)
   264  	cst.cs.db.addFCExpirationsHeight(pb.Height, types.FileContractID{})
   265  	err = cst.cs.db.Update(func(tx *bolt.Tx) error {
   266  		applyFileContractMaintenance(tx, pb)
   267  		return nil
   268  	})
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	exists := cst.cs.db.inFileContracts(types.FileContractID{})
   273  	if exists {
   274  		t.Error("file contract was not consumed in missed storage proof")
   275  	}
   276  	spoid := types.FileContractID{}.StorageProofOutputID(types.ProofMissed, 0)
   277  	exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid)
   278  	if !exists {
   279  		t.Error("missed proof output was never created")
   280  	}
   281  	exists = cst.cs.db.inSiacoinOutputs(spoid)
   282  	if exists {
   283  		t.Error("storage proof output made it into the siacoin output set")
   284  	}
   285  	exists = cst.cs.db.inFileContracts(types.FileContractID{})
   286  	if exists {
   287  		t.Error("file contract remains after expiration")
   288  	}
   289  }
   290  */