back to one adr[] slice

all adrs can be converted on the fly and displayed both ways.
in the actual UI this shouldn't happen though.

Also utxos are sorted and signed properly.  utxo selection
is still pretty low tech.
This commit is contained in:
Tadge Dryja 2016-02-19 01:24:23 -08:00
parent fbd17846d4
commit d8e62f6898
4 changed files with 99 additions and 113 deletions

@ -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
}

@ -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))

@ -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)

@ -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
}