github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/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/juju/errors" 11 ) 12 13 // BaseParser implements data parsing/handling functionality base for all other parsers 14 type BaseParser struct { 15 BlockAddressesToKeep int 16 AmountDecimalPoint int 17 } 18 19 // ParseBlock parses raw block to our Block struct - currently not implemented 20 func (p *BaseParser) ParseBlock(b []byte) (*Block, error) { 21 return nil, errors.New("ParseBlock: not implemented") 22 } 23 24 // ParseTx parses byte array containing transaction and returns Tx struct - currently not implemented 25 func (p *BaseParser) ParseTx(b []byte) (*Tx, error) { 26 return nil, errors.New("ParseTx: not implemented") 27 } 28 29 const zeros = "0000000000000000000000000000000000000000" 30 31 // AmountToBigInt converts amount in json.Number (string) to big.Int 32 // it uses string operations to avoid problems with rounding 33 func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) { 34 var r big.Int 35 s := string(n) 36 i := strings.IndexByte(s, '.') 37 if i == -1 { 38 s = s + zeros[:p.AmountDecimalPoint] 39 } else { 40 z := p.AmountDecimalPoint - len(s) + i + 1 41 if z > 0 { 42 s = s[:i] + s[i+1:] + zeros[:z] 43 } else { 44 s = s[:i] + s[i+1:len(s)+z] 45 } 46 } 47 if _, ok := r.SetString(s, 10); !ok { 48 return r, errors.New("AmountToBigInt: failed to convert") 49 } 50 return r, nil 51 } 52 53 // AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place 54 func (p *BaseParser) AmountToDecimalString(a *big.Int) string { 55 n := a.String() 56 var s string 57 if n[0] == '-' { 58 n = n[1:] 59 s = "-" 60 } 61 if len(n) <= p.AmountDecimalPoint { 62 n = zeros[:p.AmountDecimalPoint-len(n)+1] + n 63 } 64 i := len(n) - p.AmountDecimalPoint 65 ad := strings.TrimRight(n[i:], "0") 66 if len(ad) > 0 { 67 n = n[:i] + "." + ad 68 } else { 69 n = n[:i] 70 } 71 return s + n 72 } 73 74 // ParseTxFromJson parses JSON message containing transaction and returns Tx struct 75 func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { 76 var tx Tx 77 err := json.Unmarshal(msg, &tx) 78 if err != nil { 79 return nil, err 80 } 81 82 for i := range tx.Vout { 83 vout := &tx.Vout[i] 84 // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal 85 vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) 86 if err != nil { 87 return nil, err 88 } 89 vout.JsonValue = "" 90 } 91 92 return &tx, nil 93 } 94 95 // PackedTxidLen returns length in bytes of packed txid 96 func (p *BaseParser) PackedTxidLen() int { 97 return 32 98 } 99 100 // KeepBlockAddresses returns number of blocks which are to be kept in blockaddresses column 101 func (p *BaseParser) KeepBlockAddresses() int { 102 return p.BlockAddressesToKeep 103 } 104 105 // PackTxid packs txid to byte array 106 func (p *BaseParser) PackTxid(txid string) ([]byte, error) { 107 if txid == "" { 108 return nil, ErrTxidMissing 109 } 110 return hex.DecodeString(txid) 111 } 112 113 // UnpackTxid unpacks byte array to txid 114 func (p *BaseParser) UnpackTxid(buf []byte) (string, error) { 115 return hex.EncodeToString(buf), nil 116 } 117 118 // PackBlockHash packs block hash to byte array 119 func (p *BaseParser) PackBlockHash(hash string) ([]byte, error) { 120 return hex.DecodeString(hash) 121 } 122 123 // UnpackBlockHash unpacks byte array to block hash 124 func (p *BaseParser) UnpackBlockHash(buf []byte) (string, error) { 125 return hex.EncodeToString(buf), nil 126 } 127 128 // IsUTXOChain returns true if the block chain is UTXO type, otherwise false 129 func (p *BaseParser) IsUTXOChain() bool { 130 return true 131 } 132 133 // PackTx packs transaction to byte array using protobuf 134 func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) { 135 var err error 136 pti := make([]*ProtoTransaction_VinType, len(tx.Vin)) 137 for i, vi := range tx.Vin { 138 hex, err := hex.DecodeString(vi.ScriptSig.Hex) 139 if err != nil { 140 return nil, errors.Annotatef(err, "Vin %v Hex %v", i, vi.ScriptSig.Hex) 141 } 142 itxid, err := p.PackTxid(vi.Txid) 143 if err != nil { 144 return nil, errors.Annotatef(err, "Vin %v Txid %v", i, vi.Txid) 145 } 146 pti[i] = &ProtoTransaction_VinType{ 147 Addresses: vi.Addresses, 148 Coinbase: vi.Coinbase, 149 ScriptSigHex: hex, 150 Sequence: vi.Sequence, 151 Txid: itxid, 152 Vout: vi.Vout, 153 } 154 } 155 pto := make([]*ProtoTransaction_VoutType, len(tx.Vout)) 156 for i, vo := range tx.Vout { 157 hex, err := hex.DecodeString(vo.ScriptPubKey.Hex) 158 if err != nil { 159 return nil, errors.Annotatef(err, "Vout %v Hex %v", i, vo.ScriptPubKey.Hex) 160 } 161 pto[i] = &ProtoTransaction_VoutType{ 162 Addresses: vo.ScriptPubKey.Addresses, 163 N: vo.N, 164 ScriptPubKeyHex: hex, 165 ValueSat: vo.ValueSat.Bytes(), 166 } 167 } 168 pt := &ProtoTransaction{ 169 Blocktime: uint64(blockTime), 170 Height: height, 171 Locktime: tx.LockTime, 172 Vin: pti, 173 Vout: pto, 174 } 175 if pt.Hex, err = hex.DecodeString(tx.Hex); err != nil { 176 return nil, errors.Annotatef(err, "Hex %v", tx.Hex) 177 } 178 if pt.Txid, err = p.PackTxid(tx.Txid); err != nil { 179 return nil, errors.Annotatef(err, "Txid %v", tx.Txid) 180 } 181 return proto.Marshal(pt) 182 } 183 184 // UnpackTx unpacks transaction from protobuf byte array 185 func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) { 186 var pt ProtoTransaction 187 err := proto.Unmarshal(buf, &pt) 188 if err != nil { 189 return nil, 0, err 190 } 191 txid, err := p.UnpackTxid(pt.Txid) 192 if err != nil { 193 return nil, 0, err 194 } 195 vin := make([]Vin, len(pt.Vin)) 196 for i, pti := range pt.Vin { 197 itxid, err := p.UnpackTxid(pti.Txid) 198 if err != nil { 199 return nil, 0, err 200 } 201 vin[i] = Vin{ 202 Addresses: pti.Addresses, 203 Coinbase: pti.Coinbase, 204 ScriptSig: ScriptSig{ 205 Hex: hex.EncodeToString(pti.ScriptSigHex), 206 }, 207 Sequence: pti.Sequence, 208 Txid: itxid, 209 Vout: pti.Vout, 210 } 211 } 212 vout := make([]Vout, len(pt.Vout)) 213 for i, pto := range pt.Vout { 214 var vs big.Int 215 vs.SetBytes(pto.ValueSat) 216 vout[i] = Vout{ 217 N: pto.N, 218 ScriptPubKey: ScriptPubKey{ 219 Addresses: pto.Addresses, 220 Hex: hex.EncodeToString(pto.ScriptPubKeyHex), 221 }, 222 ValueSat: vs, 223 } 224 } 225 tx := Tx{ 226 Blocktime: int64(pt.Blocktime), 227 Hex: hex.EncodeToString(pt.Hex), 228 LockTime: pt.Locktime, 229 Time: int64(pt.Blocktime), 230 Txid: txid, 231 Vin: vin, 232 Vout: vout, 233 } 234 return &tx, pt.Height, nil 235 }