296 lines
7.9 KiB
Go
296 lines
7.9 KiB
Go
package bchain
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/golang/glog"
|
|
"github.com/juju/errors"
|
|
)
|
|
|
|
// BaseParser implements data parsing/handling functionality base for all other parsers
|
|
type BaseParser struct {
|
|
BlockAddressesToKeep int
|
|
AmountDecimalPoint int
|
|
}
|
|
|
|
// ParseBlock parses raw block to our Block struct - currently not implemented
|
|
func (p *BaseParser) ParseBlock(b []byte) (*Block, error) {
|
|
return nil, errors.New("ParseBlock: not implemented")
|
|
}
|
|
|
|
// ParseTx parses byte array containing transaction and returns Tx struct - currently not implemented
|
|
func (p *BaseParser) ParseTx(b []byte) (*Tx, error) {
|
|
return nil, errors.New("ParseTx: not implemented")
|
|
}
|
|
|
|
// GetAddrDescForUnknownInput returns nil AddressDescriptor
|
|
func (p *BaseParser) GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescriptor {
|
|
var iTxid string
|
|
if len(tx.Vin) > input {
|
|
iTxid = tx.Vin[input].Txid
|
|
}
|
|
glog.Warningf("tx %v, input tx %v not found in txAddresses", tx.Txid, iTxid)
|
|
return nil
|
|
}
|
|
|
|
const zeros = "0000000000000000000000000000000000000000"
|
|
|
|
// AmountToBigInt converts amount in json.Number (string) to big.Int
|
|
// it uses string operations to avoid problems with rounding
|
|
func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) {
|
|
var r big.Int
|
|
s := string(n)
|
|
i := strings.IndexByte(s, '.')
|
|
d := p.AmountDecimalPoint
|
|
if d > len(zeros) {
|
|
d = len(zeros)
|
|
}
|
|
if i == -1 {
|
|
s = s + zeros[:d]
|
|
} else {
|
|
z := d - len(s) + i + 1
|
|
if z > 0 {
|
|
s = s[:i] + s[i+1:] + zeros[:z]
|
|
} else {
|
|
s = s[:i] + s[i+1:len(s)+z]
|
|
}
|
|
}
|
|
if _, ok := r.SetString(s, 10); !ok {
|
|
return r, errors.New("AmountToBigInt: failed to convert")
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// AmountToDecimalString converts amount in big.Int to string with decimal point in the place defined by the parameter d
|
|
func AmountToDecimalString(a *big.Int, d int) string {
|
|
if a == nil {
|
|
return ""
|
|
}
|
|
n := a.String()
|
|
var s string
|
|
if n[0] == '-' {
|
|
n = n[1:]
|
|
s = "-"
|
|
}
|
|
if d > len(zeros) {
|
|
d = len(zeros)
|
|
}
|
|
if len(n) <= d {
|
|
n = zeros[:d-len(n)+1] + n
|
|
}
|
|
i := len(n) - d
|
|
ad := strings.TrimRight(n[i:], "0")
|
|
if len(ad) > 0 {
|
|
n = n[:i] + "." + ad
|
|
} else {
|
|
n = n[:i]
|
|
}
|
|
return s + n
|
|
}
|
|
|
|
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
|
|
func (p *BaseParser) AmountToDecimalString(a *big.Int) string {
|
|
return AmountToDecimalString(a, p.AmountDecimalPoint)
|
|
}
|
|
|
|
// AmountDecimals returns number of decimal places in amounts
|
|
func (p *BaseParser) AmountDecimals() int {
|
|
return p.AmountDecimalPoint
|
|
}
|
|
|
|
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
|
|
func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
|
|
var tx Tx
|
|
err := json.Unmarshal(msg, &tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := range tx.Vout {
|
|
vout := &tx.Vout[i]
|
|
// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
|
|
vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vout.JsonValue = ""
|
|
}
|
|
|
|
return &tx, nil
|
|
}
|
|
|
|
// PackedTxidLen returns length in bytes of packed txid
|
|
func (p *BaseParser) PackedTxidLen() int {
|
|
return 32
|
|
}
|
|
|
|
// KeepBlockAddresses returns number of blocks which are to be kept in blockaddresses column
|
|
func (p *BaseParser) KeepBlockAddresses() int {
|
|
return p.BlockAddressesToKeep
|
|
}
|
|
|
|
// PackTxid packs txid to byte array
|
|
func (p *BaseParser) PackTxid(txid string) ([]byte, error) {
|
|
if txid == "" {
|
|
return nil, ErrTxidMissing
|
|
}
|
|
return hex.DecodeString(txid)
|
|
}
|
|
|
|
// UnpackTxid unpacks byte array to txid
|
|
func (p *BaseParser) UnpackTxid(buf []byte) (string, error) {
|
|
return hex.EncodeToString(buf), nil
|
|
}
|
|
|
|
// PackBlockHash packs block hash to byte array
|
|
func (p *BaseParser) PackBlockHash(hash string) ([]byte, error) {
|
|
return hex.DecodeString(hash)
|
|
}
|
|
|
|
// UnpackBlockHash unpacks byte array to block hash
|
|
func (p *BaseParser) UnpackBlockHash(buf []byte) (string, error) {
|
|
return hex.EncodeToString(buf), nil
|
|
}
|
|
|
|
// GetChainType is type of the blockchain, default is ChainBitcoinType
|
|
func (p *BaseParser) GetChainType() ChainType {
|
|
return ChainBitcoinType
|
|
}
|
|
|
|
// PackTx packs transaction to byte array using protobuf
|
|
func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) {
|
|
var err error
|
|
pti := make([]*ProtoTransaction_VinType, len(tx.Vin))
|
|
for i, vi := range tx.Vin {
|
|
hex, err := hex.DecodeString(vi.ScriptSig.Hex)
|
|
if err != nil {
|
|
return nil, errors.Annotatef(err, "Vin %v Hex %v", i, vi.ScriptSig.Hex)
|
|
}
|
|
// coinbase txs do not have Vin.txid
|
|
itxid, err := p.PackTxid(vi.Txid)
|
|
if err != nil && err != ErrTxidMissing {
|
|
return nil, errors.Annotatef(err, "Vin %v Txid %v", i, vi.Txid)
|
|
}
|
|
pti[i] = &ProtoTransaction_VinType{
|
|
Addresses: vi.Addresses,
|
|
Coinbase: vi.Coinbase,
|
|
ScriptSigHex: hex,
|
|
Sequence: vi.Sequence,
|
|
Txid: itxid,
|
|
Vout: vi.Vout,
|
|
}
|
|
}
|
|
pto := make([]*ProtoTransaction_VoutType, len(tx.Vout))
|
|
for i, vo := range tx.Vout {
|
|
hex, err := hex.DecodeString(vo.ScriptPubKey.Hex)
|
|
if err != nil {
|
|
return nil, errors.Annotatef(err, "Vout %v Hex %v", i, vo.ScriptPubKey.Hex)
|
|
}
|
|
pto[i] = &ProtoTransaction_VoutType{
|
|
Addresses: vo.ScriptPubKey.Addresses,
|
|
N: vo.N,
|
|
ScriptPubKeyHex: hex,
|
|
ValueSat: vo.ValueSat.Bytes(),
|
|
}
|
|
}
|
|
pt := &ProtoTransaction{
|
|
Blocktime: uint64(blockTime),
|
|
Height: height,
|
|
Locktime: tx.LockTime,
|
|
Vin: pti,
|
|
Vout: pto,
|
|
Version: tx.Version,
|
|
}
|
|
if pt.Hex, err = hex.DecodeString(tx.Hex); err != nil {
|
|
return nil, errors.Annotatef(err, "Hex %v", tx.Hex)
|
|
}
|
|
if pt.Txid, err = p.PackTxid(tx.Txid); err != nil {
|
|
return nil, errors.Annotatef(err, "Txid %v", tx.Txid)
|
|
}
|
|
return proto.Marshal(pt)
|
|
}
|
|
|
|
// UnpackTx unpacks transaction from protobuf byte array
|
|
func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
|
|
var pt ProtoTransaction
|
|
err := proto.Unmarshal(buf, &pt)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
txid, err := p.UnpackTxid(pt.Txid)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
vin := make([]Vin, len(pt.Vin))
|
|
for i, pti := range pt.Vin {
|
|
itxid, err := p.UnpackTxid(pti.Txid)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
vin[i] = Vin{
|
|
Addresses: pti.Addresses,
|
|
Coinbase: pti.Coinbase,
|
|
ScriptSig: ScriptSig{
|
|
Hex: hex.EncodeToString(pti.ScriptSigHex),
|
|
},
|
|
Sequence: pti.Sequence,
|
|
Txid: itxid,
|
|
Vout: pti.Vout,
|
|
}
|
|
}
|
|
vout := make([]Vout, len(pt.Vout))
|
|
for i, pto := range pt.Vout {
|
|
var vs big.Int
|
|
vs.SetBytes(pto.ValueSat)
|
|
vout[i] = Vout{
|
|
N: pto.N,
|
|
ScriptPubKey: ScriptPubKey{
|
|
Addresses: pto.Addresses,
|
|
Hex: hex.EncodeToString(pto.ScriptPubKeyHex),
|
|
},
|
|
ValueSat: vs,
|
|
}
|
|
}
|
|
tx := Tx{
|
|
Blocktime: int64(pt.Blocktime),
|
|
Hex: hex.EncodeToString(pt.Hex),
|
|
LockTime: pt.Locktime,
|
|
Time: int64(pt.Blocktime),
|
|
Txid: txid,
|
|
Vin: vin,
|
|
Vout: vout,
|
|
Version: pt.Version,
|
|
}
|
|
return &tx, pt.Height, nil
|
|
}
|
|
|
|
// IsAddrDescIndexable returns true if AddressDescriptor should be added to index
|
|
// by default all AddressDescriptors are indexable
|
|
func (p *BaseParser) IsAddrDescIndexable(addrDesc AddressDescriptor) bool {
|
|
return true
|
|
}
|
|
|
|
// DerivationBasePath is unsupported
|
|
func (p *BaseParser) DerivationBasePath(xpub string) (string, error) {
|
|
return "", errors.New("Not supported")
|
|
}
|
|
|
|
// DeriveAddressDescriptors is unsupported
|
|
func (p *BaseParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]AddressDescriptor, error) {
|
|
return nil, errors.New("Not supported")
|
|
}
|
|
|
|
// DeriveAddressDescriptorsFromTo is unsupported
|
|
func (p *BaseParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) {
|
|
return nil, errors.New("Not supported")
|
|
}
|
|
|
|
// EthereumTypeGetErc20FromTx is unsupported
|
|
func (p *BaseParser) EthereumTypeGetErc20FromTx(tx *Tx) ([]Erc20Transfer, error) {
|
|
return nil, errors.New("Not supported")
|
|
}
|