blockbook/bchain/coins/dcr/decredparser.go
2019-07-07 22:41:01 +02:00

232 lines
5.9 KiB
Go

package dcr
import (
"bytes"
"encoding/hex"
"encoding/json"
"math"
"math/big"
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
cfg "github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/txscript"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
// MainnetMagic is mainnet network constant
MainnetMagic wire.BitcoinNet = 0xd9b400f9
// TestnetMagic is testnet network constant
TestnetMagic wire.BitcoinNet = 0xb194aa75
)
var (
// MainNetParams are parser parameters for mainnet
MainNetParams chaincfg.Params
// TestNet3Params are parser parameters for testnet
TestNet3Params chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{0x07, 0x3f}
MainNetParams.ScriptHashAddrID = []byte{0x07, 0x1a}
TestNet3Params = chaincfg.TestNet3Params
TestNet3Params.Net = TestnetMagic
TestNet3Params.PubKeyHashAddrID = []byte{0x0f, 0x21}
TestNet3Params.ScriptHashAddrID = []byte{0x0e, 0xfc}
}
// DecredParser handle
type DecredParser struct {
*btc.BitcoinParser
baseParser *bchain.BaseParser
}
// NewDecredParser returns new DecredParser instance
func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParser {
return &DecredParser{
BitcoinParser: btc.NewBitcoinParser(params, c),
baseParser: &bchain.BaseParser{},
}
}
// GetChainParams contains network parameters for the main Decred network,
// and the test Decred network.
func GetChainParams(chain string) *chaincfg.Params {
var param *chaincfg.Params
switch chain {
case "testnet3":
param = &TestNet3Params
default:
param = &MainNetParams
}
if !chaincfg.IsRegistered(param) {
if err := chaincfg.Register(param); err != nil {
panic(err)
}
}
return param
}
// ParseBlock parses raw block to our Block struct.
func (p *DecredParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b)
h := wire.BlockHeader{}
if err := h.Deserialize(r); err != nil {
return nil, err
}
if (h.Version & utils.VersionAuxpow) != 0 {
if err := utils.SkipAuxpow(r); err != nil {
return nil, err
}
}
var w wire.MsgBlock
if err := utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w); err != nil {
return nil, err
}
txs := make([]bchain.Tx, len(w.Transactions))
for ti, t := range w.Transactions {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
}
func (p *DecredParser) ParseTxFromJson(jsonTx json.RawMessage) (*bchain.Tx, error) {
var getTxResult GetTransactionResult
if err := json.Unmarshal([]byte(jsonTx), &getTxResult.Result); err != nil {
return nil, err
}
vins := make([]bchain.Vin, len(getTxResult.Result.Vin))
for index, input := range getTxResult.Result.Vin {
hexData := bchain.ScriptSig{}
if input.ScriptSig != nil {
hexData.Hex = input.ScriptSig.Hex
}
vins[index] = bchain.Vin{
Coinbase: input.Coinbase,
Txid: input.Txid,
Vout: input.Vout,
ScriptSig: hexData,
Sequence: input.Sequence,
// Addresses: []string{},
}
}
vouts := make([]bchain.Vout, len(getTxResult.Result.Vout))
for index, output := range getTxResult.Result.Vout {
addr := output.ScriptPubKey.Addresses
// If nulldata type found make asm field the address data.
if output.ScriptPubKey.Type == "nulldata" {
addr = []string{output.ScriptPubKey.Asm}
}
vouts[index] = bchain.Vout{
ValueSat: *big.NewInt(int64(math.Round(output.Value * 1e8))),
N: output.N,
ScriptPubKey: bchain.ScriptPubKey{
Hex: output.ScriptPubKey.Hex,
Addresses: addr,
},
}
}
tx := &bchain.Tx{
Hex: getTxResult.Result.Hex,
Txid: getTxResult.Result.Txid,
Version: getTxResult.Result.Version,
LockTime: getTxResult.Result.LockTime,
Vin: vins,
Vout: vouts,
Confirmations: uint32(getTxResult.Result.Confirmations),
Time: getTxResult.Result.Time,
Blocktime: getTxResult.Result.Blocktime,
}
tx.CoinSpecificData = getTxResult.Result.TxExtraInfo
return tx, nil
}
// GetAddrDescForUnknownInput returns nil AddressDescriptor
func (p *DecredParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
return nil
}
func (p *DecredParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
addressByte := []byte(address)
return bchain.AddressDescriptor(addressByte), nil
}
func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
script, err := hex.DecodeString(output.ScriptPubKey.Hex)
if err != nil {
return nil, err
}
var params cfg.Params
if p.Params.Name == "mainnet" {
params = cfg.MainNetParams
} else {
params = cfg.TestNet3Params
}
scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, script, &params)
if err != nil {
return nil, err
}
if scriptClass.String() == "nulldata" {
if parsedOPReturn := p.BitcoinParser.TryParseOPReturn(script); parsedOPReturn != "" {
return []byte(parsedOPReturn), nil
}
}
var addressByte []byte
for i := range addresses {
addressByte = append(addressByte, addresses[i].String()...)
}
return bchain.AddressDescriptor(addressByte), nil
}
func (p *DecredParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
var addrs []string
if addrDesc != nil {
addrs = append(addrs, string(addrDesc))
}
return addrs, true, nil
}
// PackTx packs transaction to byte array using protobuf
func (p *DecredParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
return p.baseParser.PackTx(tx, height, blockTime)
}
// UnpackTx unpacks transaction from protobuf byte array
func (p *DecredParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.baseParser.UnpackTx(buf)
}