github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/bchain/baseparser.go (about) 1 package bchain 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "math/big" 7 "strings" 8 9 "github.com/cryptohub-digital/blockbook/common" 10 "github.com/golang/glog" 11 "github.com/juju/errors" 12 "google.golang.org/protobuf/proto" 13 ) 14 15 // BaseParser implements data parsing/handling functionality base for all other parsers 16 type BaseParser struct { 17 BlockAddressesToKeep int 18 AmountDecimalPoint int 19 AddressAliases bool 20 } 21 22 // ParseBlock parses raw block to our Block struct - currently not implemented 23 func (p *BaseParser) ParseBlock(b []byte) (*Block, error) { 24 return nil, errors.New("ParseBlock: not implemented") 25 } 26 27 // ParseTx parses byte array containing transaction and returns Tx struct - currently not implemented 28 func (p *BaseParser) ParseTx(b []byte) (*Tx, error) { 29 return nil, errors.New("ParseTx: not implemented") 30 } 31 32 // GetAddrDescForUnknownInput returns nil AddressDescriptor 33 func (p *BaseParser) GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescriptor { 34 var iTxid string 35 if len(tx.Vin) > input { 36 iTxid = tx.Vin[input].Txid 37 } 38 glog.Warningf("tx %v, input tx %v not found in txAddresses", tx.Txid, iTxid) 39 return nil 40 } 41 42 const zeros = "0000000000000000000000000000000000000000" 43 44 // AmountToBigInt converts amount in common.JSONNumber (string) to big.Int 45 // it uses string operations to avoid problems with rounding 46 func (p *BaseParser) AmountToBigInt(n common.JSONNumber) (big.Int, error) { 47 var r big.Int 48 s := string(n) 49 i := strings.IndexByte(s, '.') 50 d := p.AmountDecimalPoint 51 if d > len(zeros) { 52 d = len(zeros) 53 } 54 if i == -1 { 55 s = s + zeros[:d] 56 } else { 57 z := d - len(s) + i + 1 58 if z > 0 { 59 s = s[:i] + s[i+1:] + zeros[:z] 60 } else { 61 s = s[:i] + s[i+1:len(s)+z] 62 } 63 } 64 if _, ok := r.SetString(s, 10); !ok { 65 return r, errors.New("AmountToBigInt: failed to convert") 66 } 67 return r, nil 68 } 69 70 // AmountToDecimalString converts amount in big.Int to string with decimal point in the place defined by the parameter d 71 func AmountToDecimalString(a *big.Int, d int) string { 72 if a == nil { 73 return "" 74 } 75 n := a.String() 76 var s string 77 if n[0] == '-' { 78 n = n[1:] 79 s = "-" 80 } 81 if d > len(zeros) { 82 d = len(zeros) 83 } 84 if len(n) <= d { 85 n = zeros[:d-len(n)+1] + n 86 } 87 i := len(n) - d 88 ad := strings.TrimRight(n[i:], "0") 89 if len(ad) > 0 { 90 n = n[:i] + "." + ad 91 } else { 92 n = n[:i] 93 } 94 return s + n 95 } 96 97 // AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place 98 func (p *BaseParser) AmountToDecimalString(a *big.Int) string { 99 return AmountToDecimalString(a, p.AmountDecimalPoint) 100 } 101 102 // AmountDecimals returns number of decimal places in amounts 103 func (p *BaseParser) AmountDecimals() int { 104 return p.AmountDecimalPoint 105 } 106 107 // UseAddressAliases returns true if address aliases are enabled 108 func (p *BaseParser) UseAddressAliases() bool { 109 return p.AddressAliases 110 } 111 112 // ParseTxFromJson parses JSON message containing transaction and returns Tx struct 113 func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { 114 var tx Tx 115 err := json.Unmarshal(msg, &tx) 116 if err != nil { 117 return nil, err 118 } 119 120 for i := range tx.Vout { 121 vout := &tx.Vout[i] 122 // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal 123 vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) 124 if err != nil { 125 return nil, err 126 } 127 vout.JsonValue = "" 128 } 129 130 return &tx, nil 131 } 132 133 // PackedTxidLen returns length in bytes of packed txid 134 func (p *BaseParser) PackedTxidLen() int { 135 return 32 136 } 137 138 // KeepBlockAddresses returns number of blocks which are to be kept in blockaddresses column 139 func (p *BaseParser) KeepBlockAddresses() int { 140 return p.BlockAddressesToKeep 141 } 142 143 // PackTxid packs txid to byte array 144 func (p *BaseParser) PackTxid(txid string) ([]byte, error) { 145 if txid == "" { 146 return nil, ErrTxidMissing 147 } 148 return hex.DecodeString(txid) 149 } 150 151 // UnpackTxid unpacks byte array to txid 152 func (p *BaseParser) UnpackTxid(buf []byte) (string, error) { 153 return hex.EncodeToString(buf), nil 154 } 155 156 // PackBlockHash packs block hash to byte array 157 func (p *BaseParser) PackBlockHash(hash string) ([]byte, error) { 158 return hex.DecodeString(hash) 159 } 160 161 // UnpackBlockHash unpacks byte array to block hash 162 func (p *BaseParser) UnpackBlockHash(buf []byte) (string, error) { 163 return hex.EncodeToString(buf), nil 164 } 165 166 // GetChainType is type of the blockchain, default is ChainBitcoinType 167 func (p *BaseParser) GetChainType() ChainType { 168 return ChainBitcoinType 169 } 170 171 // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent 172 func (p *BaseParser) MinimumCoinbaseConfirmations() int { 173 return 0 174 } 175 176 // SupportsVSize returns true if vsize of a transaction should be computed and returned by API 177 func (p *BaseParser) SupportsVSize() bool { 178 return false 179 } 180 181 // PackTx packs transaction to byte array using protobuf 182 func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) { 183 var err error 184 pti := make([]*ProtoTransaction_VinType, len(tx.Vin)) 185 for i, vi := range tx.Vin { 186 hex, err := hex.DecodeString(vi.ScriptSig.Hex) 187 if err != nil { 188 return nil, errors.Annotatef(err, "Vin %v Hex %v", i, vi.ScriptSig.Hex) 189 } 190 // coinbase txs do not have Vin.txid 191 itxid, err := p.PackTxid(vi.Txid) 192 if err != nil && err != ErrTxidMissing { 193 return nil, errors.Annotatef(err, "Vin %v Txid %v", i, vi.Txid) 194 } 195 pti[i] = &ProtoTransaction_VinType{ 196 Addresses: vi.Addresses, 197 Coinbase: vi.Coinbase, 198 ScriptSigHex: hex, 199 Sequence: vi.Sequence, 200 Txid: itxid, 201 Vout: vi.Vout, 202 } 203 } 204 pto := make([]*ProtoTransaction_VoutType, len(tx.Vout)) 205 for i, vo := range tx.Vout { 206 hex, err := hex.DecodeString(vo.ScriptPubKey.Hex) 207 if err != nil { 208 return nil, errors.Annotatef(err, "Vout %v Hex %v", i, vo.ScriptPubKey.Hex) 209 } 210 pto[i] = &ProtoTransaction_VoutType{ 211 Addresses: vo.ScriptPubKey.Addresses, 212 N: vo.N, 213 ScriptPubKeyHex: hex, 214 ValueSat: vo.ValueSat.Bytes(), 215 } 216 } 217 pt := &ProtoTransaction{ 218 Blocktime: uint64(blockTime), 219 Height: height, 220 Locktime: tx.LockTime, 221 Vin: pti, 222 Vout: pto, 223 Version: tx.Version, 224 VSize: tx.VSize, 225 } 226 if pt.Hex, err = hex.DecodeString(tx.Hex); err != nil { 227 return nil, errors.Annotatef(err, "Hex %v", tx.Hex) 228 } 229 if pt.Txid, err = p.PackTxid(tx.Txid); err != nil { 230 return nil, errors.Annotatef(err, "Txid %v", tx.Txid) 231 } 232 return proto.Marshal(pt) 233 } 234 235 // UnpackTx unpacks transaction from protobuf byte array 236 func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) { 237 var pt ProtoTransaction 238 err := proto.Unmarshal(buf, &pt) 239 if err != nil { 240 return nil, 0, err 241 } 242 txid, err := p.UnpackTxid(pt.Txid) 243 if err != nil { 244 return nil, 0, err 245 } 246 vin := make([]Vin, len(pt.Vin)) 247 for i, pti := range pt.Vin { 248 itxid, err := p.UnpackTxid(pti.Txid) 249 if err != nil { 250 return nil, 0, err 251 } 252 vin[i] = Vin{ 253 Addresses: pti.Addresses, 254 Coinbase: pti.Coinbase, 255 ScriptSig: ScriptSig{ 256 Hex: hex.EncodeToString(pti.ScriptSigHex), 257 }, 258 Sequence: pti.Sequence, 259 Txid: itxid, 260 Vout: pti.Vout, 261 } 262 } 263 vout := make([]Vout, len(pt.Vout)) 264 for i, pto := range pt.Vout { 265 var vs big.Int 266 vs.SetBytes(pto.ValueSat) 267 vout[i] = Vout{ 268 N: pto.N, 269 ScriptPubKey: ScriptPubKey{ 270 Addresses: pto.Addresses, 271 Hex: hex.EncodeToString(pto.ScriptPubKeyHex), 272 }, 273 ValueSat: vs, 274 } 275 } 276 tx := Tx{ 277 Blocktime: int64(pt.Blocktime), 278 Hex: hex.EncodeToString(pt.Hex), 279 LockTime: pt.Locktime, 280 Time: int64(pt.Blocktime), 281 Txid: txid, 282 Vin: vin, 283 Vout: vout, 284 Version: pt.Version, 285 VSize: pt.VSize, 286 } 287 return &tx, pt.Height, nil 288 } 289 290 // IsAddrDescIndexable returns true if AddressDescriptor should be added to index 291 // by default all AddressDescriptors are indexable 292 func (p *BaseParser) IsAddrDescIndexable(addrDesc AddressDescriptor) bool { 293 return true 294 } 295 296 // ParseXpub is unsupported 297 func (p *BaseParser) ParseXpub(xpub string) (*XpubDescriptor, error) { 298 return nil, errors.New("Not supported") 299 } 300 301 // DerivationBasePath is unsupported 302 func (p *BaseParser) DerivationBasePath(descriptor *XpubDescriptor) (string, error) { 303 return "", errors.New("Not supported") 304 } 305 306 // DeriveAddressDescriptors is unsupported 307 func (p *BaseParser) DeriveAddressDescriptors(descriptor *XpubDescriptor, change uint32, indexes []uint32) ([]AddressDescriptor, error) { 308 return nil, errors.New("Not supported") 309 } 310 311 // DeriveAddressDescriptorsFromTo is unsupported 312 func (p *BaseParser) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) { 313 return nil, errors.New("Not supported") 314 } 315 316 // EthereumTypeGetTokenTransfersFromTx is unsupported 317 func (p *BaseParser) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) { 318 return nil, errors.New("Not supported") 319 } 320 321 // FormatAddressAlias makes possible to do coin specific formatting to an address alias 322 func (p *BaseParser) FormatAddressAlias(address string, name string) string { 323 return name 324 } 325 326 // CoreCoinTypeGetTokenTransfersFromTx is unsupported 327 func (p *BaseParser) CoreCoinTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) { 328 return nil, errors.New("Not supported") 329 }