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