Add namecoin blockbook implementation

This commit is contained in:
Martin Boehm 2018-06-20 17:57:51 +02:00
parent e3f7a0ae5e
commit e909ae454b
8 changed files with 279 additions and 3 deletions

View File

@ -9,6 +9,7 @@ import (
"blockbook/bchain/coins/dogecoin"
"blockbook/bchain/coins/eth"
"blockbook/bchain/coins/litecoin"
"blockbook/bchain/coins/namecoin"
"blockbook/bchain/coins/zec"
"blockbook/common"
"context"
@ -40,6 +41,7 @@ func init() {
blockChainFactories["Litecoin"] = litecoin.NewLitecoinRPC
blockChainFactories["Litecoin Testnet"] = litecoin.NewLitecoinRPC
blockChainFactories["Dogecoin"] = dogecoin.NewDogecoinRPC
blockChainFactories["Namecoin"] = namecoin.NewNamecoinRPC
}
// GetCoinNameFromConfig gets coin name from config file

View File

@ -0,0 +1,80 @@
package namecoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
)
const (
MainnetMagic wire.BitcoinNet = 0xfeb4bef9
)
var (
MainNetParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 52
MainNetParams.ScriptHashAddrID = 13
MainNetParams.Bech32HRPSegwit = "nc"
err := chaincfg.Register(&MainNetParams)
if err != nil {
panic(err)
}
}
// NamecoinParser handle
type NamecoinParser struct {
*btc.BitcoinParser
}
// NewNamecoinParser returns new NamecoinParser instance
func NewNamecoinParser(params *chaincfg.Params, c *btc.Configuration) *NamecoinParser {
return &NamecoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
// GetChainParams contains network parameters for the main Namecoin network,
// and the test Namecoin network
func GetChainParams(chain string) *chaincfg.Params {
switch chain {
default:
return &MainNetParams
}
}
// ParseBlock parses raw block to our Block struct
// it has special handling for Auxpow blocks that cannot be parsed by standard btc wire parser
func (p *NamecoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b)
w := wire.MsgBlock{}
h := wire.BlockHeader{}
err := h.Deserialize(r)
if err != nil {
return nil, err
}
if (h.Version & utils.VersionAuxpow) != 0 {
if err = utils.SkipAuxpow(r); err != nil {
return nil, err
}
}
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
if 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{Txs: txs}, nil
}

View File

@ -0,0 +1,103 @@
package namecoin
import (
"blockbook/bchain/coins/btc"
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"path/filepath"
"reflect"
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
type args struct {
address string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "P2PKH1",
args: args{address: "MzBvZ4F759X6wHTjzwkMEbKh12am3PHT6F"},
want: "76a91427a1f12771de5cc3b73941664b2537c15316be4388ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "N8Jkcm44Uq55GdmPojkpuGyoW4Cm658TwW"},
want: "76a91480ad90d403581fa3bf46086a91b2d9d4125db6c188ac",
wantErr: false,
},
}
parser := NewNamecoinParser(GetChainParams("main"), &btc.Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
}
})
}
}
var testParseBlockTxs = map[int][]string{
40000: []string{
"e193a821393b20b99f4a4e05a481368ef8a8cfd43d0c45bdad7f53bc9535e844",
"ddcbf95797e81dd04127885bd001e96695b717e11c52721f6e8ee53f6dea8a6f",
"31ff728f24200f59fa4958e6c26de03d172b320e6eef2b8abecf6f94d01dd4ae",
},
}
func helperLoadBlock(t *testing.T, height int) []byte {
name := fmt.Sprintf("block_dump.%d", height)
path := filepath.Join("testdata", name)
d, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
d = bytes.TrimSpace(d)
b := make([]byte, hex.DecodedLen(len(d)))
_, err = hex.Decode(b, d)
if err != nil {
t.Fatal(err)
}
return b
}
func TestParseBlock(t *testing.T) {
p := NewNamecoinParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs {
b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b)
if err != nil {
t.Fatal(err)
}
if len(blk.Txs) != len(txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs))
}
for ti, tx := range txs {
if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
}
}
}
}

View File

@ -0,0 +1,76 @@
package namecoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// NamecoinRPC is an interface to JSON-RPC namecoin service.
type NamecoinRPC struct {
*btc.BitcoinRPC
}
// NewNamecoinRPC returns new NamecoinRPC instance.
func NewNamecoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &NamecoinRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
return s, nil
}
// Initialize initializes NamecoinRPC instance.
func (b *NamecoinRPC) Initialize() error {
chainName, err := b.GetChainInfoAndInitializeMempool(b)
if err != nil {
return err
}
glog.Info("Chain name ", chainName)
params := GetChainParams(chainName)
// always create parser
b.Parser = NewNamecoinParser(params, b.ChainConfig)
// parameters for getInfo request
if params.Net == MainnetMagic {
b.Testnet = false
b.Network = "livenet"
} else {
b.Testnet = true
b.Network = "testnet"
}
glog.Info("rpc: block chain ", params.Name)
return nil
}
// GetBlock returns block with given hash.
func (b *NamecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
var err error
if hash == "" {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
}
}
if !b.ParseBlocks {
return b.GetBlockFull(hash)
}
return b.GetBlockWithoutHeader(hash, height)
}
// EstimateSmartFee returns fee estimation.
func (b *NamecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -0,0 +1 @@
0101010097d1d2cc478b4133407fc1ad4d8c8933b6c609b4ad32ddc4847bc25915cf2fe262408198c85ce883099de301b529280bd925791e3cad9404135dacee2f0d4250bd87234f8780201a0000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff49043fd40c1a02a045122f503253482f42495031362f736c7573682f522cfabe6d6d40b41e94828aee66c19918b4da58a8d172329d321390ee9d260f7988402aa7b80100000000000000ffffffff0100f2052a01000000434104d31303d99295c26106241ba05bd4f0cf8019f0e2d23eb974b19b3eb13299d15e40c483af4517c8351307965d48697623e2b1a694ddff9a6d87d3ed349c1a1777ac00000000688dfcc6adf63446c8730082fa891ea665619311f4b409c4ec1800000000000004424a004f96a7100aed62e4f864c8d9234ef13bc8ceec263dbed155788c819552e72db83155702b8876691c102e598b8c7a94a32db86a54a8a47327ec2010ff2282939c62b3b1c218535a06c7912c3661c7074802d48e2d92b59acaba1f064c6e3f5352dc79864c6d856220657538a996835960ceedadc231a0ee376008d4b58d00000000000000000001000000fd28a68536508d85160396eb7341aa24749702d0c5db994d1e070000000000004e109c00dceed1da22ee7190fa9d7ad209ceb20c128ea7ce8a8cfb4befae011dc888234f3fd40c1a36d24c8d0301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08048780201a010152ffffffff01a078072a01000000434104e926558cfefc8bc7fb256b681ee0edf15565c6174549b7f1293fcc0be208ed86f1a14cc18ea94ffa9c52b171d81f1d1efed00941bafeab92cc251c2d2aa345a3ac000000000071000002a93033530122276984f390bb2f974e1a7ff8da5f1be917838fa7b0352aa47678010000008b48304502201a1c6a80a0f0a3c5ea73be2b72d1f430e9eff43b6af4c735dc4ba6acd616640f022100a6680fbe27215365015007470d8bd6f38b49110916244eff5734a30124ef842901410499891bc83f6cf8aa1e538fa24320c072105753c078edcbbd8ce672c6eda9d94fc0aa38bf9b07cc6a3390bc0d7753a285f31eafc4c3dfdfbea00b492cb24f6ac5ffffffffa93033530122276984f390bb2f974e1a7ff8da5f1be917838fa7b0352aa476780000000049483045022100b43dbe3306698000cbb760947b31c99e87479861c208434db9dae8261479bd1f022054f044785dd0e67d71b7dd8d0bc0eb92b112436352d441854c550b80a62d8bb201ffffffff0340420f00000000003e520e467574726f6e204e75636c656172094cdb42c80c00f697000852455345525645446d6d76a914053a73e0c100ae994c6cd5cb128930bf0c33eb2d88ac802a645f0000000043410480294ad87bc7bad362df1c244048bc5e767004898acf42066366e4e191d2d485935f179aaa6247c614a0403d6ca4deefd86d076a23616c5a4c957c5231a99c87ac00093d0000000000016a0000000000710000016f8aea6d3fe58e6e1f72521ce117b79566e901d05b882741d01de89757f9cbdd010000004a493046022100ec16ef5125b67272706305419077bc40b849f84610624bf744ff1b1498d0cb29022100979407f7378eb5e2beff9f5a952410e7f4a4581182ccafcbcd73aecd9ded1e8f01ffffffff02f024545f000000004341047ffb631e4f525c756bf21225845cd7cdd7736151da56db362dac829a7d8de9789deba832d88e5a943036c3221156dc28224636669ef6c5865451a41aa5ebfc37ac40420f000000000030511426dc02d4a5aaeeab17e1872c761f3cca247c00896d76a9141728f2e7af41221eb00a7fa373cc0630afd382ea88ac00000000

View File

@ -1,8 +1,9 @@
package zec
package utils
import (
"crypto/sha256"
"errors"
"github.com/btcsuite/btcutil/base58"
)

View File

@ -3,6 +3,7 @@ package zec
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
@ -54,12 +55,12 @@ func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
if len(output.ScriptPubKey.Addresses) != 1 {
return nil, nil
}
hash, _, err := CheckDecode(output.ScriptPubKey.Addresses[0])
hash, _, err := utils.CheckDecode(output.ScriptPubKey.Addresses[0])
return hash, err
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *ZCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
hash, _, err := CheckDecode(address)
hash, _, err := utils.CheckDecode(address)
return hash, err
}

12
configs/namecoin.json Normal file
View File

@ -0,0 +1,12 @@
{
"coin_name": "Namecoin",
"rpcURL": "http://127.0.0.1:8039",
"rpcUser": "rpc",
"rpcPass": "rpc",
"rpcTimeout": 25,
"parse": true,
"zeroMQBinding": "tcp://127.0.0.1:38339",
"mempoolWorkers": 8,
"mempoolSubWorkers": 2,
"blockAddressesToKeep": 300
}