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 }