diff --git a/shell.go b/shell.go index bcc84647b..81b607f1b 100644 --- a/shell.go +++ b/shell.go @@ -212,11 +212,14 @@ func Bal(args []string) error { height, _ := SCon.TS.GetDBSyncHeight() for i, a := range SCon.TS.Adrs { - fmt.Printf("address %d %s\n", i, a.PkhAdr.String()) - } - for i, a := range SCon.TS.WitAdrs { - fmt.Printf("address %d %s [WIT]\n", i, a.PkhAdr.String()) + wa, err := btcutil.NewAddressWitnessPubKeyHash( + a.PkhAdr.ScriptAddress(), Params) + if err != nil { + return err + } + fmt.Printf("address %d %s OR %s\n", i, a.PkhAdr.String(), wa.String()) } + fmt.Printf("Total known utxos: %d\n", len(allUtxos)) fmt.Printf("Total spendable coin: %d\n", score) fmt.Printf("DB sync height: %d\n", height) @@ -226,18 +229,18 @@ func Bal(args []string) error { // Adr makes a new address. func Adr(args []string) error { - // if there's an arg, make 10 regular adrs + // if there's an arg, make 10 adrs if len(args) > 0 { for i := 0; i < 10; i++ { - _, err := SCon.TS.NewAdr(false) + _, err := SCon.TS.NewAdr() if err != nil { return err } } } - // always make one segwit - a, err := SCon.TS.NewAdr(true) + // always make one + a, err := SCon.TS.NewAdr() if err != nil { return err } @@ -271,10 +274,7 @@ func Send(args []string) error { if len(args) < 2 { return fmt.Errorf("need args: ssend amount(satoshis) address wit?") } - var wit bool // whether to send to p2wpkh - if len(args) > 2 { - wit = true - } + amt, err := strconv.ParseInt(args[0], 10, 64) if err != nil { return err @@ -290,7 +290,7 @@ func Send(args []string) error { fmt.Printf("send %d to address: %s \n", amt, adr.String()) - err = SCon.SendCoins(adr, amt, wit) + err = SCon.SendCoins(adr, amt) if err != nil { return err } diff --git a/uspv/sortsignsend.go b/uspv/sortsignsend.go index 894978e9e..93813a236 100644 --- a/uspv/sortsignsend.go +++ b/uspv/sortsignsend.go @@ -92,7 +92,7 @@ func (s *SPVCon) NewOutgoingTx(tx *wire.MsgTx) error { // SendCoins does send coins, but it's very rudimentary // wit makes it into p2wpkh. Which is not yet spendable. -func (s *SPVCon) SendCoins(adr btcutil.Address, sendAmt int64, wit bool) error { +func (s *SPVCon) SendCoins(adr btcutil.Address, sendAmt int64) error { var err error var score int64 @@ -118,86 +118,98 @@ func (s *SPVCon) SendCoins(adr btcutil.Address, sendAmt int64, wit bool) error { return err } - // make user specified txout and add to tx - txout := wire.NewTxOut(sendAmt, outAdrScript) - tx.AddTxOut(txout) //////////////////////////// // generate a utxo slice for your inputs - nokori := sendAmt // nokori is how much is needed on input side var ins utxoSlice + + // add utxos until we've had enough + nokori := sendAmt // nokori is how much is needed on input side for _, utxo := range allUtxos { // yeah, lets add this utxo! ins = append(ins, *utxo) nokori -= utxo.Value - if nokori < -10000 { // minimum overage / fee is 1K now + if nokori < -10000 { // minimum overage / fee is 10K now break } } + // sort utxos on the input side sort.Sort(ins) - // add all the utxos as txins + // make user specified txout and add to tx + txout := wire.NewTxOut(sendAmt, outAdrScript) + tx.AddTxOut(txout) + // see if there's enough left to also add a change output + if nokori < -200000 { + changeOld, err := s.TS.NewAdr() // change is witnessy + if err != nil { + return err + } + changeAdr, err := btcutil.NewAddressWitnessPubKeyHash( + changeOld.ScriptAddress(), s.TS.Param) + if err != nil { + return err + } + + changeScript, err := txscript.PayToAddrScript(changeAdr) + if err != nil { + return err + } + changeOut := wire.NewTxOut((-100000)-nokori, changeScript) + tx.AddTxOut(changeOut) + } + + // generate previous pkscripts for all the (now sorted) utxos + // then make txins with the utxo and prevpk, and insert them into the tx for _, in := range ins { - var prevPKscript []byte + var prevPKs []byte - prevPKscript, err := txscript.PayToAddrScript( - s.TS.Adrs[in.KeyIdx].PkhAdr) - if err != nil { - return err + // if wit utxo, convert address to generate pkscript + if in.IsWit { + wa, err := btcutil.NewAddressWitnessPubKeyHash( + s.TS.Adrs[in.KeyIdx].PkhAdr.ScriptAddress(), s.TS.Param) + prevPKs, err = txscript.PayToAddrScript(wa) + if err != nil { + return err + } + } else { // otherwise generate directly + prevPKs, err = txscript.PayToAddrScript( + s.TS.Adrs[in.KeyIdx].PkhAdr) + if err != nil { + return err + } } - tx.AddTxIn(wire.NewTxIn(&in.Op, prevPKscript, nil)) + tx.AddTxIn(wire.NewTxIn(&in.Op, prevPKs, nil)) } + // sort tx -- this only will change txouts since inputs are already sorted + txsort.InPlaceSort(tx) - // there's enough left to make a change output - if nokori < -200000 { - change, err := s.TS.NewAdr(true) // change is witnessy + // tx is ready for signing, + sigStash := make([][]byte, len(ins)) + for i, txin := range tx.TxIn { + // pick key + child, err := s.TS.rootPrivKey.Child( + ins[i].KeyIdx + hdkeychain.HardenedKeyStart) + if err != nil { + return err + } + priv, err := child.ECPrivKey() if err != nil { return err } - changeScript, err := txscript.PayToAddrScript(change) + // This is where witness based sighash types need to happen + // sign into stash + sigStash[i], err = txscript.SignatureScript( + tx, i, txin.SignatureScript, txscript.SigHashAll, priv, true) if err != nil { return err } - changeOut := wire.NewTxOut((-100000)-nokori, changeScript) - tx.AddTxOut(changeOut) - } - - for _, utxo := range allUtxos { - // generate pkscript to sign - prevPKscript, err := txscript.PayToAddrScript( - s.TS.Adrs[utxo.KeyIdx].PkhAdr) - if err != nil { - return err - } - // make new input from this utxo - thisInput := wire.NewTxIn(&utxo.Op, prevPKscript, nil) - tx.AddTxIn(thisInput) - nokori -= utxo.Value - if nokori < -10000 { // minimum overage / fee is 1K now - break - } } - // there's enough left to make a change output - if nokori < -200000 { - change, err := s.TS.NewAdr(true) // change is witnessy - if err != nil { - return err - } - - changeScript, err := txscript.PayToAddrScript(change) - if err != nil { - return err - } - changeOut := wire.NewTxOut((-100000)-nokori, changeScript) - tx.AddTxOut(changeOut) - } - - // use txstore method to sign - err = s.TS.SignThis(tx) - if err != nil { - return err + // swap sigs into sigScripts in txins + for i, txin := range tx.TxIn { + txin.SignatureScript = sigStash[i] } fmt.Printf("tx: %s", TxToString(tx)) diff --git a/uspv/txstore.go b/uspv/txstore.go index ab93f0f3d..89b83d5fa 100644 --- a/uspv/txstore.go +++ b/uspv/txstore.go @@ -21,7 +21,6 @@ type TxStore struct { OKMutex sync.Mutex Adrs []MyAdr // endeavouring to acquire capital - WitAdrs []MyAdr // separate segwit address slice StateDB *bolt.DB // place to write all this down // Params live here, not SCon @@ -79,7 +78,7 @@ func (t *TxStore) AddTxid(txid *wire.ShaHash, height int32) error { // ... or I'm gonna fade away func (t *TxStore) GimmeFilter() (*bloom.Filter, error) { - if len(t.Adrs) == 0 && len(t.WitAdrs) == 9 { + if len(t.Adrs) == 0 { return nil, fmt.Errorf("no address to filter for") } @@ -89,7 +88,7 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) { return nil, err } - elem := uint32(len(t.Adrs) + len(t.WitAdrs) + len(allUtxos)) + elem := uint32(len(t.Adrs) + len(allUtxos)) f := bloom.NewFilter(elem, 0, 0.000001, wire.BloomUpdateAll) // note there could be false positives since we're just looking @@ -97,9 +96,6 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) { for _, a := range t.Adrs { // add 20-byte pubkeyhash f.Add(a.PkhAdr.ScriptAddress()) } - for _, a := range t.WitAdrs { // add witness 20-byte pubkeyhash - f.Add(a.PkhAdr.ScriptAddress()) - } for _, u := range allUtxos { f.AddOutPoint(&u.Op) diff --git a/uspv/utxodb.go b/uspv/utxodb.go index b4010103a..e1013c865 100644 --- a/uspv/utxodb.go +++ b/uspv/utxodb.go @@ -22,8 +22,7 @@ var ( BKTTxns = []byte("Txns") // all txs we care about, for replays BKTState = []byte("MiscState") // last state of DB // these are in the state bucket - KEYStdKeys = []byte("StdKeys") // number of p2pkh keys used - KEYWitKeys = []byte("WitKeys") // number of p2wpkh keys used + KEYNumKeys = []byte("NumKeys") // number of p2pkh keys used KEYTipHeight = []byte("TipHeight") // height synced to ) @@ -53,7 +52,7 @@ func (ts *TxStore) OpenDB(filename string) error { return err } - numKeysBytes := sta.Get(KEYStdKeys) + numKeysBytes := sta.Get(KEYNumKeys) if numKeysBytes != nil { // NumKeys exists, read into uint32 buf := bytes.NewBuffer(numKeysBytes) err := binary.Read(buf, binary.BigEndian, &numKeys) @@ -68,7 +67,7 @@ func (ts *TxStore) OpenDB(filename string) error { if err != nil { return err } - err = sta.Put(KEYStdKeys, buf.Bytes()) + err = sta.Put(KEYNumKeys, buf.Bytes()) if err != nil { return err } @@ -83,7 +82,7 @@ func (ts *TxStore) OpenDB(filename string) error { // NewAdr creates a new, never before seen address, and increments the // DB counter as well as putting it in the ram Adrs store, and returns it -func (ts *TxStore) NewAdr(wit bool) (btcutil.Address, error) { +func (ts *TxStore) NewAdr() (btcutil.Address, error) { if ts.Param == nil { return nil, fmt.Errorf("NewAdr error: nil param") } @@ -93,32 +92,14 @@ func (ts *TxStore) NewAdr(wit bool) (btcutil.Address, error) { var n uint32 var nAdr btcutil.Address - if wit { - n = uint32(len(ts.WitAdrs)) - - // witness keys are another branch down from the rootpriv - ephpriv, err := ts.rootPrivKey.Child(1<<30 + hdkeychain.HardenedKeyStart) - if err != nil { - return nil, err - } - priv, err = ephpriv.Child(n + hdkeychain.HardenedKeyStart) - if err != nil { - return nil, err - } - nAdr, err = priv.WitnessAddress(ts.Param) - if err != nil { - return nil, err - } - } else { // regular p2pkh - n = uint32(len(ts.Adrs)) - priv, err = ts.rootPrivKey.Child(n + hdkeychain.HardenedKeyStart) - if err != nil { - return nil, err - } - nAdr, err = priv.Address(ts.Param) - if err != nil { - return nil, err - } + n = uint32(len(ts.Adrs)) + priv, err = ts.rootPrivKey.Child(n + hdkeychain.HardenedKeyStart) + if err != nil { + return nil, err + } + nAdr, err = priv.Address(ts.Param) + if err != nil { + return nil, err } // total number of keys (now +1) into 4 bytes @@ -131,10 +112,9 @@ func (ts *TxStore) NewAdr(wit bool) (btcutil.Address, error) { // write to db file err = ts.StateDB.Update(func(btx *bolt.Tx) error { sta := btx.Bucket(BKTState) - if wit { - return sta.Put(KEYStdKeys, buf.Bytes()) - } - return sta.Put(KEYWitKeys, buf.Bytes()) + + return sta.Put(KEYNumKeys, buf.Bytes()) + }) if err != nil { return nil, err @@ -143,11 +123,9 @@ func (ts *TxStore) NewAdr(wit bool) (btcutil.Address, error) { var ma MyAdr ma.PkhAdr = nAdr ma.KeyIdx = n - if wit { - ts.WitAdrs = append(ts.WitAdrs, ma) - } else { - ts.Adrs = append(ts.Adrs, ma) - } + + ts.Adrs = append(ts.Adrs, ma) + return nAdr, nil }