github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/dcr/decredparser.go (about) 1 package dcr 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/hex" 7 "encoding/json" 8 "math" 9 "math/big" 10 "strconv" 11 12 "github.com/decred/dcrd/chaincfg/chainhash" 13 cfg "github.com/decred/dcrd/chaincfg/v3" 14 "github.com/decred/dcrd/dcrec" 15 "github.com/decred/dcrd/dcrutil/v3" 16 "github.com/decred/dcrd/hdkeychain/v3" 17 "github.com/decred/dcrd/txscript/v3" 18 "github.com/juju/errors" 19 "github.com/martinboehm/btcd/wire" 20 "github.com/martinboehm/btcutil/base58" 21 "github.com/martinboehm/btcutil/chaincfg" 22 "github.com/trezor/blockbook/bchain" 23 "github.com/trezor/blockbook/bchain/coins/btc" 24 "github.com/trezor/blockbook/bchain/coins/utils" 25 ) 26 27 const ( 28 // MainnetMagic is mainnet network constant 29 MainnetMagic wire.BitcoinNet = 0xd9b400f9 30 // TestnetMagic is testnet network constant 31 TestnetMagic wire.BitcoinNet = 0xb194aa75 32 ) 33 34 var ( 35 // MainNetParams are parser parameters for mainnet 36 MainNetParams chaincfg.Params 37 // TestNet3Params are parser parameters for testnet 38 TestNet3Params chaincfg.Params 39 ) 40 41 func init() { 42 MainNetParams = chaincfg.MainNetParams 43 MainNetParams.Net = MainnetMagic 44 MainNetParams.PubKeyHashAddrID = []byte{0x07, 0x3f} 45 MainNetParams.ScriptHashAddrID = []byte{0x07, 0x1a} 46 47 TestNet3Params = chaincfg.TestNet3Params 48 TestNet3Params.Net = TestnetMagic 49 TestNet3Params.PubKeyHashAddrID = []byte{0x0f, 0x21} 50 TestNet3Params.ScriptHashAddrID = []byte{0x0e, 0xfc} 51 } 52 53 // DecredParser handle 54 type DecredParser struct { 55 *btc.BitcoinLikeParser 56 baseParser *bchain.BaseParser 57 netConfig *cfg.Params 58 } 59 60 // NewDecredParser returns new DecredParser instance 61 func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParser { 62 d := &DecredParser{ 63 BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c), 64 baseParser: &bchain.BaseParser{}, 65 } 66 67 switch d.BitcoinLikeParser.Params.Name { 68 case "testnet3": 69 d.netConfig = cfg.TestNet3Params() 70 default: 71 d.netConfig = cfg.MainNetParams() 72 } 73 return d 74 } 75 76 // GetChainParams contains network parameters for the main Decred network, 77 // and the test Decred network. 78 func GetChainParams(chain string) *chaincfg.Params { 79 var param *chaincfg.Params 80 81 switch chain { 82 case "testnet3": 83 param = &TestNet3Params 84 default: 85 param = &MainNetParams 86 } 87 88 if !chaincfg.IsRegistered(param) { 89 if err := chaincfg.Register(param); err != nil { 90 panic(err) 91 } 92 } 93 return param 94 } 95 96 // ParseBlock parses raw block to our Block struct. 97 func (p *DecredParser) ParseBlock(b []byte) (*bchain.Block, error) { 98 r := bytes.NewReader(b) 99 h := wire.BlockHeader{} 100 if err := h.Deserialize(r); err != nil { 101 return nil, err 102 } 103 104 if (h.Version & utils.VersionAuxpow) != 0 { 105 if err := utils.SkipAuxpow(r); err != nil { 106 return nil, err 107 } 108 } 109 110 var w wire.MsgBlock 111 if err := utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w); err != nil { 112 return nil, err 113 } 114 115 txs := make([]bchain.Tx, len(w.Transactions)) 116 for ti, t := range w.Transactions { 117 txs[ti] = p.TxFromMsgTx(t, false) 118 } 119 120 return &bchain.Block{ 121 BlockHeader: bchain.BlockHeader{ 122 Size: len(b), 123 Time: h.Timestamp.Unix(), 124 }, 125 Txs: txs, 126 }, nil 127 } 128 129 // ParseTxFromJson parses JSON message containing transaction and returns Tx struct 130 func (p *DecredParser) ParseTxFromJson(jsonTx json.RawMessage) (*bchain.Tx, error) { 131 var getTxResult GetTransactionResult 132 if err := json.Unmarshal([]byte(jsonTx), &getTxResult.Result); err != nil { 133 return nil, err 134 } 135 136 vins := make([]bchain.Vin, len(getTxResult.Result.Vin)) 137 for index, input := range getTxResult.Result.Vin { 138 hexData := bchain.ScriptSig{} 139 if input.ScriptSig != nil { 140 hexData.Hex = input.ScriptSig.Hex 141 } 142 143 vins[index] = bchain.Vin{ 144 Coinbase: input.Coinbase, 145 Txid: input.Txid, 146 Vout: input.Vout, 147 ScriptSig: hexData, 148 Sequence: input.Sequence, 149 // Addresses: []string{}, 150 } 151 } 152 153 vouts := make([]bchain.Vout, len(getTxResult.Result.Vout)) 154 for index, output := range getTxResult.Result.Vout { 155 addr := output.ScriptPubKey.Addresses 156 // If nulldata type found make asm field the address data. 157 if output.ScriptPubKey.Type == "nulldata" { 158 addr = []string{output.ScriptPubKey.Asm} 159 } 160 161 vouts[index] = bchain.Vout{ 162 ValueSat: *big.NewInt(int64(math.Round(output.Value * 1e8))), 163 N: output.N, 164 ScriptPubKey: bchain.ScriptPubKey{ 165 Hex: output.ScriptPubKey.Hex, 166 Addresses: addr, 167 }, 168 } 169 } 170 171 tx := &bchain.Tx{ 172 Hex: getTxResult.Result.Hex, 173 Txid: getTxResult.Result.Txid, 174 Version: getTxResult.Result.Version, 175 LockTime: getTxResult.Result.LockTime, 176 BlockHeight: getTxResult.Result.BlockHeight, 177 Vin: vins, 178 Vout: vouts, 179 Confirmations: uint32(getTxResult.Result.Confirmations), 180 Time: getTxResult.Result.Time, 181 Blocktime: getTxResult.Result.Blocktime, 182 } 183 184 tx.CoinSpecificData = getTxResult.Result.TxExtraInfo 185 186 return tx, nil 187 } 188 189 // GetAddrDescForUnknownInput returns nil AddressDescriptor. 190 func (p *DecredParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor { 191 return nil 192 } 193 194 // GetAddrDescFromAddress returns internal address representation of a given address. 195 func (p *DecredParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 196 addressByte := []byte(address) 197 return bchain.AddressDescriptor(addressByte), nil 198 } 199 200 // GetAddrDescFromVout returns internal address representation of a given transaction output. 201 func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 202 script, err := hex.DecodeString(output.ScriptPubKey.Hex) 203 if err != nil { 204 return nil, err 205 } 206 207 const scriptVersion = 0 208 const treasuryEnabled = true 209 scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, script, 210 p.netConfig, treasuryEnabled) 211 if err != nil { 212 return nil, err 213 } 214 215 if scriptClass.String() == "nulldata" { 216 if parsedOPReturn := p.BitcoinLikeParser.TryParseOPReturn(script); parsedOPReturn != "" { 217 return []byte(parsedOPReturn), nil 218 } 219 } 220 221 var addressByte []byte 222 for i := range addresses { 223 addressByte = append(addressByte, addresses[i].String()...) 224 } 225 return bchain.AddressDescriptor(addressByte), nil 226 } 227 228 // GetAddressesFromAddrDesc returns addresses obtained from the internal address representation 229 func (p *DecredParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 230 var addrs []string 231 if addrDesc != nil { 232 addrs = append(addrs, string(addrDesc)) 233 } 234 return addrs, true, nil 235 } 236 237 // PackTx packs transaction to byte array using protobuf 238 func (p *DecredParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 239 return p.baseParser.PackTx(tx, height, blockTime) 240 } 241 242 // UnpackTx unpacks transaction from protobuf byte array 243 func (p *DecredParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 244 return p.baseParser.UnpackTx(buf) 245 } 246 247 func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) { 248 pk := extKey.SerializedPubKey() 249 hash := dcrutil.Hash160(pk) 250 addr, err := dcrutil.NewAddressPubKeyHash(hash, p.netConfig, dcrec.STEcdsaSecp256k1) 251 if err != nil { 252 return nil, err 253 } 254 return p.GetAddrDescFromAddress(addr.String()) 255 } 256 257 // ParseXpub parses xpub (or xpub descriptor) and returns XpubDescriptor 258 func (p *DecredParser) ParseXpub(xpub string) (*bchain.XpubDescriptor, error) { 259 var descriptor bchain.XpubDescriptor 260 extKey, err := hdkeychain.NewKeyFromString(xpub, p.netConfig) 261 if err != nil { 262 return nil, err 263 } 264 descriptor.Xpub = xpub 265 descriptor.XpubDescriptor = xpub 266 descriptor.Type = bchain.P2PKH 267 descriptor.Bip = "44" 268 descriptor.ChangeIndexes = []uint32{0, 1} 269 descriptor.ExtKey = extKey 270 return &descriptor, nil 271 } 272 273 // DeriveAddressDescriptors derives address descriptors from given xpub for 274 // listed indexes 275 func (p *DecredParser) DeriveAddressDescriptors(descriptor *bchain.XpubDescriptor, change uint32, 276 indexes []uint32) ([]bchain.AddressDescriptor, error) { 277 278 changeExtKey, err := descriptor.ExtKey.(*hdkeychain.ExtendedKey).Child(change) 279 if err != nil { 280 return nil, err 281 } 282 283 ad := make([]bchain.AddressDescriptor, len(indexes)) 284 for i, index := range indexes { 285 indexExtKey, err := changeExtKey.Child(index) 286 if err != nil { 287 return nil, err 288 } 289 ad[i], err = p.addrDescFromExtKey(indexExtKey) 290 if err != nil { 291 return nil, err 292 } 293 } 294 return ad, nil 295 } 296 297 // DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for 298 // addresses in index range 299 func (p *DecredParser) DeriveAddressDescriptorsFromTo(descriptor *bchain.XpubDescriptor, change uint32, 300 fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { 301 if toIndex <= fromIndex { 302 return nil, errors.New("toIndex<=fromIndex") 303 } 304 305 changeExtKey, err := descriptor.ExtKey.(*hdkeychain.ExtendedKey).Child(change) 306 if err != nil { 307 return nil, err 308 } 309 310 ad := make([]bchain.AddressDescriptor, toIndex-fromIndex) 311 for index := fromIndex; index < toIndex; index++ { 312 indexExtKey, err := changeExtKey.Child(index) 313 if err != nil { 314 return nil, err 315 } 316 ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey) 317 if err != nil { 318 return nil, err 319 } 320 } 321 return ad, nil 322 } 323 324 // DerivationBasePath returns base path of xpub which whose full format is 325 // m/44'/<coin type>'/<account>'/<branch>/<address index>. This function only 326 // returns a path up to m/44'/<coin type>'/<account>'/ whereby the rest of the 327 // other details (<branch>/<address index>) are populated automatically. 328 func (p *DecredParser) DerivationBasePath(descriptor *bchain.XpubDescriptor) (string, error) { 329 var c string 330 cn, depth, err := p.decodeXpub(descriptor.Xpub) 331 if err != nil { 332 return "", err 333 } 334 335 if cn >= hdkeychain.HardenedKeyStart { 336 cn -= hdkeychain.HardenedKeyStart 337 c = "'" 338 } 339 340 c = strconv.Itoa(int(cn)) + c 341 if depth != 3 { 342 return "unknown/" + c, nil 343 } 344 345 return "m/44'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil 346 } 347 348 func (p *DecredParser) decodeXpub(xpub string) (childNum uint32, depth uint16, err error) { 349 decoded := base58.Decode(xpub) 350 351 // serializedKeyLen is the length of a serialized public or private 352 // extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes 353 // fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes 354 // public/private key data. 355 serializedKeyLen := 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes 356 if len(decoded) != serializedKeyLen+4 { 357 err = errors.New("invalid extended key length") 358 return 359 } 360 361 payload := decoded[:len(decoded)-4] 362 checkSum := decoded[len(decoded)-4:] 363 expectedCheckSum := chainhash.HashB(chainhash.HashB(payload))[:4] 364 if !bytes.Equal(checkSum, expectedCheckSum) { 365 err = errors.New("bad checksum value") 366 return 367 } 368 369 depth = uint16(payload[4:5][0]) 370 childNum = binary.BigEndian.Uint32(payload[9:13]) 371 return 372 }