@@ -201,7 +201,7 @@ func (sw *SingleAddressWallet) Balance() (balance Balance, err error) {
201
201
balance .Immature = balance .Immature .Add (sco .SiacoinOutput .Value )
202
202
} else {
203
203
balance .Confirmed = balance .Confirmed .Add (sco .SiacoinOutput .Value )
204
- if time . Now (). After ( sw .locked [ sco .ID ] ) && ! tpoolSpent [sco .ID ] {
204
+ if ! sw .isLocked ( sco .ID ) && ! tpoolSpent [sco .ID ] {
205
205
balance .Spendable = balance .Spendable .Add (sco .SiacoinOutput .Value )
206
206
}
207
207
}
@@ -252,7 +252,7 @@ func (sw *SingleAddressWallet) SpendableOutputs() ([]SiacoinElement, error) {
252
252
// filter outputs that are either locked, in the pool or have not yet matured
253
253
unspent := utxos [:0 ]
254
254
for _ , sce := range utxos {
255
- if time . Now (). Before ( sw .locked [ sce .ID ] ) || inPool [sce .ID ] || bh < sce .MaturityHeight {
255
+ if sw .isLocked ( sce .ID ) || inPool [sce .ID ] || bh < sce .MaturityHeight {
256
256
continue
257
257
}
258
258
unspent = append (unspent , sce )
@@ -297,8 +297,14 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty
297
297
// remove immature, locked and spent outputs
298
298
cs := sw .cm .TipState ()
299
299
utxos := make ([]types.SiacoinElement , 0 , len (elements ))
300
+ var usedSum types.Currency
301
+ var immatureSum types.Currency
300
302
for _ , sce := range elements {
301
- if time .Now ().Before (sw .locked [sce .ID ]) || tpoolSpent [sce .ID ] || cs .Index .Height < sce .MaturityHeight {
303
+ if used := sw .isLocked (sce .ID ) || tpoolSpent [sce .ID ]; used {
304
+ usedSum = usedSum .Add (sce .SiacoinOutput .Value )
305
+ continue
306
+ } else if immature := cs .Index .Height < sce .MaturityHeight ; immature {
307
+ immatureSum = immatureSum .Add (sce .SiacoinOutput .Value )
302
308
continue
303
309
}
304
310
utxos = append (utxos , sce .SiacoinElement )
@@ -310,20 +316,22 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty
310
316
})
311
317
312
318
var unconfirmedUTXOs []types.SiacoinElement
319
+ var unconfirmedSum types.Currency
313
320
if useUnconfirmed {
314
321
for _ , sce := range tpoolUtxos {
315
- if sce .SiacoinOutput .Address != sw .addr || time . Now (). Before ( sw .locked [ sce .ID ] ) {
322
+ if sce .SiacoinOutput .Address != sw .addr || sw .isLocked ( sce .ID ) {
316
323
continue
317
324
}
318
325
unconfirmedUTXOs = append (unconfirmedUTXOs , sce )
326
+ unconfirmedSum = unconfirmedSum .Add (sce .SiacoinOutput .Value )
319
327
}
320
-
321
- // sort by value, descending
322
- sort .Slice (unconfirmedUTXOs , func (i , j int ) bool {
323
- return unconfirmedUTXOs [i ].SiacoinOutput .Value .Cmp (unconfirmedUTXOs [j ].SiacoinOutput .Value ) > 0
324
- })
325
328
}
326
329
330
+ // sort by value, descending
331
+ sort .Slice (unconfirmedUTXOs , func (i , j int ) bool {
332
+ return unconfirmedUTXOs [i ].SiacoinOutput .Value .Cmp (unconfirmedUTXOs [j ].SiacoinOutput .Value ) > 0
333
+ })
334
+
327
335
// fund the transaction using the largest utxos first
328
336
var selected []types.SiacoinElement
329
337
var inputSum types.Currency
@@ -339,19 +347,19 @@ func (sw *SingleAddressWallet) FundTransaction(txn *types.Transaction, amount ty
339
347
if inputSum .Cmp (amount ) < 0 && useUnconfirmed {
340
348
// try adding unconfirmed utxos.
341
349
for _ , sce := range unconfirmedUTXOs {
350
+ selected = append (selected , sce )
351
+ inputSum = inputSum .Add (sce .SiacoinOutput .Value )
342
352
if inputSum .Cmp (amount ) >= 0 {
343
353
break
344
354
}
345
- selected = append (selected , sce )
346
- inputSum = inputSum .Add (sce .SiacoinOutput .Value )
347
355
}
348
356
349
357
if inputSum .Cmp (amount ) < 0 {
350
358
// still not enough funds
351
- return nil , ErrNotEnoughFunds
359
+ return nil , fmt . Errorf ( "%w: inputs %v < needed %v (used: %v immature: %v unconfirmed: %v)" , ErrNotEnoughFunds , inputSum . String (), amount . String (), usedSum . String (), immatureSum . String (), unconfirmedSum . String ())
352
360
}
353
361
} else if inputSum .Cmp (amount ) < 0 {
354
- return nil , ErrNotEnoughFunds
362
+ return nil , fmt . Errorf ( "%w: inputs %v < needed %v (used: %v immature: %v" , ErrNotEnoughFunds , inputSum . String (), amount . String (), usedSum . String (), immatureSum . String ())
355
363
}
356
364
357
365
// check if remaining utxos should be defragged
@@ -496,7 +504,7 @@ func (sw *SingleAddressWallet) Redistribute(outputs int, amount, feePerByte type
496
504
// unused, matured and has the same value
497
505
utxos := make ([]types.SiacoinElement , 0 , len (elements ))
498
506
for _ , sce := range elements {
499
- inUse := time . Now (). After ( sw .locked [ sce .ID ] ) || inPool [sce .ID ]
507
+ inUse := sw .isLocked ( sce .ID ) || inPool [sce .ID ]
500
508
matured := bh >= sce .MaturityHeight
501
509
sameValue := sce .SiacoinOutput .Value .Equals (amount )
502
510
@@ -602,6 +610,12 @@ func (sw *SingleAddressWallet) ReleaseInputs(txns ...types.Transaction) {
602
610
}
603
611
}
604
612
613
+ // isLocked returns true if the siacoin output with given id is locked, this
614
+ // method must be called whilst holding the mutex lock.
615
+ func (sw * SingleAddressWallet ) isLocked (id types.Hash256 ) bool {
616
+ return time .Now ().Before (sw .locked [id ])
617
+ }
618
+
605
619
// IsRelevantTransaction returns true if the v1 transaction is relevant to the
606
620
// address
607
621
func IsRelevantTransaction (txn types.Transaction , addr types.Address ) bool {
0 commit comments