diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 1120dad..c6ae364 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -19,7 +19,6 @@ import ( "github.com/nbcorg/blockbook/bchain/coins/btg" "github.com/nbcorg/blockbook/bchain/coins/cpuchain" "github.com/nbcorg/blockbook/bchain/coins/dash" - "github.com/nbcorg/blockbook/bchain/coins/dcr" "github.com/nbcorg/blockbook/bchain/coins/deeponion" "github.com/nbcorg/blockbook/bchain/coins/digibyte" "github.com/nbcorg/blockbook/bchain/coins/divi" @@ -71,8 +70,6 @@ func init() { BlockChainFactories["Bgold"] = btg.NewBGoldRPC BlockChainFactories["Dash"] = dash.NewDashRPC BlockChainFactories["Dash Testnet"] = dash.NewDashRPC - BlockChainFactories["Decred"] = dcr.NewDecredRPC - BlockChainFactories["Decred Testnet"] = dcr.NewDecredRPC BlockChainFactories["GameCredits"] = gamecredits.NewGameCreditsRPC BlockChainFactories["Koto"] = koto.NewKotoRPC BlockChainFactories["Koto Testnet"] = koto.NewKotoRPC diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 0fbf2f0..64dcd62 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -8,12 +8,10 @@ import ( "strconv" vlq "github.com/bsm/go-vlq" - "github.com/juju/errors" "github.com/nbcorg/btcd/blockchain" "github.com/nbcorg/btcd/wire" "github.com/nbcorg/btcutil" "github.com/nbcorg/btcutil/chaincfg" - "github.com/nbcorg/btcutil/hdkeychain" "github.com/nbcorg/btcutil/txscript" "github.com/nbcorg/blockbook/bchain" ) @@ -332,104 +330,3 @@ func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { func (p *BitcoinParser) MinimumCoinbaseConfirmations() int { return p.minimumCoinbaseConfirmations } - -func (p *BitcoinParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) { - var a btcutil.Address - var err error - if extKey.Version() == p.XPubMagicSegwitP2sh { - // redeemScript <20-byte-pubKeyHash> - pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes()) - redeemScript := make([]byte, len(pubKeyHash)+2) - redeemScript[0] = 0 - redeemScript[1] = byte(len(pubKeyHash)) - copy(redeemScript[2:], pubKeyHash) - hash := btcutil.Hash160(redeemScript) - a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params) - } else if extKey.Version() == p.XPubMagicSegwitNative { - a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params) - } else { - // default to P2PKH address - a, err = extKey.Address(p.Params) - } - if err != nil { - return nil, err - } - return txscript.PayToAddrScript(a) -} - -// DeriveAddressDescriptors derives address descriptors from given xpub for listed indexes -func (p *BitcoinParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]bchain.AddressDescriptor, error) { - extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) - if err != nil { - return nil, err - } - changeExtKey, err := extKey.Child(change) - if err != nil { - return nil, err - } - ad := make([]bchain.AddressDescriptor, len(indexes)) - for i, index := range indexes { - indexExtKey, err := changeExtKey.Child(index) - if err != nil { - return nil, err - } - ad[i], err = p.addrDescFromExtKey(indexExtKey) - if err != nil { - return nil, err - } - } - return ad, nil -} - -// DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for addresses in index range -func (p *BitcoinParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { - if toIndex <= fromIndex { - return nil, errors.New("toIndex<=fromIndex") - } - extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) - if err != nil { - return nil, err - } - changeExtKey, err := extKey.Child(change) - if err != nil { - return nil, err - } - ad := make([]bchain.AddressDescriptor, toIndex-fromIndex) - for index := fromIndex; index < toIndex; index++ { - indexExtKey, err := changeExtKey.Child(index) - if err != nil { - return nil, err - } - ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey) - if err != nil { - return nil, err - } - } - return ad, nil -} - -// DerivationBasePath returns base path of xpub -func (p *BitcoinParser) DerivationBasePath(xpub string) (string, error) { - extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher) - if err != nil { - return "", err - } - var c, bip string - cn := extKey.ChildNum() - if cn >= 0x80000000 { - cn -= 0x80000000 - c = "'" - } - c = strconv.Itoa(int(cn)) + c - if extKey.Depth() != 3 { - return "unknown/" + c, nil - } - if extKey.Version() == p.XPubMagicSegwitP2sh { - bip = "49" - } else if extKey.Version() == p.XPubMagicSegwitNative { - bip = "84" - } else { - bip = "44" - } - return "m/" + bip + "'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil -} diff --git a/bchain/coins/dcr/decredparser.go b/bchain/coins/dcr/decredparser.go deleted file mode 100644 index 54c0230..0000000 --- a/bchain/coins/dcr/decredparser.go +++ /dev/null @@ -1,356 +0,0 @@ -package dcr - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "encoding/json" - "math" - "math/big" - "strconv" - - cfg "github.com/decred/dcrd/chaincfg" - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/hdkeychain" - "github.com/decred/dcrd/txscript" - "github.com/juju/errors" - "github.com/nbcorg/btcd/wire" - "github.com/nbcorg/btcutil/base58" - "github.com/nbcorg/btcutil/chaincfg" - "github.com/nbcorg/blockbook/bchain" - "github.com/nbcorg/blockbook/bchain/coins/btc" - "github.com/nbcorg/blockbook/bchain/coins/utils" -) - -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 - netConfig *cfg.Params -} - -// NewDecredParser returns new DecredParser instance -func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParser { - d := &DecredParser{ - BitcoinParser: btc.NewBitcoinParser(params, c), - baseParser: &bchain.BaseParser{}, - } - - switch d.BitcoinParser.Params.Name { - case "testnet3": - d.netConfig = &cfg.TestNet3Params - default: - d.netConfig = &cfg.MainNetParams - } - return d -} - -// 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 -} - -// ParseTxFromJson parses JSON message containing transaction and returns Tx struct -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, - BlockHeight: getTxResult.Result.BlockHeight, - 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 -} - -// GetAddrDescFromAddress returns internal address representation of a given address. -func (p *DecredParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { - addressByte := []byte(address) - return bchain.AddressDescriptor(addressByte), nil -} - -// GetAddrDescFromVout returns internal address representation of a given transaction output. -func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { - script, err := hex.DecodeString(output.ScriptPubKey.Hex) - if err != nil { - return nil, err - } - - scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, script, p.netConfig) - 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 -} - -// GetAddressesFromAddrDesc returns addresses obtained from the internal address representation -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) -} - -func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) { - var addr, err = extKey.Address(p.netConfig) - if err != nil { - return nil, err - } - return p.GetAddrDescFromAddress(addr.String()) -} - -// DeriveAddressDescriptors derives address descriptors from given xpub for -// listed indexes -func (p *DecredParser) DeriveAddressDescriptors(xpub string, change uint32, - indexes []uint32) ([]bchain.AddressDescriptor, error) { - extKey, err := hdkeychain.NewKeyFromString(xpub) - if err != nil { - return nil, err - } - - changeExtKey, err := extKey.Child(change) - if err != nil { - return nil, err - } - - ad := make([]bchain.AddressDescriptor, len(indexes)) - for i, index := range indexes { - indexExtKey, err := changeExtKey.Child(index) - if err != nil { - return nil, err - } - ad[i], err = p.addrDescFromExtKey(indexExtKey) - if err != nil { - return nil, err - } - } - return ad, nil -} - -// DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for -// addresses in index range -func (p *DecredParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, - fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) { - if toIndex <= fromIndex { - return nil, errors.New("toIndex<=fromIndex") - } - extKey, err := hdkeychain.NewKeyFromString(xpub) - if err != nil { - return nil, err - } - changeExtKey, err := extKey.Child(change) - if err != nil { - return nil, err - } - - ad := make([]bchain.AddressDescriptor, toIndex-fromIndex) - for index := fromIndex; index < toIndex; index++ { - indexExtKey, err := changeExtKey.Child(index) - if err != nil { - return nil, err - } - ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey) - if err != nil { - return nil, err - } - } - return ad, nil -} - -// DerivationBasePath returns base path of xpub which whose full format is -// m/44'/'/'//
. This function only -// returns a path up to m/44'/'/'/ whereby the rest of the -// other details (/
) are populated automatically. -func (p *DecredParser) DerivationBasePath(xpub string) (string, error) { - var c string - cn, depth, err := p.decodeXpub(xpub) - if err != nil { - return "", err - } - - if cn >= hdkeychain.HardenedKeyStart { - cn -= hdkeychain.HardenedKeyStart - c = "'" - } - - c = strconv.Itoa(int(cn)) + c - if depth != 3 { - return "unknown/" + c, nil - } - - return "m/44'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil -} - -func (p *DecredParser) decodeXpub(xpub string) (childNum uint32, depth uint16, err error) { - decoded := base58.Decode(xpub) - - // serializedKeyLen is the length of a serialized public or private - // extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes - // fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes - // public/private key data. - serializedKeyLen := 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes - if len(decoded) != serializedKeyLen+4 { - err = errors.New("invalid extended key length") - return - } - - payload := decoded[:len(decoded)-4] - checkSum := decoded[len(decoded)-4:] - expectedCheckSum := chainhash.HashB(chainhash.HashB(payload))[:4] - if !bytes.Equal(checkSum, expectedCheckSum) { - err = errors.New("bad checksum value") - return - } - - depth = uint16(payload[4:5][0]) - childNum = binary.BigEndian.Uint32(payload[9:13]) - return -} diff --git a/bchain/coins/dcr/decredparser_test.go b/bchain/coins/dcr/decredparser_test.go deleted file mode 100644 index b9711c6..0000000 --- a/bchain/coins/dcr/decredparser_test.go +++ /dev/null @@ -1,537 +0,0 @@ -// +build unittest - -package dcr - -import ( - "encoding/hex" - "math/big" - "os" - "reflect" - "testing" - - "github.com/nbcorg/blockbook/bchain" - "github.com/nbcorg/blockbook/bchain/coins/btc" -) - -var ( - testnetParser, mainnetParser *DecredParser - - testTx1 = bchain.Tx{ - Hex: "01000000012372568fe80d2f9b2ab17226158dd5732d9926dc705371eaf40ab748c9e3d9720200000001ffffffff02644b252d0000000000001976a914a862f83733cc368f386a651e03d844a5bd6116d588acacdf63090000000000001976a91491dc5d18370939b3414603a0729bcb3a38e4ef7688ac000000000000000001e48d893600000000bb3d0000020000006a4730440220378e1442cc17fa7e49184518713eedd30e13e42147e077859557da6ffbbd40c702205f85563c28b6287f9c9110e6864dd18acfd92d85509ea846913c28b6e8a7f940012102bbbd7aadef33f2d2bdd9b0c5ba278815f5d66a6a01d2c019fb73f697662038b5", - Blocktime: 1535632670, - Time: 1535632670, - Txid: "132acb5b474b45b830f7961c91c87e53cce3a37a6c6f0b0933ccdf0395c81a6a", - LockTime: 0, - Version: 1, - Vin: []bchain.Vin{ - { - Txid: "72d9e3c948b70af4ea715370dc26992d73d58d152672b12a9b2f0de88f567223", - Vout: 2, - Sequence: 4294967295, - }, - }, - Vout: []bchain.Vout{ - { - ValueSat: *big.NewInt(757418852), - N: 0, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a914a862f83733cc368f386a651e03d844a5bd6116d588ac", - Addresses: []string{ - "TsgNUZKEnUhFASLESj7fVRTkgue3QR9TAeZ", - }, - }, - }, - { - ValueSat: *big.NewInt(157540268), - N: 1, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a91491dc5d18370939b3414603a0729bcb3a38e4ef7688ac", - Addresses: []string{ - "TseKNSWYbAzaGogpnNn25teTz53PTk3sgPu", - }, - }, - }, - }, - } - - testTx2 = bchain.Tx{ - Hex: "0100000001193c189c71dff482b70ccb10ec9cf0ea3421a7fc51e4c7b0cf59c98a293a2f960200000000ffffffff027c87f00b0000000000001976a91418f10131a859912119c4a8510199f87f0a4cec2488ac9889495f0000000000001976a914631fb783b1e06c3f6e71777e16da6de13450465e88ac0000000000000000015ced3d6b0000000030740000000000006a47304402204e6afc21f6d065b9c082dad81a5f29136320e2b54c6cdf6b8722e4507e1a8d8902203933c5e592df3b0bbb0568f121f48ef6cbfae9cf479a57229742b5780dedc57a012103b89bb443b6ab17724458285b302291b082c59e5a022f273af0f61d47a414a537", - Txid: "7058766ffef2e9cee61ee4b7604a39bc91c3000cb951c4f93f3307f6e0bf4def", - Blocktime: 1463843967, - Time: 1463843967, - LockTime: 0, - Version: 1, - Vin: []bchain.Vin{ - { - Txid: "962f3a298ac959cfb0c7e451fca72134eaf09cec10cb0cb782f4df719c183c19", - Vout: 2, - Sequence: 4294967295, - ScriptSig: bchain.ScriptSig{ - Hex: "47304402204e6afc21f6d065b9c082dad81a5f29136320e2b54c6cdf6b8722e4507e1a8d8902203933c5e592df3b0bbb0568f121f48ef6cbfae9cf479a57229742b5780dedc57a012103b89bb443b6ab17724458285b302291b082c59e5a022f273af0f61d47a414a537", - }, - }, - }, - Vout: []bchain.Vout{ - { - ValueSat: *big.NewInt(200312700), - N: 0, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a91418f10131a859912119c4a8510199f87f0a4cec2488ac", - Addresses: []string{ - "DsTEnRLDEjQNeQ4A47fdS2pqtaFrGNzkqNa", - }, - }, - }, - { - ValueSat: *big.NewInt(1598654872), - N: 1, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a914631fb783b1e06c3f6e71777e16da6de13450465e88ac", - Addresses: []string{ - "Dsa12P9VnCd55hTnUXpvGgFKSeGkFkzRvYb", - }, - }, - }, - }, - } - - testTx3 = bchain.Tx{ - Hex: "0100000001c56d80756eaa7fc6e3542b29f596c60a9bcc959cf04d5f6e6b12749e241ece290200000001ffffffff02cf20b42d0000000000001976a9140799daa3cd36b44def220886802eb99e10c4a7c488ac0c25c7070000000000001976a9140b102deb3314213164cb6322211225365658407e88ac000000000000000001afa87b3500000000e33d0000000000006a47304402201ff342e5aa55b6030171f85729221ca0b81938826cc09449b77752e6e3b615be0220281e160b618e57326b95a0e0c3ac7a513bd041aba63cbace2f71919e111cfdba01210290a8de6665c8caac2bb8ca1aabd3dc09a334f997f97bd894772b1e51cab003d9", - Blocktime: 1535638326, - Time: 1535638326, - Txid: "caf34c934d4c36b410c0265222b069f52e2df459ebb09d6797a635ceee0edd60", - LockTime: 0, - Version: 1, - Vin: []bchain.Vin{ - { - Txid: "29ce1e249e74126b6e5f4df09c95cc9b0ac696f5292b54e3c67faa6e75806dc5", - Vout: 2, - Sequence: 4294967295, - }, - }, - Vout: []bchain.Vout{ - { - ValueSat: *big.NewInt(766779599), - N: 0, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a9140799daa3cd36b44def220886802eb99e10c4a7c488ac", - Addresses: []string{ - "TsRiKWsS9ucaqYDw9qhg6NukTthS5LwTRnv", - }, - }, - }, - { - ValueSat: *big.NewInt(13049166), - N: 1, - ScriptPubKey: bchain.ScriptPubKey{ - Hex: "76a9140b102deb3314213164cb6322211225365658407e88ac", - Addresses: []string{ - "TsS2dHqESY1vffjddpo1VMTbwLnDspfEj5W", - }, - }, - }, - }, - } -) - -func TestMain(m *testing.M) { - testnetParser = NewDecredParser(GetChainParams("testnet3"), &btc.Configuration{Slip44: 1}) - mainnetParser = NewDecredParser(GetChainParams("mainnet"), &btc.Configuration{Slip44: 42}) - exitCode := m.Run() - os.Exit(exitCode) -} - -func TestGetAddrDescFromAddress(t *testing.T) { - type args struct { - address string - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - { - name: "P2PKH", - args: args{address: "TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"}, - want: "5463727970474163474352565872455337685771565a62356f4c4a4b435a45746f4c31", - wantErr: false, - }, - { - name: "P2PKH", - args: args{address: "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"}, - want: "547366444c72526b6b3963695575776670326238506177776e756b59443779416a4764", - wantErr: false, - }, - { - name: "P2PKH", - args: args{address: "TsTevp3WYTiV3X1qjvZqa7nutuTqt5VNeoU"}, - want: "547354657670335759546956335831716a765a7161376e75747554717435564e656f55", - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := testnetParser.GetAddrDescFromAddress(tt.args.address) - if (err != nil) != tt.wantErr { - t.Fatalf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) - } - h := hex.EncodeToString(got) - if !reflect.DeepEqual(h, tt.want) { - t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) - } - }) - } -} - -func TestGetAddrDescFromVout(t *testing.T) { - type args struct { - vout bchain.Vout - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - { - name: "P2PK", - args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a914936f3a56a2dd0fb3bfde6bc820d4643e1701542a88ac"}}}, - want: "54736554683431516f356b594c3337614c474d535167346e67636f71396a7a44583659", - wantErr: false, - }, - { - name: "P2PK", - args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a9144b31f712b03837b1303cddcb1ae9abd98da44f1088ac"}}}, - want: "547358736a3161747744736455746e354455576b666f6d5a586e4a6151467862395139", - wantErr: false, - }, - { - name: "P2PK", - args: args{vout: bchain.Vout{ScriptPubKey: bchain.ScriptPubKey{Hex: "76a9140d85a1d3f77383eb3dacfd83c46e2c7915aba91d88ac"}}}, - want: "54735346644c79657942776e68486978737367784b34546f4664763876525931793871", - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := testnetParser.GetAddrDescFromVout(&tt.args.vout) - if (err != nil) != tt.wantErr { - t.Fatalf("GetAddrDescFromVout() error = %v, wantErr %v", err, tt.wantErr) - } - h := hex.EncodeToString(got) - if !reflect.DeepEqual(h, tt.want) { - t.Errorf("GetAddrDescFromVout() = %v, want %v", h, tt.want) - } - }) - } -} - -func TestGetAddressesFromAddrDesc(t *testing.T) { - type args struct { - script string - } - tests := []struct { - name string - args args - want []string - want2 bool - wantErr bool - }{ - { - name: "P2PKH", - args: args{script: "5463727970474163474352565872455337685771565a62356f4c4a4b435a45746f4c31"}, - want: []string{"TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1"}, - want2: true, - wantErr: false, - }, - { - name: "P2PKH", - args: args{script: "547366444c72526b6b3963695575776670326238506177776e756b59443779416a4764"}, - want: []string{"TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"}, - want2: true, - wantErr: false, - }, - { - name: "P2PKH", - args: args{script: "547354657670335759546956335831716a765a7161376e75747554717435564e656f55"}, - want: []string{"TsTevp3WYTiV3X1qjvZqa7nutuTqt5VNeoU"}, - want2: true, - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b, _ := hex.DecodeString(tt.args.script) - got, got2, err := testnetParser.GetAddressesFromAddrDesc(b) - if (err != nil) != tt.wantErr { - t.Fatalf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want) - } - if !reflect.DeepEqual(got2, tt.want2) { - t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2) - } - }) - } -} - -func TestDeriveAddressDescriptors(t *testing.T) { - type args struct { - xpub string - change uint32 - indexes []uint32 - parser *DecredParser - } - tests := []struct { - name string - args args - want []string - wantErr bool - }{ - { - name: "m/44'/42'/0'", - args: args{ - xpub: "dpubZFYFpu8cZxwrApmtot59LZLChk5JcdB8xCxVQ4pcsTig4fscH3EfAkhxcKKhXBQH6SGyYs2VDidoomA5qukTWMaHDkBsAtnpodAHm61ozbD", - change: 0, - indexes: []uint32{0, 5}, - parser: mainnetParser, - }, - want: []string{"DsUPx4NgAJzUQFRXnn2XZnWwEeQkQpwhqFD", "DsaT4kaGCeJU1Fef721J2DNt8UgcrmE2UsD"}, - }, - { - name: "m/44'/42'/1'", - args: args{ - xpub: "dpubZFYFpu8cZxwrESo75eazNjVHtC4nWJqL5aXxExZHKnyvZxKirkpypbgeJhVzhTdfnK2986DLjich4JQqcSaSyxu5KSoZ25KJ67j4mQJ9iqx", - change: 0, - indexes: []uint32{0, 5}, - parser: mainnetParser, - }, - want: []string{"DsX5px9k9XZKFNP2Z9kyZBbfHgecm1ftNz6", "Dshjbo35CSWwNo7xMgG7UM8AWykwEjJ5DCP"}, - }, - { - name: "m/44'/1'/0'", - args: args{ - xpub: "tpubVossdTiJthe9xZZ5rz47szxN6ncpLJ4XmtJS26hKciDUPtboikdwHKZPWfo4FWYuLRZ6MNkLjyPRKhxqjStBTV2BE1LCULznpqsFakkPfPr", - change: 0, - indexes: []uint32{0, 2}, - parser: testnetParser, - }, - want: []string{"TsboBwzpaH831s9J63XDcDx5GbKLcwv9ujo", "TsXrNt9nP3kBUM2Wr3rQGoPrpL7RMMSJyJH"}, - }, - { - name: "m/44'/1'/1'", - args: args{ - xpub: "tpubVossdTiJtheA1fQniKn9EN1JE1Eq1kBofaq2KwywrvuNhAk1KsEM7J2r8anhMJUmmcn9Wmoh73EctpW7Vxs3gS8cbF7N3m4zVjzuyvBj3qC", - change: 0, - indexes: []uint32{0, 3}, - parser: testnetParser, - }, - want: []string{"TsndBjzcwZVjoZEuqYKwiMbCJH9QpkEekg4", "TsbrkVdFciW3Lfh1W8qjwRY9uSbdiBmY4VP"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.args.parser.DeriveAddressDescriptors(tt.args.xpub, tt.args.change, tt.args.indexes) - if (err != nil) != tt.wantErr { - t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) - return - } - gotAddresses := make([]string, len(got)) - for i, ad := range got { - aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) - if err != nil || len(aa) != 1 { - t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) - return - } - gotAddresses[i] = aa[0] - } - if !reflect.DeepEqual(gotAddresses, tt.want) { - t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) - } - }) - } -} - -func TestDeriveAddressDescriptorsFromTo(t *testing.T) { - type args struct { - xpub string - change uint32 - fromIndex uint32 - toIndex uint32 - parser *DecredParser - } - tests := []struct { - name string - args args - want []string - wantErr bool - }{ - { - name: "m/44'/42'/2'", - args: args{ - xpub: "dpubZFYFpu8cZxwrGnWbdHmvsAcTaMve4W9EAUiSHzXp1c5hQvfeWgk7LxsE5LqopwfxV62CoB51fxw97YaNpdA3tdo4GHbLxtUzRmYcUtVPYUi", - change: 0, - fromIndex: 0, - toIndex: 1, - parser: mainnetParser, - }, - want: []string{"Dshtd1N7pKw814wgWXUq5qFVC5ENQ9oSGK7"}, - }, - { - name: "m/44'/42'/1'", - args: args{ - xpub: "dpubZFYFpu8cZxwrESo75eazNjVHtC4nWJqL5aXxExZHKnyvZxKirkpypbgeJhVzhTdfnK2986DLjich4JQqcSaSyxu5KSoZ25KJ67j4mQJ9iqx", - change: 0, - fromIndex: 0, - toIndex: 1, - parser: mainnetParser, - }, - want: []string{"DsX5px9k9XZKFNP2Z9kyZBbfHgecm1ftNz6"}, - }, - { - name: "m/44'/1'/2'", - args: args{ - xpub: "tpubVossdTiJtheA51AuNQZtqvKUbhM867Von8XBadxX3tRkDm71kyyi6U966jDPEw9RnQjNcQLwxYSnQ9kBjZxrxfmSbByRbz7D1PLjgAPmL42", - change: 0, - fromIndex: 0, - toIndex: 1, - parser: testnetParser, - }, - want: []string{"TsSpo87rBG21PLvvbzFk2Ust2Dbyvjfn8pQ"}, - }, - { - name: "m/44'/1'/1'", - args: args{ - xpub: "tpubVossdTiJtheA1fQniKn9EN1JE1Eq1kBofaq2KwywrvuNhAk1KsEM7J2r8anhMJUmmcn9Wmoh73EctpW7Vxs3gS8cbF7N3m4zVjzuyvBj3qC", - change: 0, - fromIndex: 0, - toIndex: 5, - parser: testnetParser, - }, - want: []string{"TsndBjzcwZVjoZEuqYKwiMbCJH9QpkEekg4", "TshWHbnPAVCDARTcCfTEQyL9SzeHxxexX4J", "TspE6pMdC937UHHyfYJpTiKi6vPj5rVnWiG", - "TsbrkVdFciW3Lfh1W8qjwRY9uSbdiBmY4VP", "TsagMXjC4Xj6ckPEJh8f1RKHU4cEzTtdVW6"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.args.parser.DeriveAddressDescriptorsFromTo(tt.args.xpub, tt.args.change, tt.args.fromIndex, tt.args.toIndex) - if (err != nil) != tt.wantErr { - t.Errorf("DeriveAddressDescriptorsFromTo() error = %v, wantErr %v", err, tt.wantErr) - return - } - gotAddresses := make([]string, len(got)) - for i, ad := range got { - aa, _, err := tt.args.parser.GetAddressesFromAddrDesc(ad) - if err != nil || len(aa) != 1 { - t.Errorf("DeriveAddressDescriptorsFromTo() got incorrect address descriptor %v, error %v", ad, err) - return - } - gotAddresses[i] = aa[0] - } - if !reflect.DeepEqual(gotAddresses, tt.want) { - t.Errorf("DeriveAddressDescriptorsFromTo() = %v, want %v", gotAddresses, tt.want) - } - }) - } -} - -func TestDerivationBasePath(t *testing.T) { - tests := []struct { - name string - xpub string - parser *DecredParser - }{ - { - name: "m/44'/42'/2'", - xpub: "dpubZFYFpu8cZxwrGnWbdHmvsAcTaMve4W9EAUiSHzXp1c5hQvfeWgk7LxsE5LqopwfxV62CoB51fxw97YaNpdA3tdo4GHbLxtUzRmYcUtVPYUi", - parser: mainnetParser, - }, - { - name: "m/44'/42'/1'", - xpub: "dpubZFYFpu8cZxwrESo75eazNjVHtC4nWJqL5aXxExZHKnyvZxKirkpypbgeJhVzhTdfnK2986DLjich4JQqcSaSyxu5KSoZ25KJ67j4mQJ9iqx", - parser: mainnetParser, - }, - { - name: "m/44'/1'/2'", - xpub: "tpubVossdTiJtheA51AuNQZtqvKUbhM867Von8XBadxX3tRkDm71kyyi6U966jDPEw9RnQjNcQLwxYSnQ9kBjZxrxfmSbByRbz7D1PLjgAPmL42", - parser: testnetParser, - }, - { - name: "m/44'/1'/1'", - xpub: "tpubVossdTiJtheA1fQniKn9EN1JE1Eq1kBofaq2KwywrvuNhAk1KsEM7J2r8anhMJUmmcn9Wmoh73EctpW7Vxs3gS8cbF7N3m4zVjzuyvBj3qC", - parser: testnetParser, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.parser.DerivationBasePath(tt.xpub) - if err != nil { - t.Errorf("DerivationBasePath() expected no error but got %v", err) - return - } - - if got != tt.name { - t.Errorf("DerivationBasePath() = %v, want %v", got, tt.name) - } - }) - } -} - -func TestPackAndUnpack(t *testing.T) { - tests := []struct { - name string - txInfo *bchain.Tx - height uint32 - parser *DecredParser - }{ - { - name: "Test_1", - txInfo: &testTx1, - height: 15819, - parser: testnetParser, - }, - { - name: "Test_2", - txInfo: &testTx2, - height: 300000, - parser: mainnetParser, - }, - { - name: "Test_3", - txInfo: &testTx3, - height: 15859, - parser: testnetParser, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - packedTx, err := tt.parser.PackTx(tt.txInfo, tt.height, tt.txInfo.Blocktime) - if err != nil { - t.Errorf("PackTx() expected no error but got %v", err) - return - } - - unpackedtx, gotHeight, err := tt.parser.UnpackTx(packedTx) - if err != nil { - t.Errorf("PackTx() expected no error but got %v", err) - return - } - - if !reflect.DeepEqual(tt.txInfo, unpackedtx) { - t.Errorf("TestPackAndUnpack() expected the raw tx and the unpacked tx to match but they didn't") - } - - if gotHeight != tt.height { - t.Errorf("TestPackAndUnpack() = got height %v, but want %v", gotHeight, tt.height) - } - }) - } - -} diff --git a/bchain/coins/dcr/decredrpc.go b/bchain/coins/dcr/decredrpc.go deleted file mode 100644 index f88f41d..0000000 --- a/bchain/coins/dcr/decredrpc.go +++ /dev/null @@ -1,876 +0,0 @@ -package dcr - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/big" - "net" - "net/http" - "runtime/debug" - "strconv" - "strings" - "sync" - "time" - - "github.com/decred/dcrd/dcrjson" - "github.com/golang/glog" - "github.com/juju/errors" - "github.com/nbcorg/blockbook/bchain" - "github.com/nbcorg/blockbook/bchain/coins/btc" - "github.com/nbcorg/blockbook/common" -) - -// voteBitYes defines the vote bit set when a given block validates the previous -// block -const voteBitYes = 0x0001 - -type DecredRPC struct { - *btc.BitcoinRPC - mtx sync.Mutex - client http.Client - rpcURL string - rpcUser string - bestBlock uint32 - rpcPassword string -} - -// NewDecredRPC returns new DecredRPC instance. -func NewDecredRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { - b, err := btc.NewBitcoinRPC(config, pushHandler) - if err != nil { - return nil, err - } - - var c btc.Configuration - if err = json.Unmarshal(config, &c); err != nil { - return nil, errors.Annotate(err, "Invalid configuration file") - } - - transport := &http.Transport{ - Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial, - MaxIdleConns: 100, - MaxIdleConnsPerHost: 100, // necessary to not to deplete ports - } - - d := &DecredRPC{ - BitcoinRPC: b.(*btc.BitcoinRPC), - client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport}, - rpcURL: c.RPCURL, - rpcUser: c.RPCUser, - rpcPassword: c.RPCPass, - } - - d.BitcoinRPC.RPCMarshaler = btc.JSONMarshalerV1{} - d.BitcoinRPC.ChainConfig.SupportsEstimateSmartFee = false - - return d, nil -} - -// Initialize initializes DecredRPC instance. -func (d *DecredRPC) Initialize() error { - chainInfo, err := d.GetChainInfo() - if err != nil { - return err - } - - chainName := chainInfo.Chain - glog.Info("Chain name ", chainName) - - params := GetChainParams(chainName) - - // always create parser - d.BitcoinRPC.Parser = NewDecredParser(params, d.BitcoinRPC.ChainConfig) - - // parameters for getInfo request - if params.Net == MainnetMagic { - d.BitcoinRPC.Testnet = false - d.BitcoinRPC.Network = "livenet" - } else { - d.BitcoinRPC.Testnet = true - d.BitcoinRPC.Network = "testnet" - } - - glog.Info("rpc: block chain ", params.Name) - - return nil -} - -type Error struct { - Code int `json:"code"` - Message string `json:"message"` -} - -type GenericCmd struct { - ID int `json:"id"` - Method string `json:"method"` - Params []interface{} `json:"params,omitempty"` -} - -type GetBlockChainInfoResult struct { - Error Error `json:"error"` - Result struct { - Chain string `json:"chain"` - Blocks int64 `json:"blocks"` - Headers int64 `json:"headers"` - SyncHeight int64 `json:"syncheight"` - BestBlockHash string `json:"bestblockhash"` - Difficulty uint32 `json:"difficulty"` - VerificationProgress float64 `json:"verificationprogress"` - ChainWork string `json:"chainwork"` - InitialBlockDownload bool `json:"initialblockdownload"` - MaxBlockSize int64 `json:"maxblocksize"` - } `json:"result"` -} - -type GetNetworkInfoResult struct { - Error Error `json:"error"` - Result struct { - Version int32 `json:"version"` - ProtocolVersion int32 `json:"protocolversion"` - TimeOffset int64 `json:"timeoffset"` - Connections int32 `json:"connections"` - RelayFee float64 `json:"relayfee"` - } `json:"result"` -} - -type GetInfoChainResult struct { - Error Error `json:"error"` - Result struct { - Version int32 `json:"version"` - ProtocolVersion int32 `json:"protocolversion"` - Blocks int64 `json:"blocks"` - TimeOffset int64 `json:"timeoffset"` - Connections int32 `json:"connections"` - Proxy string `json:"proxy"` - Difficulty float64 `json:"difficulty"` - TestNet bool `json:"testnet"` - RelayFee float64 `json:"relayfee"` - Errors string `json:"errors"` - } -} - -type GetBestBlockResult struct { - Error Error `json:"error"` - Result struct { - Hash string `json:"hash"` - Height uint32 `json:"height"` - } `json:"result"` -} - -type GetBlockHashResult struct { - Error Error `json:"error"` - Result string `json:"result"` -} - -type GetBlockResult struct { - Error Error `json:"error"` - Result struct { - Hash string `json:"hash"` - Confirmations int64 `json:"confirmations"` - Size int32 `json:"size"` - Height uint32 `json:"height"` - Version common.JSONNumber `json:"version"` - MerkleRoot string `json:"merkleroot"` - StakeRoot string `json:"stakeroot"` - RawTx []RawTx `json:"rawtx"` - Tx []string `json:"tx,omitempty"` - STx []string `json:"stx,omitempty"` - Time int64 `json:"time"` - Nonce common.JSONNumber `json:"nonce"` - VoteBits uint16 `json:"votebits"` - FinalState string `json:"finalstate"` - Voters uint16 `json:"voters"` - FreshStake uint8 `json:"freshstake"` - Revocations uint8 `json:"revocations"` - PoolSize uint32 `json:"poolsize"` - Bits string `json:"bits"` - SBits float64 `json:"sbits"` - ExtraData string `json:"extradata"` - StakeVersion uint32 `json:"stakeversion"` - Difficulty float64 `json:"difficulty"` - ChainWork string `json:"chainwork"` - PreviousHash string `json:"previousblockhash"` - NextHash string `json:"nextblockhash,omitempty"` - } `json:"result"` -} - -type GetBlockHeaderResult struct { - Error Error `json:"error"` - Result struct { - Hash string `json:"hash"` - Confirmations int64 `json:"confirmations"` - Version common.JSONNumber `json:"version"` - MerkleRoot string `json:"merkleroot"` - StakeRoot string `json:"stakeroot"` - VoteBits uint16 `json:"votebits"` - FinalState string `json:"finalstate"` - Voters uint16 `json:"voters"` - FreshStake uint8 `json:"freshstake"` - Revocations uint8 `json:"revocations"` - PoolSize uint32 `json:"poolsize"` - Bits string `json:"bits"` - SBits float64 `json:"sbits"` - Height uint32 `json:"height"` - Size uint32 `json:"size"` - Time int64 `json:"time"` - Nonce uint32 `json:"nonce"` - ExtraData string `json:"extradata"` - StakeVersion uint32 `json:"stakeversion"` - Difficulty float64 `json:"difficulty"` - ChainWork string `json:"chainwork"` - PreviousHash string `json:"previousblockhash,omitempty"` - NextHash string `json:"nextblockhash,omitempty"` - } `json:"result"` -} - -type ScriptSig struct { - Asm string `json:"asm"` - Hex string `json:"hex"` -} - -type Vin struct { - Coinbase string `json:"coinbase"` - Stakebase string `json:"stakebase"` - Txid string `json:"txid"` - Vout uint32 `json:"vout"` - Tree int8 `json:"tree"` - Sequence uint32 `json:"sequence"` - AmountIn float64 `json:"amountin"` - BlockHeight uint32 `json:"blockheight"` - BlockIndex uint32 `json:"blockindex"` - ScriptSig *ScriptSig `json:"scriptsig"` -} - -type ScriptPubKeyResult struct { - Asm string `json:"asm"` - Hex string `json:"hex,omitempty"` - ReqSigs int32 `json:"reqSigs,omitempty"` - Type string `json:"type"` - Addresses []string `json:"addresses,omitempty"` - CommitAmt *float64 `json:"commitamt,omitempty"` -} - -type Vout struct { - Value float64 `json:"value"` - N uint32 `json:"n"` - Version uint16 `json:"version"` - ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` -} - -type RawTx struct { - Hex string `json:"hex"` - Txid string `json:"txid"` - Version int32 `json:"version"` - LockTime uint32 `json:"locktime"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - Expiry uint32 `json:"expiry"` - BlockIndex uint32 `json:"blockindex,omitempty"` - Confirmations int64 `json:"confirmations,omitempty"` - Time int64 `json:"time,omitempty"` - Blocktime int64 `json:"blocktime,omitempty"` - TxExtraInfo -} - -type GetTransactionResult struct { - Error Error `json:"error"` - Result struct { - RawTx - } `json:"result"` -} - -type MempoolTxsResult struct { - Error Error `json:"error"` - Result []string `json:"result"` -} - -type EstimateSmartFeeResult struct { - Error Error `json:"error"` - Result struct { - FeeRate float64 `json:"feerate"` - Errors []string `json:"errors"` - Blocks int64 `json:"blocks"` - } `json:"result"` -} - -type EstimateFeeResult struct { - Error Error `json:"error"` - Result common.JSONNumber `json:"result"` -} - -type SendRawTransactionResult struct { - Error Error `json:"error"` - Result string `json:"result"` -} - -type DecodeRawTransactionResult struct { - Error Error `json:"error"` - Result struct { - Txid string `json:"txid"` - Version int32 `json:"version"` - Locktime uint32 `json:"locktime"` - Expiry uint32 `json:"expiry"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - TxExtraInfo - } `json:"result"` -} - -type TxExtraInfo struct { - BlockHeight uint32 `json:"blockheight,omitempty"` - BlockHash string `json:"blockhash,omitempty"` -} - -func (d *DecredRPC) GetChainInfo() (*bchain.ChainInfo, error) { - blockchainInfoRequest := GenericCmd{ - ID: 1, - Method: "getblockchaininfo", - } - - var blockchainInfoResult GetBlockChainInfoResult - if err := d.Call(blockchainInfoRequest, &blockchainInfoResult); err != nil { - return nil, err - } - - if blockchainInfoResult.Error.Message != "" { - return nil, mapToStandardErr("Error fetching blockchain info: %s", blockchainInfoResult.Error) - } - - infoChainRequest := GenericCmd{ - ID: 2, - Method: "getinfo", - } - - var infoChainResult GetInfoChainResult - if err := d.Call(infoChainRequest, &infoChainResult); err != nil { - return nil, err - } - - if infoChainResult.Error.Message != "" { - return nil, mapToStandardErr("Error fetching network info: %s", infoChainResult.Error) - } - - chainInfo := &bchain.ChainInfo{ - Chain: blockchainInfoResult.Result.Chain, - Blocks: int(blockchainInfoResult.Result.Blocks), - Headers: int(blockchainInfoResult.Result.Headers), - Bestblockhash: blockchainInfoResult.Result.BestBlockHash, - Difficulty: strconv.Itoa(int(blockchainInfoResult.Result.Difficulty)), - SizeOnDisk: blockchainInfoResult.Result.SyncHeight, - Version: strconv.Itoa(int(infoChainResult.Result.Version)), - Subversion: "", - ProtocolVersion: strconv.Itoa(int(infoChainResult.Result.ProtocolVersion)), - Timeoffset: float64(infoChainResult.Result.TimeOffset), - Warnings: "", - } - return chainInfo, nil -} - -// getChainBestBlock returns the best block according to dcrd chain. This block -// has no atleast one confirming block. -func (d *DecredRPC) getChainBestBlock() (*GetBestBlockResult, error) { - bestBlockRequest := GenericCmd{ - ID: 1, - Method: "getbestblock", - } - - var bestBlockResult GetBestBlockResult - if err := d.Call(bestBlockRequest, &bestBlockResult); err != nil { - return nil, err - } - - if bestBlockResult.Error.Message != "" { - return nil, mapToStandardErr("Error fetching best block: %s", bestBlockResult.Error) - } - - return &bestBlockResult, nil -} - -// getBestBlock returns details for the block mined immediately before the -// official dcrd chain's bestblock i.e. it has a minimum of 1 confirmation. -// The chain's best block is not returned as its block validity is not guarranteed. -func (d *DecredRPC) getBestBlock() (*GetBestBlockResult, error) { - bestBlockResult, err := d.getChainBestBlock() - if err != nil { - return nil, err - } - - // remove the block with less than 1 confirming block - bestBlockResult.Result.Height-- - validBlockHash, err := d.getBlockHashByHeight(bestBlockResult.Result.Height) - if err != nil { - return nil, err - } - - bestBlockResult.Result.Hash = validBlockHash.Result - - return bestBlockResult, nil -} - -// GetBestBlockHash returns the block hash of the most recent block to be mined -// and has a minimum of 1 confirming block. -func (d *DecredRPC) GetBestBlockHash() (string, error) { - bestBlock, err := d.getBestBlock() - if err != nil { - return "", err - } - - return bestBlock.Result.Hash, nil -} - -// GetBestBlockHeight returns the block height of the most recent block to be mined -// and has a minimum of 1 confirming block. -func (d *DecredRPC) GetBestBlockHeight() (uint32, error) { - bestBlock, err := d.getBestBlock() - if err != nil { - return 0, err - } - - return uint32(bestBlock.Result.Height), err -} - -// GetBlockHash returns the block hash of the block at the provided height. -func (d *DecredRPC) GetBlockHash(height uint32) (string, error) { - blockHashResult, err := d.getBlockHashByHeight(height) - if err != nil { - return "", err - } - - return blockHashResult.Result, nil -} - -func (d *DecredRPC) getBlockHashByHeight(height uint32) (*GetBlockHashResult, error) { - blockHashRequest := GenericCmd{ - ID: 1, - Method: "getblockhash", - Params: []interface{}{height}, - } - - var blockHashResult GetBlockHashResult - if err := d.Call(blockHashRequest, &blockHashResult); err != nil { - return nil, err - } - - if blockHashResult.Error.Message != "" { - return nil, mapToStandardErr("Error fetching block hash: %s", blockHashResult.Error) - } - - return &blockHashResult, nil -} - -// GetBlockHeader returns the block header of the block the provided block hash. -func (d *DecredRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) { - blockHeaderRequest := GenericCmd{ - ID: 1, - Method: "getblockheader", - Params: []interface{}{hash}, - } - - var blockHeader GetBlockHeaderResult - if err := d.Call(blockHeaderRequest, &blockHeader); err != nil { - return nil, err - } - - if blockHeader.Error.Message != "" { - return nil, mapToStandardErr("Error fetching block info: %s", blockHeader.Error) - } - - header := &bchain.BlockHeader{ - Hash: blockHeader.Result.Hash, - Prev: blockHeader.Result.PreviousHash, - Next: blockHeader.Result.NextHash, - Height: blockHeader.Result.Height, - Confirmations: int(blockHeader.Result.Confirmations), - Size: int(blockHeader.Result.Size), - Time: blockHeader.Result.Time, - } - - return header, nil -} - -// GetBlock returns the block retrieved using the provided block hash by default -// or using the block height if an empty hash string was provided. If the -// requested block has less than 2 confirmation bchain.ErrBlockNotFound error -// is returned. This rule is in places to guarrantee that only validated block -// details (txs) are saved to the db. Access to the bestBlock height is threadsafe. -func (d *DecredRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { - // Confirm if the block at provided height has at least 2 confirming blocks. - d.mtx.Lock() - if height > d.bestBlock { - bestBlock, err := d.getBestBlock() - if err != nil || height > bestBlock.Result.Height { - // If an error occurred or the current height doesn't have a minimum - // of two confirming blocks (greater than best block), quit. - d.mtx.Unlock() - return nil, bchain.ErrBlockNotFound - } - - d.bestBlock = bestBlock.Result.Height - } - d.mtx.Unlock() // Releases the lock soonest possible - - if hash == "" { - getHashResult, err := d.getBlockHashByHeight(height) - if err != nil { - return nil, err - } - hash = getHashResult.Result - } - - block, err := d.getBlock(hash) - if err != nil { - return nil, err - } - - header := bchain.BlockHeader{ - Hash: block.Result.Hash, - Prev: block.Result.PreviousHash, - Next: block.Result.NextHash, - Height: block.Result.Height, - Confirmations: int(block.Result.Confirmations), - Size: int(block.Result.Size), - Time: block.Result.Time, - } - - bchainBlock := &bchain.Block{BlockHeader: header} - - // Check the current block validity by fetch the next block - nextBlockHashResult, err := d.getBlockHashByHeight(height + 1) - if err != nil { - return nil, err - } - - nextBlock, err := d.getBlock(nextBlockHashResult.Result) - if err != nil { - return nil, err - } - - // If the Votesbits set equals to voteBitYes append the regular transactions. - if nextBlock.Result.VoteBits == voteBitYes { - for _, txID := range block.Result.Tx { - if block.Result.Height == 0 { - continue - } - - tx, err := d.GetTransaction(txID) - if err != nil { - return nil, err - } - - bchainBlock.Txs = append(bchainBlock.Txs, *tx) - } - } - - return bchainBlock, nil -} - -func (d *DecredRPC) getBlock(hash string) (*GetBlockResult, error) { - blockRequest := GenericCmd{ - ID: 1, - Method: "getblock", - Params: []interface{}{hash}, - } - - var block GetBlockResult - if err := d.Call(blockRequest, &block); err != nil { - return nil, err - } - - if block.Error.Message != "" { - return nil, mapToStandardErr("Error fetching block info: %s", block.Error) - } - - return &block, nil -} - -func (d *DecredRPC) decodeRawTransaction(txHex string) (*bchain.Tx, error) { - decodeRawTxRequest := GenericCmd{ - ID: 1, - Method: "decoderawtransaction", - Params: []interface{}{txHex}, - } - - var decodeRawTxResult DecodeRawTransactionResult - if err := d.Call(decodeRawTxRequest, &decodeRawTxResult); err != nil { - return nil, err - } - - if decodeRawTxResult.Error.Message != "" { - return nil, mapToStandardErr("Error decoding raw tx: %s", decodeRawTxResult.Error) - } - - tx := &bchain.Tx{ - Hex: txHex, - Txid: decodeRawTxResult.Result.Txid, - Version: decodeRawTxResult.Result.Version, - LockTime: decodeRawTxResult.Result.Locktime, - } - - // Add block height and block hash info - tx.CoinSpecificData = decodeRawTxResult.Result.TxExtraInfo - - return tx, nil -} - -func (d *DecredRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { - block, err := d.getBlock(hash) - if err != nil { - return nil, err - } - - header := bchain.BlockHeader{ - Hash: block.Result.Hash, - Prev: block.Result.PreviousHash, - Next: block.Result.NextHash, - Height: block.Result.Height, - Confirmations: int(block.Result.Confirmations), - Size: int(block.Result.Size), - Time: int64(block.Result.Time), - } - - bInfo := &bchain.BlockInfo{ - BlockHeader: header, - MerkleRoot: block.Result.MerkleRoot, - Version: block.Result.Version, - Nonce: block.Result.Nonce, - Bits: block.Result.Bits, - Difficulty: common.JSONNumber(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)), - Txids: block.Result.Tx, - } - - return bInfo, nil -} - -// GetTransaction returns a transaction by the transaction ID -func (d *DecredRPC) GetTransaction(txid string) (*bchain.Tx, error) { - r, err := d.getRawTransaction(txid) - if err != nil { - return nil, err - } - - tx, err := d.Parser.ParseTxFromJson(r) - if err != nil { - return nil, errors.Annotatef(err, "txid %v", txid) - } - - return tx, nil -} - -// getRawTransaction returns json as returned by backend, with all coin specific data -func (d *DecredRPC) getRawTransaction(txid string) (json.RawMessage, error) { - if txid == "" { - return nil, bchain.ErrTxidMissing - } - - verbose := 1 - getTxRequest := GenericCmd{ - ID: 1, - Method: "getrawtransaction", - Params: []interface{}{txid, &verbose}, - } - - var getTxResult GetTransactionResult - if err := d.Call(getTxRequest, &getTxResult); err != nil { - return nil, err - } - - if getTxResult.Error.Message != "" { - return nil, mapToStandardErr("Error fetching transaction: %s", getTxResult.Error) - } - - bytes, err := json.Marshal(getTxResult.Result) - if err != nil { - return nil, errors.Annotatef(err, "txid %v", txid) - } - - return json.RawMessage(bytes), nil -} - -// GetTransactionForMempool returns the full tx information identified by the -// provided txid. -func (d *DecredRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { - return d.GetTransaction(txid) -} - -// GetMempoolTransactions returns a slice of regular transactions currently in -// the mempool. The block whose validation is still undecided will have its txs, -// listed like they are still in the mempool till the block is confirmed. -func (d *DecredRPC) GetMempoolTransactions() ([]string, error) { - verbose := false - txType := "regular" - mempoolRequest := GenericCmd{ - ID: 1, - Method: "getrawmempool", - Params: []interface{}{&verbose, &txType}, - } - - var mempool MempoolTxsResult - if err := d.Call(mempoolRequest, &mempool); err != nil { - return []string{}, err - } - - if mempool.Error.Message != "" { - return nil, mapToStandardErr("Error fetching mempool data: %s", mempool.Error) - } - - unvalidatedBlockResult, err := d.getChainBestBlock() - if err != nil { - return nil, err - } - - unvalidatedBlock, err := d.getBlock(unvalidatedBlockResult.Result.Hash) - if err != nil { - return nil, err - } - - mempool.Result = append(mempool.Result, unvalidatedBlock.Result.Tx...) - - return mempool.Result, nil -} - -// GetTransactionSpecific returns the json raw message for the tx identified by -// the provided txid. -func (d *DecredRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) { - return d.getRawTransaction(tx.Txid) -} - -// EstimateSmartFee returns fee estimation -func (d *DecredRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { - estimateSmartFeeRequest := GenericCmd{ - ID: 1, - Method: "estimatesmartfee", - Params: []interface{}{blocks}, - } - - var smartFeeEstimate EstimateSmartFeeResult - if err := d.Call(estimateSmartFeeRequest, &smartFeeEstimate); err != nil { - return *big.NewInt(0), err - } - - if smartFeeEstimate.Error.Message != "" { - return *big.NewInt(0), mapToStandardErr("Error fetching smart fee estimate: %s", smartFeeEstimate.Error) - } - - return *big.NewInt(int64(smartFeeEstimate.Result.FeeRate)), nil -} - -// EstimateFee returns fee estimation. -func (d *DecredRPC) EstimateFee(blocks int) (big.Int, error) { - estimateFeeRequest := GenericCmd{ - ID: 1, - Method: "estimatefee", - Params: []interface{}{blocks}, - } - - var feeEstimate EstimateFeeResult - if err := d.Call(estimateFeeRequest, &feeEstimate); err != nil { - return *big.NewInt(0), err - } - - if feeEstimate.Error.Message != "" { - return *big.NewInt(0), mapToStandardErr("Error fetching fee estimate: %s", feeEstimate.Error) - } - - r, err := d.Parser.AmountToBigInt(feeEstimate.Result) - if err != nil { - return r, err - } - - return r, nil -} - -func (d *DecredRPC) SendRawTransaction(tx string) (string, error) { - sendRawTxRequest := &GenericCmd{ - ID: 1, - Method: "sendrawtransaction", - Params: []interface{}{tx}, - } - - var sendRawTxResult SendRawTransactionResult - err := d.Call(sendRawTxRequest, &sendRawTxResult) - if err != nil { - return "", err - } - - if sendRawTxResult.Error.Message != "" { - return "", mapToStandardErr("error sending raw transaction: %s", sendRawTxResult.Error) - } - - return sendRawTxResult.Result, nil -} - -// Call calls Backend RPC interface, using RPCMarshaler interface to marshall the request -func (d *DecredRPC) Call(req interface{}, res interface{}) error { - httpData, err := json.Marshal(req) - if err != nil { - return err - } - - httpReq, err := http.NewRequest("POST", d.rpcURL, bytes.NewBuffer(httpData)) - if err != nil { - return err - } - httpReq.SetBasicAuth(d.rpcUser, d.rpcPassword) - httpRes, err := d.client.Do(httpReq) - // in some cases the httpRes can contain data even if it returns error - // see http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ - if httpRes != nil { - defer httpRes.Body.Close() - } - if err != nil { - return err - } - - // if server returns HTTP error code it might not return json with response - // handle both cases - if httpRes.StatusCode != 200 { - if err = safeDecodeResponse(httpRes.Body, &res); err != nil { - return errors.Errorf("%v %v", httpRes.Status, err) - } - return nil - } - return safeDecodeResponse(httpRes.Body, &res) -} - -func safeDecodeResponse(body io.ReadCloser, res *interface{}) (err error) { - var data []byte - defer func() { - if r := recover(); r != nil { - glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data)) - debug.PrintStack() - if len(data) > 0 && len(data) < 2048 { - err = errors.Errorf("Error: %v", string(data)) - } else { - err = errors.New("Internal error") - } - } - }() - data, err = ioutil.ReadAll(body) - if err != nil { - return err - } - - error := json.Unmarshal(data, res) - return error -} - -// mapToStandardErr map the dcrd API Message errors to the standard error messages -// supported by trezor. Dcrd errors to be mapped are listed here: -// https://github.com/decred/dcrd/blob/2f5e47371263b996bb99e8dc3484f659309bd83a/dcrjson/jsonerr.go -func mapToStandardErr(customPrefix string, err Error) error { - switch { - case strings.Contains(err.Message, dcrjson.ErrBlockNotFound.Message) || // Block not found - strings.Contains(err.Message, dcrjson.ErrOutOfRange.Message) || // Block number out of range - strings.Contains(err.Message, dcrjson.ErrBestBlockHash.Message): // Error getting best block hash - return bchain.ErrBlockNotFound - case strings.Contains(err.Message, dcrjson.ErrNoTxInfo.Message): // No information available about transaction - return bchain.ErrTxNotFound - case strings.Contains(err.Message, dcrjson.ErrInvalidTxVout.Message): // Output index number (vout) does not exist for transaction - return bchain.ErrTxidMissing - default: - return fmt.Errorf(customPrefix, err.Message) - } -}