github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/xcb/xcbparser.go (about)

     1  package xcb
     2  
     3  import (
     4  	"encoding/hex"
     5  	"math/big"
     6  	"strconv"
     7  
     8  	xcbcommon "github.com/core-coin/go-core/v2/common"
     9  	"github.com/core-coin/go-core/v2/common/hexutil"
    10  	"github.com/cryptohub-digital/blockbook-fork/bchain"
    11  	"github.com/golang/protobuf/proto"
    12  	"github.com/juju/errors"
    13  )
    14  
    15  // CoreCoinTypeAddressDescriptorLen - the AddressDescriptor of Core Coin has fixed length
    16  const CoreCoinTypeAddressDescriptorLen = 22
    17  
    18  // CoreCoinTypeTxidLen - the length of Txid
    19  const CoreCoinTypeTxidLen = 32
    20  
    21  // CoreAmountDecimalPoint defines number of decimal points in Core amounts
    22  const CoreAmountDecimalPoint = 18
    23  
    24  // CoreCoinParser handle
    25  type CoreCoinParser struct {
    26  	*bchain.BaseParser
    27  }
    28  
    29  // NewCoreCoinParser returns new CoreCoinParser instance
    30  func NewCoreCoinParser(b int) *CoreCoinParser {
    31  	return &CoreCoinParser{&bchain.BaseParser{
    32  		BlockAddressesToKeep: b,
    33  		AmountDecimalPoint:   CoreAmountDecimalPoint,
    34  	}}
    35  }
    36  
    37  type rpcHeader struct {
    38  	Hash       string `json:"hash"`
    39  	ParentHash string `json:"parentHash"`
    40  	Difficulty string `json:"difficulty"`
    41  	Number     string `json:"number"`
    42  	Time       string `json:"timestamp"`
    43  	Size       string `json:"size"`
    44  	Nonce      string `json:"nonce"`
    45  }
    46  
    47  type rpcLogWithTxHash struct {
    48  	RpcLog
    49  	Hash string `json:"transactionHash"`
    50  }
    51  
    52  type rpcBlockTransactions struct {
    53  	Transactions []RpcTransaction `json:"transactions"`
    54  }
    55  
    56  type rpcBlockTxids struct {
    57  	Transactions []string `json:"transactions"`
    58  }
    59  
    60  func xcbNumber(n string) (int64, error) {
    61  	if len(n) > 2 {
    62  		return strconv.ParseInt(n[2:], 16, 64)
    63  	}
    64  	return 0, errors.Errorf("Not a number: '%v'", n)
    65  }
    66  
    67  func (p *CoreCoinParser) xcbTxToTx(tx *RpcTransaction, receipt *RpcReceipt, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
    68  	txid := tx.Hash
    69  	var (
    70  		fa, ta []string
    71  		err    error
    72  	)
    73  	if len(tx.From) > 2 {
    74  		fa = []string{tx.From}
    75  	}
    76  	if len(tx.To) > 2 {
    77  		ta = []string{tx.To}
    78  	}
    79  	ct := CoreCoinSpecificData{
    80  		Tx:      tx,
    81  		Receipt: receipt,
    82  	}
    83  	vs, err := hexutil.DecodeBig(tx.Value)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	return &bchain.Tx{
    88  		Blocktime:     blocktime,
    89  		Confirmations: confirmations,
    90  		Time:          blocktime,
    91  		Txid:          txid,
    92  		Vin: []bchain.Vin{
    93  			{
    94  				Addresses: fa,
    95  			},
    96  		},
    97  		Vout: []bchain.Vout{
    98  			{
    99  				N:        0, // there is always up to one To address
   100  				ValueSat: *vs,
   101  				ScriptPubKey: bchain.ScriptPubKey{
   102  					Addresses: ta,
   103  				},
   104  			},
   105  		},
   106  		CoinSpecificData: ct,
   107  	}, nil
   108  }
   109  
   110  // GetAddrDescFromVout returns internal address representation of given transaction output
   111  func (p *CoreCoinParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
   112  	if len(output.ScriptPubKey.Addresses) != 1 {
   113  		return nil, bchain.ErrAddressMissing
   114  	}
   115  	return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0])
   116  }
   117  
   118  func has0xPrefix(s string) bool {
   119  	return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x'
   120  }
   121  
   122  // GetAddrDescFromAddress returns internal address representation of given address
   123  func (p *CoreCoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
   124  	if address == "" {
   125  		return nil, bchain.ErrAddressMissing
   126  	}
   127  	parsed, err := xcbcommon.HexToAddress(address)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	return parsed.Bytes(), nil
   132  }
   133  
   134  // GetAddressesFromAddrDesc returns addresse for given address descriptor
   135  func (p *CoreCoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
   136  	addr, err := xcbcommon.HexToAddress(xcbcommon.Bytes2Hex(addrDesc))
   137  	if err != nil {
   138  		return []string{}, false, err
   139  	}
   140  	return []string{addr.Hex()}, true, nil
   141  }
   142  
   143  // GetScriptFromAddrDesc returns output script for given address descriptor
   144  func (p *CoreCoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) {
   145  	return addrDesc, nil
   146  }
   147  
   148  func hexDecode(s string) ([]byte, error) {
   149  	b, err := hexutil.Decode(s)
   150  	if err != nil && err != hexutil.ErrEmptyString {
   151  		return nil, err
   152  	}
   153  	return b, nil
   154  }
   155  
   156  func hexDecodeBig(s string) ([]byte, error) {
   157  	b, err := hexutil.DecodeBig(s)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return b.Bytes(), nil
   162  }
   163  
   164  func hexEncodeBig(b []byte) string {
   165  	var i big.Int
   166  	i.SetBytes(b)
   167  	return hexutil.EncodeBig(&i)
   168  }
   169  
   170  func decodeAddress(addr []byte) (string, error) {
   171  	address, err := xcbcommon.HexToAddress(xcbcommon.Bytes2Hex(addr))
   172  	if err != nil {
   173  		return "", err
   174  	}
   175  	return address.Hex(), nil
   176  }
   177  
   178  // PackTx packs transaction to byte array
   179  // completeTransaction.InternalData are not packed, they are stored in a different table
   180  func (p *CoreCoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
   181  	var err error
   182  	var n uint64
   183  	r, ok := tx.CoinSpecificData.(CoreCoinSpecificData)
   184  	if !ok {
   185  		return nil, errors.New("Missing CoinSpecificData")
   186  	}
   187  	pt := &ProtoXCBCompleteTransaction{}
   188  	pt.Tx = &ProtoXCBCompleteTransaction_TxType{}
   189  	if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil {
   190  		return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce)
   191  	}
   192  	// pt.BlockNumber = height
   193  	if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil {
   194  		return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber)
   195  	}
   196  	pt.BlockNumber = uint32(n)
   197  	pt.BlockTime = uint64(blockTime)
   198  	if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil {
   199  		return nil, errors.Annotatef(err, "From %v", r.Tx.From)
   200  	}
   201  	if pt.Tx.EnergyLimit, err = hexutil.DecodeUint64(r.Tx.EnergyLimit); err != nil {
   202  		return nil, errors.Annotatef(err, "EnergyLimit %v", r.Tx.EnergyLimit)
   203  	}
   204  	if pt.Tx.Hash, err = hexDecode(r.Tx.Hash); err != nil {
   205  		return nil, errors.Annotatef(err, "Hash %v", r.Tx.Hash)
   206  	}
   207  	if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil {
   208  		return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload)
   209  	}
   210  	if pt.Tx.EnergyPrice, err = hexDecodeBig(r.Tx.EnergyPrice); err != nil {
   211  		return nil, errors.Annotatef(err, "EnergyPrice %v", r.Tx.EnergyPrice)
   212  	}
   213  	if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil {
   214  		return nil, errors.Annotatef(err, "To %v", r.Tx.To)
   215  	}
   216  	if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil {
   217  		return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex)
   218  	}
   219  	pt.Tx.TransactionIndex = uint32(n)
   220  	if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil {
   221  		return nil, errors.Annotatef(err, "Value %v", r.Tx.Value)
   222  	}
   223  	if r.Receipt != nil {
   224  		pt.Receipt = &ProtoXCBCompleteTransaction_ReceiptType{}
   225  		if pt.Receipt.EnergyUsed, err = hexDecodeBig(r.Receipt.EnergyUsed); err != nil {
   226  			return nil, errors.Annotatef(err, "EnergyUsed %v", r.Receipt.EnergyUsed)
   227  		}
   228  		if r.Receipt.Status != "" {
   229  			if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil {
   230  				return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status)
   231  			}
   232  		} else {
   233  			// unknown status, use 'U' as status bytes
   234  			// there is a potential for conflict with value 0x55 but this is not used by any chain at this moment
   235  			pt.Receipt.Status = []byte{'U'}
   236  		}
   237  		ptLogs := make([]*ProtoXCBCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs))
   238  		for i, l := range r.Receipt.Logs {
   239  			a, err := hexutil.Decode(l.Address)
   240  			if err != nil {
   241  				return nil, errors.Annotatef(err, "Address cannot be decoded %v", l)
   242  			}
   243  			d, err := hexutil.Decode(l.Data)
   244  			if err != nil {
   245  				return nil, errors.Annotatef(err, "Data cannot be decoded %v", l)
   246  			}
   247  			t := make([][]byte, len(l.Topics))
   248  			for j, s := range l.Topics {
   249  				t[j], err = hexutil.Decode(s)
   250  				if err != nil {
   251  					return nil, errors.Annotatef(err, "Topic cannot be decoded %v", l)
   252  				}
   253  			}
   254  			ptLogs[i] = &ProtoXCBCompleteTransaction_ReceiptType_LogType{
   255  				Address: a,
   256  				Data:    d,
   257  				Topics:  t,
   258  			}
   259  
   260  		}
   261  		pt.Receipt.Log = ptLogs
   262  	}
   263  	return proto.Marshal(pt)
   264  }
   265  
   266  // UnpackTx unpacks transaction from byte array
   267  func (p *CoreCoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
   268  	var pt ProtoXCBCompleteTransaction
   269  	err := proto.Unmarshal(buf, &pt)
   270  	if err != nil {
   271  		return nil, 0, err
   272  	}
   273  	from, err := decodeAddress(pt.Tx.From)
   274  	if err != nil {
   275  		return nil, 0, err
   276  	}
   277  
   278  	to, err := decodeAddress(pt.Tx.To)
   279  	if err != nil {
   280  		return nil, 0, err
   281  	}
   282  	rt := RpcTransaction{
   283  		AccountNonce:     hexutil.EncodeUint64(pt.Tx.AccountNonce),
   284  		BlockNumber:      hexutil.EncodeUint64(uint64(pt.BlockNumber)),
   285  		From:             from,
   286  		EnergyLimit:      hexutil.EncodeUint64(pt.Tx.EnergyLimit),
   287  		Hash:             hexutil.Encode(pt.Tx.Hash),
   288  		Payload:          hexutil.Encode(pt.Tx.Payload),
   289  		EnergyPrice:      hexEncodeBig(pt.Tx.EnergyPrice),
   290  		To:               to,
   291  		TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)),
   292  		Value:            hexEncodeBig(pt.Tx.Value),
   293  	}
   294  	var rr *RpcReceipt
   295  	if pt.Receipt != nil {
   296  		logs := make([]*RpcLog, len(pt.Receipt.Log))
   297  		for i, l := range pt.Receipt.Log {
   298  			topics := make([]string, len(l.Topics))
   299  			for j, t := range l.Topics {
   300  				topics[j] = hexutil.Encode(t)
   301  			}
   302  			address, err := decodeAddress(l.Address)
   303  			if err != nil {
   304  				return nil, 0, err
   305  			}
   306  			logs[i] = &RpcLog{
   307  				Address: address,
   308  				Data:    hexutil.Encode(l.Data),
   309  				Topics:  topics,
   310  			}
   311  		}
   312  		status := ""
   313  		// handle a special value []byte{'U'} as unknown state
   314  		if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' {
   315  			status = hexEncodeBig(pt.Receipt.Status)
   316  		}
   317  		rr = &RpcReceipt{
   318  			EnergyUsed: hexEncodeBig(pt.Receipt.EnergyUsed),
   319  			Status:     status,
   320  			Logs:       logs,
   321  		}
   322  	}
   323  	tx, err := p.xcbTxToTx(&rt, rr, int64(pt.BlockTime), 0)
   324  	if err != nil {
   325  		return nil, 0, err
   326  	}
   327  	return tx, pt.BlockNumber, nil
   328  }
   329  
   330  // PackedTxidLen returns length in bytes of packed txid
   331  func (p *CoreCoinParser) PackedTxidLen() int {
   332  	return CoreCoinTypeTxidLen
   333  }
   334  
   335  // PackTxid packs txid to byte array
   336  func (p *CoreCoinParser) PackTxid(txid string) ([]byte, error) {
   337  	if has0xPrefix(txid) {
   338  		txid = txid[2:]
   339  	}
   340  	return hex.DecodeString(txid)
   341  }
   342  
   343  // UnpackTxid unpacks byte array to txid
   344  func (p *CoreCoinParser) UnpackTxid(buf []byte) (string, error) {
   345  	return hexutil.Encode(buf), nil
   346  }
   347  
   348  // PackBlockHash packs block hash to byte array
   349  func (p *CoreCoinParser) PackBlockHash(hash string) ([]byte, error) {
   350  	if has0xPrefix(hash) {
   351  		hash = hash[2:]
   352  	}
   353  	return hex.DecodeString(hash)
   354  }
   355  
   356  // UnpackBlockHash unpacks byte array to block hash
   357  func (p *CoreCoinParser) UnpackBlockHash(buf []byte) (string, error) {
   358  	return hexutil.Encode(buf), nil
   359  }
   360  
   361  // GetChainType returns CoreCoinType
   362  func (p *CoreCoinParser) GetChainType() bchain.ChainType {
   363  	return bchain.ChainCoreCoinType
   364  }
   365  
   366  // GetHeightFromTx returns core coin specific data from bchain.Tx
   367  func GetHeightFromTx(tx *bchain.Tx) (uint32, error) {
   368  	var bn string
   369  	csd, ok := tx.CoinSpecificData.(CoreCoinSpecificData)
   370  	if !ok {
   371  		return 0, errors.New("Missing CoinSpecificData")
   372  	}
   373  	bn = csd.Tx.BlockNumber
   374  	n, err := hexutil.DecodeUint64(bn)
   375  	if err != nil {
   376  		return 0, errors.Annotatef(err, "BlockNumber %v", bn)
   377  	}
   378  	return uint32(n), nil
   379  }
   380  
   381  // CoreCoinTypeGetTokenTransfersFromTx returns tokens data from bchain.Tx
   382  func (p *CoreCoinParser) CoreCoinTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bchain.TokenTransfers, error) {
   383  	var r bchain.TokenTransfers
   384  	var err error
   385  	csd, ok := tx.CoinSpecificData.(CoreCoinSpecificData)
   386  	if ok {
   387  		if csd.Receipt != nil {
   388  			r, err = getTokenTransfersFromLog(csd.Receipt.Logs)
   389  		} else {
   390  			r, err = getTokenTransfersFromTx(csd.Tx)
   391  		}
   392  		if err != nil {
   393  			return nil, err
   394  		}
   395  	}
   396  	return r, nil
   397  }
   398  
   399  // TxStatus is status of transaction
   400  type TxStatus int
   401  
   402  // statuses of transaction
   403  const (
   404  	TxStatusUnknown = TxStatus(iota - 2)
   405  	TxStatusPending
   406  	TxStatusFailure
   407  	TxStatusOK
   408  )
   409  
   410  // CoreCoinTxData contains core coin specific transaction data
   411  type CoreCoinTxData struct {
   412  	Status      TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown
   413  	Nonce       uint64   `json:"nonce"`
   414  	EnergyLimit *big.Int `json:"energylimit"`
   415  	EnergyUsed  *big.Int `json:"energyused"`
   416  	EnergyPrice *big.Int `json:"energyprice"`
   417  	Data        string   `json:"data"`
   418  }
   419  
   420  // GetCoreCoinTxData returns CoreCoinTxData from bchain.Tx
   421  func GetCoreCoinTxData(tx *bchain.Tx) *CoreCoinTxData {
   422  	return GetCoreCoinTxDataFromSpecificData(tx.CoinSpecificData)
   423  }
   424  
   425  // GetCoreCoinTxDataFromSpecificData returns CoreCoinTxData from coinSpecificData
   426  func GetCoreCoinTxDataFromSpecificData(coinSpecificData interface{}) *CoreCoinTxData {
   427  	etd := CoreCoinTxData{Status: TxStatusPending}
   428  	csd, ok := coinSpecificData.(CoreCoinSpecificData)
   429  	if ok {
   430  		if csd.Tx != nil {
   431  			etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
   432  			etd.EnergyLimit, _ = hexutil.DecodeBig(csd.Tx.EnergyLimit)
   433  			etd.EnergyPrice, _ = hexutil.DecodeBig(csd.Tx.EnergyPrice)
   434  			etd.Data = csd.Tx.Payload
   435  		}
   436  		if csd.Receipt != nil {
   437  			switch csd.Receipt.Status {
   438  			case "0x1":
   439  				etd.Status = TxStatusOK
   440  			case "": // old transactions did not set status
   441  				etd.Status = TxStatusUnknown
   442  			default:
   443  				etd.Status = TxStatusFailure
   444  			}
   445  			etd.EnergyUsed, _ = hexutil.DecodeBig(csd.Receipt.EnergyUsed)
   446  		}
   447  	}
   448  	return &etd
   449  }