diff --git a/uspv/eight333.go b/uspv/eight333.go index 75f8c52e..b20a21ea 100644 --- a/uspv/eight333.go +++ b/uspv/eight333.go @@ -156,6 +156,45 @@ func (s *SPVCon) SendFilter(f *bloom.Filter) { return } +// HeightFromHeader gives you the block height given a 80 byte block header +// seems like looking for the merkle root is the best way to do this +func (s *SPVCon) HeightFromHeader(query wire.BlockHeader) (uint32, error) { + // start from the most recent and work back in time; even though that's + // kind of annoying it's probably a lot faster since things tend to have + // happened recently. + + // seek to last header + lastPos, err := s.headerFile.Seek(-80, os.SEEK_END) + if err != nil { + return 0, err + } + height := lastPos / 80 + + var current wire.BlockHeader + + for height > 0 { + // grab header from disk + err = current.Deserialize(s.headerFile) + if err != nil { + return 0, err + } + // check if merkle roots match + if current.MerkleRoot.IsEqual(&query.MerkleRoot) { + // if they do, great, return height + return uint32(height), nil + } + // skip back one header (2 because we just read one) + _, err = s.headerFile.Seek(-160, os.SEEK_CUR) + if err != nil { + return 0, err + } + // decrement height + height-- + } + // finished for loop without finding match + return 0, fmt.Errorf("Header not found on disk") +} + func (s *SPVCon) AskForHeaders() error { var hdr wire.BlockHeader ghdr := wire.NewMsgGetHeaders() @@ -201,6 +240,7 @@ func (s *SPVCon) AskForHeaders() error { func (s *SPVCon) IngestHeaders(m *wire.MsgHeaders) (bool, error) { var err error + // seek to last header _, err = s.headerFile.Seek(-80, os.SEEK_END) if err != nil { return false, err diff --git a/uspv/mblock.go b/uspv/mblock.go index dc71608c..6200f0c2 100644 --- a/uspv/mblock.go +++ b/uspv/mblock.go @@ -77,6 +77,9 @@ func checkMBlock(m *wire.MsgMerkleBlock) ([]*wire.ShaHash, error) { if len(m.Flags) == 0 { return nil, fmt.Errorf("No flag bits") } + if len(m.Hashes) < 2 { // nothing but the merkle root + return nil, nil // nothin. + } var s []merkleNode // the stack var r []*wire.ShaHash // slice to return; txids we care about diff --git a/uspv/msghandler.go b/uspv/msghandler.go index b0db4b01..ab431d94 100644 --- a/uspv/msghandler.go +++ b/uspv/msghandler.go @@ -39,13 +39,21 @@ func (s *SPVCon) incomingMessageHandler() { if err != nil { log.Printf("Merkle block error: %s\n", err.Error()) return - // continue } fmt.Printf(" got %d txs ", len(txids)) // fmt.Printf(" = got %d txs from block %s\n", // len(txids), m.Header.BlockSha().String()) + var height uint32 + if len(txids) > 0 { + // make sure block is in our store before adding txs + height, err = s.HeightFromHeader(m.Header) + if err != nil { + log.Printf("Merkle block height error: %s\n", err.Error()) + continue + } + } for _, txid := range txids { - err := s.TS.AddTxid(txid) + err := s.TS.AddTxid(txid, height) if err != nil { log.Printf("Txid store error: %s\n", err.Error()) } diff --git a/uspv/txstore.go b/uspv/txstore.go index 7d4f4e5a..b847479c 100644 --- a/uspv/txstore.go +++ b/uspv/txstore.go @@ -11,10 +11,11 @@ import ( ) type TxStore struct { - KnownTxids []*wire.ShaHash - Utxos []Utxo // stacks on stacks - Sum int64 // racks on racks - Adrs []MyAdr // endeavouring to acquire capital + OKTxids map[wire.ShaHash]uint32 // known good txids and their heights + + Utxos []Utxo // stacks on stacks + Sum int64 // racks on racks + Adrs []MyAdr // endeavouring to acquire capital } type Utxo struct { // cash money. @@ -30,6 +31,12 @@ type MyAdr struct { // an address I have the private key for KeyIdx uint32 // index for private key needed to sign / spend } +func NewTxStore() TxStore { + var txs TxStore + txs.OKTxids = make(map[wire.ShaHash]uint32) + return txs +} + // add addresses into the TxStore func (t *TxStore) AddAdr(a btcutil.Address, kidx uint32) { var ma MyAdr @@ -40,11 +47,11 @@ func (t *TxStore) AddAdr(a btcutil.Address, kidx uint32) { } // add txid of interest -func (t *TxStore) AddTxid(txid *wire.ShaHash) error { +func (t *TxStore) AddTxid(txid *wire.ShaHash, height uint32) error { if txid == nil { return fmt.Errorf("tried to add nil txid") } - t.KnownTxids = append(t.KnownTxids, txid) + t.OKTxids[*txid] = height return nil } @@ -62,19 +69,13 @@ func (t *TxStore) GimmeFilter() (*bloom.Filter, error) { // Ingest a tx into wallet, dealing with both gains and losses func (t *TxStore) IngestTx(tx *wire.MsgTx) error { - var match bool inTxid := tx.TxSha() - for _, ktxid := range t.KnownTxids { - if inTxid.IsEqual(ktxid) { - match = true - break // found tx match, - } - } - if !match { + height, ok := t.OKTxids[inTxid] + if !ok { return fmt.Errorf("we don't care about tx %s", inTxid.String()) } - err := t.AbsorbTx(tx) + err := t.AbsorbTx(tx, height) if err != nil { return err } @@ -87,7 +88,7 @@ func (t *TxStore) IngestTx(tx *wire.MsgTx) error { } // Absorb money into wallet from a tx -func (t *TxStore) AbsorbTx(tx *wire.MsgTx) error { +func (t *TxStore) AbsorbTx(tx *wire.MsgTx, height uint32) error { if tx == nil { return fmt.Errorf("Tried to add nil tx") } @@ -102,6 +103,7 @@ func (t *TxStore) AbsorbTx(tx *wire.MsgTx) error { hits++ acq += out.Value var newu Utxo + newu.AtHeight = height newu.KeyIdx = a.KeyIdx newu.Txo = *out