diff --git a/shell.go b/shell.go index 0e197e5a..a1b85d9a 100644 --- a/shell.go +++ b/shell.go @@ -206,7 +206,7 @@ func Bal(args []string) error { if err != nil { return err } - var score int64 + var score, confScore int64 for i, u := range allUtxos { fmt.Printf("\tutxo %d height %d %s key:%d amt %d", i, u.AtHeight, u.Op.String(), u.KeyIdx, u.Value) @@ -215,6 +215,9 @@ func Bal(args []string) error { } fmt.Printf("\n") score += u.Value + if u.AtHeight != 0 { + confScore += u.Value + } } height, _ := SCon.TS.GetDBSyncHeight() @@ -228,7 +231,7 @@ func Bal(args []string) error { } fmt.Printf("Total known utxos: %d\n", len(allUtxos)) - fmt.Printf("Total spendable coin: %d\n", score) + fmt.Printf("Total coin: %d confirmed: %d\n", score, confScore) fmt.Printf("DB sync height: %d\n", height) return nil } @@ -266,20 +269,22 @@ func Adr(args []string) error { } // Fan generates a bunch of fanout. Only for testing, can be expensive. -// syntax: fan numOutputs valOutputs witty +// syntax: fan adr numOutputs valOutputs witty func Fan(args []string) error { if len(args) < 3 { - return fmt.Errorf("fan syntax: fan numOutputs valOutputs witty") + return fmt.Errorf("fan syntax: fan adr numOutputs valOutputs") } - numOutputs, err := strconv.ParseInt(args[0], 10, 64) + + adr, err := btcutil.DecodeAddress(args[0], SCon.TS.Param) + if err != nil { + fmt.Printf("error parsing %s as address\t", args[0]) + return err + } + numOutputs, err := strconv.ParseInt(args[1], 10, 64) if err != nil { return err } - valOutputs, err := strconv.ParseInt(args[1], 10, 64) - if err != nil { - return err - } - wittynum, err := strconv.ParseInt(args[2], 10, 64) + valOutputs, err := strconv.ParseInt(args[2], 10, 64) if err != nil { return err } @@ -287,21 +292,8 @@ func Fan(args []string) error { adrs := make([]btcutil.Address, numOutputs) amts := make([]int64, numOutputs) - oAdr, err := SCon.TS.NewAdr() - if err != nil { - return err - } for i := int64(0); i < numOutputs; i++ { - if wittynum != 0 { - wAdr, err := btcutil.NewAddressWitnessPubKeyHash( - oAdr.ScriptAddress(), SCon.TS.Param) - if err != nil { - return err - } - adrs[i] = wAdr - } else { - adrs[i] = oAdr - } + adrs[i] = adr amts[i] = valOutputs + i } @@ -331,21 +323,21 @@ func Send(args []string) error { } // need args, fail if len(args) < 2 { - return fmt.Errorf("need args: ssend amount(satoshis) address wit?") + return fmt.Errorf("need args: ssend address amount(satoshis) wit?") } - - amt, err := strconv.ParseInt(args[0], 10, 64) + adr, err := btcutil.DecodeAddress(args[0], SCon.TS.Param) + if err != nil { + fmt.Printf("error parsing %s as address\t", args[0]) + return err + } + amt, err := strconv.ParseInt(args[1], 10, 64) if err != nil { return err } if amt < 1000 { return fmt.Errorf("can't send %d, too small", amt) } - adr, err := btcutil.DecodeAddress(args[1], SCon.TS.Param) - if err != nil { - fmt.Printf("error parsing %s as address\t", args[1]) - return err - } + fmt.Printf("send %d to address: %s \n", amt, adr.String()) diff --git a/uspv/sortsignsend.go b/uspv/sortsignsend.go index 1d294483..c41d806d 100644 --- a/uspv/sortsignsend.go +++ b/uspv/sortsignsend.go @@ -43,7 +43,7 @@ func (s *SPVCon) Rebroadcast() { return } -// make utxo slices sortable +// make utxo slices sortable -- same as txsort type utxoSlice []Utxo // Sort utxos just like txins -- Len, Less, Swap @@ -68,6 +68,13 @@ func (s utxoSlice) Less(i, j int) bool { return bytes.Compare(ihash[:], jhash[:]) == -1 } +type utxoByAmt []Utxo + +// utxoByAmts get sorted by utxo value +func (s utxoByAmt) Len() int { return len(s) } +func (s utxoByAmt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s utxoByAmt) Less(i, j int) bool { return s[i].Value < s[j].Value } + func (s *SPVCon) NewOutgoingTx(tx *wire.MsgTx) error { txid := tx.TxSha() // assign height of zero for txs we create @@ -99,14 +106,22 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error { var err error var score, totalSend, fee int64 dustCutoff := int64(20000) // below this amount, just give to miners - satPerByte := int64(30) // satoshis per byte fee; have as arg later - allUtxos, err := s.TS.GetAllUtxos() + satPerByte := int64(80) // satoshis per byte fee; have as arg later + rawUtxos, err := s.TS.GetAllUtxos() if err != nil { return err } + var allUtxos utxoByAmt + // start with utxos sorted by value. - for _, utxo := range allUtxos { + for _, utxo := range rawUtxos { score += utxo.Value + allUtxos = append(allUtxos, *utxo) + } + sort.Sort(sort.Reverse(allUtxos)) + // sort.Reverse(allUtxos) + for _, u := range allUtxos { + fmt.Printf("%d ", u.Value) } for _, amt := range sendAmts { totalSend += amt @@ -136,8 +151,13 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error { // add utxos until we've had enough nokori := totalSend // nokori is how much is needed on input side for _, utxo := range allUtxos { + // skip unconfirmed. Or de-prioritize? + // if utxo.AtHeight == 0 { + // continue + // } + // yeah, lets add this utxo! - ins = append(ins, *utxo) + ins = append(ins, utxo) // as we add utxos, fill in sigscripts // generate previous pkscripts (subscritpt?) for all utxos // then make txins with the utxo and prevpk, and insert them into the tx @@ -205,6 +225,11 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error { // tx is ready for signing, sigStash := make([][]byte, len(ins)) witStash := make([][][]byte, len(ins)) + + // generate tx-wide hashCache for segwit stuff + // middle index number doesn't matter for sighashAll. + hCache := txscript.CalcHashCache(tx, 0, txscript.SigHashAll) + for i, txin := range tx.TxIn { // pick key child, err := s.TS.rootPrivKey.Child( @@ -221,7 +246,7 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error { // sign into stash if ins[i].IsWit { witStash[i], err = txscript.WitnessScript( - tx, i, ins[i].Value, txin.SignatureScript, + tx, hCache, i, ins[i].Value, txin.SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err diff --git a/uspv/utxodb.go b/uspv/utxodb.go index 8383fe31..4f1c1746 100644 --- a/uspv/utxodb.go +++ b/uspv/utxodb.go @@ -368,16 +368,14 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) { spentOPs := make([][]byte, len(tx.TxIn)) // before entering into db, serialize all inputs of the ingested tx for i, txin := range tx.TxIn { - nOP, err := outPointToBytes(&txin.PreviousOutPoint) + spentOPs[i], err = outPointToBytes(&txin.PreviousOutPoint) if err != nil { return hits, err } - spentOPs[i] = nOP } // also generate PKscripts for all addresses (maybe keep storing these?) for _, adr := range ts.Adrs { // iterate through all our addresses - // convert regular address to witness address. (split adrs later) wa, err := btcutil.NewAddressWitnessPubKeyHash( adr.PkhAdr.ScriptAddress(), ts.Param) @@ -437,59 +435,49 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) { // first see if we lose utxos // iterate through duffel bag and look for matches // this makes us lose money, which is regrettable, but we need to know. - - var delOPs [][]byte - // fmt.Printf("%d nOP to iterate over\n", len(spentOPs)) + // var delOPs [][]byte for _, nOP := range spentOPs { - duf.ForEach(func(k, v []byte) error { - if bytes.Equal(k, nOP) { // matched, we lost utxo - fmt.Printf("will delete point %x\n", k) - hits++ - // do all this just to figure out value we lost - x := make([]byte, len(k)+len(v)) - copy(x, k) - y := make([]byte, len(k)) - // mark utxo for deletion - copy(y, k) - delOPs = append(delOPs, y) - - copy(x[len(k):], v) - lostTxo, err := UtxoFromBytes(x) - if err != nil { - return err - } - - // after marking for deletion, save stxo to old bucket - var st Stxo // generate spent txo - st.Utxo = lostTxo // assign outpoint - st.SpendHeight = height // spent at height - st.SpendTxid = tx.TxSha() // spent by txid - stxb, err := st.ToBytes() // serialize - if err != nil { - return err - } - err = old.Put(k, stxb) // write k:v outpoint:stxo bytes - if err != nil { - return err - } - // store this relevant tx - sha := tx.TxSha() - var buf bytes.Buffer - tx.SerializeWitness(&buf) // always store witness version - err = txns.Put(sha.Bytes(), buf.Bytes()) - if err != nil { - return err - } - - return nil // matched utxo k, won't match another + v := duf.Get(nOP) + if v != nil { + hits++ + // do all this just to figure out value we lost + x := make([]byte, len(nOP)+len(v)) + copy(x, nOP) + copy(x[len(nOP):], v) + lostTxo, err := UtxoFromBytes(x) + if err != nil { + return err } - return nil // no match - }) - // delete after done with foreach + + // after marking for deletion, save stxo to old bucket + var st Stxo // generate spent txo + st.Utxo = lostTxo // assign outpoint + st.SpendHeight = height // spent at height + st.SpendTxid = tx.TxSha() // spent by txid + stxb, err := st.ToBytes() // serialize + if err != nil { + return err + } + err = old.Put(nOP, stxb) // write nOP:v outpoint:stxo bytes + if err != nil { + return err + } + // store this relevant tx + sha := tx.TxSha() + var buf bytes.Buffer + tx.SerializeWitness(&buf) // always store witness version + err = txns.Put(sha.Bytes(), buf.Bytes()) + if err != nil { + return err + } + // stash for deletion + // delOPs = append(delOPs, nOP) + } } - for _, y := range delOPs { - fmt.Printf("deleting outpoint %x\n", y) - err = duf.Delete(y) + + //delete everything even if it doesn't exist! + for _, dOP := range spentOPs { + err = duf.Delete(dOP) if err != nil { return err }