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  }