diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 7c864b49..d8856b1c 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -24,6 +24,7 @@ import ( "github.com/trezor/blockbook/bchain/coins/digibyte" "github.com/trezor/blockbook/bchain/coins/divi" "github.com/trezor/blockbook/bchain/coins/dogecoin" + "github.com/trezor/blockbook/bchain/coins/ecash" "github.com/trezor/blockbook/bchain/coins/eth" "github.com/trezor/blockbook/bchain/coins/firo" "github.com/trezor/blockbook/bchain/coins/flo" @@ -124,6 +125,7 @@ func init() { BlockChainFactories["Omotenashicoin Testnet"] = omotenashicoin.NewOmotenashiCoinRPC BlockChainFactories["BitZeny"] = bitzeny.NewBitZenyRPC BlockChainFactories["Trezarcoin"] = trezarcoin.NewTrezarcoinRPC + BlockChainFactories["ECash"] = ecash.NewECashRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/ecash/ecashparser.go b/bchain/coins/ecash/ecashparser.go new file mode 100644 index 00000000..e4547ae8 --- /dev/null +++ b/bchain/coins/ecash/ecashparser.go @@ -0,0 +1,189 @@ +package ecash + +import ( + "fmt" + + "github.com/pirk/ecashutil" + "github.com/martinboehm/btcutil" + "github.com/martinboehm/btcutil/chaincfg" + "github.com/martinboehm/btcutil/txscript" + "github.com/pirk/ecashaddr-converter/address" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/btc" +) + +// AddressFormat type is used to specify different formats of address +type AddressFormat = uint8 + +const ( + // Legacy AddressFormat is the same as Bitcoin + Legacy AddressFormat = iota + // CashAddr AddressFormat is new eCash standard + CashAddr +) + +const ( + // MainNetPrefix is CashAddr prefix for mainnet + MainNetPrefix = "ecash:" + // TestNetPrefix is CashAddr prefix for testnet + TestNetPrefix = "ectest:" + // RegTestPrefix is CashAddr prefix for regtest + RegTestPrefix = "ecreg:" +) + +var ( + // MainNetParams are parser parameters for mainnet + MainNetParams chaincfg.Params + // TestNetParams are parser parameters for testnet + TestNetParams chaincfg.Params + // RegtestParams are parser parameters for regtest + RegtestParams chaincfg.Params +) + +func init() { + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = ecashutil.MainnetMagic + + TestNetParams = chaincfg.TestNet3Params + TestNetParams.Net = ecashutil.TestnetMagic + + RegtestParams = chaincfg.RegressionNetParams + RegtestParams.Net = ecashutil.Regtestmagic +} + +// ECashParser handle +type ECashParser struct { + *btc.BitcoinLikeParser + AddressFormat AddressFormat +} + +// NewECashParser returns new ECashParser instance +func NewECashParser(params *chaincfg.Params, c *btc.Configuration) (*ECashParser, error) { + var format AddressFormat + switch c.AddressFormat { + case "": + fallthrough + case "cashaddr": + format = CashAddr + case "legacy": + format = Legacy + default: + return nil, fmt.Errorf("Unknown address format: %s", c.AddressFormat) + } + p := &ECashParser{ + BitcoinLikeParser: btc.NewBitcoinLikeParser(params, c), + AddressFormat: format, + } + p.OutputScriptToAddressesFunc = p.outputScriptToAddresses + p.AmountDecimalPoint = 2 + return p, nil +} + +// GetChainParams contains network parameters for the main eCash network, +// the regression test eCash network, the test eCash network and +// the simulation test eCash network, in this order +func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err == nil { + err = chaincfg.Register(&TestNetParams) + } + if err == nil { + err = chaincfg.Register(&RegtestParams) + } + if err != nil { + panic(err) + } + } + switch chain { + case "test": + return &TestNetParams + case "regtest": + return &RegtestParams + default: + return &MainNetParams + } +} + +// GetAddrDescFromAddress returns internal address representation of given address +func (p *ECashParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { + return p.addressToOutputScript(address) +} + +// addressToOutputScript converts address to ScriptPubKey +func (p *ECashParser) addressToOutputScript(address string) ([]byte, error) { + if isCashAddr(address) { + da, err := ecashutil.DecodeAddress(address, p.Params) + if err != nil { + return nil, err + } + script, err := ecashutil.PayToAddrScript(da) + if err != nil { + return nil, err + } + return script, nil + } + da, err := btcutil.DecodeAddress(address, p.Params) + if err != nil { + return nil, err + } + script, err := txscript.PayToAddrScript(da) + if err != nil { + return nil, err + } + return script, nil +} + +func isCashAddr(addr string) bool { + n := len(addr) + switch { + case n > len(MainNetPrefix) && addr[0:len(MainNetPrefix)] == MainNetPrefix: + return true + case n > len(TestNetPrefix) && addr[0:len(TestNetPrefix)] == TestNetPrefix: + return true + case n > len(RegTestPrefix) && addr[0:len(RegTestPrefix)] == RegTestPrefix: + return true + } + + return false +} + +// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses +func (p *ECashParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { + // convert possible P2PK script to P2PK, which ecashutil can process + var err error + script, err = txscript.ConvertP2PKtoP2PKH(p.Params.Base58CksumHasher, script) + if err != nil { + return nil, false, err + } + a, err := ecashutil.ExtractPkScriptAddrs(script, p.Params) + if err != nil { + // do not return unknown script type error as error + if err.Error() == "unknown script type" { + // try OP_RETURN script + or := p.TryParseOPReturn(script) + if or != "" { + return []string{or}, false, nil + } + return []string{}, false, nil + } + return nil, false, err + } + // EncodeAddress returns CashAddr address + addr := a.EncodeAddress() + if p.AddressFormat == Legacy { + da, err := address.NewFromString(addr) + if err != nil { + return nil, false, err + } + ca, err := da.Legacy() + if err != nil { + return nil, false, err + } + addr, err = ca.Encode() + if err != nil { + return nil, false, err + } + } + return []string{addr}, len(addr) > 0, nil +} diff --git a/bchain/coins/ecash/ecashparser_test.go b/bchain/coins/ecash/ecashparser_test.go new file mode 100644 index 00000000..218299bd --- /dev/null +++ b/bchain/coins/ecash/ecashparser_test.go @@ -0,0 +1,353 @@ +//go:build unittest + +package ecash + +import ( + "encoding/hex" + "math/big" + "os" + "reflect" + "testing" + + "github.com/martinboehm/btcutil/chaincfg" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/btc" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +func Test_GetAddrDescFromAddress(t *testing.T) { + mainParserCashAddr, mainParserLegacy, testParserCashAddr, _ := setupParsers(t) + tests := []struct { + name string + parser *ECashParser + addresses []string + hex string + wantErr bool + }{ + { + name: "test-P2PKH-0", + parser: testParserCashAddr, + addresses: []string{"mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr"}, + hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac", + wantErr: false, + }, + { + name: "test-P2PKH-1", + parser: testParserCashAddr, + addresses: []string{"ectest:qp86jfla8084048rckpv85ht90falr050s59h7rejp"}, + hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac", + wantErr: false, + }, + { + name: "main-P2PKH-0", + parser: mainParserLegacy, + addresses: []string{"129HiRqekqPVucKy2M8zsqvafGgKypciPp"}, + hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac", + wantErr: false, + }, + { + name: "main-P2PKH-1", + parser: mainParserCashAddr, + addresses: []string{"ecash:qqxgjelx8qk85t9xfk8g2zlunxmhxms6p5dtfghs9r"}, + hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac", + wantErr: false, + }, + { + name: "main-P2SH-0", + parser: mainParserCashAddr, + addresses: []string{"3EBEFWPtDYWCNszQ7etoqtWmmygccayLiH"}, + hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787", + wantErr: false, + }, + { + name: "main-P2SH-1", + parser: mainParserLegacy, + addresses: []string{"ecash:pzy0wuj9pjps5v8dmlwq32fatu4wrgcwzuyf5lgn3q"}, + hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.parser.GetAddrDescFromAddress(tt.addresses[0]) + if (err != nil) != tt.wantErr { + t.Errorf("%v", tt.addresses[0]) + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.hex) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.hex) + } + }) + } +} + +func Test_GetAddressesFromAddrDesc(t *testing.T) { + mainParserCashAddr, mainParserLegacy, testParserCashAddr, testParserLegacy := setupParsers(t) + tests := []struct { + name string + parser *ECashParser + addresses []string + searchable bool + hex string + wantErr bool + }{ + { + name: "test-P2PKH-0", + parser: testParserLegacy, + addresses: []string{"mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr"}, + searchable: true, + hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac", + + wantErr: false, + }, + { + name: "test-P2PKH-1", + parser: testParserCashAddr, + addresses: []string{"ectest:qp86jfla8084048rckpv85ht90falr050s59h7rejp"}, + searchable: true, + hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac", + wantErr: false, + }, + { + name: "main-P2PKH-0", + parser: mainParserLegacy, + addresses: []string{"129HiRqekqPVucKy2M8zsqvafGgKypciPp"}, + searchable: true, + hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac", + wantErr: false, + }, + { + name: "main-P2PKH-0", + parser: mainParserCashAddr, + addresses: []string{"ecash:qqxgjelx8qk85t9xfk8g2zlunxmhxms6p5dtfghs9r"}, + searchable: true, + hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac", + wantErr: false, + }, + { + name: "main-P2SH-0", + parser: mainParserLegacy, + addresses: []string{"3EBEFWPtDYWCNszQ7etoqtWmmygccayLiH"}, + searchable: true, + hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787", + wantErr: false, + }, + { + name: "main-P2SH-1", + parser: mainParserCashAddr, + addresses: []string{"ecash:pzy0wuj9pjps5v8dmlwq32fatu4wrgcwzuyf5lgn3q"}, + searchable: true, + hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787", + wantErr: false, + }, + { + name: "main-P2PK", + parser: mainParserCashAddr, + addresses: []string{"ecash:qqr95pwp0w5jqnh9vcjl4qm4x45atr0er587pw66cr"}, + searchable: true, + hex: "2103db3c3977c5165058bf38c46f72d32f4e872112dbafc13083a948676165cd1603ac", + wantErr: false, + }, + { + name: "OP_RETURN ascii", + parser: mainParserCashAddr, + addresses: []string{"OP_RETURN (ahoj)"}, + searchable: false, + hex: "6a0461686f6a", + wantErr: false, + }, + { + name: "OP_RETURN hex", + parser: mainParserCashAddr, + addresses: []string{"OP_RETURN 2020f1686f6a20"}, + searchable: false, + hex: "6a072020f1686f6a20", + wantErr: false, + }, + { + name: "empty", + parser: mainParserCashAddr, + addresses: []string{}, + searchable: false, + hex: "", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, _ := hex.DecodeString(tt.hex) + got, got2, err := tt.parser.GetAddressesFromAddrDesc(b) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.addresses) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.addresses) + } + if !reflect.DeepEqual(got2, tt.searchable) { + t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.searchable) + } + }) + } +} + +var ( + testTx1, testTx2 bchain.Tx + testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700" + testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000" +) + +func setupParsers(t *testing.T) (mainParserCashAddr, mainParserLegacy, testParserCashAddr, testParserLegacy *ECashParser) { + parser1, err := NewECashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "cashaddr"}) + if err != nil { + t.Fatalf("NewECashParser() error = %v", err) + } + parser2, err := NewECashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"}) + if err != nil { + t.Fatalf("NewECashParser() error = %v", err) + } + parser3, err := NewECashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "cashaddr"}) + if err != nil { + t.Fatalf("NewECashParser() error = %v", err) + } + parser4, err := NewECashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"}) + if err != nil { + t.Fatalf("NewECashParser() error = %v", err) + } + return parser1, parser2, parser3, parser4 +} + +func init() { + + testTx1 = bchain.Tx{ + Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700", + Blocktime: 1519053802, + Txid: "056e3d82e5ffd0e915fb9b62797d76263508c34fe3e5dbed30dd3e943930f204", + LockTime: 512115, + Version: 1, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80", + }, + Txid: "425fed43ba74e9205875eb934d5bcf7bf338f146f70d4002d94bf5cbc9229a7f", + Vout: 4, + Sequence: 4294967294, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(38812), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87", + Addresses: []string{ + "ecash:pps5f4tu3tl5sjfvnhaeznsjpvst44eddu3yvcmmzj", + }, + }, + }, + }, + } + + testTx2 = bchain.Tx{ + Hex: "010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000", + Blocktime: 1235678901, + Txid: "474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7", + LockTime: 0, + Version: 1, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "160014550da1f5d25a9dae2eafd6902b4194c4c6500af6", + }, + Txid: "c13e32a4428e31f85d7aee4ec7344504b12e72aaffcbde0160200d2ac7f0649d", + Vout: 0, + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(10000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887", + Addresses: []string{ + "ectest:prxkdrtcrm8xqrh6fvjqfhy3l5nt3w9wmq7a4ujkec", + }, + }, + }, + { + ValueSat: *big.NewInt(920081157), + N: 1, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87", + Addresses: []string{ + "ectest:pqjxv4dah42v0erh6r4zxa0gdcxm9w8cpg55qt4qtq", + }, + }, + }, + }, + } +} + +func Test_UnpackTx(t *testing.T) { + mainParser, _, testParser, _ := setupParsers(t) + + type args struct { + packedTx string + parser *ECashParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "ecash-1", + args: args{ + packedTx: testTxPacked1, + parser: mainParser, + }, + want: &testTx1, + want1: 123456, + wantErr: false, + }, + { + name: "testnet-1", + args: args{ + packedTx: testTxPacked2, + parser: testParser, + }, + want: &testTx2, + want1: 510234, + 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) + } + }) + } +} diff --git a/bchain/coins/ecash/ecashrpc.go b/bchain/coins/ecash/ecashrpc.go new file mode 100644 index 00000000..a6be662b --- /dev/null +++ b/bchain/coins/ecash/ecashrpc.go @@ -0,0 +1,207 @@ +package ecash + +import ( + "encoding/hex" + "encoding/json" + "math/big" + + "github.com/golang/glog" + "github.com/juju/errors" + "github.com/pirk/ecashutil" + "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/bchain/coins/btc" +) + +// ECashRPC is an interface to JSON-RPC bitcoind service. +type ECashRPC struct { + *btc.BitcoinRPC +} + +// NewECashRPC returns new ECashRPC instance. +func NewECashRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &ECashRPC{ + b.(*btc.BitcoinRPC), + } + s.ChainConfig.SupportsEstimateSmartFee = false + + return s, nil +} + +// Initialize initializes ECashRPC instance. +func (b *ECashRPC) Initialize() error { + ci, err := b.GetChainInfo() + if err != nil { + return err + } + chainName := ci.Chain + + params := GetChainParams(chainName) + + // always create parser + b.Parser, err = NewECashParser(params, b.ChainConfig) + + if err != nil { + return err + } + + // parameters for getInfo request + if params.Net == ecashutil.MainnetMagic { + b.Testnet = false + b.Network = "livenet" + } else { + b.Testnet = true + b.Network = "testnet" + } + + glog.Info("rpc: block chain ", params.Name) + + return nil +} + +// getblock + +type cmdGetBlock struct { + Method string `json:"method"` + Params struct { + BlockHash string `json:"blockhash"` + Verbose bool `json:"verbose"` + } `json:"params"` +} + +// estimatesmartfee + +type cmdEstimateSmartFee struct { + Method string `json:"method"` + Params struct { + Blocks int `json:"nblocks"` + } `json:"params"` +} + +// GetBlock returns block with given hash. +func (b *ECashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { + var err error + if hash == "" && height > 0 { + hash, err = b.GetBlockHash(height) + if err != nil { + return nil, err + } + } + header, err := b.GetBlockHeader(hash) + if err != nil { + return nil, err + } + data, err := b.GetBlockBytes(hash) + if err != nil { + return nil, err + } + block, err := b.Parser.ParseBlock(data) + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + // size is not returned by GetBlockHeader and would be overwritten + size := block.Size + block.BlockHeader = *header + block.Size = size + return block, nil +} + +// GetBlockRaw returns block with given hash as bytes. +func (b *ECashRPC) GetBlockRaw(hash string) (string, error) { + glog.V(1).Info("rpc: getblock (verbose=0) ", hash) + + res := btc.ResGetBlockRaw{} + req := cmdGetBlock{Method: "getblock"} + req.Params.BlockHash = hash + req.Params.Verbose = false + err := b.Call(&req, &res) + + if err != nil { + return "", errors.Annotatef(err, "hash %v", hash) + } + if res.Error != nil { + if isErrBlockNotFound(res.Error) { + return "", bchain.ErrBlockNotFound + } + return "", errors.Annotatef(res.Error, "hash %v", hash) + } + return res.Result, nil +} + +// GetBlockBytes returns block with given hash as bytes +func (b *ECashRPC) GetBlockBytes(hash string) ([]byte, error) { + block, err := b.GetBlockRaw(hash) + if err != nil { + return nil, err + } + return hex.DecodeString(block) +} + +// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids +func (b *ECashRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { + glog.V(1).Info("rpc: getblock (verbosity=1) ", hash) + + res := btc.ResGetBlockInfo{} + req := cmdGetBlock{Method: "getblock"} + req.Params.BlockHash = hash + req.Params.Verbose = true + err := b.Call(&req, &res) + + if err != nil { + return nil, errors.Annotatef(err, "hash %v", hash) + } + if res.Error != nil { + if isErrBlockNotFound(res.Error) { + return nil, bchain.ErrBlockNotFound + } + return nil, errors.Annotatef(res.Error, "hash %v", hash) + } + return &res.Result, nil +} + +// GetBlockFull returns block with given hash. +func (b *ECashRPC) GetBlockFull(hash string) (*bchain.Block, error) { + return nil, errors.New("Not implemented") +} + +func isErrBlockNotFound(err *bchain.RPCError) bool { + return err.Message == "Block not found" || + err.Message == "Block height out of range" +} + +// EstimateFee returns fee estimation +func (b *ECashRPC) EstimateFee(blocks int) (big.Int, error) { + glog.V(1).Info("rpc: estimatefee ", blocks) + + res := btc.ResEstimateFee{} + req := struct { + Method string `json:"method"` + }{ + Method: "estimatefee", + } + + err := b.Call(&req, &res) + + var r big.Int + if err != nil { + return r, err + } + if res.Error != nil { + return r, res.Error + } + r, err = b.Parser.AmountToBigInt(res.Result) + if err != nil { + return r, err + } + return r, nil +} + +// EstimateSmartFee returns fee estimation +func (b *ECashRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { + // EstimateSmartFee is not supported by ecash + return b.EstimateFee(blocks) +} diff --git a/configs/coins/ecash.json b/configs/coins/ecash.json new file mode 100644 index 00000000..af40823e --- /dev/null +++ b/configs/coins/ecash.json @@ -0,0 +1,69 @@ +{ + "coin": { + "name": "ECash", + "shortcut": "XEC", + "label": "eCash", + "alias": "ecash" + }, + "ports": { + "backend_rpc": 8097, + "backend_message_queue": 38397, + "blockbook_internal": 9097, + "blockbook_public": 9197 + }, + "ipc": { + "rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}", + "rpc_user": "rpc", + "rpc_pass": "rpc", + "rpc_timeout": 25, + "message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}" + }, + "backend": { + "package_name": "backend-ecash", + "package_revision": "satoshilabs-1", + "system_user": "ecash", + "version": "0.25.1", + "binary_url": "https://download.bitcoinabc.org/0.25.1/linux/bitcoin-abc-0.25.1-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "295183578ce67444fd6a504d2dcb85d07345454881ba4db5f52d82dd3a659bed", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/bitcoin-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/bitcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid", + "logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log", + "postinst_script_template": "", + "service_type": "forking", + "service_additional_params_template": "", + "protect_memory": true, + "mainnet": true, + "server_config_file": "bcash.conf", + "client_config_file": "bitcoin_like_client.conf" + }, + "blockbook": { + "package_name": "blockbook-ecash", + "system_user": "blockbook-ecash", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "", + "block_chain": { + "parse": true, + "subversion": "/Bitcoin ABC:0.24.9(EB32.0)/", + "address_format": "cashaddr", + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "xpub_magic": 76067358, + "slip44": 899, + "additional_params": { + "fiat_rates": "coingecko", + "fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ecash\", \"periodSeconds\": 60}" + } + } + }, + "meta": { + "package_maintainer": "eCash", + "package_maintainer_email": "contact@e.cash" + } +} diff --git a/docs/ports.md b/docs/ports.md index 1150f839..e9a5dd1a 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -45,6 +45,7 @@ | Omotenashicoin | 9094 | 9194 | 8094 | 38394 | | BitZeny | 9095 | 9195 | 8095 | 38395 | | Trezarcoin | 9096 | 9196 | 8096 | 38396 | +| eCash | 9097 | 9197 | 8097 | 38397 | | Bitcoin Signet | 19020 | 19120 | 18020 | 48320 | | Bitcoin Regtest | 19021 | 19121 | 18021 | 48321 | | Ethereum Goerli | 19026 | 19126 | 18026 | 48326 p2p | @@ -55,7 +56,7 @@ | Litecoin Testnet | 19034 | 19134 | 18034 | 48334 | | Bitcoin Gold Testnet | 19035 | 19135 | 18035 | 48335 | | Ethereum Ropsten | 19036 | 19136 | 18036 | 48336 p2p | -| Dogecoin Testnet | 19038 | 19138 | 18038 | 48338 +| Dogecoin Testnet | 19038 | 19138 | 18038 | 48338 | | Vertcoin Testnet | 19040 | 19140 | 18040 | 48340 | | Monacoin Testnet | 19041 | 19141 | 18041 | 48341 | | DigiByte Testnet | 19042 | 19142 | 18042 | 48342 | diff --git a/go.mod b/go.mod index 06e80140..eb384ddb 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,8 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pebbe/zmq4 v1.2.1 + github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 + github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e github.com/prometheus/client_golang v1.8.0 github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 @@ -41,6 +43,7 @@ require ( ) require ( + github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index 4e924ca8..dc27b61e 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c h1:8bY github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c/go.mod h1:DwgC62sAn4RgH4L+O8REgcE7f0XplHPNeRYFy+ffy1M= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:B11BryeZQ1LrAzzM0lCpblwleB7SyxPfvN2AsNbyvQc= +github.com/PiRK/cashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:+39XiGr9m9TPY49sG4XIH5CVaRxHGFWT0U4MOY6dy3o= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= @@ -499,6 +501,10 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29 h1:awILOeL107zIYvPB1zhkz6ZTp0AaMpLGMoV16DMairA= +github.com/pirk/ecashaddr-converter v0.0.0-20220121162910-c6cb45163b29/go.mod h1:ATZjpmb9u55Kcrd5M/ca/40H73BZLhduMzCmGwpfWw0= +github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e h1:WrnL52yXO0jNpHC7UbthJl9mnHPHY7bW3xzmWIuWzh8= +github.com/pirk/ecashutil v0.0.0-20220124103933-d37f548d249e/go.mod h1:y/B3gomTdd1s23RvcBij/X738fcTobeupT30EhV6nPE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/tests/rpc/testdata/ecash.json b/tests/rpc/testdata/ecash.json new file mode 100644 index 00000000..a3bfde9c --- /dev/null +++ b/tests/rpc/testdata/ecash.json @@ -0,0 +1,92 @@ +{ + "blockHeight": 723621, + "blockHash": "0000000000000000016ee40672952698db9a72054014077e8065d0ed3843be38", + "blockTime": 1643027028, + "blockTxs": [ + "79a716de966042dedb1294e662c4340fcc121f31fc67d4f37734b6838f7309c4", + "1568e2fcf08ad3eaa0a004f2cb8ca2456ea4c624652843a04f727d984c1c5ea6", + "6ddcf32bdc26f12200a3cc41d9d660bf0afc39ee3f1c94706e202373bcd782d6", + "9057bdf099646b3f28498dd33302a28db5c3556e80593d506f45dfa240206c4b", + "d19abd0ea6b2dfa745c1a194d87b0d621f3d55aa7e1bc53d9d2d7fe44f0e1226", + "d864b206afccff0a37ce6a1bb697a6c6217c537760b291d832ad526a7d94b270", + "ff7cdcf1f3bd5bf7551133309cc466d32caa0cb11860e2ffea5bee8372240b9f" + ], + "txDetails": { + "d864b206afccff0a37ce6a1bb697a6c6217c537760b291d832ad526a7d94b270": { + "hex": "0200000001e37114f0c54cb6c6eac13140f38f356784082a289812540e8dc2d5fa81321bbf010000006a473044022064d48cd0549b3c64e4fe012b2a6303856ba44a711c4aa39fd991e071b1e9e46c0220485fde649603a0e1f963d106aa6a34bd2a96756397cee5475ff451385f17f1f441210244810e359356866bfd28729dcc252314f760d6b46c5b430a0f47d805fab44507ffffffff023447de31010000001976a9146e233ca966c8616981bb556999ebe48c39e742a988acb838740f040000001976a914e330f03a2bf6afc5a0e5819f28fde1671d017e8a88ac00000000", + "txid": "d864b206afccff0a37ce6a1bb697a6c6217c537760b291d832ad526a7d94b270", + "blocktime":1643027028, + "time":1643027028, + "locktime": 0, + "version": 2, + "vin": [ + { + "txid": "bf1b3281fad5c28d0e541298282a088467358ff34031c1eac6b64cc5f01471e3", + "vout": 1, + "sequence": 4294967295, + "scriptSig": { + "hex": "473044022064d48cd0549b3c64e4fe012b2a6303856ba44a711c4aa39fd991e071b1e9e46c0220485fde649603a0e1f963d106aa6a34bd2a96756397cee5475ff451385f17f1f441210244810e359356866bfd28729dcc252314f760d6b46c5b430a0f47d805fab44507" + } + } + ], + "vout": [ + { + "value": 51316181, + "n": 0, + "scriptPubKey": { + "hex": "76a9146e233ca966c8616981bb556999ebe48c39e742a988ac" + } + }, + { + "value": 174391441.2, + "n": 1, + "scriptPubKey": { + "hex": "76a914e330f03a2bf6afc5a0e5819f28fde1671d017e8a88ac" + } + } + ] + }, + "ff7cdcf1f3bd5bf7551133309cc466d32caa0cb11860e2ffea5bee8372240b9f": { + "hex": "020000000234a3e9790ac787d2c7276a8e952f25ef76e8c048a56a8effc804363c7708e2e7000000006a4730440220305bcd24adaa24843558fbaed56db3c64d3afab6be533ce92736dcc606de7ade022067f1469cdf17395d2cc30e98b5cc9d448c803ae3d4c121eacefca5698eec5fe5412103398deb450f13c10234d6c5b74d2cdf8dc7c3ce46731815cba3d845facf44e037feffffff67260c8e7f40362980cf491831c0a1753cc263cf9bdad1f689eb2570be592106010000006a47304402203ae38828d2568f1fecd1f2b19e4220e318d0e70c48bf0282538628a4ddea8bdc02200358499ce2e077ae699ecf4fd91bf5878774d1f935eac730d55a19921847e874412103fc530a380acfed725bec8ebc2c686104f0be4e5ba0e64bc8d78bd57580ea3fa8feffffff02a8391400000000001976a914fd14b9272106691a47b8a9c0b661355ffd3f4eaa88acab2a070f000000001976a9145994e1c4b207d0503525e943702ffd820b332f5288aca40a0b00", + "txid": "ff7cdcf1f3bd5bf7551133309cc466d32caa0cb11860e2ffea5bee8372240b9f", + "blocktime":1643027028, + "time":1643027028, + "locktime": 723620, + "version": 2, + "vin": [ + { + "txid": "e7e208773c3604c8ff8e6aa548c0e876ef252f958e6a27c7d287c70a79e9a334", + "vout": 0, + "sequence": 4294967294, + "scriptSig": { + "hex": "4730440220305bcd24adaa24843558fbaed56db3c64d3afab6be533ce92736dcc606de7ade022067f1469cdf17395d2cc30e98b5cc9d448c803ae3d4c121eacefca5698eec5fe5412103398deb450f13c10234d6c5b74d2cdf8dc7c3ce46731815cba3d845facf44e037" + } + }, + { + "txid": "062159be7025eb89f6d1da9bcf63c23c75a1c0311849cf802936407f8e0c2667", + "vout": 1, + "sequence": 4294967294, + "scriptSig": { + "hex": "47304402203ae38828d2568f1fecd1f2b19e4220e318d0e70c48bf0282538628a4ddea8bdc02200358499ce2e077ae699ecf4fd91bf5878774d1f935eac730d55a19921847e874412103fc530a380acfed725bec8ebc2c686104f0be4e5ba0e64bc8d78bd57580ea3fa8" + } + } + ], + "vout": [ + { + "value": 13254.8, + "n": 0, + "scriptPubKey": { + "hex": "76a914fd14b9272106691a47b8a9c0b661355ffd3f4eaa88ac" + } + }, + { + "value": 2521279.15, + "n": 1, + "scriptPubKey": { + "hex": "76a9145994e1c4b207d0503525e943702ffd820b332f5288ac" + } + } + ] + } + } +} diff --git a/tests/sync/testdata/ecash.json b/tests/sync/testdata/ecash.json new file mode 100644 index 00000000..8907367b --- /dev/null +++ b/tests/sync/testdata/ecash.json @@ -0,0 +1,247 @@ +{ + "connectBlocks": { + "syncRanges": [ + {"lower": 550716, "upper": 550736} + ], + "blocks": { + "550736": { + "height": 550736, + "hash": "00000000000000000181e327d1165480db82d915d46b9d39232526309072e014", + "noTxs": 6, + "txDetails": [ + { + "hex": "020000000118a6b44b981cab21e486ee56c8bc74c58d907649842a0088bb0a80f2faa22263010000006b483045022100dea77aa13dcdd457c7bad51e7ee310046a8f8e3c370c25217010db57a60335ca022024937a8c0c179fc8ef9923694b2f7e276904ae859010dd12b46e356ce6d76147412102619a1e932ef161c58f0d8c24fbf6b5b709f8e996aa8caa7a26130240a3416176feffffff0582efe71f000000001976a914ab4c29c2e271a5378ffedfdb1a4bedd4cd571cde88ace0dced27000000001976a914cf5d4c7955cdf8bb3e516db1eb76efe538a448a588aca0910203000000001976a9148d0e5abd1c82e99670d332582ba20d423fa4323288acc4ba0800000000001976a91419f529fccd87142620546e1bbefdbc30a2a93a6188ac18841300000000001976a9149c90ec9c987a451961ea29a29e265c4db0466a2788ac4e670800", + "txid": "c100893fc0fb74c38277978a78433aa92df5473d94e1651962fff0e55c5762fc", + "version": 2, + "vin": [ + { + "txid": "6322a2faf2800abb88002a844976908dc574bcc856ee86e421ab1c984bb4a618", + "vout": 1, + "scriptSig": { + "hex": "483045022100dea77aa13dcdd457c7bad51e7ee310046a8f8e3c370c25217010db57a60335ca022024937a8c0c179fc8ef9923694b2f7e276904ae859010dd12b46e356ce6d76147412102619a1e932ef161c58f0d8c24fbf6b5b709f8e996aa8caa7a26130240a3416176" + }, + "sequence": 4294967294 + } + ], + "vout": [ + { + "value": 5352938.26, + "n": 0, + "scriptPubKey": { + "hex": "76a914ab4c29c2e271a5378ffedfdb1a4bedd4cd571cde88ac" + } + }, + { + "value": 6699000.00, + "n": 1, + "scriptPubKey": { + "hex": "76a914cf5d4c7955cdf8bb3e516db1eb76efe538a448a588ac" + } + }, + { + "value": 505000.00, + "n": 2, + "scriptPubKey": { + "hex": "76a9148d0e5abd1c82e99670d332582ba20d423fa4323288ac" + } + }, + { + "value": 5721.00, + "n": 3, + "scriptPubKey": { + "hex": "76a91419f529fccd87142620546e1bbefdbc30a2a93a6188ac" + } + }, + { + "value": 12790.00, + "n": 4, + "scriptPubKey": { + "hex": "76a9149c90ec9c987a451961ea29a29e265c4db0466a2788ac" + } + } + ], + "time": 1538669466, + "blocktime": 1538669466 + }, + { + "hex": "020000000e34620707fc49a213e57c18880360c465c51b3e8709e26829229bf6e0499cf1d9010000006a473044022057269d92e28fc3956075172173ef67a1f8e8e81bfe3a94e139373b5d8bfbace402202e6d80dcf22ce50ced02df68161c8af3f95476c498cc36f0b8450934fcf6387441210209bd24ca51b659d8950af12363513af576e776131eaad2baf72cf2d5ed2db4d0feffffff6991b494656029ed424f149c7fce99089a02dd534cc4b657312ac61e07691845010000006a4730440220773b5faf496664b9da746853148b9f4e65d7901b9e775f0d304f60bb45b6bce702207c725522f19c2ab1e40012ba76443368005ab45e50faff706b8a9f4e4f1ade294121029f5115229faf57ec14b710bbbb71cea99663f1987b0642095d957f4365ae8189feffffff922caf594105198fef72665241286caceba276ccd4f25de230d0154d01d20bf4010000006a4730440220232a37b6f939395d4acb5ea6deb95894833a08eceb7b47cd26f54f9b6ea0c0630220284837d62429c2d919e5371e461436a8b2f8364e5a391d2620c3da943f6da7734121034032e450981630d01d6f7f67b2f0984a4f8b447b8c17da16d448f3b911fb6e31feffffff1b663b64fa1d070e1c557bf51f9f9bb3ff1d57e7bb6699487fe6465e6124d357000000006b483045022100d1b020e2d6b54ee90bada53872669921b5dc532863a0d4195958248ac74e7233022025f89661164946dc4318b7f2a21aa034847cee15b1597ece4399931f8b8ebe724121035fa81b02f2a72f9e8a35ed2f4fc1913a3aa74a1991d4b125e895168f015fe399feffffff5b3306f9d512292ae2644af096ab1529b6caff7cc1a0d233fc3a7d3850165073010000006a47304402202014a66e2a6c27f9eda4e87f1495764995507e9b7379f7cea58ba8c344e1ab770220446218231005a000e8f20abfba75e964ebe4cef5d3e4f60bc6be46a548ad7c71412102c384cca7a64cb48f7dc3a8ded4ee0ac07146108799fcb6020c07a7b166661699feffffff63049d8a5b57abf73f080d38d28ea14d159589993910ffa1c1d4a566fe0a4bfe010000006b483045022100b3584a86c1dc3481af9d9a75ebc421d78c35f2818c4b4366d8be383732d7aa4c022029c09ec4c4fc181b3cbebe4634a4fded19288559972e551d8382a4140f44a1794121024c066efd26424849d0a15910d0a49ced61cd7b2c311d50ac0f95bd5a9d609a83feffffff27e91003f5c59999d0c78e278b6eca1ee1a6fed82bb60e92a5946443cd73a3ba010000006a47304402200b88b727cf75ade32a1cf6915bc6cfa1e9102813c4d69db5fac613170a205f22022016bebf2825c288d24732772fff1376936502dc79789ee5cf3879e2bf2564fe58412102a5e690c5f8218ac5521a0ea3b1f0e59d6ba1c17d938659affa585a9d81799cbafeffffffa37670dd96e4814d30487edb4b647d48dcd2fd81ab3f1f9f3c669ef93fa3f9fe010000006a473044022064f2ed5f821b0e98fb556eadf5a5636e36e98f965796baf2ff0ad0080f4e68e7022010891d52d74f1dc55d861ad4898ec18318345ed2b358c698213944b381ab900e412102beb30e14403c4a03277d559019b60cd8fb877872896565fc0cdebb843797accdfeffffff1c7bb2fa455084c1447914819004365df5500a2fc32d9e6250fd74c795082590010000006b483045022100a73951625dc0635ec373851730b5d268c60b36fcbbcda85228452176024a1db30220332ddb3940c0d05c215a41bb751a77d1969e240a3dd3612e7b2ec49913907761412103cf972121caef0095019ae80fc3a91cc3aa3590f57ad5751319b1a5bde03549bcfeffffff50540eee49c962f60790966073658688b74a6fc1c7dbd846f114405143ab9267010000006b483045022100d2487d304222d511d3c2645315f4821486f250c2edba85c1aec5ce5604ab6f0b0220275d210b5c97c90f275be2193413792511bfaa6d3338ea418fc01cbba42f96334121037cff19a246685228edd8df2c7dd5ed1efa1d779e8ce9ae99274c20912922bd59feffffff999e5c78b01055b92f36e277c8fda3f0253cecf8b9a7640fb85b0ebd79b58dc5000000006a473044022010e90626ae890f8d9ac5be7b780f93269794f897732d4ca78b45797994b88ab402200fe4f787e4b5cf66a29b7a8f0a07b6a85d7a9ea18b89f216e3211b53641032bd4121030803961c4606e6cacba6b1ddd615a35a3abaaec945f60245da9e777d23975fdcfefffffff27534af3a65ee1b60692cead34519e798bf01bd108a6e5ec6f6422bfe605820010000006b483045022100ae8a1a285a324544e588215e878c02d0db4853bb4064dbb7babde6f9d403738202201caab0d5afdbda4ee6a27f9e10fec2045717ac63b55ac647a5c2a5440c7c00c6412102ade72a40797ce9bf5662169d3317c1bd31455b086ecddbdc1bcb4cdf8ba6fc89feffffff40c16f37c67b80f53cf6fd906c3c53922bc21c050d560464258e45993bc20e92000000006a47304402205acc6b45d4c3e330a45dcd4ef69840edcd2b31da247151e741c0468f8e65ae98022032da991edacd2b5437a44bd2463a5f93ef1b32f3467d5748ceed5c70aa5eac1041210221099010e3629434851ceb0c5992388b815e9290b78aef55de30a08f73c29716feffffff4b7ebd29cb82e8cef3f7ebb275edf7f8604ae67821f137a4a3a309920431bd24000000006a47304402207a0f2ad56df4bb879fd645c2cf07d034079ff0574cbe54bf51234968c19f526802207b25da2a8304a4e1f8d4a78f0c7902e9eef647a856c6f5193bed10d1fa26b338412102aa396f2b3bdbc39134b4e1f52d02ec83d0cee811d35ae78a539c8428781f3876feffffff02dea0446d000000001976a914d1e5cdc2076e9e4890d5f5d0356dc4543b86e85d88ac033b0f00000000001976a91418195caf282889538a9ed9958bcc47a6bf7326b388ac00000000", + "txid": "28abbf7f1360142b11534051aac3fb05a26815cef4a8a726feb761a6e24bdf34", + "version": 2, + "vin": [ + { + "txid": "d9f19c49e0f69b222968e209873e1bc565c4600388187ce513a249fc07076234", + "vout": 1, + "scriptSig": { + "hex": "473044022057269d92e28fc3956075172173ef67a1f8e8e81bfe3a94e139373b5d8bfbace402202e6d80dcf22ce50ced02df68161c8af3f95476c498cc36f0b8450934fcf6387441210209bd24ca51b659d8950af12363513af576e776131eaad2baf72cf2d5ed2db4d0" + }, + "sequence": 4294967294 + }, + { + "txid": "451869071ec62a3157b6c44c53dd029a0899ce7f9c144f42ed29606594b49169", + "vout": 1, + "scriptSig": { + "hex": "4730440220773b5faf496664b9da746853148b9f4e65d7901b9e775f0d304f60bb45b6bce702207c725522f19c2ab1e40012ba76443368005ab45e50faff706b8a9f4e4f1ade294121029f5115229faf57ec14b710bbbb71cea99663f1987b0642095d957f4365ae8189" + }, + "sequence": 4294967294 + }, + { + "txid": "f40bd2014d15d030e25df2d4cc76a2ebac6c2841526672ef8f19054159af2c92", + "vout": 1, + "scriptSig": { + "hex": "4730440220232a37b6f939395d4acb5ea6deb95894833a08eceb7b47cd26f54f9b6ea0c0630220284837d62429c2d919e5371e461436a8b2f8364e5a391d2620c3da943f6da7734121034032e450981630d01d6f7f67b2f0984a4f8b447b8c17da16d448f3b911fb6e31" + }, + "sequence": 4294967294 + }, + { + "txid": "57d324615e46e67f489966bbe7571dffb39b9f1ff57b551c0e071dfa643b661b", + "vout": 0, + "scriptSig": { + "hex": "483045022100d1b020e2d6b54ee90bada53872669921b5dc532863a0d4195958248ac74e7233022025f89661164946dc4318b7f2a21aa034847cee15b1597ece4399931f8b8ebe724121035fa81b02f2a72f9e8a35ed2f4fc1913a3aa74a1991d4b125e895168f015fe399" + }, + "sequence": 4294967294 + }, + { + "txid": "73501650387d3afc33d2a0c17cffcab62915ab96f04a64e22a2912d5f906335b", + "vout": 1, + "scriptSig": { + "hex": "47304402202014a66e2a6c27f9eda4e87f1495764995507e9b7379f7cea58ba8c344e1ab770220446218231005a000e8f20abfba75e964ebe4cef5d3e4f60bc6be46a548ad7c71412102c384cca7a64cb48f7dc3a8ded4ee0ac07146108799fcb6020c07a7b166661699" + }, + "sequence": 4294967294 + }, + { + "txid": "fe4b0afe66a5d4c1a1ff1039998995154da18ed2380d083ff7ab575b8a9d0463", + "vout": 1, + "scriptSig": { + "hex": "483045022100b3584a86c1dc3481af9d9a75ebc421d78c35f2818c4b4366d8be383732d7aa4c022029c09ec4c4fc181b3cbebe4634a4fded19288559972e551d8382a4140f44a1794121024c066efd26424849d0a15910d0a49ced61cd7b2c311d50ac0f95bd5a9d609a83" + }, + "sequence": 4294967294 + }, + { + "txid": "baa373cd436494a5920eb62bd8fea6e11eca6e8b278ec7d09999c5f50310e927", + "vout": 1, + "scriptSig": { + "hex": "47304402200b88b727cf75ade32a1cf6915bc6cfa1e9102813c4d69db5fac613170a205f22022016bebf2825c288d24732772fff1376936502dc79789ee5cf3879e2bf2564fe58412102a5e690c5f8218ac5521a0ea3b1f0e59d6ba1c17d938659affa585a9d81799cba" + }, + "sequence": 4294967294 + }, + { + "txid": "fef9a33ff99e663c9f1f3fab81fdd2dc487d644bdb7e48304d81e496dd7076a3", + "vout": 1, + "scriptSig": { + "hex": "473044022064f2ed5f821b0e98fb556eadf5a5636e36e98f965796baf2ff0ad0080f4e68e7022010891d52d74f1dc55d861ad4898ec18318345ed2b358c698213944b381ab900e412102beb30e14403c4a03277d559019b60cd8fb877872896565fc0cdebb843797accd" + }, + "sequence": 4294967294 + }, + { + "txid": "90250895c774fd50629e2dc32f0a50f55d36049081147944c1845045fab27b1c", + "vout": 1, + "scriptSig": { + "hex": "483045022100a73951625dc0635ec373851730b5d268c60b36fcbbcda85228452176024a1db30220332ddb3940c0d05c215a41bb751a77d1969e240a3dd3612e7b2ec49913907761412103cf972121caef0095019ae80fc3a91cc3aa3590f57ad5751319b1a5bde03549bc" + }, + "sequence": 4294967294 + }, + { + "txid": "6792ab43514014f146d8dbc7c16f4ab78886657360969007f662c949ee0e5450", + "vout": 1, + "scriptSig": { + "hex": "483045022100d2487d304222d511d3c2645315f4821486f250c2edba85c1aec5ce5604ab6f0b0220275d210b5c97c90f275be2193413792511bfaa6d3338ea418fc01cbba42f96334121037cff19a246685228edd8df2c7dd5ed1efa1d779e8ce9ae99274c20912922bd59" + }, + "sequence": 4294967294 + }, + { + "txid": "c58db579bd0e5bb80f64a7b9f8ec3c25f0a3fdc877e2362fb95510b0785c9e99", + "vout": 0, + "scriptSig": { + "hex": "473044022010e90626ae890f8d9ac5be7b780f93269794f897732d4ca78b45797994b88ab402200fe4f787e4b5cf66a29b7a8f0a07b6a85d7a9ea18b89f216e3211b53641032bd4121030803961c4606e6cacba6b1ddd615a35a3abaaec945f60245da9e777d23975fdc" + }, + "sequence": 4294967294 + }, + { + "txid": "205860fe2b42f6c65e6e8a10bd01bf98e71945d3ea2c69601bee653aaf3475f2", + "vout": 1, + "scriptSig": { + "hex": "483045022100ae8a1a285a324544e588215e878c02d0db4853bb4064dbb7babde6f9d403738202201caab0d5afdbda4ee6a27f9e10fec2045717ac63b55ac647a5c2a5440c7c00c6412102ade72a40797ce9bf5662169d3317c1bd31455b086ecddbdc1bcb4cdf8ba6fc89" + }, + "sequence": 4294967294 + }, + { + "txid": "920ec23b99458e256404560d051cc22b92533c6c90fdf63cf5807bc6376fc140", + "vout": 0, + "scriptSig": { + "hex": "47304402205acc6b45d4c3e330a45dcd4ef69840edcd2b31da247151e741c0468f8e65ae98022032da991edacd2b5437a44bd2463a5f93ef1b32f3467d5748ceed5c70aa5eac1041210221099010e3629434851ceb0c5992388b815e9290b78aef55de30a08f73c29716" + }, + "sequence": 4294967294 + }, + { + "txid": "24bd31049209a3a3a437f12178e64a60f8f7ed75b2ebf7f3cee882cb29bd7e4b", + "vout": 0, + "scriptSig": { + "hex": "47304402207a0f2ad56df4bb879fd645c2cf07d034079ff0574cbe54bf51234968c19f526802207b25da2a8304a4e1f8d4a78f0c7902e9eef647a856c6f5193bed10d1fa26b338412102aa396f2b3bdbc39134b4e1f52d02ec83d0cee811d35ae78a539c8428781f3876" + }, + "sequence": 4294967294 + } + ], + "vout": [ + { + "value": 18332141.74, + "scriptPubKey": { + "hex": "76a914d1e5cdc2076e9e4890d5f5d0356dc4543b86e85d88ac" + } + }, + { + "value": 9981.47, + "n": 1, + "scriptPubKey": { + "hex": "76a91418195caf282889538a9ed9958bcc47a6bf7326b388ac" + } + } + ], + "time": 1538669466, + "blocktime": 1538669466 + } + ] + } + } + }, + "handleFork": { + "syncRanges": [ + {"lower": 550716, "upper": 550736} + ], + "fakeBlocks": { + "550733": { + "height": 550733, + "hash": "000000000000000000a517d8c6f3073e6872d9f3333314de7456687b228a6358" + }, + "550734": { + "height": 550734, + "hash": "000000000000000001afb0d221f7e4d62937a8aa60fd9e2ecadb74554c7033f6" + }, + "550735": { + "height": 550735, + "hash": "00000000000000000003b4357ac6904a5985f5175dafa31ff7e6ec4fcafec310" + }, + "550736": { + "height": 550736, + "hash": "000000000000000001d21a5f91bf04b1bc68b0bee17b02763547192b8deda84b" + } + }, + "realBlocks": { + "550733": { + "height": 550733, + "hash": "000000000000000000555800282da8751765b6ae1d4979c0d024fe025d9a32da" + }, + "550734": { + "height": 550734, + "hash": "0000000000000000013f343250868b23037c84082725c5ec317e1e09bf4a2dcf" + }, + "550735": { + "height": 550735, + "hash": "000000000000000000cfe8d66f1f8d2f155b61425ebc2daf5d10b133edc93af5" + }, + "550736": { + "height": 550736, + "hash": "00000000000000000181e327d1165480db82d915d46b9d39232526309072e014" + } + } + } +} diff --git a/tests/tests.json b/tests/tests.json index 564837b0..82da03c0 100644 --- a/tests/tests.json +++ b/tests/tests.json @@ -89,6 +89,11 @@ "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"], "sync": ["ConnectBlocksParallel", "ConnectBlocks"] }, + "ecash": { + "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", + "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"], + "sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"] + }, "ethereum_testnet_ropsten": { "rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]