github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/uspv/chainhook.go (about) 1 package uspv 2 3 import ( 4 "path/filepath" 5 6 "github.com/mit-dci/lit/logging" 7 8 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 9 "github.com/mit-dci/lit/coinparam" 10 "github.com/mit-dci/lit/lnutil" 11 "github.com/mit-dci/lit/wire" 12 ) 13 14 // ChainHook is an interface which provides access to a blockchain for the 15 // wallit. Right now the USPV package conforms to this interface, but it 16 // should also be possible to plug in things like fullnode-rpc clients, and 17 // (yuck) trusted block explorers. 18 19 /* The model here is that the wallit has lots of state on disk about 20 what it's seen. The ChainHook is ephemeral, and has some state in RAM but 21 shouldn't have to keep track of too much; or at least what it keeps track of 22 is internal and not exported out to the wallit (eg a fullnode keeps track 23 of a lot, and SPV node, somewhat less, and a block explorer shim basically nothing) 24 */ 25 26 // ChainHook is a thing that lets you interact with the actual blockchains 27 type ChainHook interface { 28 29 // Start turns on the ChainHook. Later on, pass more parameters here. 30 // Also gets the txChan where txs come in from the ChainHook to the wallit. 31 // The TxChannel should never give txs that the wallit doesn't care about. In the 32 // case of bloom filters, false positives should be handled and stopped at the 33 // ChainHook layer; wallit should not have to handle ingesting irrelevant txs. 34 // You get back an error and 2 channels: one for txs with height attached, and 35 // one with block heights. Subject to change; maybe this is redundant. 36 37 // Note that for reorgs, the height chan just sends a lower height than you 38 // already have, and that means "reorg back!" 39 Start(height int32, host, path string, proxyURL string, params *coinparam.Params) ( 40 chan lnutil.TxAndHeight, chan int32, error) 41 42 // The Register functions send information to the ChainHook about what txs to 43 // return. Txs matching either the addresses or outpoints will be returned 44 // via the TxChannel 45 46 // RegisterAddress tells the ChainHook about an address of interest. 47 // Give it an array; Currently needs to be 20 bytes. Only p2pkh / p2wpkh 48 // are supported. 49 // Later could add a separate function for script hashes (20/32) 50 RegisterAddress(address [20]byte) error 51 52 // RegisterOutPoint tells the ChainHook about an outpoint of interest. 53 RegisterOutPoint(wire.OutPoint) error 54 55 // UnregisterOutPoint tells the ChainHook about loss of interest in an outpoint. 56 UnregisterOutPoint(wire.OutPoint) error 57 58 // SetHeight sets the height ChainHook needs to look above. 59 // Returns a channel which tells the wallit what height the ChainHook has 60 // sync'd up to. This chan should push int32s *after* the TxAndHeights 61 // have come in; so getting a "54" here means block 54 is done and fully parsed. 62 // Removed, put into Start(). 63 // SetHeight(startHeight int32) chan int32 64 65 // NewRawBlocksChannel returns a new channel to receive blocks. 66 NewRawBlocksChannel() chan *wire.MsgBlock 67 68 // NewHeightChannel returns a new channel to receive height events. 69 NewHeightChannel() chan int32 70 71 // PushTx sends a tx out to the network via the ChainHook. 72 // Note that this does NOT register anything in the tx, so by just using this, 73 // nothing will come back about confirmation. It WILL come back with errors 74 // though, so this takes some time. 75 PushTx(tx *wire.MsgTx) error 76 77 // Request all incoming blocks over this channel. If RawBlocks isn't called, 78 // then the undelying hook package doesn't need to get full blocks. 79 // Currently you always call it with uspv... 80 RawBlocks() chan *wire.MsgBlock 81 // TODO -- reorgs. Oh and doublespends and stuff. 82 } 83 84 /* 85 type ChainHook interface { 86 87 Start() chan lnutil.TxAndHeight 88 RegisterAddress(address [20]byte) error 89 RegisterOutPoint(wire.OutPoint) error 90 SetHeight(startHeight int32) chan int32 91 PushTx(tx *wire.MsgTx) error 92 RawBlocks() chan *wire.MsgBlock 93 } 94 */ 95 96 // --- implementation of ChainHook interface ---- 97 98 func (s *SPVCon) Start( 99 startHeight int32, host, path string, proxyURL string, params *coinparam.Params) ( 100 chan lnutil.TxAndHeight, chan int32, error) { 101 102 // These can be set before calling Start() 103 s.HardMode = true 104 s.Ironman = false 105 s.ProxyURL = proxyURL 106 107 s.Param = params 108 109 s.inMsgQueue = make(chan wire.Message) 110 s.outMsgQueue = make(chan wire.Message) 111 s.TrackingAdrs = make(map[[20]byte]bool) 112 s.TrackingOPs = make(map[wire.OutPoint]bool) 113 114 s.TxMap = make(map[chainhash.Hash]*wire.MsgTx) 115 116 s.OKTxids = make(map[chainhash.Hash]int32) 117 118 s.TxUpToWallit = make(chan lnutil.TxAndHeight, 1) 119 s.CurrentHeightChan = make(chan int32, 1) 120 121 s.syncHeight = startHeight 122 123 headerFilePath := filepath.Join(path, "header.bin") 124 // open header file 125 err := s.openHeaderFile(headerFilePath) 126 if err != nil { 127 return nil, nil, err 128 } 129 130 err = s.Connect(host) 131 if err != nil { 132 logging.Errorf("Can't connect to host %s\n", host) 133 return nil, nil, err 134 } 135 136 err = s.AskForHeaders() 137 if err != nil { 138 logging.Errorf("AskForHeaders error\n") 139 return nil, nil, err 140 } 141 142 return s.TxUpToWallit, s.CurrentHeightChan, nil 143 } 144 145 // RegisterAddress ... 146 func (s *SPVCon) RegisterAddress(adr160 [20]byte) error { 147 s.TrackingAdrsMtx.Lock() 148 s.TrackingAdrs[adr160] = true 149 s.TrackingAdrsMtx.Unlock() 150 return nil 151 } 152 153 // RegisterOutPoint ... 154 func (s *SPVCon) RegisterOutPoint(op wire.OutPoint) error { 155 s.TrackingOPsMtx.Lock() 156 s.TrackingOPs[op] = true 157 s.TrackingOPsMtx.Unlock() 158 return nil 159 } 160 161 func (s *SPVCon) UnregisterOutPoint(op wire.OutPoint) error { 162 s.TrackingOPsMtx.Lock() 163 delete(s.TrackingOPs, op) 164 s.TrackingOPsMtx.Unlock() 165 return nil 166 } 167 168 // PushTx sends a tx out to the global network 169 func (s *SPVCon) PushTx(tx *wire.MsgTx) error { 170 // store tx in the RAM map for when other nodes ask for it 171 txid := tx.TxHash() 172 s.TxMap[txid] = tx 173 174 // since we never delete txs, this will eventually run out of RAM. 175 // But might take years... might be nice to fix. 176 177 // send out an inv message telling nodes we have this new tx 178 iv1 := wire.NewInvVect(wire.InvTypeWitnessTx, &txid) 179 invMsg := wire.NewMsgInv() 180 err := invMsg.AddInvVect(iv1) 181 if err != nil { 182 return err 183 } 184 // broadcast inv message 185 s.outMsgQueue <- invMsg 186 187 // TODO wait a few seconds here for a reject message and return it 188 return nil 189 } 190 191 // RawBlocks returns a channel where all the blocks appear. 192 func (s *SPVCon) RawBlocks() chan *wire.MsgBlock { 193 s.RawBlockActive = true 194 s.RawBlockSender = make(chan *wire.MsgBlock, 8) // I dunno, 8? 195 return s.RawBlockSender 196 } 197 198 // NewRawBlocksChannel appends to the raw block distribution list and returns a new channel to receive blocks. 199 func (s *SPVCon) NewRawBlocksChannel() chan *wire.MsgBlock { 200 newBlockSender := make(chan *wire.MsgBlock, 8) 201 s.RawBlockDistribute = append(s.RawBlockDistribute, newBlockSender) 202 return newBlockSender 203 } 204 205 // NewHeightChannel appends to the height distribution list and returns a new channel to receive height events. 206 func (s *SPVCon) NewHeightChannel() chan int32 { 207 newHeightSender := make(chan int32, 8) 208 s.HeightDistribute = append(s.HeightDistribute, newHeightSender) 209 return newHeightSender 210 }