github.com/cerberus-wallet/blockbook@v0.3.2/bchain/coins/btc/bitcoinparser.go (about) 1 package btc 2 3 import ( 4 "blockbook/bchain" 5 "bytes" 6 "encoding/binary" 7 "encoding/hex" 8 "math/big" 9 "strconv" 10 11 vlq "github.com/bsm/go-vlq" 12 "github.com/juju/errors" 13 "github.com/martinboehm/btcd/blockchain" 14 "github.com/martinboehm/btcd/wire" 15 "github.com/martinboehm/btcutil" 16 "github.com/martinboehm/btcutil/chaincfg" 17 "github.com/martinboehm/btcutil/hdkeychain" 18 "github.com/martinboehm/btcutil/txscript" 19 ) 20 21 // OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses 22 type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error) 23 24 // BitcoinParser handle 25 type BitcoinParser struct { 26 *bchain.BaseParser 27 Params *chaincfg.Params 28 OutputScriptToAddressesFunc OutputScriptToAddressesFunc 29 XPubMagic uint32 30 XPubMagicSegwitP2sh uint32 31 XPubMagicSegwitNative uint32 32 Slip44 uint32 33 minimumCoinbaseConfirmations int 34 } 35 36 // NewBitcoinParser returns new BitcoinParser instance 37 func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser { 38 p := &BitcoinParser{ 39 BaseParser: &bchain.BaseParser{ 40 BlockAddressesToKeep: c.BlockAddressesToKeep, 41 AmountDecimalPoint: 8, 42 }, 43 Params: params, 44 XPubMagic: c.XPubMagic, 45 XPubMagicSegwitP2sh: c.XPubMagicSegwitP2sh, 46 XPubMagicSegwitNative: c.XPubMagicSegwitNative, 47 Slip44: c.Slip44, 48 minimumCoinbaseConfirmations: c.MinimumCoinbaseConfirmations, 49 } 50 p.OutputScriptToAddressesFunc = p.outputScriptToAddresses 51 return p 52 } 53 54 // GetChainParams contains network parameters for the main Bitcoin network, 55 // the regression test Bitcoin network, the test Bitcoin network and 56 // the simulation test Bitcoin network, in this order 57 func GetChainParams(chain string) *chaincfg.Params { 58 if !chaincfg.IsRegistered(&chaincfg.MainNetParams) { 59 chaincfg.RegisterBitcoinParams() 60 } 61 switch chain { 62 case "test": 63 return &chaincfg.TestNet3Params 64 case "regtest": 65 return &chaincfg.RegressionNetParams 66 } 67 return &chaincfg.MainNetParams 68 } 69 70 // GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output 71 func (p *BitcoinParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 72 ad, err := hex.DecodeString(output.ScriptPubKey.Hex) 73 if err != nil { 74 return ad, err 75 } 76 // convert possible P2PK script to P2PKH 77 // so that all transactions by given public key are indexed together 78 return txscript.ConvertP2PKtoP2PKH(p.Params.Base58CksumHasher, ad) 79 } 80 81 // GetAddrDescFromAddress returns internal address representation (descriptor) of given address 82 func (p *BitcoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 83 return p.addressToOutputScript(address) 84 } 85 86 // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable 87 func (p *BitcoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 88 return p.OutputScriptToAddressesFunc(addrDesc) 89 } 90 91 // GetScriptFromAddrDesc returns output script for given address descriptor 92 func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) { 93 return addrDesc, nil 94 } 95 96 // IsAddrDescIndexable returns true if AddressDescriptor should be added to index 97 // empty or OP_RETURN scripts are not indexed 98 func (p *BitcoinParser) IsAddrDescIndexable(addrDesc bchain.AddressDescriptor) bool { 99 if len(addrDesc) == 0 || addrDesc[0] == txscript.OP_RETURN { 100 return false 101 } 102 return true 103 } 104 105 // addressToOutputScript converts bitcoin address to ScriptPubKey 106 func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) { 107 da, err := btcutil.DecodeAddress(address, p.Params) 108 if err != nil { 109 return nil, err 110 } 111 script, err := txscript.PayToAddrScript(da) 112 if err != nil { 113 return nil, err 114 } 115 return script, nil 116 } 117 118 // TryParseOPReturn tries to process OP_RETURN script and return its string representation 119 func (p *BitcoinParser) TryParseOPReturn(script []byte) string { 120 if len(script) > 1 && script[0] == txscript.OP_RETURN { 121 // trying 2 variants of OP_RETURN data 122 // 1) OP_RETURN OP_PUSHDATA1 <datalen> <data> 123 // 2) OP_RETURN <datalen> <data> 124 var data []byte 125 var l int 126 if script[1] == txscript.OP_PUSHDATA1 && len(script) > 2 { 127 l = int(script[2]) 128 data = script[3:] 129 if l != len(data) { 130 l = int(script[1]) 131 data = script[2:] 132 } 133 } else { 134 l = int(script[1]) 135 data = script[2:] 136 } 137 if l == len(data) { 138 var ed string 139 140 ed = p.tryParseOmni(data) 141 if ed != "" { 142 return ed 143 } 144 145 isASCII := true 146 for _, c := range data { 147 if c < 32 || c > 127 { 148 isASCII = false 149 break 150 } 151 } 152 if isASCII { 153 ed = "(" + string(data) + ")" 154 } else { 155 ed = hex.EncodeToString(data) 156 } 157 return "OP_RETURN " + ed 158 } 159 } 160 return "" 161 } 162 163 var omniCurrencyMap = map[uint32]string{ 164 1: "Omni", 165 2: "Test Omni", 166 31: "TetherUS", 167 } 168 169 // tryParseOmni tries to extract Omni simple send transaction from script 170 func (p *BitcoinParser) tryParseOmni(data []byte) string { 171 172 // currently only simple send transaction version 0 is supported, see 173 // https://github.com/OmniLayer/spec#transfer-coins-simple-send 174 if len(data) != 20 || data[0] != 'o' { 175 return "" 176 } 177 // omni (4) <tx_version> (2) <tx_type> (2) 178 omniHeader := []byte{'o', 'm', 'n', 'i', 0, 0, 0, 0} 179 if bytes.Compare(data[0:8], omniHeader) != 0 { 180 return "" 181 } 182 183 currencyID := binary.BigEndian.Uint32(data[8:12]) 184 currency, ok := omniCurrencyMap[currencyID] 185 if !ok { 186 return "" 187 } 188 amount := new(big.Int) 189 amount.SetBytes(data[12:]) 190 amountStr := p.AmountToDecimalString(amount) 191 192 ed := "OMNI Simple Send: " + amountStr + " " + currency + " (#" + strconv.Itoa(int(currencyID)) + ")" 193 return ed 194 } 195 196 // outputScriptToAddresses converts ScriptPubKey to addresses with a flag that the addresses are searchable 197 func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { 198 sc, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params) 199 if err != nil { 200 return nil, false, err 201 } 202 rv := make([]string, len(addresses)) 203 for i, a := range addresses { 204 rv[i] = a.EncodeAddress() 205 } 206 var s bool 207 if sc == txscript.PubKeyHashTy || sc == txscript.WitnessV0PubKeyHashTy || sc == txscript.ScriptHashTy || sc == txscript.WitnessV0ScriptHashTy { 208 s = true 209 } else if len(rv) == 0 { 210 or := p.TryParseOPReturn(script) 211 if or != "" { 212 rv = []string{or} 213 } 214 } 215 return rv, s, nil 216 } 217 218 // TxFromMsgTx converts bitcoin wire Tx to bchain.Tx 219 func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { 220 vin := make([]bchain.Vin, len(t.TxIn)) 221 for i, in := range t.TxIn { 222 if blockchain.IsCoinBaseTx(t) { 223 vin[i] = bchain.Vin{ 224 Coinbase: hex.EncodeToString(in.SignatureScript), 225 Sequence: in.Sequence, 226 } 227 break 228 } 229 s := bchain.ScriptSig{ 230 Hex: hex.EncodeToString(in.SignatureScript), 231 // missing: Asm, 232 } 233 vin[i] = bchain.Vin{ 234 Txid: in.PreviousOutPoint.Hash.String(), 235 Vout: in.PreviousOutPoint.Index, 236 Sequence: in.Sequence, 237 ScriptSig: s, 238 } 239 } 240 vout := make([]bchain.Vout, len(t.TxOut)) 241 for i, out := range t.TxOut { 242 addrs := []string{} 243 if parseAddresses { 244 addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript) 245 } 246 s := bchain.ScriptPubKey{ 247 Hex: hex.EncodeToString(out.PkScript), 248 Addresses: addrs, 249 // missing: Asm, 250 // missing: Type, 251 } 252 var vs big.Int 253 vs.SetInt64(out.Value) 254 vout[i] = bchain.Vout{ 255 ValueSat: vs, 256 N: uint32(i), 257 ScriptPubKey: s, 258 } 259 } 260 tx := bchain.Tx{ 261 Txid: t.TxHash().String(), 262 Version: t.Version, 263 LockTime: t.LockTime, 264 Vin: vin, 265 Vout: vout, 266 // skip: BlockHash, 267 // skip: Confirmations, 268 // skip: Time, 269 // skip: Blocktime, 270 } 271 return tx 272 } 273 274 // ParseTx parses byte array containing transaction and returns Tx struct 275 func (p *BitcoinParser) ParseTx(b []byte) (*bchain.Tx, error) { 276 t := wire.MsgTx{} 277 r := bytes.NewReader(b) 278 if err := t.Deserialize(r); err != nil { 279 return nil, err 280 } 281 tx := p.TxFromMsgTx(&t, true) 282 tx.Hex = hex.EncodeToString(b) 283 return &tx, nil 284 } 285 286 // ParseBlock parses raw block to our Block struct 287 func (p *BitcoinParser) ParseBlock(b []byte) (*bchain.Block, error) { 288 w := wire.MsgBlock{} 289 r := bytes.NewReader(b) 290 291 if err := w.Deserialize(r); err != nil { 292 return nil, err 293 } 294 295 txs := make([]bchain.Tx, len(w.Transactions)) 296 for ti, t := range w.Transactions { 297 txs[ti] = p.TxFromMsgTx(t, false) 298 } 299 300 return &bchain.Block{ 301 BlockHeader: bchain.BlockHeader{ 302 Size: len(b), 303 Time: w.Header.Timestamp.Unix(), 304 }, 305 Txs: txs, 306 }, nil 307 } 308 309 // PackTx packs transaction to byte array 310 func (p *BitcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 311 buf := make([]byte, 4+vlq.MaxLen64+len(tx.Hex)/2) 312 binary.BigEndian.PutUint32(buf[0:4], height) 313 vl := vlq.PutInt(buf[4:4+vlq.MaxLen64], blockTime) 314 hl, err := hex.Decode(buf[4+vl:], []byte(tx.Hex)) 315 return buf[0 : 4+vl+hl], err 316 } 317 318 // UnpackTx unpacks transaction from byte array 319 func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 320 height := binary.BigEndian.Uint32(buf) 321 bt, l := vlq.Int(buf[4:]) 322 tx, err := p.ParseTx(buf[4+l:]) 323 if err != nil { 324 return nil, 0, err 325 } 326 tx.Blocktime = bt 327 328 return tx, height, nil 329 } 330 331 // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent 332 func (p *BitcoinParser) MinimumCoinbaseConfirmations() int { 333 return p.minimumCoinbaseConfirmations 334 } 335 336 func (p *BitcoinParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) { 337 var a btcutil.Address 338 var err error 339 if extKey.Version() == p.XPubMagicSegwitP2sh { 340 // redeemScript <witness version: OP_0><len pubKeyHash: 20><20-byte-pubKeyHash> 341 pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes()) 342 redeemScript := make([]byte, len(pubKeyHash)+2) 343 redeemScript[0] = 0 344 redeemScript[1] = byte(len(pubKeyHash)) 345 copy(redeemScript[2:], pubKeyHash) 346 hash := btcutil.Hash160(redeemScript) 347 a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params) 348 } else if extKey.Version() == p.XPubMagicSegwitNative { 349 a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params) 350 } else { 351 // default to P2PKH address 352 a, err = extKey.Address(p.Params) 353 } 354 if err != nil { 355 return nil, err 356 } 357 return txscript.PayToAddrScript(a) 358 } 359 360 // DeriveAddressDescriptors derives address descriptors from given xpub for listed indexes 361 func (p *BitcoinParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]bchain.AddressDescriptor, error) { 362 extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) 363 if err != nil { 364 return nil, err 365 } 366 changeExtKey, err := extKey.Child(change) 367 if err != nil { 368 return nil, err 369 } 370 ad := make([]bchain.AddressDescriptor, len(indexes)) 371 for i, index := range indexes { 372 indexExtKey, err := changeExtKey.Child(index) 373 if err != nil { 374 return nil, err 375 } 376 ad[i], err = p.addrDescFromExtKey(indexExtKey) 377 if err != nil { 378 return nil, err 379 } 380 } 381 return ad, nil 382 } 383 384 // DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for addresses in index range 385 func (p *BitcoinParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { 386 if toIndex <= fromIndex { 387 return nil, errors.New("toIndex<=fromIndex") 388 } 389 extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) 390 if err != nil { 391 return nil, err 392 } 393 changeExtKey, err := extKey.Child(change) 394 if err != nil { 395 return nil, err 396 } 397 ad := make([]bchain.AddressDescriptor, toIndex-fromIndex) 398 for index := fromIndex; index < toIndex; index++ { 399 indexExtKey, err := changeExtKey.Child(index) 400 if err != nil { 401 return nil, err 402 } 403 ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey) 404 if err != nil { 405 return nil, err 406 } 407 } 408 return ad, nil 409 } 410 411 // DerivationBasePath returns base path of xpub 412 func (p *BitcoinParser) DerivationBasePath(xpub string) (string, error) { 413 extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) 414 if err != nil { 415 return "", err 416 } 417 var c, bip string 418 cn := extKey.ChildNum() 419 if cn >= 0x80000000 { 420 cn -= 0x80000000 421 c = "'" 422 } 423 c = strconv.Itoa(int(cn)) + c 424 if extKey.Depth() != 3 { 425 return "unknown/" + c, nil 426 } 427 if extKey.Version() == p.XPubMagicSegwitP2sh { 428 bip = "49" 429 } else if extKey.Version() == p.XPubMagicSegwitNative { 430 bip = "84" 431 } else { 432 bip = "44" 433 } 434 return "m/" + bip + "'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil 435 }