decred.org/dcrwallet/v3@v3.1.0/wallet/udb/stakevalidation_test.go (about)

     1  // Copyright (c) 2016 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package udb
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"testing"
    11  	"time"
    12  
    13  	_ "decred.org/dcrwallet/v3/wallet/internal/bdb"
    14  	"decred.org/dcrwallet/v3/wallet/walletdb"
    15  	"github.com/decred/dcrd/dcrutil/v4"
    16  	gcs2 "github.com/decred/dcrd/gcs/v4"
    17  	"github.com/decred/dcrd/wire"
    18  )
    19  
    20  func insertMainChainHeaders(s *Store, dbtx walletdb.ReadWriteTx,
    21  	headerData []BlockHeaderData, filters []*gcs2.FilterV2) error {
    22  
    23  	ns := dbtx.ReadWriteBucket(wtxmgrBucketKey)
    24  
    25  	for i := range headerData {
    26  		h := &headerData[i]
    27  		f := filters[i]
    28  		header := new(wire.BlockHeader)
    29  		err := header.Deserialize(bytes.NewReader(h.SerializedHeader[:]))
    30  		if err != nil {
    31  			return err
    32  		}
    33  		err = s.ExtendMainChain(ns, header, f)
    34  		if err != nil {
    35  			return err
    36  		}
    37  	}
    38  	return nil
    39  }
    40  
    41  func TestStakeInvalidationOfTip(t *testing.T) {
    42  	ctx := context.Background()
    43  	db, _, s, _, teardown, err := cloneDB(ctx, "stake_inv_of_tip.kv")
    44  	defer teardown()
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  
    49  	g := makeBlockGenerator()
    50  	block1Header := g.generate(dcrutil.BlockValid)
    51  	block2Header := g.generate(dcrutil.BlockValid)
    52  	block3Header := g.generate(0)
    53  
    54  	block1Tx := wire.MsgTx{
    55  		TxOut: []*wire.TxOut{{Value: 2e8}},
    56  	}
    57  	block2Tx := wire.MsgTx{
    58  		TxIn: []*wire.TxIn{
    59  			{
    60  				PreviousOutPoint: wire.OutPoint{
    61  					Hash:  block1Tx.TxHash(),
    62  					Index: 0,
    63  					Tree:  0,
    64  				},
    65  			},
    66  		},
    67  		TxOut: []*wire.TxOut{{Value: 1e8}},
    68  	}
    69  	block1TxRec, err := NewTxRecordFromMsgTx(&block1Tx, time.Time{})
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	block2TxRec, err := NewTxRecordFromMsgTx(&block2Tx, time.Time{})
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  
    78  	err = walletdb.Update(ctx, db, func(dbtx walletdb.ReadWriteTx) error {
    79  		err := s.InsertMemPoolTx(dbtx, block1TxRec)
    80  		if err != nil {
    81  			return err
    82  		}
    83  		err = s.AddCredit(dbtx, block1TxRec, nil, 0, false, 0)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		err = s.InsertMemPoolTx(dbtx, block2TxRec)
    88  		if err != nil {
    89  			return err
    90  		}
    91  		err = s.AddCredit(dbtx, block2TxRec, nil, 0, false, 0)
    92  		if err != nil {
    93  			return err
    94  		}
    95  
    96  		bal, err := s.AccountBalance(dbtx, 0, 0)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		if bal.Total != 1e8 {
   101  			t.Errorf("Wrong balance before mining either transaction: %v", bal)
   102  		}
   103  
   104  		headerData := makeHeaderDataSlice(block1Header, block2Header)
   105  		filters := emptyFilters(2)
   106  		err = insertMainChainHeaders(s, dbtx, headerData, filters)
   107  		if err != nil {
   108  			return err
   109  		}
   110  
   111  		err = s.InsertMinedTx(dbtx, block1TxRec, &headerData[0].BlockHash)
   112  		if err != nil {
   113  			return err
   114  		}
   115  		err = s.InsertMinedTx(dbtx, block2TxRec, &headerData[1].BlockHash)
   116  		if err != nil {
   117  			return err
   118  		}
   119  
   120  		// At this point there should only be one credit for the tx in block 2.
   121  		bal, err = s.AccountBalance(dbtx, 1, 0)
   122  		if err != nil {
   123  			return err
   124  		}
   125  		if bal.Total != dcrutil.Amount(block2Tx.TxOut[0].Value) {
   126  			t.Errorf("Wrong balance: expected %v got %v",
   127  				dcrutil.Amount(block2Tx.TxOut[0].Value), bal)
   128  		}
   129  		credits, err := s.UnspentOutputs(dbtx)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		if len(credits) != 1 {
   134  			t.Errorf("Expected only 1 credit, got %v", len(credits))
   135  			return nil
   136  		}
   137  		if credits[0].Hash != block2Tx.TxHash() {
   138  			t.Errorf("Credit hash does match tx from block 2")
   139  			return nil
   140  		}
   141  		if credits[0].Amount != dcrutil.Amount(block2Tx.TxOut[0].Value) {
   142  			t.Errorf("Credit value does not match tx output 0 from block 2")
   143  			return nil
   144  		}
   145  
   146  		// Add the next block header which invalidates the regular tx tree of
   147  		// block 2.
   148  		t.Log("Invalidating block 2")
   149  		headerData = makeHeaderDataSlice(block3Header)
   150  		filters = emptyFilters(1)
   151  		err = insertMainChainHeaders(s, dbtx, headerData, filters)
   152  		if err != nil {
   153  			return err
   154  		}
   155  
   156  		// Now the transaction in block 2 is invalidated.  There should only be
   157  		// one unspent output, from block 1.
   158  		bal, err = s.AccountBalance(dbtx, 1, 0)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		if bal.Total != dcrutil.Amount(block1Tx.TxOut[0].Value) {
   163  			t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block1Tx.TxOut[0].Value), bal)
   164  		}
   165  		credits, err = s.UnspentOutputs(dbtx)
   166  		if err != nil {
   167  			return err
   168  		}
   169  		if len(credits) != 1 {
   170  			t.Errorf("Expected only 1 credit, got %v", len(credits))
   171  			return nil
   172  		}
   173  		if credits[0].Hash != block1Tx.TxHash() {
   174  			t.Errorf("Credit hash does not match tx from block 1")
   175  			return nil
   176  		}
   177  		if credits[0].Amount != dcrutil.Amount(block1Tx.TxOut[0].Value) {
   178  			t.Errorf("Credit value does not match tx output 0 from block 1")
   179  			return nil
   180  		}
   181  
   182  		return nil
   183  	})
   184  	if err != nil {
   185  		t.Error(err)
   186  	}
   187  }