gitlab.com/jokerrs1/Sia@v1.3.2/modules/wallet/money_test.go (about) 1 package wallet 2 3 import ( 4 "sort" 5 "testing" 6 7 "github.com/NebulousLabs/Sia/modules" 8 "github.com/NebulousLabs/Sia/types" 9 ) 10 11 // TestSendSiacoins probes the SendSiacoins method of the wallet. 12 func TestSendSiacoins(t *testing.T) { 13 if testing.Short() { 14 t.SkipNow() 15 } 16 wt, err := createWalletTester(t.Name(), &modules.ProductionDependencies{}) 17 if err != nil { 18 t.Fatal(err) 19 } 20 defer wt.closeWt() 21 22 // Get the initial balance - should be 1 block. The unconfirmed balances 23 // should be 0. 24 confirmedBal, _, _ := wt.wallet.ConfirmedBalance() 25 unconfirmedOut, unconfirmedIn := wt.wallet.UnconfirmedBalance() 26 if !confirmedBal.Equals(types.CalculateCoinbase(1)) { 27 t.Error("unexpected confirmed balance") 28 } 29 if !unconfirmedOut.Equals(types.ZeroCurrency) { 30 t.Error("unconfirmed balance should be 0") 31 } 32 if !unconfirmedIn.Equals(types.ZeroCurrency) { 33 t.Error("unconfirmed balance should be 0") 34 } 35 36 // Send 5000 hastings. The wallet will automatically add a fee. Outgoing 37 // unconfirmed siacoins - incoming unconfirmed siacoins should equal 5000 + 38 // fee. 39 sendValue := types.SiacoinPrecision.Mul64(3) 40 _, tpoolFee := wt.wallet.tpool.FeeEstimation() 41 tpoolFee = tpoolFee.Mul64(750) 42 _, err = wt.wallet.SendSiacoins(sendValue, types.UnlockHash{}) 43 if err != nil { 44 t.Fatal(err) 45 } 46 confirmedBal2, _, _ := wt.wallet.ConfirmedBalance() 47 unconfirmedOut2, unconfirmedIn2 := wt.wallet.UnconfirmedBalance() 48 if !confirmedBal2.Equals(confirmedBal) { 49 t.Error("confirmed balance changed without introduction of blocks") 50 } 51 if !unconfirmedOut2.Equals(unconfirmedIn2.Add(sendValue).Add(tpoolFee)) { 52 t.Error("sending siacoins appears to be ineffective") 53 } 54 55 // Move the balance into the confirmed set. 56 b, _ := wt.miner.FindBlock() 57 err = wt.cs.AcceptBlock(b) 58 if err != nil { 59 t.Fatal(err) 60 } 61 confirmedBal3, _, _ := wt.wallet.ConfirmedBalance() 62 unconfirmedOut3, unconfirmedIn3 := wt.wallet.UnconfirmedBalance() 63 if !confirmedBal3.Equals(confirmedBal2.Add(types.CalculateCoinbase(2)).Sub(sendValue).Sub(tpoolFee)) { 64 t.Error("confirmed balance did not adjust to the expected value") 65 } 66 if !unconfirmedOut3.Equals(types.ZeroCurrency) { 67 t.Error("unconfirmed balance should be 0") 68 } 69 if !unconfirmedIn3.Equals(types.ZeroCurrency) { 70 t.Error("unconfirmed balance should be 0") 71 } 72 } 73 74 // TestIntegrationSendOverUnder sends too many siacoins, resulting in an error, 75 // followed by sending few enough siacoins that the send should complete. 76 // 77 // This test is here because of a bug found in production where the wallet 78 // would mark outputs as spent before it knew that there was enough money to 79 // complete the transaction. This meant that, after trying to send too many 80 // coins, all outputs got marked 'sent'. This test reproduces those conditions 81 // to ensure it does not happen again. 82 func TestIntegrationSendOverUnder(t *testing.T) { 83 if testing.Short() { 84 t.SkipNow() 85 } 86 wt, err := createWalletTester(t.Name(), &modules.ProductionDependencies{}) 87 if err != nil { 88 t.Fatal(err) 89 } 90 defer wt.closeWt() 91 92 // Spend too many siacoins. 93 tooManyCoins := types.SiacoinPrecision.Mul64(1e12) 94 _, err = wt.wallet.SendSiacoins(tooManyCoins, types.UnlockHash{}) 95 if err == nil { 96 t.Error("low balance err not returned after attempting to send too many coins:", err) 97 } 98 99 // Spend a reasonable amount of siacoins. 100 reasonableCoins := types.SiacoinPrecision.Mul64(100e3) 101 _, err = wt.wallet.SendSiacoins(reasonableCoins, types.UnlockHash{}) 102 if err != nil { 103 t.Error("unexpected error: ", err) 104 } 105 } 106 107 // TestIntegrationSpendHalfHalf spends more than half of the coins, and then 108 // more than half of the coins again, to make sure that the wallet is not 109 // reusing outputs that it has already spent. 110 func TestIntegrationSpendHalfHalf(t *testing.T) { 111 if testing.Short() { 112 t.SkipNow() 113 } 114 wt, err := createWalletTester(t.Name(), &modules.ProductionDependencies{}) 115 if err != nil { 116 t.Fatal(err) 117 } 118 defer wt.closeWt() 119 120 // Spend more than half of the coins twice. 121 halfPlus := types.SiacoinPrecision.Mul64(200e3) 122 _, err = wt.wallet.SendSiacoins(halfPlus, types.UnlockHash{}) 123 if err != nil { 124 t.Error("unexpected error: ", err) 125 } 126 _, err = wt.wallet.SendSiacoins(halfPlus, types.UnlockHash{1}) 127 if err == nil { 128 t.Error("wallet appears to be reusing outputs when building transactions: ", err) 129 } 130 } 131 132 // TestIntegrationSpendUnconfirmed spends an unconfirmed siacoin output. 133 func TestIntegrationSpendUnconfirmed(t *testing.T) { 134 if testing.Short() { 135 t.SkipNow() 136 } 137 wt, err := createWalletTester(t.Name(), &modules.ProductionDependencies{}) 138 if err != nil { 139 t.Fatal(err) 140 } 141 defer wt.closeWt() 142 143 // Spend the only output. 144 halfPlus := types.SiacoinPrecision.Mul64(200e3) 145 _, err = wt.wallet.SendSiacoins(halfPlus, types.UnlockHash{}) 146 if err != nil { 147 t.Error("unexpected error: ", err) 148 } 149 someMore := types.SiacoinPrecision.Mul64(75e3) 150 _, err = wt.wallet.SendSiacoins(someMore, types.UnlockHash{1}) 151 if err != nil { 152 t.Error("wallet appears to be struggling to spend unconfirmed outputs") 153 } 154 } 155 156 // TestIntegrationSortedOutputsSorting checks that the outputs are being correctly sorted 157 // by the currency value. 158 func TestIntegrationSortedOutputsSorting(t *testing.T) { 159 if testing.Short() { 160 t.SkipNow() 161 } 162 so := sortedOutputs{ 163 ids: []types.SiacoinOutputID{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}}, 164 outputs: []types.SiacoinOutput{ 165 {Value: types.NewCurrency64(2)}, 166 {Value: types.NewCurrency64(3)}, 167 {Value: types.NewCurrency64(4)}, 168 {Value: types.NewCurrency64(7)}, 169 {Value: types.NewCurrency64(6)}, 170 {Value: types.NewCurrency64(0)}, 171 {Value: types.NewCurrency64(1)}, 172 {Value: types.NewCurrency64(5)}, 173 }, 174 } 175 sort.Sort(so) 176 177 expectedIDSorting := []types.SiacoinOutputID{{5}, {6}, {0}, {1}, {2}, {7}, {4}, {3}} 178 for i := uint64(0); i < 8; i++ { 179 if so.ids[i] != expectedIDSorting[i] { 180 t.Error("an id is out of place: ", i) 181 } 182 if !so.outputs[i].Value.Equals64(i) { 183 t.Error("a value is out of place: ", i) 184 } 185 } 186 } 187 188 // TestSendSiacoinsFailed checks if SendSiacoins and SendSiacoinsMulti behave 189 // correctly when funcing the Transaction succeeded but accepting it didn't. 190 func TestSendSiacoinsAcceptTxnSetFailed(t *testing.T) { 191 if testing.Short() { 192 t.SkipNow() 193 } 194 deps := &dependencySendSiacoinsInterrupted{} 195 wt, err := createWalletTester(t.Name(), deps) 196 if err != nil { 197 t.Fatal(err) 198 } 199 defer wt.closeWt() 200 201 // There should be no spent transactions in the database at this point 202 wt.wallet.mu.Lock() 203 wt.wallet.syncDB() 204 if wt.wallet.dbTx.Bucket(bucketSpentOutputs).Stats().KeyN != 0 { 205 wt.wallet.mu.Unlock() 206 t.Fatal("bucketSpentOutputs isn't empty") 207 } 208 wt.wallet.mu.Unlock() 209 210 // Try to send coins using SendSiacoinsMulti 211 numOutputs := 10 212 scos := make([]types.SiacoinOutput, numOutputs) 213 for i := 0; i < numOutputs; i++ { 214 uc, err := wt.wallet.NextAddress() 215 if err != nil { 216 t.Fatal(err) 217 } 218 scos[i].Value = types.SiacoinPrecision 219 scos[i].UnlockHash = uc.UnlockHash() 220 } 221 deps.fail() 222 _, err = wt.wallet.SendSiacoinsMulti(scos) 223 if err == nil { 224 t.Fatal("SendSiacoinsMulti should have failed but didn't") 225 } 226 227 // Send some coins using SendSiacoins 228 uc, err := wt.wallet.NextAddress() 229 if err != nil { 230 t.Fatal(err) 231 } 232 deps.fail() 233 _, err = wt.wallet.SendSiacoins(types.SiacoinPrecision, uc.UnlockHash()) 234 if err == nil { 235 t.Fatal("SendSiacoins should have failed but didn't") 236 } 237 238 // There should still be no spent transactions in the database 239 wt.wallet.mu.Lock() 240 wt.wallet.syncDB() 241 bucket := wt.wallet.dbTx.Bucket(bucketSpentOutputs) 242 if bucket.Stats().KeyN != 0 { 243 wt.wallet.mu.Unlock() 244 t.Fatal("bucketSpentOutputs isn't empty") 245 } 246 wt.wallet.mu.Unlock() 247 248 // Send the money again without the failing dependency 249 _, err = wt.wallet.SendSiacoinsMulti(scos) 250 if err != nil { 251 t.Fatalf("SendSiacoinsMulti failed: %v", err) 252 } 253 254 // Send some coins using SendSiacoins 255 _, err = wt.wallet.SendSiacoins(types.SiacoinPrecision, uc.UnlockHash()) 256 if err != nil { 257 t.Fatalf("SendSiacoins failed: %v", err) 258 } 259 }