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