Merge branch 'dogecoin'

This commit is contained in:
Martin Boehm 2018-06-18 12:46:30 +02:00
commit f0d6c46af3
32 changed files with 905 additions and 11 deletions

View File

@ -6,6 +6,7 @@ import (
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/btg"
"blockbook/bchain/coins/dash"
"blockbook/bchain/coins/dogecoin"
"blockbook/bchain/coins/eth"
"blockbook/bchain/coins/litecoin"
"blockbook/bchain/coins/zec"
@ -38,6 +39,7 @@ func init() {
blockChainFactories["Dash Testnet"] = dash.NewDashRPC
blockChainFactories["Litecoin"] = litecoin.NewLitecoinRPC
blockChainFactories["Litecoin Testnet"] = litecoin.NewLitecoinRPC
blockChainFactories["Dogecoin"] = dogecoin.NewDogecoinRPC
}
// GetCoinNameFromConfig gets coin name from config file

View File

@ -458,7 +458,7 @@ func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
}
// optimization
if height > 0 {
return b.getBlockWithoutHeader(hash, height)
return b.GetBlockWithoutHeader(hash, height)
}
header, err := b.GetBlockHeader(hash)
if err != nil {
@ -478,7 +478,7 @@ func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
// getBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
// instead it sets to header only block hash and height passed in parameters
func (b *BitcoinRPC) getBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
data, err := b.GetBlockRaw(hash)
if err != nil {
return nil, err

View File

@ -0,0 +1,167 @@
package dogecoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"fmt"
"io"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
)
const (
MainnetMagic wire.BitcoinNet = 0xc0c0c0c0
)
var (
MainNetParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 30
MainNetParams.ScriptHashAddrID = 22
err := chaincfg.Register(&MainNetParams)
if err != nil {
panic(err)
}
}
// DogecoinParser handle
type DogecoinParser struct {
*btc.BitcoinParser
}
// NewDogecoinParser returns new DogecoinParser instance
func NewDogecoinParser(params *chaincfg.Params, c *btc.Configuration) *DogecoinParser {
return &DogecoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
// GetChainParams contains network parameters for the main Dogecoin network,
// and the test Dogecoin network
func GetChainParams(chain string) *chaincfg.Params {
switch chain {
default:
return &MainNetParams
}
}
// minTxPayload is the minimum payload size for a transaction. Note
// that any realistically usable transaction must have at least one
// input or output, but that is a rule enforced at a higher layer, so
// it is intentionally not included here.
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
// number of transaction outputs 1 byte + LockTime 4 bytes + min input
// payload + min output payload.
const minTxPayload = 10
// maxTxPerBlock is the maximum number of transactions that could
// possibly fit into a block.
const maxTxPerBlock = (wire.MaxBlockPayload / minTxPayload) + 1
const versionAuxpow = (1 << 8)
// 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 *DogecoinParser) 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 & versionAuxpow) != 0 {
// skip Auxpow part of the block
// https://github.com/dogecoin/dogecoin/blob/master/src/auxpow.h#L130
// CMerkleTx CTransaction
tx := wire.MsgTx{}
err = tx.BtcDecode(r, 0, wire.WitnessEncoding)
if err != nil {
return nil, err
}
// CMerkleTx uint256 hashBlock
_, err = r.Seek(32, io.SeekCurrent)
if err != nil {
return nil, err
}
// CMerkleTx std::vector<uint256> vMerkleBranch
size, err := wire.ReadVarInt(r, 0)
if err != nil {
return nil, err
}
_, err = r.Seek(int64(size)*32, io.SeekCurrent)
if err != nil {
return nil, err
}
// CMerkleTx int nIndex
_, err = r.Seek(4, io.SeekCurrent)
if err != nil {
return nil, err
}
// CAuxPow std::vector<uint256> vChainMerkleBranch;
size, err = wire.ReadVarInt(r, 0)
if err != nil {
return nil, err
}
_, err = r.Seek(int64(size)*32, io.SeekCurrent)
if err != nil {
return nil, err
}
// CAuxPow int nChainIndex;
_, err = r.Seek(4, io.SeekCurrent)
if err != nil {
return nil, err
}
// CAuxPow CPureBlockHeader parentBlock;
ph := wire.BlockHeader{}
err = ph.Deserialize(r)
if err != nil {
return nil, err
}
}
err = 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
}
func decodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk *wire.MsgBlock) error {
txCount, err := wire.ReadVarInt(r, pver)
if err != nil {
return err
}
// Prevent more transactions than could possibly fit into a block.
// It would be possible to cause memory exhaustion and panics without
// a sane upper bound on this count.
if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock)
return &wire.MessageError{Func: "btg.decodeTransactions", Description: str}
}
blk.Transactions = make([]*wire.MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ {
tx := wire.MsgTx{}
err := tx.BtcDecode(r, pver, enc)
if err != nil {
return err
}
blk.Transactions = append(blk.Transactions, &tx)
}
return nil
}

View File

@ -0,0 +1,406 @@
package dogecoin
import (
"blockbook/bchain"
"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: "DHZYinsaM9nW5piCMN639ELRKbZomThPnZ"},
want: "76a9148841590909747c0f97af158f22fadacb1652522088ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "DSzaAYEYyy9ngjoJ294r7jzFM3xhD6bKHK"},
want: "76a914efb6158f75743c611858fdfd0f4aaec6cc6196bc88ac",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "9tg1kVUk339Tk58ewu5T8QT82Z6cE4UvSU"},
want: "a9141889a089400ea25d28694fd98aa7702b21eeeab187",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "9sLa1AKzjWuNTe1CkLh5GDYyRP9enb1Spp"},
want: "a91409e41aff9f97412ab3d4a07cf0667fdba84caf4487",
wantErr: false,
},
}
parser := NewDogecoinParser(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 (
testTx1 bchain.Tx
testTxPacked1 = "00030e6d8ba8d7aa2001000000016b3c0c53267964120acf7f7e72217e3f463e52ce622f89659f6a6bb8e69a4d91000000006c493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9dffffffff0217e823ca7f0200001976a914eef21768a546590993e313c7f3dfadf6a6efa1e888acaddf4cba010000001976a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac00000000"
testTx2 bchain.Tx
testTxPacked2 = "0001193a8ba8d7835601000000016d0211b5656f1b8c2ac002445638e247082090ffc5d5fa7c38b445b84a2c2054000000006b4830450221008856f2f620df278c0fc6a5d5e2d50451c0a65a75aaf7a4a9cbfcac3918b5536802203dc685a784d49e2a95eb72763ad62f02094af78507c57b0a3c3f1d8a60f74db6012102db814cd43df584804fde1949365a6309714e342aef0794dc58385d7e413444cdffffffff0237daa2ee0a4715001976a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac8008389e7e8d03001976a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac00000000"
)
func init() {
var (
addr1, addr2, addr3, addr4 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i")
if err == nil {
addr2, err = bchain.NewBaseAddress("DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6")
}
if err == nil {
addr4, err = bchain.NewBaseAddress("DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000016b3c0c53267964120acf7f7e72217e3f463e52ce622f89659f6a6bb8e69a4d91000000006c493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9dffffffff0217e823ca7f0200001976a914eef21768a546590993e313c7f3dfadf6a6efa1e888acaddf4cba010000001976a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac00000000",
Blocktime: 1519053456,
Txid: "097ea09ba284f3f2a9e880e11f837edf7e5cea81c8da2238f5bc7c2c4c407943",
LockTime: 0,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9d",
},
Txid: "914d9ae6b86b6a9f65892f62ce523e463f7e21727e7fcf0a12647926530c3c6b",
Vout: 0,
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
Value: 27478.75452951,
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914eef21768a546590993e313c7f3dfadf6a6efa1e888ac",
Addresses: []string{
"DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i",
},
},
Address: addr1,
},
{
Value: 74.20567469,
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac",
Addresses: []string{
"DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK",
},
},
Address: addr2,
},
},
}
testTx2 = bchain.Tx{
Hex: "01000000016d0211b5656f1b8c2ac002445638e247082090ffc5d5fa7c38b445b84a2c2054000000006b4830450221008856f2f620df278c0fc6a5d5e2d50451c0a65a75aaf7a4a9cbfcac3918b5536802203dc685a784d49e2a95eb72763ad62f02094af78507c57b0a3c3f1d8a60f74db6012102db814cd43df584804fde1949365a6309714e342aef0794dc58385d7e413444cdffffffff0237daa2ee0a4715001976a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac8008389e7e8d03001976a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac00000000",
Blocktime: 1519050987,
Txid: "b276545af246e3ed5a4e3e5b60d359942a1808579effc53ff4f343e4f6cfc5a0",
LockTime: 0,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "4830450221008856f2f620df278c0fc6a5d5e2d50451c0a65a75aaf7a4a9cbfcac3918b5536802203dc685a784d49e2a95eb72763ad62f02094af78507c57b0a3c3f1d8a60f74db6012102db814cd43df584804fde1949365a6309714e342aef0794dc58385d7e413444cd",
},
Txid: "54202c4ab845b4387cfad5c5ff90200847e238564402c02a8c1b6f65b511026d",
Vout: 0,
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
Value: 59890867.89818935,
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac",
Addresses: []string{
"DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6",
},
},
Address: addr3,
},
{
Value: 9999998.90000000,
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac",
Addresses: []string{
"DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG",
},
},
Address: addr4,
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *DogecoinParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "dogecoin-1",
args: args{
tx: testTx1,
height: 200301,
blockTime: 1519053456,
parser: NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
},
{
name: "dogecoin-2",
args: args{
tx: testTx2,
height: 71994,
blockTime: 1519050987,
parser: NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked2,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime)
if (err != nil) != tt.wantErr {
t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("packTx() = %v, want %v", h, tt.want)
}
})
}
}
func Test_UnpackTx(t *testing.T) {
type args struct {
packedTx string
parser *DogecoinParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "dogecoin-1",
args: args{
packedTx: testTxPacked1,
parser: NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 200301,
wantErr: false,
},
{
name: "dogecoin-2",
args: args{
packedTx: testTxPacked2,
parser: NewDogecoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx2,
want1: 71994,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.packedTx)
got, got1, err := tt.args.parser.UnpackTx(b)
if (err != nil) != tt.wantErr {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
var testParseBlockTxs = map[int][]string{
// block without auxpow
12345: []string{
"9d1662dcc1443af9999c4fd1d6921b91027b5e2d0d3ebfaa41d84163cb99cad5",
"8284292cedeb0c9c509f9baa235802d52a546e1e9990040d35d018b97ad11cfa",
"3299d93aae5c3d37c795c07150ceaf008aefa5aad3205ea2519f94a35adbbe10",
"3f03016f32b63db48fdc0b17443c2d917ba5e307dcc2fc803feeb21c7219ee1b",
"a889449e9bc618c131c01f564cd309d2217ba1c5731480314795e44f1e02609b",
"29f79d91c10bc311ff5b69fe7ba57101969f68b6391cf0ca67d5f37ca1f0601b",
"b794ebc7c0176c35b125cd8b84a980257cf3dd9cefe2ed47da4ed1d73ee568f3",
"0ec479ba3c954dd422d75c4c5488a6edc3c588deb10ebdbfa8bd8edb7afcfea0",
"f357b6e667dfa456e7988bfa474377df25d0e0bfe07e5f97fc97ea3a0155f031",
"4ff189766f0455721a93d6be27a91eafa750383c800cb053fad2f86c434122d2",
"446d164e2ec4c9f2ac6c499c110735606d949a3625fb849274ac627c033eddbc",
"c489edebd8a2e17fd08f2801f528b95663aaafe15c897d56686423dd430e2d1f",
"3f42a7f1a356897da324d41eed94169c79438212bb9874eea58e9cbaf07481df",
"62c88fdd0fb111676844fcbaebc9e2211a0c990aa7e7529539cb25947a307a1b",
"522c47e315bc1949826339c535d419eb206aec4a332f91dfbd25c206f3c9527b",
"18ea78346e7e34cbdf2d2b6ba1630f8b15f9ef9a940114a3e6ee92d26f96691e",
"43dc0fbd1b9b87bcfc9a51c89457a7b3274855c01d429193aff1181791225f3c",
"d78cdfaadbe5b6b591529cb5c6869866a4cabe46ef82aa835fd2432056b4a383",
"d181759c7a3900ccaf4958f1f25a44949163ceefc306006502efc7a1de6f579e",
"8610b9230188854c7871258163cd1c2db353443d631c5512bff17224a24e95bf",
"e82f40a6bea32122f1d568d427c92708dcb684bdb3035ff3905617230e5ae5b8",
"c50ae6c127f8c346c60e7438fbd10c44c3629f3fe426646db77a2250fb2939f9",
"585202c03894ecaf25188ba4e5447dadd413f2010c2dc2a65c37598dbc6ad907",
"8bd766fde8c65e2f724dad581944dde4e23e4dbb4f7f7faf55bc348923f4d5ee",
"2d2fa25691088181569e508dd8f683b21f2b80ceefb5ccbd6714ebe2a697139f",
"5954622ffc602bec177d61da6c26a68990c42c1886627b218c3ab0e9e3491f4a",
"01b634bc53334df1cd9f04522729a34d811c418c2535144c3ed156cbc319e43e",
"c429a6c8265482b2d824af03afe1c090b233a856f243791485cb4269f2729649",
"dbe79231b916b6fb47a91ef874f35150270eb571af60c2d640ded92b41749940",
"1c396493a8dfd59557052b6e8643123405894b64f48b2eb6eb7a003159034077",
"2e2816ffb7bf1378f11acf5ba30d498efc8fd219d4b67a725e8254ce61b1b7ee",
},
// 1st block with auxpow
371337: []string{
"4547b14bc16db4184fa9f141d645627430dd3dfa662d0e6f418fba497091da75",
"a965dba2ed06827ed9a24f0568ec05b73c431bc7f0fb6913b144e62db7faa519",
"5e3ab18cb7ba3abc44e62fb3a43d4c8168d00cf0a2e0f8dbeb2636bb9a212d12",
"f022935ac7c4c734bd2c9c6a780f8e7280352de8bd358d760d0645b7fe734a93",
"ec063cc8025f9f30a6ed40fc8b1fe63b0cbd2ea2c62664eb26b365e6243828ca",
"02c16e3389320da3e77686d39773dda65a1ecdf98a2ef9cfb938c9f4b58f7a40",
},
// block with auxpow
567890: []string{
"db20feea53be1f60848a66604d5bca63df62de4f6c66220f9c84436d788625a8",
"cf7e9e27c0f56f0b100eaf5c776ce106025e3412bd5927c6e1ce575500e24eaa",
"af84e010c1cf0bd927740d08e5e8163db45397b70f00df07aea5339c14d5f3aa",
"7362e25e8131255d101e5d874e6b6bb2faa7a821356cb041f1843d0901dffdbd",
"3b875344302e8893f6d5c9e7269d806ed27217ec67944940ae9048fc619bdae9",
"e3b95e269b7c251d87e8e241ea2a08a66ec14d12a1012762be368b3db55471e3",
"6ba3f95a37bcab5d0cb5b8bd2fe48040db0a6ae390f320d6dcc8162cc096ff8f",
"3211ccc66d05b10959fa6e56d1955c12368ea52b40303558b254d7dc22570382",
"54c1b279e78b924dfa15857c80131c3ddf835ab02f513dc03aa514f87b680493",
},
// recent block
2264125: []string{
"76f0126562c99e020b5fba41b68dd8141a4f21eef62012b76a1e0635092045e9",
"7bb6688bec16de94014574e3e1d3f6f5fb956530d6b179b28db367f1fd8ae099",
"d7e2ee30c3d179ac896651fc09c1396333f41d952d008af8d5d6665cbea377bf",
"8e4783878df782003c43d014fcbb9c57d2034dfd1d9fcd7319bb1a9f501dbbb7",
"8d2a4ae226b6f23eea545957be5d71c68cd08674d96a3502d4ca21ffadacb5a9",
"a0da2b49de881133655c54b1b5c23af443a71c2b937e2d9bbdf3f498247e6b7b",
"c780a19b9cf46ed70b53c5d5722e8d33951211a4051cb165b25fb0c22a4ae1ff",
"ce29c2644d642bb4fedd09d0840ed98c9945bf292967fede8fcc6b26054b4058",
"a360b0566f68c329e2757918f67ee6421d3d76f70f1b452cdd32266805986119",
"17e85bd33cc5fb5035e489c5188979f45e75e92d14221eca937e14f5f7d7b074",
"3973eb930fd2d0726abbd81912eae645384268cd3500b9ec84d806fdd65a426a",
"b91cc1c98e5c77e80eec9bf93e86af27f810b00dfbce3ee2646758797a28d5f2",
"1a8c7bd3389dcbbc1133ee600898ed9e082f7a9c75f9eb52f33940ed7c2247ef",
"9b1782449bbd3fc3014c363167777f7bdf41f5ef6db192fbda784b29603911b0",
"afab4bcdc1a32891d638579c3029ae49ee72be3303425c6d62e1f8eaebe0ce18",
"5f839f9cd5293c02ff4f7cf5589c53dec52adb42a077599dc7a2c5842a156ca9",
"756d2dfd1d2872ba2531fae3b8984008506871bec41d19cb299f5e0f216cfb9b",
"6aa82514ab7a9cc624fabf3d06ccbd46ecb4009b3c784768e6243d7840d4bf93",
"d1430b3f7ecf147534796c39ba631ea22ac03530e25b9428367c0dc381b10863",
"2aeb69b1eb9eef8039da6b97d7851e46f57325851e6998ef5a84fc9a826c2c74",
"fc61d13eef806af8da693cfa621fe92110694f1514567b186a35c54e7ef4a188",
"a02dd44e60ba62fa00c83a67116f8079bf71062939b207bee0808cb98b30cf22",
"279f97cfc606fe62777b44614ff28675ce661687904e068e3ec79f619c4fdae7",
"d515d271849717b091a9c46bf11c47efb9d975e72b668c137786a208cf0a9739",
"a800da44e6eed944043561fe22ee0a6e11341e6bc1a8ec2789b83930cc9b170e",
},
}
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 := NewDogecoinParser(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 dogecoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// DogecoinRPC is an interface to JSON-RPC dogecoind service.
type DogecoinRPC struct {
*btc.BitcoinRPC
}
// NewDogecoinRPC returns new DogecoinRPC instance.
func NewDogecoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &DogecoinRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
return s, nil
}
// Initialize initializes DogecoinRPC instance.
func (b *DogecoinRPC) 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 = NewDogecoinParser(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 *DogecoinRPC) 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 *DogecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
020162000d6f03470d329026cd1fc720c0609cd378ca8691a117bd1aa46f01fb09b1a8468a15bf6f0b0e83f2e5036684169eafb9406468d4f075c999fb5b2a78fbb827ee41fb11548441361b0000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff380345bf09fabe6d6d980ba42120410de0554d42a5b5ee58167bcd86bf7591f429005f24da45fb51cf0800000000000000cdb1f1ff0e000000ffffffff01800c0c2a010000001976a914aa3750aa18b8a0f3f0590731e1fab934856680cf88ac00000000b3e64e02fff596209c498f1b18f798d62f216f11c8462bf3922319000000000003a979a636db2450363972d211aee67b71387a3daaa3051be0fd260c5acd4739cd52a418d29d8a0e56c8714c95a0dc24e1c9624480ec497fe2441941f3fee8f9481a3370c334178415c83d1d0c2deeec727c2330617a47691fc5e79203669312d100000000036fa40307b3a439538195245b0de56a2c1db6ba3a64f8bdd2071d00bc48c841b5e77b98e5c7d6f06f92dec5cf6d61277ecb9a0342406f49f34c51ee8ce4abd678038129485de14238bd1ca12cd2de12ff0e383aee542d90437cd664ce139446a00000000002000000d2ec7dfeb7e8f43fe77aba3368df95ac2088034420402730ee0492a2084217083411b3fc91033bfdeea339bc11b9efc986e161c703e07a9045338c165673f09940fb11548b54021b58cc9ae50601000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d0389aa050101062f503253482fffffffff010066f33caf050000232102b73438165461b826b30a46078f211aa005d1e7e430b1e0ed461678a5fe516c73ac000000000100000001ef2e86aa5f027e13d7fc1f0bd4a1fc677d698e42850680634ccd1834668ff320010000006b483045022100fcf5dc43afa85978a71e76a9f4c11cd6bf2a7d5677212f9001ad085d420a5d3a022068982e1e53e94fc6007cf8b60ff3919bcaf7f0b70fefb79112cb840777d8c7cf0121022b050b740dd02c1b4e1e7cdbffe6d836d987c9db4c4db734b58526f08942193bffffffff02004e7253000000001976a91435cb1f77e88e96fb3094d84e8d3b7789a092636d88ac00d4b7e8b00700001976a9146ca1f634daa4efc7871abab945c7cefd282b481f88ac0000000001000000010a6c24bbc92fd0ec32bb5b0a051c44eba0c1325f0b24d9523c109f8bb1281f49000000006a4730440220608577619fb3a0b826f09df5663ffbf121c8e0164f43b73d9affe2f9e4576bd0022040782c9a7df0a20afe1a7e3578bf27e1331c862253af21ced4fde5ef1b44b787012103e4f91ad831a87cc532249944bc7138a355f7d0aac25dc4737a8701181ce680a5ffffffff010019813f0d0000001976a91481db1aa49ebc6a71cad96949eb28e22af85eb0bd88ac0000000001000000017b82db0f644ecff378217d9b8dc0de8817eaf85ceefacab23bf344e2e495dca5010000006b483045022100f07ced6bfdbd6cdeb8b2c8fc92b9803f5798754b5b6c454c8f084198bea303f402205616f84d7ec882af9c34a3fd2457ca3fb81ec5a463a963a6e684edee427d4525012102c056b10494520dbd7b37e2e6bb8f72f98d73a609a926901221bfb114fa1d5a80ffffffff02f0501a22000000001976a914ca63ded8b23d0252158a3bdc816747ef89fb438988ac80b65ea1350700001976a914fb26a7c16ace531a8e7bbd925e46c67c3150c1c888ac000000000100000001c9bdba900e1579ebf4e44415fe8b9abec57a763f8c70a30604bea7fbe7c55d42000000006a47304402204ccbeeace0630e72102fdaf0836e41f8f6dcdde6a178f0fbc2d96a4d17a1df8f02207e4a91203a2abd87fdddee96510482ef96535741b6c17a1acae93c977ad248e5012103e0747583a342b76a5de9c21db138b9640d49b4f3b67a306d3b3f217416d49b55ffffffff020058850c020000001976a9144417c63a91208a02a5f46a0f7a2b806adc7d19a788ac0042dc06030000001976a9147b61c5adef0d559e5acf2901c2989294624b651988ac0000000001000000017c1423b198dfc3da37ae9a5fc11a3720e4343b3049d3b289b8285eb04595c04b000000006b483045022100b0c1cb9608bf644d7a8916bf61f36ced95bd045e97612804ca774f60e05e7bde022017c12255eecc474c8d8b05d0910013b2df8703af68212cf0962b6b8ee0e101ee01210341e154088c23b8ea943bca94c1d4f65361668a242b168522f00199365414b46affffffff01019891ad000000001976a91481db1aa49ebc6a71cad96949eb28e22af85eb0bd88ac00000000

File diff suppressed because one or more lines are too long

View File

@ -26,15 +26,9 @@ import (
_ "net/http/pprof"
)
// resync index at least each resyncIndexPeriodMs (could be more often if invoked by message from ZeroMQ)
const resyncIndexPeriodMs = 935093
// debounce too close requests for resync
const debounceResyncIndexMs = 1009
// resync mempool at least each resyncMempoolPeriodMs (could be more often if invoked by message from ZeroMQ)
const resyncMempoolPeriodMs = 60017
// debounce too close requests for resync mempool (ZeroMQ sends message for each tx, when new block there are many transactions)
const debounceResyncMempoolMs = 1009
@ -71,6 +65,12 @@ var (
noTxCache = flag.Bool("notxcache", false, "disable tx cache")
computeColumnStats = flag.Bool("computedbstats", false, "compute column stats and exit")
// resync index at least each resyncIndexPeriodMs (could be more often if invoked by message from ZeroMQ)
resyncIndexPeriodMs = flag.Int("resyncindexperiod", 935093, "resync index period in milliseconds")
// resync mempool at least each resyncMempoolPeriodMs (could be more often if invoked by message from ZeroMQ)
resyncMempoolPeriodMs = flag.Int("resyncmempoolperiod", 60017, "resync mempool period in milliseconds")
)
var (
@ -384,7 +384,7 @@ func syncIndexLoop() {
defer close(chanSyncIndexDone)
glog.Info("syncIndexLoop starting")
// resync index about every 15 minutes if there are no chanSyncIndex requests, with debounce 1 second
tickAndDebounce(resyncIndexPeriodMs*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() {
tickAndDebounce(time.Duration(*resyncIndexPeriodMs)*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() {
if err := syncWorker.ResyncIndex(onNewBlockHash); err != nil {
glog.Error("syncIndexLoop ", errors.ErrorStack(err))
}
@ -402,7 +402,7 @@ func syncMempoolLoop() {
defer close(chanSyncMempoolDone)
glog.Info("syncMempoolLoop starting")
// resync mempool about every minute if there are no chanSyncMempool requests, with debounce 1 second
tickAndDebounce(resyncMempoolPeriodMs*time.Millisecond, debounceResyncMempoolMs*time.Millisecond, chanSyncMempool, func() {
tickAndDebounce(time.Duration(*resyncMempoolPeriodMs)*time.Millisecond, debounceResyncMempoolMs*time.Millisecond, chanSyncMempool, func() {
internalState.StartedMempoolSync()
if count, err := chain.ResyncMempool(onNewTxAddr); err != nil {
glog.Error("syncMempoolLoop ", errors.ErrorStack(err))

View File

@ -0,0 +1 @@
/opt/coins/blockbook/dogecoin/config/blockchaincfg.json

View File

@ -0,0 +1,2 @@
#!/bin/sh
/opt/coins/blockbook/dogecoin/bin/logrotate.sh

View File

@ -0,0 +1,2 @@
/opt/coins/data/dogecoin/blockbook
/opt/coins/blockbook/dogecoin/logs

View File

@ -0,0 +1,6 @@
#!/usr/bin/dh-exec
blockbook /opt/coins/blockbook/dogecoin/bin
cert /opt/coins/blockbook/dogecoin
static /opt/coins/blockbook/dogecoin
configs/dogecoin.json => /opt/coins/blockbook/dogecoin/config/blockchaincfg.json
logrotate.sh /opt/coins/blockbook/dogecoin/bin

View File

@ -0,0 +1,2 @@
/opt/coins/blockbook/dogecoin/cert/testcert.crt /opt/coins/blockbook/dogecoin/cert/blockbook.crt
/opt/coins/blockbook/dogecoin/cert/testcert.key /opt/coins/blockbook/dogecoin/cert/blockbook.key

View File

@ -0,0 +1,23 @@
#!/bin/bash
set -e
case "$1" in
configure)
if ! id -u blockbook-dogecoin &> /dev/null
then
useradd --system -M -U blockbook-dogecoin -s /bin/false
fi
for dir in /opt/coins/data/dogecoin/blockbook /opt/coins/blockbook/dogecoin/logs
do
if [ "$(stat -c '%U' $dir)" != "blockbook-dogecoin" ]
then
chown -R blockbook-dogecoin:blockbook-dogecoin $dir
fi
done
;;
esac
#DEBHELPER#

View File

@ -0,0 +1,43 @@
# It is not recommended to modify this file in-place, because it will
# be overwritten during package upgrades. If you want to add further
# options or overwrite existing ones then use
# $ systemctl edit blockbook-dogecoin.service
# See "man systemd.service" for details.
[Unit]
Description=Blockbook daemon (Dogecoin mainnet)
After=network.target
Wants=backend-dogecoin.service
[Service]
ExecStart=/opt/coins/blockbook/dogecoin/bin/blockbook -blockchaincfg=/opt/coins/blockbook/dogecoin/config/blockchaincfg.json -datadir=/opt/coins/data/dogecoin/blockbook/db -sync -httpserver=:9038 -socketio=:9138 -certfile=/opt/coins/blockbook/dogecoin/cert/blockbook -explorer=https://dogechain.info/ -resyncindexperiod=30011 -resyncmempoolperiod=2011 -log_dir=/opt/coins/blockbook/dogecoin/logs
User=blockbook-dogecoin
Type=simple
Restart=on-failure
WorkingDirectory=/opt/coins/blockbook/dogecoin
# Resource limits
LimitNOFILE=500000
# Hardening measures
####################
# Provide a private /tmp and /var/tmp.
PrivateTmp=true
# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full
# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true
# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true
# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target

View File

@ -74,3 +74,8 @@ Package: blockbook-ethereum-testnet-ropsten
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, coreutils, passwd, findutils, psmisc, backend-ethereum-testnet-ropsten
Description: Satoshilabs blockbook server (Ethereum testnet ropsten)
Package: blockbook-dogecoin
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, coreutils, passwd, findutils, psmisc, backend-dogecoin
Description: Satoshilabs blockbook server (Dogecoin mainnet)

12
configs/dogecoin.json Normal file
View File

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

View File

@ -1,4 +1,4 @@
TARGETS = bitcoin zcash bcash ethereum bgold dash litecoin
TARGETS = bitcoin zcash bcash ethereum bgold dash litecoin dogecoin
IMAGE = blockbook-backend-build-deb
NO_CACHE = false

View File

@ -0,0 +1,13 @@
DOGECOIN_VERSION := 1.10.0
all:
wget https://github.com/dogecoin/dogecoin/releases/download/v${DOGECOIN_VERSION}/dogecoin-${DOGECOIN_VERSION}-linux64.tar.gz
tar -xf dogecoin-${DOGECOIN_VERSION}-linux64.tar.gz
mv dogecoin-${DOGECOIN_VERSION} dogecoin
rm dogecoin/bin/dogecoin-qt
rm dogecoin/bin/test_dogecoin*
clean:
rm -rf dogecoin
rm -f dogecoin-${DOGECOIN_VERSION}-linux64.tar.gz

View File

@ -0,0 +1 @@
/opt/coins/nodes/dogecoin/dogecoin.conf

View File

@ -0,0 +1 @@
/opt/coins/data/dogecoin/backend

View File

@ -0,0 +1,2 @@
dogecoin/* /opt/coins/nodes/dogecoin
dogecoin.conf /opt/coins/nodes/dogecoin

View File

@ -0,0 +1,10 @@
/opt/coins/data/dogecoin/backend/debug.log
/opt/coins/data/dogecoin/backend/db.log
{
rotate 7
daily
compress
missingok
notifempty
copytruncate
}

View File

@ -0,0 +1,20 @@
#!/bin/bash
set -e
case "$1" in
configure)
if ! id -u dogecoin &> /dev/null
then
useradd --system -M -U dogecoin -s /bin/false
fi
if [ "$(stat -c '%U' /opt/coins/data/dogecoin/backend)" != "dogecoin" ]
then
chown -R dogecoin:dogecoin /opt/coins/data/dogecoin/backend
fi
;;
esac
#DEBHELPER#

View File

@ -0,0 +1,47 @@
# It is not recommended to modify this file in-place, because it will
# be overwritten during package upgrades. If you want to add further
# options or overwrite existing ones then use
# $ systemctl edit dogecoin.service
# See "man systemd.service" for details.
# Note that almost all daemon options could be specified in
# /opt/coins/nodes/dogecoin/dogecoin.conf
[Unit]
Description=Dogecoin daemon (mainnet)
After=network.target
[Service]
ExecStart=/opt/coins/nodes/dogecoin/bin/dogecoind -datadir=/opt/coins/data/dogecoin/backend -conf=/opt/coins/nodes/dogecoin/dogecoin.conf -pid=/run/dogecoind/dogecoin.pid
# Creates /run/dogecoind owned by dogecoin
RuntimeDirectory=dogecoind
User=dogecoin
Type=forking
PIDFile=/run/dogecoind/dogecoin.pid
Restart=on-failure
# Resource limits
LimitNOFILE=500000
# Hardening measures
####################
# Provide a private /tmp and /var/tmp.
PrivateTmp=true
# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full
# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true
# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true
# Deny the creation of writable and executable memory mappings.
# MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,5 @@
dogecoin (1.10.0-satoshilabs1) unstable; urgency=medium
* Initial build
-- Martin Bohm <martin.bohm@satoshilabs.com> Thu, 14 Jun 2018 11:12:13 +0200

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,11 @@
Source: dogecoin
Section: satoshilabs
Priority: optional
Maintainer: martin.bohm@satoshilabs.com
Build-Depends: debhelper, wget, tar, gzip, make, dh-systemd, dh-exec
Standards-Version: 3.9.5
Package: backend-dogecoin
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, logrotate
Description: Satoshilabs packaged dogecoin server

View File

@ -0,0 +1,11 @@
#!/usr/bin/make -f
DH_VERBOSE = 1
%:
dh $@ --with=systemd
override_dh_systemd_start:
dh_systemd_start --no-start
override_dh_installinit:

View File

@ -0,0 +1,21 @@
daemon=1
server=1
nolisten=1
rpcuser=rpc
rpcpassword=rpcp
rpcport=8038
txindex=1
whitelist=127.0.0.1
upnp=0
discover=0
zmqpubhashtx=tcp://127.0.0.1:38338
zmqpubhashblock=tcp://127.0.0.1:38338
rpcthreads=32
rpcworkqueue=1100
maxmempool=2000
dbcache=1000