gitlab.com/jokerrs1/Sia@v1.3.2/node/api/transactionpool_test.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"net/url"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/NebulousLabs/Sia/encoding"
    12  	"github.com/NebulousLabs/Sia/types"
    13  )
    14  
    15  // TestTransactionPoolRawHandler verifies that the transaction pools' raw
    16  // transaction post endpoint works correctly.
    17  func TestTransactionPoolRawHandlerPOST(t *testing.T) {
    18  	if testing.Short() {
    19  		t.SkipNow()
    20  	}
    21  	t.Parallel()
    22  	st, err := createServerTester(t.Name())
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	// Spin up a second and fourth server tester, and get them all on the same
    28  	// block. The fourth server tester will be used later, after a third is
    29  	// created and used.
    30  	st2, err := blankServerTester(t.Name() + "-st2")
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	st4, err := blankServerTester(t.Name() + "-st4")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	err = fullyConnectNodes([]*serverTester{st, st2, st4})
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	// Reset the peers, giving them different ip addresses, preventing them
    44  	// from connecting to eachother.
    45  	err = st.server.Close()
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	err = st2.server.Close()
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	err = st4.server.Close()
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	st, err = assembleServerTester(st.walletKey, st.dir)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	defer st.panicClose()
    62  	st2, err = assembleServerTester(st2.walletKey, st2.dir)
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	defer st2.panicClose()
    67  	st4, err = assembleServerTester(st4.walletKey, st4.dir)
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	defer st4.panicClose()
    72  
    73  	// Create a transaction on one node and fetch it.
    74  	sentValue := types.SiacoinPrecision.Mul64(1000)
    75  	txns, err := st.wallet.SendSiacoins(sentValue, types.UnlockHash{})
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	lastTxn := txns[len(txns)-1]
    80  	var trg TpoolRawGET
    81  	err = st.getAPI("/tpool/raw/"+lastTxn.ID().String(), &trg)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	// Verify the correctness of the transaction.
    87  	var decodedTxn types.Transaction
    88  	err = encoding.Unmarshal(trg.Transaction, &decodedTxn)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	if decodedTxn.ID() != lastTxn.ID() {
    93  		t.Fatal("tpool raw get returned the wrong transaction")
    94  	}
    95  	// Verify the correctness of the parents.
    96  	var decodedParents []types.Transaction
    97  	err = encoding.Unmarshal(trg.Parents, &decodedParents)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  	if len(decodedParents) != len(txns)-1 {
   102  		t.Fatal("returned the incorrect number of parents")
   103  	}
   104  
   105  	// Transaction should not be visible on node 2.
   106  	var trg2 TpoolRawGET
   107  	err = st2.getAPI("/tpool/raw/"+lastTxn.ID().String(), &trg2)
   108  	if err.Error() != "transaction not found in transaction pool" {
   109  		t.Fatal("transaction should be missing initially from the second tpool")
   110  	}
   111  
   112  	// Try posting the transaction to node 2.
   113  	postValues := url.Values{}
   114  	postValues.Set("parents", string(trg.Parents))
   115  	postValues.Set("transaction", string(trg.Transaction))
   116  	err = st2.stdPostAPI("/tpool/raw", postValues)
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	// Verify that the two transactions returned from each server are
   122  	// identical.
   123  	err = st2.getAPI("/tpool/raw/"+lastTxn.ID().String(), &trg2)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if !bytes.Equal(trg2.Parents, trg.Parents) {
   128  		t.Error("transaction parents mismatch")
   129  	}
   130  	if !bytes.Equal(trg2.Transaction, trg.Transaction) {
   131  		t.Error("transaction parents mismatch")
   132  	}
   133  
   134  	// Create a third server tester, connect it to the second one.
   135  	st3, err := blankServerTester(t.Name() + "-st3")
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	defer st3.server.panicClose()
   140  	err = fullyConnectNodes([]*serverTester{st2, st3})
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	// Posting the raw transaction to the second server again should cause it
   145  	// to be broadcast to the third server.
   146  	err = st2.stdPostAPI("/tpool/raw", postValues)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	retry(100, time.Millisecond*100, func() error {
   151  		return st3.getAPI("/tpool/raw/"+lastTxn.ID().String(), &trg)
   152  	})
   153  
   154  	// Mine a block on the first server, which should clear its transaction
   155  	// pool.
   156  	_, err = st.miner.AddBlock()
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	err = st.getAPI("/tpool/raw/"+lastTxn.ID().String(), &trg)
   161  	if err.Error() != "transaction not found in transaction pool" {
   162  		t.Fatal("transaction should be gone from the pool after mining a block")
   163  	}
   164  
   165  	// Convert the returned transactions to base64, which is how they will be
   166  	// presetned to someone using curl. Submit those to the POST endpoint. The
   167  	// POST endpoint should gracefully handle that submission as base64.
   168  	//
   169  	// The first 3 st's all have the transactions already, so now we'll use st4.
   170  	b64Parents := base64.StdEncoding.EncodeToString(trg.Parents)
   171  	b64Transaction := base64.StdEncoding.EncodeToString(trg.Transaction)
   172  	postValues = url.Values{}
   173  	postValues.Set("parents", b64Parents)
   174  	postValues.Set("transaction", b64Transaction)
   175  	err = st4.stdPostAPI("/tpool/raw", postValues)
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  }
   180  
   181  // TestTransactionPoolFee tests the /tpool/fee endpoint.
   182  func TestTransactionPoolFee(t *testing.T) {
   183  	if testing.Short() {
   184  		t.SkipNow()
   185  	}
   186  	t.Parallel()
   187  	st, err := createServerTester(t.Name())
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	var fees TpoolFeeGET
   193  	err = st.getAPI("/tpool/fee", &fees)
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	min, max := st.tpool.FeeEstimation()
   199  	if !min.Equals(fees.Minimum) || !max.Equals(fees.Maximum) {
   200  		t.Fatal("fee mismatch")
   201  	}
   202  }
   203  
   204  // TestTransactionPoolConfirmed tests the /tpool/confirmed endpoint.
   205  func TestTransactionPoolConfirmed(t *testing.T) {
   206  	if testing.Short() {
   207  		t.SkipNow()
   208  	}
   209  	t.Parallel()
   210  	st, err := createServerTester(t.Name())
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  
   215  	// Create a transaction.
   216  	sentValue := types.SiacoinPrecision.Mul64(1000)
   217  	txns, err := st.wallet.SendSiacoins(sentValue, types.UnlockHash{})
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	txnID := txns[len(txns)-1].ID().String()
   222  
   223  	// Transaction should not be confirmed yet.
   224  	var tcg TpoolConfirmedGET
   225  	err = st.getAPI("/tpool/confirmed/"+txnID, &tcg)
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	} else if tcg.Confirmed {
   229  		t.Fatal("transaction should not be confirmed")
   230  	}
   231  
   232  	// Mine the block containing the transaction
   233  	_, err = st.miner.AddBlock()
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	// Transaction should now be confirmed.
   239  	err = st.getAPI("/tpool/confirmed/"+txnID, &tcg)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	} else if !tcg.Confirmed {
   243  		t.Fatal("transaction should be confirmed")
   244  	}
   245  
   246  	// Check for a nonexistent transaction.
   247  	badID := strings.Repeat("0", len(txnID))
   248  	err = st.getAPI("/tpool/confirmed/"+badID, &tcg)
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	} else if tcg.Confirmed {
   252  		t.Fatal("transaction should not be confirmed")
   253  	}
   254  }