add fanout. fix boltdb weirdness.

again with keys / values within the bolttx acting weird.
it wasn't deleting utxos that had been spent by the ingested tx.
it'd do the first 30 then stop.  Deferred deletion and copied the
serialized utxo.  Not sure which of those fixed it.  Maybe both.
This commit is contained in:
Tadge Dryja 2016-02-22 16:32:58 -08:00
parent bc4cf5ba80
commit dd1f2c00fe
3 changed files with 104 additions and 31 deletions

@ -140,6 +140,13 @@ func Shellparse(cmdslice []string) error {
}
return nil
}
if cmd == "fan" {
err = Fan(args)
if err != nil {
fmt.Printf("fan error: %s\n", err)
}
return nil
}
if cmd == "txs" {
err = Txs(args)
if err != nil {
@ -238,6 +245,14 @@ func Adr(args []string) error {
}
}
}
if len(args) > 1 {
for i := 0; i < 1000; i++ {
_, err := SCon.TS.NewAdr()
if err != nil {
return err
}
}
}
// always make one
a, err := SCon.TS.NewAdr()
@ -250,6 +265,50 @@ func Adr(args []string) error {
return nil
}
// Fan generates a bunch of fanout. Only for testing, can be expensive.
// syntax: fan numOutputs valOutputs witty
func Fan(args []string) error {
if len(args) < 3 {
return fmt.Errorf("fan syntax: fan numOutputs valOutputs witty")
}
numOutputs, err := strconv.ParseInt(args[0], 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)
if err != nil {
return err
}
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
}
amts[i] = valOutputs + i
}
return SCon.SendCoins(adrs, amts)
}
// Send sends coins.
func Send(args []string) error {
if SCon.RBytes == 0 {

@ -160,14 +160,17 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error {
}
tx.AddTxIn(wire.NewTxIn(&utxo.Op, prevPKs, nil))
nokori -= utxo.Value
// if nokori is positive, don't bother checking fee yet.
if nokori < 0 {
fee = EstFee(tx, satPerByte)
if nokori < -fee { // done adding utxos: nokori below negative est. fee
break
}
}
}
// see if there's enough left to also add a change output
if nokori < -dustCutoff {
changeOld, err := s.TS.NewAdr() // change is witnessy
if err != nil {
return err
@ -187,6 +190,9 @@ func (s *SPVCon) SendCoins(adrs []btcutil.Address, sendAmts []int64) error {
tx.AddTxOut(changeOut)
fee = EstFee(tx, satPerByte)
changeOut.Value = -(nokori + fee)
if changeOut.Value < dustCutoff {
// remove last output (change) : not worth it
tx.TxOut = tx.TxOut[:len(tx.TxOut)-1]
}
// sort utxos on the input side. use this instead of txsort

@ -353,7 +353,6 @@ func (ts *TxStore) PopulateAdrs(lastKey uint32) error {
func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) {
var hits uint32
var err error
var spentOPs [][]byte
var nUtxoBytes [][]byte
// tx has been OK'd by SPV; check tx sanity
@ -366,13 +365,14 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) {
// note that you can't check signatures; this is SPV.
// 0 conf SPV means pretty much nothing. Anyone can say anything.
spentOPs := make([][]byte, len(tx.TxIn))
// before entering into db, serialize all inputs of the ingested tx
for _, txin := range tx.TxIn {
for i, txin := range tx.TxIn {
nOP, err := outPointToBytes(&txin.PreviousOutPoint)
if err != nil {
return hits, err
}
spentOPs = append(spentOPs, nOP)
spentOPs[i] = nOP
}
// also generate PKscripts for all addresses (maybe keep storing these?)
for _, adr := range ts.Adrs {
@ -419,7 +419,7 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) {
}
nUtxoBytes = append(nUtxoBytes, b)
hits++
break // only one match
// break // keep looking! address re-use in same tx
}
}
}
@ -437,24 +437,25 @@ 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))
for _, nOP := range spentOPs {
duf.ForEach(func(k, v []byte) error {
if bytes.Equal(k, nOP) { // matched, we lost utxo
// do all this just to figure out value we lost
x := make([]byte, len(k)+len(v))
copy(x, k)
// mark utxos for deletion
delOPs = append(delOPs, x)
copy(x[len(k):], v)
lostTxo, err := UtxoFromBytes(x)
if err != nil {
return err
}
hits++
// then delete the utxo from duf, save to old
err = duf.Delete(k)
if err != nil {
return err
}
// after deletion, save stxo to old bucket
// 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
@ -480,6 +481,13 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32) (uint32, error) {
}
return nil // no match
})
// delete after done with foreach
for _, k := range delOPs {
err = duf.Delete(k)
if err != nil {
return err
}
}
} // done losing utxos, next gain utxos
// next add all new utxos to db, this is quick as the work is above
for _, ub := range nUtxoBytes {