diff --git a/uspv/txstore.go b/uspv/txstore.go index 15b6d5628..5937bc85f 100644 --- a/uspv/txstore.go +++ b/uspv/txstore.go @@ -1,6 +1,8 @@ package uspv import ( + "bytes" + "encoding/binary" "fmt" "log" @@ -16,10 +18,8 @@ import ( type TxStore struct { OKTxids map[wire.ShaHash]int32 // known good txids and their heights - Utxos []*Utxo // stacks on stacks Sum int64 // racks on racks Adrs []MyAdr // endeavouring to acquire capital - LastIdx uint32 // should equal len(Adrs) StateDB *bolt.DB // place to write all this down // this is redundant with the SPVCon param... ugly and should be taken out Param *chaincfg.Params // network parameters (testnet3, testnetL) @@ -100,15 +100,6 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) { return f, nil } -// need this because before I was comparing pointers maybe? -// so they were the same outpoint but stored in 2 places so false negative? -func OutPointsEqual(a, b wire.OutPoint) bool { - if !a.Hash.IsEqual(&b.Hash) { - return false - } - return a.Index == b.Index -} - // TxToString prints out some info about a transaction. for testing / debugging func TxToString(tx *wire.MsgTx) string { str := "\t\t\t - Tx - \n" @@ -126,3 +117,193 @@ func TxToString(tx *wire.MsgTx) string { } return str } + +// need this because before I was comparing pointers maybe? +// so they were the same outpoint but stored in 2 places so false negative? +func OutPointsEqual(a, b wire.OutPoint) bool { + if !a.Hash.IsEqual(&b.Hash) { + return false + } + return a.Index == b.Index +} + +/*----- serialization for tx outputs ------- */ + +// outPointToBytes turns an outpoint into 36 bytes. +func outPointToBytes(op *wire.OutPoint) ([]byte, error) { + var buf bytes.Buffer + _, err := buf.Write(op.Hash.Bytes()) + if err != nil { + return nil, err + } + // write 4 byte outpoint index within the tx to spend + err = binary.Write(&buf, binary.BigEndian, op.Index) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// ToBytes turns a Utxo into some bytes. +// note that the txid is the first 36 bytes and in our use cases will be stripped +// off, but is left here for other applications +func (u *Utxo) ToBytes() ([]byte, error) { + var buf bytes.Buffer + // write 32 byte txid of the utxo + _, err := buf.Write(u.Op.Hash.Bytes()) + if err != nil { + return nil, err + } + // write 4 byte outpoint index within the tx to spend + err = binary.Write(&buf, binary.BigEndian, u.Op.Index) + if err != nil { + return nil, err + } + // write 4 byte height of utxo + err = binary.Write(&buf, binary.BigEndian, u.AtHeight) + if err != nil { + return nil, err + } + // write 4 byte key index of utxo + err = binary.Write(&buf, binary.BigEndian, u.KeyIdx) + if err != nil { + return nil, err + } + // write 8 byte amount of money at the utxo + err = binary.Write(&buf, binary.BigEndian, u.Value) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// UtxoFromBytes turns bytes into a Utxo. Note it wants the txid and outindex +// in the first 36 bytes, which isn't stored that way in the boldDB, +// but can be easily appended. +func UtxoFromBytes(b []byte) (Utxo, error) { + var u Utxo + if b == nil { + return u, fmt.Errorf("nil input slice") + } + buf := bytes.NewBuffer(b) + if buf.Len() < 52 { // utxos are 52 bytes + return u, fmt.Errorf("Got %d bytes for utxo, expect 52", buf.Len()) + } + // read 32 byte txid + err := u.Op.Hash.SetBytes(buf.Next(32)) + if err != nil { + return u, err + } + // read 4 byte outpoint index within the tx to spend + err = binary.Read(buf, binary.BigEndian, &u.Op.Index) + if err != nil { + return u, err + } + // read 4 byte height of utxo + err = binary.Read(buf, binary.BigEndian, &u.AtHeight) + if err != nil { + return u, err + } + // read 4 byte key index of utxo + err = binary.Read(buf, binary.BigEndian, &u.KeyIdx) + if err != nil { + return u, err + } + // read 8 byte amount of money at the utxo + err = binary.Read(buf, binary.BigEndian, &u.Value) + if err != nil { + return u, err + } + return u, nil +} + +// ToBytes turns an Stxo into some bytes. +// outpoint txid, outpoint idx, height, key idx, amt, spendheight, spendtxid +func (s *Stxo) ToBytes() ([]byte, error) { + var buf bytes.Buffer + // write 32 byte txid of the utxo + _, err := buf.Write(s.Op.Hash.Bytes()) + if err != nil { + return nil, err + } + // write 4 byte outpoint index within the tx to spend + err = binary.Write(&buf, binary.BigEndian, s.Op.Index) + if err != nil { + return nil, err + } + // write 4 byte height of utxo + err = binary.Write(&buf, binary.BigEndian, s.AtHeight) + if err != nil { + return nil, err + } + // write 4 byte key index of utxo + err = binary.Write(&buf, binary.BigEndian, s.KeyIdx) + if err != nil { + return nil, err + } + // write 8 byte amount of money at the utxo + err = binary.Write(&buf, binary.BigEndian, s.Value) + if err != nil { + return nil, err + } + // write 4 byte height where the txo was spent + err = binary.Write(&buf, binary.BigEndian, s.SpendHeight) + if err != nil { + return nil, err + } + // write 32 byte txid of the spending transaction + _, err = buf.Write(s.SpendTxid.Bytes()) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// StxoFromBytes turns bytes into a Stxo. +func StxoFromBytes(b []byte) (Stxo, error) { + var s Stxo + if b == nil { + return s, fmt.Errorf("nil input slice") + } + buf := bytes.NewBuffer(b) + if buf.Len() < 88 { // stxos are 88 bytes + return s, fmt.Errorf("Got %d bytes for stxo, expect 88", buf.Len()) + } + // read 32 byte txid + err := s.Op.Hash.SetBytes(buf.Next(32)) + if err != nil { + return s, err + } + // read 4 byte outpoint index within the tx to spend + err = binary.Read(buf, binary.BigEndian, &s.Op.Index) + if err != nil { + return s, err + } + // read 4 byte height of utxo + err = binary.Read(buf, binary.BigEndian, &s.AtHeight) + if err != nil { + return s, err + } + // read 4 byte key index of utxo + err = binary.Read(buf, binary.BigEndian, &s.KeyIdx) + if err != nil { + return s, err + } + // read 8 byte amount of money at the utxo + err = binary.Read(buf, binary.BigEndian, &s.Value) + if err != nil { + return s, err + } + // read 4 byte spend height + err = binary.Read(buf, binary.BigEndian, &s.SpendHeight) + if err != nil { + return s, err + } + // read 32 byte txid + err = s.SpendTxid.SetBytes(buf.Next(32)) + if err != nil { + return s, err + } + return s, nil +} diff --git a/uspv/utxodb.go b/uspv/utxodb.go index 25e0bad84..7a3af432f 100644 --- a/uspv/utxodb.go +++ b/uspv/utxodb.go @@ -290,7 +290,7 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx) (uint32, error) { duf := btx.Bucket(BKTUtxos) // sta := btx.Bucket(BKTState) old := btx.Bucket(BKTStxos) - // txns := btx.Bucket(BKTTxns) + txns := btx.Bucket(BKTTxns) // first see if we lose utxos // iterate through duffel bag and look for matches @@ -326,6 +326,14 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx) (uint32, error) { if err != nil { return err } + // store this relevant tx + sha := tx.TxSha() + var buf bytes.Buffer + tx.Serialize(&buf) + err = txns.Put(sha.Bytes(), buf.Bytes()) + if err != nil { + return err + } return nil // matched utxo k, won't match another } @@ -343,227 +351,3 @@ func (ts *TxStore) Ingest(tx *wire.MsgTx) (uint32, error) { }) return hits, err } - -func (ts *TxStore) MarkSpent(ut Utxo, h int32, stx *wire.MsgTx) error { - // we write in key = outpoint (32 hash, 4 index) - // value = spending txid - // if we care about the spending tx we can store that in another bucket. - - var st Stxo - st.Utxo = ut - st.SpendHeight = h - st.SpendTxid = stx.TxSha() - - return ts.StateDB.Update(func(btx *bolt.Tx) error { - duf := btx.Bucket(BKTUtxos) - old := btx.Bucket(BKTStxos) - txns := btx.Bucket(BKTTxns) - - opb, err := outPointToBytes(&st.Op) - if err != nil { - return err - } - - err = duf.Delete(opb) // not utxo anymore - if err != nil { - return err - } - - stxb, err := st.ToBytes() - if err != nil { - return err - } - - err = old.Put(opb, stxb) // write k:v outpoint:stxo bytes - if err != nil { - return err - } - - // store spending tx - sha := stx.TxSha() - var buf bytes.Buffer - stx.Serialize(&buf) - txns.Put(sha.Bytes(), buf.Bytes()) - - return nil - }) -} - -// outPointToBytes turns an outpoint into 36 bytes. -func outPointToBytes(op *wire.OutPoint) ([]byte, error) { - var buf bytes.Buffer - _, err := buf.Write(op.Hash.Bytes()) - if err != nil { - return nil, err - } - // write 4 byte outpoint index within the tx to spend - err = binary.Write(&buf, binary.BigEndian, op.Index) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// ToBytes turns a Utxo into some bytes. -// note that the txid is the first 36 bytes and in our use cases will be stripped -// off, but is left here for other applications -func (u *Utxo) ToBytes() ([]byte, error) { - var buf bytes.Buffer - // write 32 byte txid of the utxo - _, err := buf.Write(u.Op.Hash.Bytes()) - if err != nil { - return nil, err - } - // write 4 byte outpoint index within the tx to spend - err = binary.Write(&buf, binary.BigEndian, u.Op.Index) - if err != nil { - return nil, err - } - // write 4 byte height of utxo - err = binary.Write(&buf, binary.BigEndian, u.AtHeight) - if err != nil { - return nil, err - } - // write 4 byte key index of utxo - err = binary.Write(&buf, binary.BigEndian, u.KeyIdx) - if err != nil { - return nil, err - } - // write 8 byte amount of money at the utxo - err = binary.Write(&buf, binary.BigEndian, u.Value) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// UtxoFromBytes turns bytes into a Utxo. Note it wants the txid and outindex -// in the first 36 bytes, which isn't stored that way in the boldDB, -// but can be easily appended. -func UtxoFromBytes(b []byte) (Utxo, error) { - var u Utxo - if b == nil { - return u, fmt.Errorf("nil input slice") - } - buf := bytes.NewBuffer(b) - if buf.Len() < 52 { // utxos are 52 bytes - return u, fmt.Errorf("Got %d bytes for utxo, expect 52", buf.Len()) - } - // read 32 byte txid - err := u.Op.Hash.SetBytes(buf.Next(32)) - if err != nil { - return u, err - } - // read 4 byte outpoint index within the tx to spend - err = binary.Read(buf, binary.BigEndian, &u.Op.Index) - if err != nil { - return u, err - } - // read 4 byte height of utxo - err = binary.Read(buf, binary.BigEndian, &u.AtHeight) - if err != nil { - return u, err - } - // read 4 byte key index of utxo - err = binary.Read(buf, binary.BigEndian, &u.KeyIdx) - if err != nil { - return u, err - } - // read 8 byte amount of money at the utxo - err = binary.Read(buf, binary.BigEndian, &u.Value) - if err != nil { - return u, err - } - return u, nil -} - -// ToBytes turns an Stxo into some bytes. -// outpoint txid, outpoint idx, height, key idx, amt, spendheight, spendtxid -func (s *Stxo) ToBytes() ([]byte, error) { - var buf bytes.Buffer - // write 32 byte txid of the utxo - _, err := buf.Write(s.Op.Hash.Bytes()) - if err != nil { - return nil, err - } - // write 4 byte outpoint index within the tx to spend - err = binary.Write(&buf, binary.BigEndian, s.Op.Index) - if err != nil { - return nil, err - } - // write 4 byte height of utxo - err = binary.Write(&buf, binary.BigEndian, s.AtHeight) - if err != nil { - return nil, err - } - // write 4 byte key index of utxo - err = binary.Write(&buf, binary.BigEndian, s.KeyIdx) - if err != nil { - return nil, err - } - // write 8 byte amount of money at the utxo - err = binary.Write(&buf, binary.BigEndian, s.Value) - if err != nil { - return nil, err - } - // write 4 byte height where the txo was spent - err = binary.Write(&buf, binary.BigEndian, s.SpendHeight) - if err != nil { - return nil, err - } - // write 32 byte txid of the spending transaction - _, err = buf.Write(s.SpendTxid.Bytes()) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// StxoFromBytes turns bytes into a Stxo. -func StxoFromBytes(b []byte) (Stxo, error) { - var s Stxo - if b == nil { - return s, fmt.Errorf("nil input slice") - } - buf := bytes.NewBuffer(b) - if buf.Len() < 88 { // stxos are 88 bytes - return s, fmt.Errorf("Got %d bytes for stxo, expect 88", buf.Len()) - } - // read 32 byte txid - err := s.Op.Hash.SetBytes(buf.Next(32)) - if err != nil { - return s, err - } - // read 4 byte outpoint index within the tx to spend - err = binary.Read(buf, binary.BigEndian, &s.Op.Index) - if err != nil { - return s, err - } - // read 4 byte height of utxo - err = binary.Read(buf, binary.BigEndian, &s.AtHeight) - if err != nil { - return s, err - } - // read 4 byte key index of utxo - err = binary.Read(buf, binary.BigEndian, &s.KeyIdx) - if err != nil { - return s, err - } - // read 8 byte amount of money at the utxo - err = binary.Read(buf, binary.BigEndian, &s.Value) - if err != nil { - return s, err - } - // read 4 byte spend height - err = binary.Read(buf, binary.BigEndian, &s.SpendHeight) - if err != nil { - return s, err - } - // read 32 byte txid - err = s.SpendTxid.SetBytes(buf.Next(32)) - if err != nil { - return s, err - } - return s, nil -}