From aad4f70f0c43d34b506cbfd40ac38509b4effe6f Mon Sep 17 00:00:00 2001 From: Jeremiah Buddenhagen Date: Thu, 24 Jan 2019 11:08:37 -0800 Subject: [PATCH] flo --- Gopkg.lock | 197 ++++++++++++++++++-- bchain/coins/blockchain.go | 4 +- bchain/coins/flo/floparser.go | 63 +++++++ bchain/coins/flo/floparser_test.go | 288 +++++++++++++++++++++++++++++ bchain/coins/flo/florpc.go | 57 ++++++ configs/coins/flo.json | 65 +++++++ docs/ports.md | 1 + 7 files changed, 662 insertions(+), 13 deletions(-) create mode 100644 bchain/coins/flo/floparser.go create mode 100644 bchain/coins/flo/floparser_test.go create mode 100644 bchain/coins/flo/florpc.go create mode 100644 configs/coins/flo.json diff --git a/Gopkg.lock b/Gopkg.lock index cf32c2f0..578d275b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,217 +3,390 @@ [[projects]] branch = "master" + digest = "1:8a2b12fb7e723619cc185b2ee1bad0b0c39e5791f0e576fc45e630242b5c5b7a" name = "github.com/Groestlcoin/go-groestl-hash" - packages = ["groestl","hash"] + packages = [ + "groestl", + "hash", + ] + pruneopts = "" revision = "790653ac190c4029ee200e82a8f21b5d1afaf7d6" [[projects]] branch = "master" + digest = "1:c0bec5f9b98d0bc872ff5e834fac186b807b656683bd29cb82fb207a1513fabb" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] branch = "master" + digest = "1:624a0f2b645f86d870e85d1875bae00313b295ea962be32627099efc9e57358f" name = "github.com/bsm/go-vlq" packages = ["."] + pruneopts = "" revision = "ec6e8d4f5f4ec0f6e808ffc7f4dcc7516d4d7d49" [[projects]] branch = "master" + digest = "1:53af1aee2d9d8544de49a42d39c6da2ab656a03639781169d697419d42dbdbd5" name = "github.com/btcsuite/btcd" - packages = ["blockchain","btcec","chaincfg","chaincfg/chainhash","database","txscript","wire"] + packages = [ + "blockchain", + "btcec", + "chaincfg", + "chaincfg/chainhash", + "database", + "txscript", + "wire", + ] + pruneopts = "" revision = "2be2f12b358dc57d70b8f501b00be450192efbc3" [[projects]] branch = "master" + digest = "1:30d4a548e09bca4a0c77317c58e7407e2a65c15325e944f9c08a7b7992f8a59e" name = "github.com/btcsuite/btclog" packages = ["."] + pruneopts = "" revision = "84c8d2346e9fc8c7b947e243b9c24e6df9fd206a" [[projects]] branch = "master" + digest = "1:827862daac23c3e946d7f0800957967e88e5d4b4b8437a303291237db3f565f3" name = "github.com/btcsuite/btcutil" - packages = [".","base58","bech32"] + packages = [ + ".", + "base58", + "bech32", + ] + pruneopts = "" revision = "501929d3d046174c3d39f0ea54ece471aa17238c" [[projects]] + digest = "1:c0243ca6ea596b1c547ae44d824fa42f9127af26788efaeb930898586d186c4d" name = "github.com/deckarep/golang-set" packages = ["."] + pruneopts = "" revision = "1d4478f51bed434f1dadf96dcd9b43aabac66795" version = "v1.7" [[projects]] + digest = "1:8acdd83e35b407d026e546bf4d18cc778e9604433d03d468eccca62b9c51c718" name = "github.com/ethereum/go-ethereum" - packages = [".","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","ethdb","log","metrics","p2p/netutil","params","rlp","rpc","trie"] + packages = [ + ".", + "common", + "common/hexutil", + "common/math", + "core/types", + "crypto", + "crypto/secp256k1", + "crypto/sha3", + "ethclient", + "ethdb", + "log", + "metrics", + "p2p/netutil", + "params", + "rlp", + "rpc", + "trie", + ] + pruneopts = "" revision = "89451f7c382ad2185987ee369f16416f89c28a7d" version = "v1.8.15" [[projects]] + digest = "1:9ca737b471693542351e112c9e86be9bf7385e42256893a09ecb2a98e2036f74" name = "github.com/go-stack/stack" packages = ["."] + pruneopts = "" revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" version = "v1.7.0" [[projects]] + digest = "1:61416415ed09917c36481eb30d3d5836c7b10e3225f412546a1f7a80fc5b33ce" name = "github.com/gobuffalo/packr" packages = ["."] + pruneopts = "" revision = "5a2cbb54c4e7d482e3f518c56f1f86f133d5204f" version = "v1.13.7" [[projects]] + digest = "1:0a3f6a0c68ab8f3d455f8892295503b179e571b7fefe47cc6c556405d1f83411" name = "github.com/gogo/protobuf" packages = ["proto"] + pruneopts = "" revision = "1adfc126b41513cc696b209667c8656ea7aac67c" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:107b233e45174dbab5b1324201d092ea9448e58243ab9f039e4c0f332e121e3a" name = "github.com/golang/glog" packages = ["."] + pruneopts = "" revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" [[projects]] + digest = "1:bcb38c8fc9b21bb8682ce2d605a7d4aeb618abc7f827e3ac0b27c0371fdb23fb" name = "github.com/golang/protobuf" packages = ["proto"] + pruneopts = "" revision = "925541529c1fa6821df4e44ce2723319eb2be768" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:09307dfb1aa3f49a2bf869dcfa4c6c06ecd3c207221bd1c1a1141f0e51f209eb" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "" revision = "553a641470496b2327abcac10b36396bd98e45c9" [[projects]] + digest = "1:64d212c703a2b94054be0ce470303286b177ad260b2f89a307e3d1bb6c073ef6" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:f52c08004f0c94b201138a846bdc0162ed8860a569cb57f2956315daaa27d8a3" name = "github.com/jakm/bchutil" packages = ["."] + pruneopts = "" revision = "5a273ca8a96628732c07ff5cfb9f3e7d965241e8" [[projects]] branch = "master" + digest = "1:b5544974001cf7d5290cdb9e74ed939dcb3d7f52d4f618d664e0135dda5fa87c" name = "github.com/jakm/btcutil" - packages = [".","base58","bech32","chaincfg","txscript"] + packages = [ + ".", + "base58", + "bech32", + "chaincfg", + "txscript", + ] + pruneopts = "" revision = "224b76333062172edefdeb502123fdda12205f76" [[projects]] branch = "master" + digest = "1:b38a66184fb38b517900ab365f43557ea7e4efe36f939e2fcb53ec0aa92b29c5" name = "github.com/juju/errors" packages = ["."] + pruneopts = "" revision = "c7d06af17c68cd34c835053720b21f6549d9b0ee" [[projects]] branch = "master" + digest = "1:538b60b5e6074d45962fa3dc0bfe721c303f5b31be99e809653380a67afcc289" name = "github.com/martinboehm/golang-socketio" - packages = [".","protocol","transport"] + packages = [ + ".", + "protocol", + "transport", + ] + pruneopts = "" revision = "f60b0a8befde091474a624a8ffd81ee9912957b3" [[projects]] + digest = "1:4c23ced97a470b17d9ffd788310502a077b9c1f60221a85563e49696276b4147" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "" revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:4b0d251e61fe112ebeb52ce2162decdf13ae4a031c6e598c552a9b349dc4c74d" name = "github.com/mr-tron/base58" packages = ["base58"] + pruneopts = "" revision = "c1bdf7c52f59d6685ca597b9955a443ff95eeee6" [[projects]] branch = "master" + digest = "1:d2df7451f4829b4dd6422e1ebf336fe9400220efe735f80f27d84a07e3180a1f" name = "github.com/pebbe/zmq4" packages = ["."] + pruneopts = "" revision = "5b443b6471cea4b4f9f85025530c04c93233f76a" [[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:4142d94383572e74b42352273652c62afec5b23f325222ed09198f46009022d1" name = "github.com/prometheus/client_golang" - packages = ["prometheus","prometheus/promhttp"] + packages = [ + "prometheus", + "prometheus/promhttp", + ] + pruneopts = "" revision = "c5b7fccd204277076155f10851dad72b76a49317" version = "v0.8.0" [[projects]] branch = "master" + digest = "1:60aca47f4eeeb972f1b9da7e7db51dee15ff6c59f7b401c1588b8e6771ba15ef" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "" revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" [[projects]] branch = "master" + digest = "1:af21ee3e0a8212f17bb317cd7237f9920bcb2641a291ac111f30f63b3cab817f" name = "github.com/prometheus/common" - packages = ["expfmt","internal/bitbucket.org/ww/goautoneg","model"] + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "" revision = "d0f7cd64bda49e08b22ae8a730aa57aa0db125d6" [[projects]] branch = "master" + digest = "1:61df0898746840afc7be5dc2c3eeec83022fab70df11ecee5b16c85e912cf5ed" name = "github.com/prometheus/procfs" - packages = [".","internal/util","nfs","xfs"] + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "" revision = "8b1c2da0d56deffdbb9e48d4414b4e674bd8083e" [[projects]] + digest = "1:fe0d7d3a09f5f5ee86ac94afb59c4ef39b08580417732aee805b3d91a90370e7" name = "github.com/rs/cors" packages = ["."] + pruneopts = "" revision = "feef513b9575b32f84bafa580aad89b011259019" version = "v1.3.0" [[projects]] + digest = "1:94e86732ddbefd10e7f570b1c850b47cd69500ddeb58d3e4c77f40683dfa6bbe" name = "github.com/schancel/cashaddr-converter" - packages = ["address","baseconv","cashaddress","legacy"] + packages = [ + "address", + "baseconv", + "cashaddress", + "legacy", + ] + pruneopts = "" revision = "0a38f5822f795dc3727b4caacc298e02938d9eb1" version = "v9" [[projects]] branch = "master" + digest = "1:175bc8d87d54849b9b9fc6cd473c5830e090fbf3c2f0ca71c7642a697e5d9237" name = "github.com/syndtr/goleveldb" - packages = ["leveldb","leveldb/cache","leveldb/comparer","leveldb/errors","leveldb/filter","leveldb/iterator","leveldb/journal","leveldb/memdb","leveldb/opt","leveldb/storage","leveldb/table","leveldb/util"] + packages = [ + "leveldb", + "leveldb/cache", + "leveldb/comparer", + "leveldb/errors", + "leveldb/filter", + "leveldb/iterator", + "leveldb/journal", + "leveldb/memdb", + "leveldb/opt", + "leveldb/storage", + "leveldb/table", + "leveldb/util", + ] + pruneopts = "" revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad" [[projects]] branch = "master" + digest = "1:406915e133a786c711bbff085eb9c20f8c50b01ddae6e9f3eb00557bc080b153" name = "github.com/tecbot/gorocksdb" packages = ["."] + pruneopts = "" revision = "214b6b7bc0f06812ab5602fdc502a3e619916f38" [[projects]] branch = "master" + digest = "1:47ff8b3229cff95d3cf3738c7a8461fdeacd3f46801e54d301a62500605ce202" name = "golang.org/x/crypto" packages = ["ripemd160"] + pruneopts = "" revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e" [[projects]] branch = "master" + digest = "1:40dd5a4f1e82c4d3dc3de550a96152f6a8937bcfcb20c7ee34685f8764e1bda5" name = "golang.org/x/net" packages = ["websocket"] + pruneopts = "" revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41" [[projects]] branch = "v2" + digest = "1:a585c075875ab9c344f7840a927f09f3285563f7318e761a1d61d642316f2217" name = "gopkg.in/karalabe/cookiejar.v2" packages = ["collections/prque"] + pruneopts = "" revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57" [[projects]] branch = "v2" + digest = "1:4f830ee018eb8c56d0def653ad7c9a1d2a053f0cef2ac6b2200f73b98fa6a681" name = "gopkg.in/natefinch/npipe.v2" packages = ["."] + pruneopts = "" revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "3a7b863bec3806ba9460f3961e2d6e8ff4b77e5f66d196b1f79bd37fa6bf8d82" + input-imports = [ + "github.com/bsm/go-vlq", + "github.com/btcsuite/btcd/blockchain", + "github.com/btcsuite/btcd/chaincfg/chainhash", + "github.com/btcsuite/btcd/wire", + "github.com/deckarep/golang-set", + "github.com/ethereum/go-ethereum", + "github.com/ethereum/go-ethereum/common", + "github.com/ethereum/go-ethereum/common/hexutil", + "github.com/ethereum/go-ethereum/core/types", + "github.com/ethereum/go-ethereum/ethclient", + "github.com/ethereum/go-ethereum/rpc", + "github.com/gobuffalo/packr", + "github.com/gogo/protobuf/proto", + "github.com/golang/glog", + "github.com/golang/protobuf/proto", + "github.com/gorilla/websocket", + "github.com/jakm/bchutil", + "github.com/jakm/btcutil", + "github.com/jakm/btcutil/base58", + "github.com/jakm/btcutil/chaincfg", + "github.com/jakm/btcutil/txscript", + "github.com/juju/errors", + "github.com/martinboehm/golang-socketio", + "github.com/martinboehm/golang-socketio/transport", + "github.com/pebbe/zmq4", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/schancel/cashaddr-converter/address", + "github.com/tecbot/gorocksdb", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 1fa2bdec..2be27370 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -9,7 +9,8 @@ import ( "blockbook/bchain/coins/digibyte" "blockbook/bchain/coins/dogecoin" "blockbook/bchain/coins/eth" - "blockbook/bchain/coins/fujicoin" + "blockbook/bchain/coins/flo" + "blockbook/bchain/coins/fujicoin" "blockbook/bchain/coins/gamecredits" "blockbook/bchain/coins/grs" "blockbook/bchain/coins/litecoin" @@ -61,6 +62,7 @@ func init() { BlockChainFactories["Groestlcoin"] = grs.NewGroestlcoinRPC BlockChainFactories["Groestlcoin Testnet"] = grs.NewGroestlcoinRPC BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC + BlockChainFactories["Flo"] = flo.NewFloRPC } // GetCoinNameFromConfig gets coin name and coin shortcut from config file diff --git a/bchain/coins/flo/floparser.go b/bchain/coins/flo/floparser.go new file mode 100644 index 00000000..be0e5030 --- /dev/null +++ b/bchain/coins/flo/floparser.go @@ -0,0 +1,63 @@ +package flo + +import ( + "blockbook/bchain/coins/btc" + + "github.com/btcsuite/btcd/wire" + "github.com/jakm/btcutil/chaincfg" +) + +const ( + MainnetMagic wire.BitcoinNet = 0xf1a5c0fd + TestnetMagic wire.BitcoinNet = 0xf25ac0fd + RegtestMagic wire.BitcoinNet = 0xdab5bffa +) + +var ( + MainNetParams chaincfg.Params + TestNetParams chaincfg.Params +) + +func init() { + MainNetParams = chaincfg.MainNetParams + MainNetParams.Net = MainnetMagic + MainNetParams.PubKeyHashAddrID = []byte{35} + MainNetParams.ScriptHashAddrID = []byte{94} + MainNetParams.Bech32HRPSegwit = "flo" + + TestNetParams = chaincfg.TestNet3Params + TestNetParams.Net = TestnetMagic + TestNetParams.PubKeyHashAddrID = []byte{115} + TestNetParams.ScriptHashAddrID = []byte{198} + TestNetParams.Bech32HRPSegwit = "tflo" +} + +// FloParser handle +type FloParser struct { + *btc.BitcoinParser +} + +// NewFloParser returns new FloParser instance +func NewFloParser(params *chaincfg.Params, c *btc.Configuration) *FloParser { + return &FloParser{BitcoinParser: btc.NewBitcoinParser(params, c)} +} + +// GetChainParams contains network parameters for the main Flo network, +// and the test Flo network +func GetChainParams(chain string) *chaincfg.Params { + if !chaincfg.IsRegistered(&MainNetParams) { + err := chaincfg.Register(&MainNetParams) + if err == nil { + err = chaincfg.Register(&TestNetParams) + } + if err != nil { + panic(err) + } + } + switch chain { + case "test": + return &TestNetParams + default: + return &MainNetParams + } +} diff --git a/bchain/coins/flo/floparser_test.go b/bchain/coins/flo/floparser_test.go new file mode 100644 index 00000000..07da85c3 --- /dev/null +++ b/bchain/coins/flo/floparser_test.go @@ -0,0 +1,288 @@ +// +build unittest + +package flo + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/hex" + "math/big" + "os" + "reflect" + "testing" + + "github.com/jakm/btcutil/chaincfg" +) + +func TestMain(m *testing.M) { + c := m.Run() + chaincfg.ResetParams() + os.Exit(c) +} + +func Test_GetAddrDescFromAddress_Testnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "mgPdTgEq6YqUJ4yzQgR8jH5TCX5c5yRwCP"}, + want: "76a91409957dfdb3eb620a94b99857e13949551584c33688ac", + wantErr: false, + }, + { + name: "P2SH1-legacy", + args: args{address: "2MvGVySztevmycxrSmMRjJaVj2iJin7qpap"}, + want: "a9142126232e3f47ae0f1246ec5f05fc400d83c86a0d87", + wantErr: false, + }, + { + name: "P2SH2-legacy", + args: args{address: "2N9a2TNzWz1FEKGFxUdMEh62V83URdZ5QAZ"}, + want: "a914b31049e7ee51501fe19e3e0cdb803dc84cf99f9e87", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "QPdG6Ts8g2q4m9cVPTTkPGwAB6kYgXB7Hc"}, + want: "a9142126232e3f47ae0f1246ec5f05fc400d83c86a0d87", + wantErr: false, + }, + { + name: "P2SH2", + args: args{address: "QcvnaPrm17JKTT216jPFmnTvGRvFX2fWzN"}, + want: "a914b31049e7ee51501fe19e3e0cdb803dc84cf99f9e87", + wantErr: false, + }, + } + parser := NewLitecoinParser(GetChainParams("test"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.GetAddrDescFromAddress(tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) { + type args struct { + address string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "P2PKH1", + args: args{address: "LgJGe7aKy1wfXESKhiKeRWj6z4KjzCfXNW"}, + want: "76a914e72ba56ab6afccac045d696b979e3b5077e88d1988ac", + wantErr: false, + }, + { + name: "P2PKH2", + args: args{address: "LiTVReQ6N8rWc2pNg2XMwCWq7A9P15teWg"}, + want: "76a914feda50542e61108cf53b93dbffa0959f91ccb32588ac", + wantErr: false, + }, + { + name: "P2SH1", + args: args{address: "MLTQ8niHMnpJLNvK72zBeY91hQmUtoo8nX"}, + want: "a91489ba6cf45546f91f1bdf553e695d63fc6b8795bd87", + wantErr: false, + }, + { + name: "P2SH2", + args: args{address: "MAVWzxXm8KGkZTesqLtqywzrvbs96FEoKy"}, + want: "a9141c6fbaf46d64221e80cbae182c33ddf81b9294ac87", + wantErr: false, + }, + { + name: "witness_v0_keyhash", + args: args{address: "ltc1q5fgkuac9s2ry56jka5s6zqsyfcugcchrqgz2yl"}, + want: "0014a2516e770582864a6a56ed21a102044e388c62e3", + wantErr: false, + }, + { + name: "witness_v0_scripthashx", + args: args{address: "ltc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzsk3x5nd"}, + want: "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", + wantErr: false, + }, + } + parser := NewFloParser(GetChainParams("main"), &btc.Configuration{}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parser.GetAddrDescFromAddress(tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want) + } + }) + } +} + +var ( + testTx1 bchain.Tx + + testTxPacked1 = "0001e4538ba8d7aa2002000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac00000000" +) + +func init() { + testTx1 = bchain.Tx{ + Hex: "02000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac00000000", + Blocktime: 1519053456, + Txid: "1c50c1770374d7de2f81a87463a5225bb620d25fd467536223a5b715a47c9e32", + LockTime: 0, + Version: 2, + Vin: []bchain.Vin{ + { + ScriptSig: bchain.ScriptSig{ + Hex: "", + }, + Txid: "5ee62e3f7757f5fd995b2fa1bdb5b744298146098d5de92959ec4b52dc77191e", + Vout: 1, + Sequence: 4294967295, + }, + { + ScriptSig: bchain.ScriptSig{ + Hex: "", + }, + Txid: "3cd215c1c6b7ae33bc6266f268fd212b83860e13902b454502ce6d54448e398a", + Vout: 25, + Sequence: 4294967295, + }, + { + ScriptSig: bchain.ScriptSig{ + Hex: "", + }, + Txid: "0f941395005b80a61726baecebb11d7caa184a1a58246daff7dffca793ab07b8", + Vout: 12, + Sequence: 4294967295, + }, + }, + Vout: []bchain.Vout{ + { + ValueSat: *big.NewInt(1252000000), + N: 0, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a9141ae882e788091732da6910595314447c9e38bd8d88ac", + Addresses: []string{ + "LMgENNXzzuPxp7vfMjDrCU44bsmrEMgqvc", + }, + }, + }, + { + ValueSat: *big.NewInt(1000487), + N: 1, + ScriptPubKey: bchain.ScriptPubKey{ + Hex: "76a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac", + Addresses: []string{ + "LV1ByjbJNFTHyFQqwqwdJXKJznYDzXzg4B", + }, + }, + }, + }, + } +} + +func Test_PackTx(t *testing.T) { + type args struct { + tx bchain.Tx + height uint32 + blockTime int64 + parser *FloParser + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "flo-1", + args: args{ + tx: testTx1, + height: 123987, + blockTime: 1519053456, + parser: NewFloParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: testTxPacked1, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime) + if (err != nil) != tt.wantErr { + t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + h := hex.EncodeToString(got) + if !reflect.DeepEqual(h, tt.want) { + t.Errorf("packTx() = %v, want %v", h, tt.want) + } + }) + } +} + +func Test_UnpackTx(t *testing.T) { + type args struct { + packedTx string + parser *FloParser + } + tests := []struct { + name string + args args + want *bchain.Tx + want1 uint32 + wantErr bool + }{ + { + name: "flo-1", + args: args{ + packedTx: testTxPacked1, + parser: NewFloParser(GetChainParams("main"), &btc.Configuration{}), + }, + want: &testTx1, + want1: 123987, + 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/flo/florpc.go b/bchain/coins/flo/florpc.go new file mode 100644 index 00000000..f5917caf --- /dev/null +++ b/bchain/coins/flo/florpc.go @@ -0,0 +1,57 @@ +package flo + +import ( + "blockbook/bchain" + "blockbook/bchain/coins/btc" + "encoding/json" + + "github.com/golang/glog" +) + +// FloRPC is an interface to JSON-RPC flod service. +type FloRPC struct { + *btc.BitcoinRPC +} + +// NewFloRPC returns new FloRPC instance. +func NewFloRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { + b, err := btc.NewBitcoinRPC(config, pushHandler) + if err != nil { + return nil, err + } + + s := &FloRPC{ + b.(*btc.BitcoinRPC), + } + s.RPCMarshaler = btc.JSONMarshalerV2{} + s.ChainConfig.SupportsEstimateFee = false + + return s, nil +} + +// Initialize initializes FloRPC instance. +func (b *FloRPC) Initialize() error { + chainName, err := b.GetChainInfoAndInitializeMempool(b) + if err != nil { + return err + } + + glog.Info("Chain name ", chainName) + params := GetChainParams(chainName) + + // always create parser + b.Parser = NewFloParser(params, b.ChainConfig) + + // parameters for getInfo request + if params.Net == MainnetMagic { + b.Testnet = false + b.Network = "livenet" + } else { + b.Testnet = true + b.Network = "testnet" + } + + glog.Info("rpc: block chain ", params.Name) + + return nil +} diff --git a/configs/coins/flo.json b/configs/coins/flo.json new file mode 100644 index 00000000..33ae4a5c --- /dev/null +++ b/configs/coins/flo.json @@ -0,0 +1,65 @@ +{ + "coin": { + "name": "Flo", + "shortcut": "FLO", + "label": "Flo", + "alias": "flo" + }, + "ports": { + "backend_rpc": 8066, + "backend_message_queue": 38366, + "blockbook_internal": 9066, + "blockbook_public": 9166 + }, + "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-flo", + "package_revision": "satoshilabs-1", + "system_user": "flo", + "version": "0.15.1.1", + "binary_url": "https://github.com/floblockchain/flo/releases/download/v0.15.1.1/flo-0.15.1-x86_64-linux-gnu.tar.gz", + "verification_type": "sha256", + "verification_source": "40a725947d00ee4af519d9166b824cf2bfe1a144bf4a14004d756ece5063bf8d", + "extract_command": "tar -C backend --strip 1 -xf", + "exclude_files": [ + "bin/flo-qt" + ], + "exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/flod -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": "bitcoin_like.conf", + "client_config_file": "bitcoin_like_client.conf", + "additional_params": { + "deprecatedrpc": "estimatefee" + } + }, + "blockbook": { + "package_name": "blockbook-flo", + "system_user": "blockbook-flo", + "internal_binding_template": ":{{.Ports.BlockbookInternal}}", + "public_binding_template": ":{{.Ports.BlockbookPublic}}", + "explorer_url": "", + "additional_params": "-dbcache=1073741824", + "block_chain": { + "parse": true, + "mempool_workers": 8, + "mempool_sub_workers": 2, + "block_addresses_to_keep": 300, + "additional_params": {} + } + }, + "meta": { + "package_maintainer": "bitspill", + "package_maintainer_email": "blockbook@bitspill.net" + } +} diff --git a/docs/ports.md b/docs/ports.md index 630c4a18..1d0fc04a 100644 --- a/docs/ports.md +++ b/docs/ports.md @@ -20,6 +20,7 @@ | Groestlcoin | 9045 | 9145 | 8045 | 38345 | | Bitcoin Cash SV | 9046 | 9146 | 8046 | 38346 | | Fujicoin | 9048 | 9148 | 8048 | 38348 | +| Flo | 9066 | 9166 | 8066 | 38366 | | Bitcoin Testnet | 19030 | 19130 | 18030 | 48330 | | Bitcoin Cash Testnet | 19031 | 19131 | 18031 | 48331 | | Zcash Testnet | 19032 | 19132 | 18032 | 48332 |