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  }