├── .gitignore
├── Makefile
├── README.md
├── cmd
└── pscli
│ ├── README.md
│ ├── commands.go
│ └── main.go
├── common
├── conversion.go
├── log.go
└── utils.go
├── config.go
├── connectors
├── daemons
│ ├── bitcoind
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── size.go
│ │ ├── tx.go
│ │ ├── tx_test.go
│ │ ├── utils.go
│ │ └── utils_test.go
│ ├── bitcoind_simple
│ │ ├── connector.go
│ │ ├── storage.go
│ │ └── utils.go
│ ├── geth
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── errors.go
│ │ ├── extension.go
│ │ ├── storage.go
│ │ └── utils.go
│ └── lnd
│ │ ├── lnd.go
│ │ ├── log.go
│ │ └── utils.go
├── interface.go
├── payment.go
├── payment_details.go
├── payment_details_test.go
├── rpc
│ ├── bitcoin
│ │ ├── client.go
│ │ ├── params.go
│ │ ├── validation.go
│ │ └── validation_test.go
│ ├── bitcoincash
│ │ ├── client.go
│ │ ├── params.go
│ │ ├── validation.go
│ │ └── validation_test.go
│ ├── dash
│ │ ├── client.go
│ │ ├── params.go
│ │ ├── validation.go
│ │ └── validation_test.go
│ ├── ethereum
│ │ ├── validation.go
│ │ └── validation_test.go
│ ├── interface.go
│ ├── litecoin
│ │ ├── client.go
│ │ ├── params.go
│ │ ├── validation.go
│ │ └── validation_test.go
│ └── log.go
└── storage.go
├── crpc
├── README.md
├── errors.go
├── generate.sh
├── log.go
├── rpc.pb.go
├── rpc.proto
├── server.go
└── utils.go
├── db
├── inmemory
│ └── payments.go
└── sqlite
│ ├── bitcoin_simnet_state.go
│ ├── connector_state.go
│ ├── connector_state_test.go
│ ├── db.go
│ ├── geth_accounts.go
│ ├── geth_accounts_test.go
│ ├── log.go
│ ├── migrations.go
│ ├── migrations_test.go
│ ├── payment.go
│ ├── payment_test.go
│ ├── test_db_add_status_field
│ └── utils.go
├── docker
├── mainnet
│ ├── .env.empty
│ ├── .gitignore
│ ├── bitcoin-cash
│ │ ├── Dockerfile
│ │ ├── bitcoin-cash.mainnet.conf
│ │ └── entrypoint.sh
│ ├── bitcoin-lightning
│ │ ├── Dockerfile
│ │ ├── bitcoin-lightning.mainnet.conf
│ │ └── entrypoint.sh
│ ├── bitcoin-neutrino
│ │ ├── Dockerfile
│ │ ├── bitcoin-neutrino.mainnet.conf
│ │ └── entrypoint.sh
│ ├── bitcoin
│ │ ├── Dockerfile
│ │ ├── bitcoin.mainnet.conf
│ │ └── entrypoint.sh
│ ├── connector
│ │ ├── Dockerfile
│ │ ├── connector.mainnet.conf
│ │ └── entrypoint.sh
│ ├── dash
│ │ ├── Dockerfile
│ │ ├── dash.mainnet.conf
│ │ └── entrypoint.sh
│ ├── docker-compose.yml
│ ├── ethereum
│ │ ├── Dockerfile
│ │ ├── entrypoint.sh
│ │ └── ethereum.mainnet.conf
│ ├── fluentd
│ │ ├── Dockerfile
│ │ └── fluent.conf
│ ├── litecoin
│ │ ├── Dockerfile
│ │ ├── entrypoint.sh
│ │ └── litecoin.mainnet.conf
│ ├── logrotate.conf
│ └── rsyslog.conf
├── simnet
│ ├── .env
│ ├── bitcoin-cash
│ │ ├── Dockerfile
│ │ ├── bitcoin.simnet.primary.conf
│ │ ├── bitcoin.simnet.secondary.conf
│ │ └── entrypoint.sh
│ ├── bitcoin-lightning-helper
│ │ ├── Dockerfile
│ │ └── http-server.py
│ ├── bitcoin-lightning
│ │ ├── Dockerfile
│ │ ├── bitcoin-lightning.simnet.primary.conf
│ │ ├── bitcoin-lightning.simnet.secondary.conf
│ │ └── entrypoint.sh
│ ├── bitcoin
│ │ ├── Dockerfile
│ │ ├── bitcoin.simnet.primary.conf
│ │ ├── bitcoin.simnet.secondary.conf
│ │ └── entrypoint.sh
│ ├── blocks-generator
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
│ ├── connector
│ │ ├── Dockerfile
│ │ ├── connector.simnet.conf
│ │ └── entrypoint.sh
│ ├── dash
│ │ ├── Dockerfile
│ │ ├── dash.simnet.primary.conf
│ │ ├── dash.simnet.secondary.conf
│ │ └── entrypoint.sh
│ ├── docker-compose.yml
│ ├── ethereum-bootnode
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
│ ├── ethereum
│ │ ├── Dockerfile
│ │ ├── entrypoint.sh
│ │ ├── ethereum.simnet.primary.conf
│ │ ├── ethereum.simnet.secondary.conf
│ │ └── genesis.json
│ ├── init.pl
│ ├── litecoin
│ │ ├── Dockerfile
│ │ ├── entrypoint.sh
│ │ ├── litecoin.simnet.primary.conf
│ │ └── litecoin.simnet.secondary.conf
│ ├── logrotate.conf
│ └── rsyslog.conf
└── testnet
│ ├── .env.empty
│ ├── .gitignore
│ ├── bitcoin-cash
│ ├── Dockerfile
│ ├── bitcoin-cash.testnet.conf
│ └── entrypoint.sh
│ ├── bitcoin-lightning
│ ├── Dockerfile
│ ├── bitcoin-lightning.testnet.conf
│ └── entrypoint.sh
│ ├── bitcoin-neutrino
│ ├── Dockerfile
│ ├── bitcoin-neutrino.testnet.conf
│ └── entrypoint.sh
│ ├── bitcoin
│ ├── Dockerfile
│ ├── bitcoin.testnet.conf
│ └── entrypoint.sh
│ ├── connector
│ ├── Dockerfile
│ ├── connector.testnet.conf
│ └── entrypoint.sh
│ ├── dash
│ ├── Dockerfile
│ ├── dash.testnet.conf
│ └── entrypoint.sh
│ ├── docker-compose.yml
│ ├── ethereum
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── ethereum.testnet.conf
│ ├── litecoin
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── litecoin.testnet.conf
│ ├── logrotate.conf
│ └── rsyslog.conf
├── go.mod
├── go.sum
├── log.go
├── main.go
├── metrics
├── crypto
│ ├── backend.go
│ └── metric.go
├── labels.go
├── log.go
├── rpc
│ └── backend.go
└── server.go
├── signal.go
├── utils.go
└── version.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 |
16 | vendor/
17 | .idea
18 |
19 | ./connector
20 | connector.conf
21 |
22 | docker/**/bin
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Pay server - is the blockchain microservice which is working on [zigzag.io](https://zigzag.io). It is used as unified API for other microservices to receive and send cryptocurrency.
2 |
3 | | State | Feature |
4 | | ------------- | ------------- |
5 | | implemented | Unify payment API for BTC, LTC, DASH, ETH, BCH, and Lightning Network |
6 | | implemented | Report health statistics about internal state of synchronisation, fees, request delays, sent and received volume, amount of fees spent on payments |
7 | | not implemented | Payment re-try in case of failure |
8 | | not implemented | UTXO re-orginisation |
9 | | not implemented | Lightning Network channel re-balancing |
10 | |not implemented|Support of payments on HTLC addresses|
11 |
12 | ```
13 | GRPC API:
14 |
15 | // CreateReceipt is used to create blockchain deposit address in
16 | // case of blockchain media, and lightning network invoice in
17 | // case of the lightning media, which will be used to receive money from
18 | // external entity.
19 | rpc CreateReceipt (CreateReceiptRequest) returns (CreateReceiptResponse);
20 |
21 | // ValidateReceipt is used to validate receipt for given asset and media.
22 | rpc ValidateReceipt (ValidateReceiptRequest) returns (EmptyResponse);
23 |
24 | // Balance is used to determine balance.
25 | rpc Balance (BalanceRequest) returns (BalanceResponse);
26 |
27 | // EstimateFee estimates the fee of the payment.
28 | rpc EstimateFee (EstimateFeeRequest) returns (EstimateFeeResponse);
29 |
30 | // SendPayment sends payment to the given recipient,
31 | // ensures in the validity of the receipt as well as the
32 | // account has enough money for doing that.
33 | rpc SendPayment (SendPaymentRequest) returns (Payment);
34 |
35 | // PaymentByID is used to fetch the information about payment, by the
36 | // given system payment id.
37 | rpc PaymentByID (PaymentByIDRequest) returns (Payment);
38 |
39 | // PaymentsByReceipt is used to fetch the information about payment, by the
40 | // given receipt.
41 | rpc PaymentsByReceipt (PaymentsByReceiptRequest) returns (PaymentsByReceiptResponse);
42 |
43 | // ListPayments returnes list of payment which were registered by the
44 | // system.
45 | rpc ListPayments (ListPaymentsRequest) returns (ListPaymentsResponse);
46 | ```
47 |
--------------------------------------------------------------------------------
/cmd/pscli/README.md:
--------------------------------------------------------------------------------
1 | Command line interface is an tool which helps to access payserver RPC API.
2 |
--------------------------------------------------------------------------------
/cmd/pscli/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "github.com/bitlum/connector/crpc"
7 | "github.com/urfave/cli"
8 | "google.golang.org/grpc"
9 | )
10 |
11 | const (
12 | defaultRPCPort = "9002"
13 | defaultRPCHostPort = "localhost:" + defaultRPCPort
14 | )
15 |
16 | func fatal(err error) {
17 | fmt.Fprintf(os.Stderr, "[pscli] %v\n", err)
18 | os.Exit(1)
19 | }
20 |
21 | func getClient(ctx *cli.Context) (crpc.PayServerClient, func()) {
22 | conn := getClientConn(ctx, false)
23 |
24 | cleanUp := func() {
25 | conn.Close()
26 | }
27 |
28 | return crpc.NewPayServerClient(conn), cleanUp
29 | }
30 |
31 | func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
32 | // Create a dial options array.
33 | opts := []grpc.DialOption{
34 | grpc.WithInsecure(),
35 | }
36 |
37 | conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...)
38 | if err != nil {
39 | fatal(err)
40 | }
41 |
42 | return conn
43 | }
44 |
45 | func main() {
46 | app := cli.NewApp()
47 | app.Name = "pscli"
48 | app.Version = fmt.Sprintf("0.1")
49 | app.Usage = "Control plane for your PayServer Daemon (psd)"
50 | app.Flags = []cli.Flag{
51 | cli.StringFlag{
52 | Name: "rpcserver",
53 | Value: defaultRPCHostPort,
54 | Usage: "host:port of payserver",
55 | },
56 | }
57 | app.Commands = []cli.Command{
58 | createReceiptCommand,
59 | validateReceiptCommand,
60 | balanceCommand,
61 | estimateFeeCommand,
62 | sendPaymentCommand,
63 | paymentByIDCommand,
64 | paymentByReceiptCommand,
65 | listPaymentsCommand,
66 | }
67 |
68 | if err := app.Run(os.Args); err != nil {
69 | fatal(err)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/common/conversion.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "github.com/shopspring/decimal"
5 | "math/big"
6 | "github.com/go-errors/errors"
7 | "github.com/btcsuite/btcutil"
8 | )
9 |
10 | var SatoshiPerBitcoin = decimal.New(btcutil.SatoshiPerBitcoin, 0)
11 |
12 | func BtcStrToSatoshi(amount string) (int64, error) {
13 | amt, err := decimal.NewFromString(amount)
14 | if err != nil {
15 | return 0, errors.Errorf("unable to parse amount(%v): %v",
16 | amount, err)
17 | }
18 |
19 | a, _ := amt.Float64()
20 | btcAmount, err := btcutil.NewAmount(a)
21 | if err != nil {
22 | return 0, errors.Errorf("unable to parse amount(%v): %v", a, err)
23 | }
24 |
25 | return int64(btcAmount), nil
26 | }
27 |
28 | func Sat2DecAmount(amount btcutil.Amount) decimal.Decimal {
29 | amt := decimal.NewFromBigInt(big.NewInt(int64(amount)), 0)
30 | return amt.Div(SatoshiPerBitcoin)
31 | }
32 |
--------------------------------------------------------------------------------
/common/log.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/btcsuite/btclog"
7 | )
8 |
9 | // NamedLogger is an extension of btclog.Logger which adds additional name to
10 | // the logging.
11 | type NamedLogger struct {
12 | Logger btclog.Logger
13 | Name string
14 | }
15 |
16 | func (l *NamedLogger) convert(format string, params []interface{}) (string,
17 | []interface{}) {
18 | format = fmt.Sprintf("(%v) %v", "%v", format)
19 | var nparams []interface{}
20 | nparams = append(nparams, l.Name)
21 | nparams = append(nparams, params...)
22 | return format, nparams
23 | }
24 |
25 | // Tracef formats message according to format specifier and writes to
26 | // to log with LevelTrace.
27 | func (l *NamedLogger) Tracef(format string, params ...interface{}) {
28 | format, params = l.convert(format, params)
29 | l.Logger.Tracef(format, params...)
30 | }
31 |
32 | // Debugf formats message according to format specifier and writes to
33 | // log with LevelDebug.
34 | func (l *NamedLogger) Debugf(format string, params ...interface{}) {
35 | format, params = l.convert(format, params)
36 | l.Logger.Debugf(format, params...)
37 | }
38 |
39 | // Infof formats message according to format specifier and writes to
40 | // log with LevelInfo.
41 | func (l *NamedLogger) Infof(format string, params ...interface{}) {
42 | format, params = l.convert(format, params)
43 | l.Logger.Infof(format, params...)
44 | }
45 |
46 | // Warnf formats message according to format specifier and writes to
47 | // to log with LevelWarn.
48 | func (l *NamedLogger) Warnf(format string, params ...interface{}) {
49 | format, params = l.convert(format, params)
50 | l.Logger.Warnf(format, params...)
51 | }
52 |
53 | // Errorf formats message according to format specifier and writes to
54 | // to log with LevelError.
55 | func (l *NamedLogger) Errorf(format string, params ...interface{}) {
56 | format, params = l.convert(format, params)
57 | l.Logger.Errorf(format, params...)
58 | }
59 |
60 | // Criticalf formats message according to format specifier and writes to
61 | // log with LevelCritical.
62 | func (l *NamedLogger) Criticalf(format string, params ...interface{}) {
63 | format, params = l.convert(format, params)
64 | l.Logger.Criticalf(format, params...)
65 | }
66 |
67 | // Trace formats message using the default formats for its operands
68 | // and writes to log with LevelTrace.
69 | func (l *NamedLogger) Trace(v ...interface{}) {
70 | var k []interface{}
71 | k = append(k, fmt.Sprintf("(%v)", l.Name))
72 | k = append(k, v...)
73 | l.Logger.Trace(k...)
74 | }
75 |
76 | // Debug formats message using the default formats for its operands
77 | // and writes to log with LevelDebug.
78 | func (l *NamedLogger) Debug(v ...interface{}) {
79 | var k []interface{}
80 | k = append(k, fmt.Sprintf("(%v)", l.Name))
81 | k = append(k, v...)
82 | l.Logger.Debug(k...)
83 | }
84 |
85 | // Info formats message using the default formats for its operands
86 | // and writes to log with LevelInfo.
87 | func (l *NamedLogger) Info(v ...interface{}) {
88 | var k []interface{}
89 | k = append(k, fmt.Sprintf("(%v)", l.Name))
90 | k = append(k, v...)
91 | l.Logger.Info(k...)
92 | }
93 |
94 | // Warn formats message using the default formats for its operands
95 | // and writes to log with LevelWarn.
96 | func (l *NamedLogger) Warn(v ...interface{}) {
97 | var k []interface{}
98 | k = append(k, fmt.Sprintf("(%v)", l.Name))
99 | k = append(k, v...)
100 | l.Logger.Warn(k...)
101 | }
102 |
103 | // Error formats message using the default formats for its operands
104 | // and writes to log with LevelError.
105 | func (l *NamedLogger) Error(v ...interface{}) {
106 | var k []interface{}
107 | k = append(k, fmt.Sprintf("(%v)", l.Name))
108 | k = append(k, v...)
109 | l.Logger.Error(k...)
110 | }
111 |
112 | // Critical formats message using the default formats for its operands
113 | // and writes to log with LevelCritical.
114 | func (l *NamedLogger) Critical(v ...interface{}) {
115 | var k []interface{}
116 | k = append(k, fmt.Sprintf("(%v)", l.Name))
117 | k = append(k, v...)
118 | l.Logger.Critical(k...)
119 | }
120 |
--------------------------------------------------------------------------------
/common/utils.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "strings"
5 | "runtime"
6 | )
7 |
8 | // GetFunctionName() returns name of the function within which it executed.
9 | func GetFunctionName() string {
10 | pc, _, _, _ := runtime.Caller(1)
11 | fullName := runtime.FuncForPC(pc).Name()
12 | parts := strings.Split(fullName, ".")
13 | return parts[len(parts)-1]
14 | }
15 |
--------------------------------------------------------------------------------
/connectors/daemons/bitcoind/tx_test.go:
--------------------------------------------------------------------------------
1 | package bitcoind
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors/rpc"
5 | "github.com/btcsuite/btcutil"
6 | "testing"
7 | )
8 |
9 | // TestSimpleCreateReorganisationOutputs test creation of outputs without
10 | // specifying fee, to just check general logic.
11 | func TestSimpleCreateReorganisationOutputs(t *testing.T) {
12 | feeRatePerByte := uint64(0)
13 |
14 | outputValue := 1.0
15 | inputs := []rpc.UnspentInput{
16 | {
17 | Amount: outputValue,
18 | },
19 | {
20 | Amount: outputValue,
21 | },
22 | }
23 |
24 | var overallUTXOAmount btcutil.Amount
25 | for _, input := range inputs {
26 | amount, _ := btcutil.NewAmount(input.Amount)
27 | overallUTXOAmount += amount
28 | }
29 |
30 | utxoValue, _ := btcutil.NewAmount(0.5)
31 | outputAmounts, fee, err := createReorganisationOutputs(feeRatePerByte,
32 | inputs, utxoValue)
33 | if err != nil {
34 | t.Fatalf("unable craft reoganisation outputs: %v", err)
35 | }
36 |
37 | overallOutputsAmount := btcutil.Amount(0)
38 | for _, amount := range outputAmounts {
39 | overallOutputsAmount += amount
40 | }
41 |
42 | if (overallUTXOAmount - overallOutputsAmount) != fee {
43 | t.Fatalf("returned fee is wrong")
44 | }
45 |
46 | if len(outputAmounts) != 4 {
47 | t.Fatalf("wrong outputs number")
48 | }
49 | }
50 |
51 | // TestCreateReorganisationOutputsWithFee take real case, and check that
52 | // function will behave properly.
53 | func TestCreateReorganisationOutputsWithFee(t *testing.T) {
54 | feeRatePerByte := uint64(10)
55 |
56 | outputValue := 0.5
57 | inputs := []rpc.UnspentInput{
58 | {
59 | Amount: outputValue,
60 | },
61 | {
62 | Amount: outputValue,
63 | },
64 | }
65 |
66 | var overallUTXOAmount btcutil.Amount
67 | for _, input := range inputs {
68 | amount, _ := btcutil.NewAmount(input.Amount)
69 | overallUTXOAmount += amount
70 | }
71 |
72 | outputAmounts, fee, err := createReorganisationOutputs(feeRatePerByte,
73 | inputs, optimalUTXOValue)
74 | if err != nil {
75 | t.Fatalf("unable craft reoganisation outputs: %v", err)
76 | }
77 |
78 | overallOutputsAmount := btcutil.Amount(0)
79 | for _, amount := range outputAmounts {
80 | overallOutputsAmount += amount
81 | }
82 |
83 | if (overallUTXOAmount - overallOutputsAmount) != fee {
84 | t.Fatalf("returned fee is wrong")
85 | }
86 |
87 | if len(outputAmounts) != 124 {
88 | t.Fatalf("wrong number of outputs")
89 | }
90 |
91 | // Check all outputs except the last one which pays the fees,
92 | // than they are equal to the optimal value.
93 | for i := 0; i < len(outputAmounts)-1; i++ {
94 | if outputAmounts[i] != optimalUTXOValue {
95 | t.Fatalf("wrong output amount")
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/connectors/daemons/bitcoind/utils.go:
--------------------------------------------------------------------------------
1 | package bitcoind
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/bitlum/connector/connectors"
7 | "github.com/bitlum/connector/connectors/rpc/bitcoin"
8 | "github.com/bitlum/connector/connectors/rpc/bitcoincash"
9 | "github.com/bitlum/connector/connectors/rpc/dash"
10 | "github.com/bitlum/connector/connectors/rpc/litecoin"
11 | "github.com/btcsuite/btcd/chaincfg"
12 | "github.com/btcsuite/btcutil"
13 | "github.com/go-errors/errors"
14 | "github.com/shopspring/decimal"
15 | )
16 |
17 | var satoshiPerBitcoin = decimal.New(btcutil.SatoshiPerBitcoin, 0)
18 |
19 | func decAmount2Sat(amount decimal.Decimal) btcutil.Amount {
20 | // If we would try to convert amount in float representation than it
21 | // could lead to precious error, for that reason convert in manually rather
22 | // than using btcutil.NewAmount().
23 | return btcutil.Amount(amount.Mul(satoshiPerBitcoin).IntPart())
24 | }
25 |
26 | func sat2DecAmount(amount btcutil.Amount) decimal.Decimal {
27 | amt := decimal.NewFromBigInt(big.NewInt(int64(amount)), 0)
28 | return amt.Div(satoshiPerBitcoin)
29 | }
30 |
31 | func printAmount(a btcutil.Amount) string {
32 | return decimal.NewFromFloat(a.ToBTC()).Round(8).String()
33 | }
34 |
35 | func isProperNet(desiredNet, actualNet string) bool {
36 | // Handle the case of different simulation networks names
37 | if desiredNet == "simnet" && actualNet == "regtest" {
38 | return true
39 | }
40 |
41 | // Handle the case of different testnet networks names
42 | if desiredNet == "testnet" && actualNet == "test" {
43 | return true
44 | }
45 |
46 | // Handle the case of different mainnet networks names
47 | if desiredNet == "mainnet" && actualNet == "main" {
48 | return true
49 | }
50 |
51 | return desiredNet == actualNet
52 | }
53 |
54 | func decodeAddress(asset connectors.Asset, address,
55 | network string) (btcutil.Address, error) {
56 | switch asset {
57 | case connectors.BTC:
58 | return bitcoin.DecodeAddress(address, network)
59 | case connectors.LTC:
60 | return litecoin.DecodeAddress(address, network)
61 | case connectors.BCH:
62 | return bitcoincash.DecodeAddress(address, network)
63 | case connectors.DASH:
64 | return dash.DecodeAddress(address, network)
65 | default:
66 | return nil, errors.Errorf("unsupported asset asset(%v)", asset)
67 | }
68 | }
69 |
70 | func getParams(asset connectors.Asset, network string) (*chaincfg.Params, error) {
71 | switch asset {
72 | case connectors.BTC:
73 | return bitcoin.GetParams(network)
74 | case connectors.LTC:
75 | return litecoin.GetParams(network)
76 | case connectors.BCH:
77 | return bitcoincash.GetParams(network)
78 | case connectors.DASH:
79 | return dash.GetParams(network)
80 | default:
81 | return nil, errors.Errorf("unsupported asset asset(%v)", asset)
82 | }
83 | }
84 |
85 | func accountToAlias(account string) connectors.AccountAlias {
86 | switch account {
87 | case defaultAccount:
88 | return connectors.DefaultAccount
89 |
90 | case allAccounts:
91 | return connectors.AllAccounts
92 |
93 | default:
94 | return connectors.AccountAlias(account)
95 | }
96 | }
97 |
98 | func aliasToAccount(alias connectors.AccountAlias) string {
99 | switch alias {
100 | case connectors.SentAccount:
101 | // In bitcoin-like asset you daemons is working in a way that you
102 | // could use use all the money from all accounts.
103 | return allAccounts
104 |
105 | case connectors.DefaultAccount:
106 | return defaultAccount
107 |
108 | case connectors.AllAccounts:
109 | return allAccounts
110 |
111 | default:
112 | return string(alias)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/connectors/daemons/bitcoind/utils_test.go:
--------------------------------------------------------------------------------
1 | package bitcoind
2 |
3 | import (
4 | "github.com/shopspring/decimal"
5 | "testing"
6 | )
7 |
8 | func TestDec2Amount(t *testing.T) {
9 | amt, err := decimal.NewFromString("0.01688044695939197")
10 | if err != nil {
11 | t.Fatalf("unable convert amount: %v", err)
12 | }
13 |
14 | if decAmount2Sat(amt) != 1688044 {
15 | t.Fatalf("wrong amount")
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/connectors/daemons/bitcoind_simple/storage.go:
--------------------------------------------------------------------------------
1 | package bitcoind_simple
2 |
3 | // StateStorage is used to keep data which is needed for connector to
4 | // properly synchronise and track transactions.
5 | //
6 | // NOTE: This storage should be persistent.
7 | type StateStorage interface {
8 | // PutLastSyncedTxCounter is used to save last synchronised confirmed tx
9 | // counter.
10 | PutLastSyncedTxCounter(counter int) error
11 |
12 | // LastTxCounter is used to retrieve last synchronised confirmed tx
13 | // counter.
14 | LastTxCounter() (int, error)
15 | }
16 |
--------------------------------------------------------------------------------
/connectors/daemons/bitcoind_simple/utils.go:
--------------------------------------------------------------------------------
1 | package bitcoind_simple
2 |
3 | import (
4 | "math/big"
5 |
6 | "github.com/bitlum/connector/connectors"
7 | "github.com/bitlum/connector/connectors/rpc/bitcoin"
8 | "github.com/bitlum/connector/connectors/rpc/bitcoincash"
9 | "github.com/bitlum/connector/connectors/rpc/dash"
10 | "github.com/bitlum/connector/connectors/rpc/litecoin"
11 | "github.com/btcsuite/btcd/chaincfg"
12 | "github.com/btcsuite/btcutil"
13 | "github.com/go-errors/errors"
14 | "github.com/shopspring/decimal"
15 | )
16 |
17 | var satoshiPerBitcoin = decimal.New(btcutil.SatoshiPerBitcoin, 0)
18 |
19 | func decAmount2Sat(amount decimal.Decimal) btcutil.Amount {
20 | // If we would try to convert amount in float representation than it
21 | // could lead to precious error, for that reason convert in manually rather
22 | // than using btcutil.NewAmount().
23 | return btcutil.Amount(amount.Mul(satoshiPerBitcoin).IntPart())
24 | }
25 |
26 | func sat2DecAmount(amount btcutil.Amount) decimal.Decimal {
27 | amt := decimal.NewFromBigInt(big.NewInt(int64(amount)), 0)
28 | return amt.Div(satoshiPerBitcoin).Round(8)
29 | }
30 |
31 | func printAmount(a btcutil.Amount) string {
32 | return decimal.NewFromFloat(a.ToBTC()).Round(8).String()
33 | }
34 |
35 | func isProperNet(desiredNet, actualNet string) bool {
36 | // Handle the case of different simulation networks names
37 | if desiredNet == "simnet" && actualNet == "regtest" {
38 | return true
39 | }
40 |
41 | // Handle the case of different testnet networks names
42 | if desiredNet == "testnet" && actualNet == "test" {
43 | return true
44 | }
45 |
46 | // Handle the case of different mainnet networks names
47 | if desiredNet == "mainnet" && actualNet == "main" {
48 | return true
49 | }
50 |
51 | return desiredNet == actualNet
52 | }
53 |
54 | func decodeAddress(asset connectors.Asset, address,
55 | network string) (btcutil.Address, error) {
56 | switch asset {
57 | case connectors.BTC:
58 | return bitcoin.DecodeAddress(address, network)
59 | case connectors.LTC:
60 | return litecoin.DecodeAddress(address, network)
61 | case connectors.BCH:
62 | return bitcoincash.DecodeAddress(address, network)
63 | case connectors.DASH:
64 | return dash.DecodeAddress(address, network)
65 | default:
66 | return nil, errors.Errorf("unsupported asset asset(%v)", asset)
67 | }
68 | }
69 |
70 | func getParams(asset connectors.Asset, network string) (*chaincfg.Params, error) {
71 | switch asset {
72 | case connectors.BTC:
73 | return bitcoin.GetParams(network)
74 | case connectors.LTC:
75 | return litecoin.GetParams(network)
76 | case connectors.BCH:
77 | return bitcoincash.GetParams(network)
78 | case connectors.DASH:
79 | return dash.GetParams(network)
80 | default:
81 | return nil, errors.Errorf("unsupported asset asset(%v)", asset)
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/connectors/daemons/geth/client_test.go:
--------------------------------------------------------------------------------
1 | package geth
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "math/big"
8 |
9 | "github.com/onrik/ethrpc"
10 | )
11 |
12 | func TestClient(t *testing.T) {
13 | client := ExtendedEthRpc{ethrpc.NewEthRPC("http://165.227.118.113:20306")}
14 |
15 | version, err := client.Web3ClientVersion()
16 | if err != nil {
17 | t.Fatalf("unable to get version: %v", err)
18 | }
19 |
20 | number, err := client.EthNewBlockFilter()
21 | if err != nil {
22 | t.Fatalf("unable to get version: %v", err)
23 | }
24 |
25 | pversion, err := client.EthProtocolVersion()
26 | if err != nil {
27 | t.Fatalf("unable to get version: %v", err)
28 | }
29 |
30 | nversion, err := client.NetVersion()
31 | if err != nil {
32 | t.Fatalf("unable to get version: %v", err)
33 | }
34 |
35 | account, err := client.PersonalNewAddress("kek")
36 | if err != nil {
37 | t.Fatalf("unable to get version: %v", err)
38 | }
39 |
40 | fmt.Println("version:", version)
41 | fmt.Println("number:", number)
42 | fmt.Println("pversion:", pversion)
43 | fmt.Println("nversion:", nversion)
44 | fmt.Println("account:", account)
45 | }
46 |
47 | func TestSignTransaction(t *testing.T) {
48 | client := ExtendedEthRpc{ethrpc.NewEthRPC("http://165.227.118.113:20306")}
49 |
50 | address, err := client.PersonalNewAddress("kek")
51 | if err != nil {
52 | t.Fatalf("unable to get version: %v", err)
53 | }
54 |
55 | _, err = client.PersonalUnlockAddress(address, "kek", 2)
56 | if err != nil {
57 | t.Fatalf("unable to unlock account: %v", err)
58 | }
59 |
60 | _, _, err = client.EthSignTransaction(ethrpc.T{
61 | From: address,
62 | To: address,
63 | Gas: 21000,
64 | GasPrice: big.NewInt(21),
65 | Value: big.NewInt(1000),
66 | Data: "",
67 | Nonce: 0,
68 | })
69 | if err != nil {
70 | t.Fatalf("unable to sign tx: %v", err)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/connectors/daemons/geth/errors.go:
--------------------------------------------------------------------------------
1 | package geth
2 |
3 | import "github.com/go-errors/errors"
4 |
5 | var (
6 | ErrAccountAddressNotFound = errors.Errorf("unable to find account address")
7 | )
8 |
--------------------------------------------------------------------------------
/connectors/daemons/geth/extension.go:
--------------------------------------------------------------------------------
1 | package geth
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/onrik/ethrpc"
7 | )
8 |
9 | type ExtendedEthRpc struct {
10 | *ethrpc.EthRPC
11 | }
12 |
13 | func (c *ExtendedEthRpc) PersonalNewAddress(pass string) (string, error) {
14 | var res string
15 | err := c.call("personal_newAccount", &res, pass)
16 | return res, err
17 | }
18 |
19 | func (c *ExtendedEthRpc) PersonalUnlockAddress(address,
20 | pass string, delay int) (bool, error) {
21 | var res bool
22 | err := c.call("personal_unlockAccount", &res, address, pass, delay)
23 | return res, err
24 | }
25 |
26 | func (c *ExtendedEthRpc) EthSignTransaction(t ethrpc.T) (*ethrpc.Transaction,
27 | string, error) {
28 | var resp struct {
29 | Tx *ethrpc.Transaction
30 | Raw string
31 | }
32 | err := c.call("eth_signTransaction", &resp, t)
33 | if err != nil {
34 | return nil, "", err
35 | }
36 |
37 | return resp.Tx, resp.Raw, err
38 | }
39 |
40 | // EthGetPendingTxs returns transactions from daemon mempool which are
41 | // belongs to one of our accounts.
42 | //
43 | // NOTE: Return both incoming pending transaction because we use
44 | // changed version of the geth client.
45 | func (c *ExtendedEthRpc) EthGetPendingTxs() ([]ethrpc.Transaction, error) {
46 | var txs []ethrpc.Transaction
47 | err := c.call("eth_pendingTransactions", &txs)
48 | return txs, err
49 | }
50 |
51 | // EthGasPrice returns the current price per gas in wei.
52 | func (c *ExtendedEthRpc) EthGasPrice() (string, error) {
53 | var response string
54 | if err := c.call("eth_gasPrice", &response); err != nil {
55 | return "", err
56 | }
57 |
58 | return response, nil
59 | }
60 |
61 | func (c *ExtendedEthRpc) call(method string, target interface{},
62 | params ...interface{}) error {
63 | result, err := c.Call(method, params...)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | if target == nil {
69 | return nil
70 | }
71 |
72 | if err := json.Unmarshal(result, target); err != nil {
73 | return err
74 | }
75 |
76 | return nil
77 | }
78 |
--------------------------------------------------------------------------------
/connectors/daemons/geth/storage.go:
--------------------------------------------------------------------------------
1 | package geth
2 |
3 | // AccountsStorage is used to keep track connections between addresses and
4 | // accounts, because of the reason of Ethereum client not having this mapping
5 | // internally.
6 | //
7 | // NOTE: This storage has to be persistent.
8 | type AccountsStorage interface {
9 | // GetAccountByAddress returns account by given address.
10 | GetAccountByAddress(address string) (string, error)
11 |
12 | // GetAddressesByAccount returns addressed belonging to the given account.
13 | GetAddressesByAccount(account string) ([]string, error)
14 |
15 | // GetLastAccountAddress returns last address which were assigned to
16 | // account.
17 | GetLastAccountAddress(account string) (string, error)
18 |
19 | // AddAddressToAccount assigns new address to account.
20 | AddAddressToAccount(address, account string) error
21 |
22 | // AllAddresses returns all created addresses.
23 | AllAddresses() ([]string, error)
24 |
25 | // PutDefaultAddressNonce puts returns default address transaction nonce.
26 | // This method is needed because if we send transaction too frequently
27 | // ethereum transaction counter couldn't keep up and transaction fails,
28 | // because of replacement error.
29 | PutDefaultAddressNonce(nonce int) error
30 |
31 | // DefaultAddressNonce returns default address transaction nonce.
32 | // This method is needed because if we send transaction too frequently
33 | // ethereum transaction counter couldn't keep up and transaction fails,
34 | // because of replacement error.
35 | DefaultAddressNonce() (int, error)
36 | }
37 |
--------------------------------------------------------------------------------
/connectors/daemons/geth/utils.go:
--------------------------------------------------------------------------------
1 | package geth
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "github.com/ethereum/go-ethereum/params"
6 | )
7 |
8 | // pendingMap stores the information about pending transactions corresponding
9 | // to accounts.
10 | type pendingMap map[string]map[string]*connectors.Payment
11 |
12 | func (m pendingMap) add(tx *connectors.Payment) {
13 | if _, ok := m[tx.Account]; !ok {
14 | m[tx.Account] = make(map[string]*connectors.Payment)
15 | }
16 |
17 | m[tx.Account][tx.PaymentID] = tx
18 | }
19 |
20 | // merge merges two pending maps and invokes handler with new entry populated
21 | // as an argument.
22 | func (m pendingMap) merge(m2 pendingMap,
23 | newEntryHandler func(tx *connectors.Payment)) {
24 | for account, txs := range m2 {
25 | // Add all txs if there is no transaction for this
26 | // account and continue.
27 | if _, ok := m[account]; !ok {
28 | m[account] = txs
29 |
30 | for _, tx := range txs {
31 | newEntryHandler(tx)
32 | }
33 |
34 | continue
35 | }
36 |
37 | // If account exist that we should populate it
38 | // with transactions which aren't there yet.
39 | for txid, tx := range txs {
40 | if _, ok := m[account][txid]; !ok {
41 | m[account][txid] = tx
42 | newEntryHandler(tx)
43 | }
44 | }
45 | }
46 |
47 | for account, txs := range m {
48 | if _, ok := m2[account]; !ok {
49 | delete(m, account)
50 | continue
51 | }
52 |
53 | for txid, _ := range txs {
54 | if _, ok := m[account][txid]; !ok {
55 | delete(m[account], txid)
56 | }
57 | }
58 | }
59 | }
60 |
61 | func convertVersion(actualNet string) string {
62 | net := "simnet"
63 |
64 | switch actualNet {
65 | case params.RinkebyChainConfig.ChainID.String():
66 | net = "testnet"
67 | case params.MainnetChainConfig.ChainID.String():
68 | net = "mainnet"
69 | }
70 |
71 | return net
72 | }
73 |
74 | // generatePaymentID generates unique string based on the tx id and receive
75 | // address, which are together
76 | func generatePaymentID(txID, receiveAddress string,
77 | direction connectors.PaymentDirection, system connectors.PaymentSystem) string {
78 | return connectors.GeneratePaymentID(txID, receiveAddress,
79 | string(direction), string(system))
80 | }
81 |
--------------------------------------------------------------------------------
/connectors/daemons/lnd/log.go:
--------------------------------------------------------------------------------
1 | package lnd
2 |
3 | import (
4 | "github.com/btcsuite/btclog"
5 | )
6 |
7 | // log is a logger that is initialized with no output filters. This
8 | // means the package will not perform any logging by default until the caller
9 | // requests it.
10 | var log btclog.Logger
11 |
12 | // The default amount of logging is none.
13 | func init() {
14 | DisableLog()
15 | }
16 |
17 | // DisableLog disables all library log output. Logging output is disabled
18 | // by default until UseLogger is called.
19 | func DisableLog() {
20 | log = btclog.Disabled
21 | }
22 |
23 | // UseLogger uses a specified Logger to output package logging info.
24 | // This should be used in preference to SetLogWriter if the caller is also
25 | // using btclog.
26 | func UseLogger(logger btclog.Logger) {
27 | log = logger
28 | }
29 |
30 | // logClosure is used to provide a closure over expensive logging operations
31 | // so don't have to be performed when the logging level doesn't warrant it.
32 | type logClosure func() string
33 |
34 | // String invokes the underlying function and returns the result.
35 | func (c logClosure) String() string {
36 | return c()
37 | }
38 |
39 | // newLogClosure returns a new closure over a function that returns a string
40 | // which itself provides a Stringer interface so that it can be used with the
41 | // logging system.
42 | func newLogClosure(c func() string) logClosure {
43 | return logClosure(c)
44 | }
45 |
--------------------------------------------------------------------------------
/connectors/daemons/lnd/utils.go:
--------------------------------------------------------------------------------
1 | package lnd
2 |
3 | import (
4 | "github.com/btcsuite/btcutil"
5 | "github.com/pkg/errors"
6 | "github.com/shopspring/decimal"
7 | "math/big"
8 | "github.com/bitlum/connector/connectors"
9 | "io/ioutil"
10 | "strconv"
11 | "google.golang.org/grpc/credentials"
12 | "github.com/lightningnetwork/lnd/lnrpc"
13 | "google.golang.org/grpc"
14 | "gopkg.in/macaroon.v2"
15 | "github.com/lightningnetwork/lnd/macaroons"
16 | "net"
17 | )
18 |
19 | var satoshiPerBitcoin = decimal.New(btcutil.SatoshiPerBitcoin, 0)
20 |
21 | func btcToSatoshi(amount string) (int64, error) {
22 | amt, err := decimal.NewFromString(amount)
23 | if err != nil {
24 | return 0, errors.Errorf("unable to parse amount(%v): %v",
25 | amount, err)
26 | }
27 |
28 | a, _ := amt.Float64()
29 | btcAmount, err := btcutil.NewAmount(a)
30 | if err != nil {
31 | return 0, errors.Errorf("unable to parse amount(%v): %v", a, err)
32 | }
33 |
34 | return int64(btcAmount), nil
35 | }
36 |
37 | func sat2DecAmount(amount btcutil.Amount) decimal.Decimal {
38 | amt := decimal.NewFromBigInt(big.NewInt(int64(amount)), 0)
39 | return amt.Div(satoshiPerBitcoin).Round(8)
40 | }
41 |
42 | func generatePaymentID(invoiceStr string,
43 | direction connectors.PaymentDirection) string {
44 | return connectors.GeneratePaymentID(invoiceStr, string(direction))
45 | }
46 |
47 | // getClient return lightning network grpc client.
48 | func (c *Connector) getClient(macaroonPath string) (lnrpc.LightningClient,
49 | *grpc.ClientConn, error) {
50 |
51 | creds, err := credentials.NewClientTLSFromFile(c.cfg.TlsCertPath, "")
52 | if err != nil {
53 | return nil, nil, errors.Errorf("unable to load credentials: %v", err)
54 | }
55 |
56 | opts := []grpc.DialOption{
57 | grpc.WithTransportCredentials(creds),
58 | }
59 |
60 | if macaroonPath != "" {
61 | macaroonBytes, err := ioutil.ReadFile(macaroonPath)
62 | if err != nil {
63 | return nil, nil, errors.Errorf("Unable to read macaroon file: %v", err)
64 | }
65 |
66 | mac := &macaroon.Macaroon{}
67 | if err = mac.UnmarshalBinary(macaroonBytes); err != nil {
68 | return nil, nil, errors.Errorf("Unable to unmarshal macaroon: %v", err)
69 | }
70 |
71 | opts = append(opts,
72 | grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)))
73 | }
74 |
75 | target := net.JoinHostPort(c.cfg.Host, strconv.Itoa(c.cfg.Port))
76 | log.Infof("lightning client connection to lnd: %v", target)
77 |
78 | conn, err := grpc.Dial(target, opts...)
79 | if err != nil {
80 | return nil, nil, errors.Errorf("unable to to dial grpc: %v", err)
81 | }
82 |
83 | return lnrpc.NewLightningClient(conn), conn, nil
84 | }
85 |
--------------------------------------------------------------------------------
/connectors/interface.go:
--------------------------------------------------------------------------------
1 | package connectors
2 |
3 | import (
4 | "github.com/lightningnetwork/lnd/lnrpc"
5 | "github.com/lightningnetwork/lnd/zpay32"
6 | "github.com/shopspring/decimal"
7 | )
8 |
9 | type LightningInfo struct {
10 | Host string
11 | Port string
12 | MinAmount string
13 | MaxAmount string
14 | *lnrpc.GetInfoResponse
15 | }
16 |
17 | // BlockchainConnector is an interface which describes the blockchain service
18 | // which is able to connect to blockchain daemon of particular currency and
19 | // operate with transactions, addresses, and also able to notify other
20 | // subsystems when transaction passes required number of confirmations.
21 | type BlockchainConnector interface {
22 | // CreateAddress is used to create deposit address.
23 | CreateAddress() (string, error)
24 |
25 | // ConfirmedBalance return the amount of confirmed funds available for account.
26 | ConfirmedBalance() (decimal.Decimal, error)
27 |
28 | // PendingBalance return the amount of funds waiting to be confirmed.
29 | PendingBalance() (decimal.Decimal, error)
30 |
31 | // SendPayment sends payment with given amount to the given address.
32 | SendPayment(address, amount string) (*Payment, error)
33 |
34 | // ValidateAddress takes the blockchain address and ensure its valid.
35 | ValidateAddress(address string) error
36 |
37 | // EstimateFee estimate fee for the transaction with the given sending
38 | // amount.
39 | EstimateFee(amount string) (decimal.Decimal, error)
40 | }
41 |
42 | // LightningConnector is an interface which describes the service
43 | // which is able to connect lightning network daemon of particular currency and
44 | // operate with transactions, addresses, and also able to notify other
45 | // subsystems when invoice is settled.
46 | type LightningConnector interface {
47 | // Info returns the information about our lnd node.
48 | Info() (*LightningInfo, error)
49 |
50 | // CreateInvoice is used to create lightning network invoice.
51 | CreateInvoice(receipt, amount, description string) (string,
52 | *zpay32.Invoice, error)
53 |
54 | // SendTo is used to send specific amount of money to address within this
55 | // payment system.
56 | SendTo(invoice, amount string) (*Payment, error)
57 |
58 | // ConfirmedBalance return the amount of confirmed funds available for account.
59 | // TODO(andrew.shvv) Implement lightning wallet balance
60 | ConfirmedBalance() (decimal.Decimal, error)
61 |
62 | // PendingBalance return the amount of funds waiting to be confirmed.
63 | // TODO(andrew.shvv) Implement lightning wallet balance
64 | PendingBalance() (decimal.Decimal, error)
65 |
66 | // QueryRoutes returns list of routes from to the given lnd node,
67 | // and insures the the capacity of the channels is sufficient.
68 | QueryRoutes(pubKey, amount string, limit int32) ([]*lnrpc.Route, error)
69 |
70 | // ValidateInvoice takes the encoded lightning network invoice and ensure
71 | // its valid.
72 | ValidateInvoice(invoice, amount string) (*zpay32.Invoice, error)
73 |
74 | // EstimateFee estimate fee for the payment with the given sending
75 | // amount, to the given node.
76 | EstimateFee(invoice string) (decimal.Decimal, error)
77 | }
78 |
--------------------------------------------------------------------------------
/connectors/payment_details.go:
--------------------------------------------------------------------------------
1 | package connectors
2 |
3 | import (
4 | "io"
5 | "encoding/json"
6 | "io/ioutil"
7 | )
8 |
9 | // Serializable is an interface which defines a serializable
10 | // object.
11 | type Serializable interface {
12 | // Decode reads the bytes stream and converts it to the object.
13 | Decode(io.Reader, uint32) error
14 |
15 | // Encode converts object to the bytes stream and write it into the
16 | // writer.
17 | Encode(io.Writer, uint32) error
18 | }
19 |
20 | // Runtime check to ensure that BlockchainPendingDetails implements
21 | // Serializable interface.
22 | var _ Serializable = (*BlockchainPendingDetails)(nil)
23 |
24 | // Decode reads the bytes stream and converts it to the object.
25 | func (d *BlockchainPendingDetails) Decode(r io.Reader, v uint32) error {
26 | data, err := ioutil.ReadAll(r)
27 | if err != nil {
28 | return err
29 | }
30 |
31 | return json.Unmarshal(data, d)
32 | }
33 |
34 | // Encode converts object to the bytes stream and write it into the
35 | // writer.
36 | func (d *BlockchainPendingDetails) Encode(w io.Writer, v uint32) error {
37 | data, err := json.Marshal(d)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | _, err = w.Write(data)
43 | return err
44 | }
45 |
46 | // GeneratedTxDetails is the string form of signed blockchain transaction
47 | // which.
48 | type GeneratedTxDetails struct {
49 | // RawTx byte representation of blockchain transaction.
50 | RawTx []byte
51 |
52 | // TxID blockchain identification of transaction.
53 | TxID string
54 | }
55 |
56 | // Runtime check to ensure that BlockchainPendingDetails implements
57 | // Serializable interface.
58 | var _ Serializable = (*GeneratedTxDetails)(nil)
59 |
60 | // Decode reads the bytes stream and converts it to the object.
61 | func (d *GeneratedTxDetails) Decode(r io.Reader, v uint32) error {
62 | data, err := ioutil.ReadAll(r)
63 | if err != nil {
64 | return err
65 | }
66 |
67 | return json.Unmarshal(data, d)
68 | }
69 |
70 | // Encode converts object to the bytes stream and write it into the
71 | // writer.
72 | func (d *GeneratedTxDetails) Encode(w io.Writer, v uint32) error {
73 | data, err := json.Marshal(d)
74 | if err != nil {
75 | return err
76 | }
77 |
78 | _, err = w.Write(data)
79 | return err
80 | }
81 |
--------------------------------------------------------------------------------
/connectors/payment_details_test.go:
--------------------------------------------------------------------------------
1 | package connectors
2 |
3 | import (
4 | "testing"
5 | "bytes"
6 | "reflect"
7 | )
8 |
9 | func TestBlockchainPendingDetailsEncodeDecode(t *testing.T) {
10 | d := &BlockchainPendingDetails{
11 | Confirmations: 1,
12 | ConfirmationsLeft: 3,
13 | }
14 |
15 | var b bytes.Buffer
16 | if err := d.Encode(&b, 0); err != nil {
17 | t.Fatalf("unable to encode details: %v", err)
18 | }
19 |
20 | d1 := &BlockchainPendingDetails{}
21 | if err := d1.Decode(&b, 0); err != nil {
22 | t.Fatalf("unable to decode details: %v", err)
23 | }
24 |
25 | if !reflect.DeepEqual(d1, d) {
26 | t.Fatal("objects are different")
27 | }
28 | }
29 |
30 | func TestGeneratedTxDetailsEncodeDecode(t *testing.T) {
31 | d := &GeneratedTxDetails{
32 | RawTx: []byte("rawtx"),
33 | TxID: "txid",
34 | }
35 |
36 | var b bytes.Buffer
37 | if err := d.Encode(&b, 0); err != nil {
38 | t.Fatalf("unable to encode details: %v", err)
39 | }
40 |
41 | d1 := &GeneratedTxDetails{}
42 | if err := d1.Decode(&b, 0); err != nil {
43 | t.Fatalf("unable to encode details: %v", err)
44 | }
45 |
46 | if !reflect.DeepEqual(d1, d) {
47 | t.Fatal("objects are different")
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/connectors/rpc/bitcoin/params.go:
--------------------------------------------------------------------------------
1 | package bitcoin
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 | "github.com/go-errors/errors"
6 | )
7 |
8 | func GetParams(netName string) (*chaincfg.Params, error) {
9 | switch netName {
10 | case "mainnet", "main":
11 | return &chaincfg.MainNetParams, nil
12 | case "regtest", "simnet":
13 | return &chaincfg.RegressionNetParams, nil
14 | case "testnet3", "test", "testnet":
15 | return &chaincfg.TestNet3Params, nil
16 | }
17 |
18 | return nil, errors.Errorf("network '%s' is invalid or unsupported",
19 | netName)
20 | }
21 |
--------------------------------------------------------------------------------
/connectors/rpc/bitcoin/validation.go:
--------------------------------------------------------------------------------
1 | package bitcoin
2 |
3 | import (
4 | "github.com/btcsuite/btcutil"
5 | "github.com/go-errors/errors"
6 | )
7 |
8 | func DecodeAddress(address, netName string) (btcutil.Address, error) {
9 | netParams, err := GetParams(netName)
10 | if err != nil {
11 | return nil, errors.Errorf("unable to get net params: %v", err)
12 | }
13 |
14 | decodedAddress, err := btcutil.DecodeAddress(address, netParams)
15 | if err != nil {
16 | return nil, err
17 | }
18 |
19 | if !decodedAddress.IsForNet(netParams) {
20 | return nil, errors.New("address is not for specified network")
21 | }
22 |
23 | return decodedAddress, nil
24 | }
25 |
--------------------------------------------------------------------------------
/connectors/rpc/bitcoincash/client.go:
--------------------------------------------------------------------------------
1 | package bitcoincash
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors/rpc/bitcoin"
5 | "github.com/go-errors/errors"
6 | "github.com/bitlum/connector/connectors/rpc"
7 | "github.com/davecgh/go-spew/spew"
8 | "github.com/bitlum/connector/common"
9 | )
10 |
11 | type ClientConfig bitcoin.ClientConfig
12 | type Client struct {
13 | *bitcoin.Client
14 | }
15 |
16 | // Runtime check to ensure that Client implements rpc.Client interface.
17 | var _ rpc.Client = (*Client)(nil)
18 |
19 | func NewClient(cfg ClientConfig) (*Client, error) {
20 | client, err := bitcoin.NewClient(bitcoin.ClientConfig(cfg))
21 | return &Client{Client: client}, err
22 | }
23 |
24 | // NOTE: Part of the rpc.Client interface.
25 | func (c *Client) EstimateFee() (float64, error) {
26 | // Bitcoin Cash has removed estimatesmartfee in 17.2 version of their
27 | // client.
28 | res, err := c.Client.Daemon.EstimateFee(2)
29 | if err != nil {
30 | c.Logger.Tracef("method: %v, error: %v", err)
31 | return 0, err
32 | }
33 |
34 | if res == nil {
35 | err := errors.Errorf("result is nil")
36 | c.Logger.Tracef("method: %v, error: %v", err)
37 | return 0, err
38 | }
39 |
40 | feeRate := **res
41 | if feeRate <= 0 {
42 | err := errors.New("not enough data to make an estimation")
43 | c.Logger.Tracef("method: %v, error: %v", err)
44 | return 0, err
45 | }
46 |
47 | c.Logger.Tracef("method: %v, response: %v", common.GetFunctionName(),
48 | spew.Sdump(feeRate))
49 |
50 | return feeRate, nil
51 | }
52 |
--------------------------------------------------------------------------------
/connectors/rpc/bitcoincash/params.go:
--------------------------------------------------------------------------------
1 | package bitcoincash
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 | "github.com/btcsuite/btcd/wire"
6 | "github.com/go-errors/errors"
7 | )
8 |
9 | // chainIDPrefix is created to distinguish different chains during
10 | // the process of registration with btcutil mustRegister function.
11 | //
12 | // NOTE: This is needed because of the fact how btcutil DecodeAddress works,
13 | // it couldn't proper decode address if its networks wasn't previously
14 | // registered.
15 | var chainIDPrefix wire.BitcoinNet = 1
16 |
17 | var (
18 | // Mainnet represents the main test network.
19 | Mainnet = wire.MainNet + chainIDPrefix
20 |
21 | // TestNet represents the regression test network.
22 | TestNet = wire.TestNet + chainIDPrefix
23 |
24 | // TestNet3 represents the test network.
25 | TestNet3 = wire.TestNet3 + chainIDPrefix
26 | )
27 |
28 | // MainNetParams defines the network parameters for the main network.
29 | var MainNetParams = chaincfg.Params{
30 | Net: Mainnet,
31 | Name: "mainnet",
32 | PubKeyHashAddrID: 0, // addresses start with 'X'
33 | ScriptHashAddrID: 5, // script addresses start with '7'
34 | PrivateKeyID: 128, // private keys start with '7' or 'X'
35 |
36 | // BIP32 hierarchical deterministic extended key magics
37 | HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
38 | HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
39 | }
40 |
41 | // TestNet3Params defines the network parameters for the test network
42 | // (version 3). Not to be confused with the regression test network, this
43 | // network is sometimes simply called "testnet".
44 | var TestNet3Params = chaincfg.Params{
45 | Net: TestNet3,
46 | Name: "testnet3",
47 | PubKeyHashAddrID: 111,
48 | ScriptHashAddrID: 196,
49 | PrivateKeyID: 239,
50 |
51 | // BIP32 hierarchical deterministic extended key magics
52 | HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xCF},
53 | HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94},
54 | }
55 |
56 | // RegressionNetParams defines the network parameters for the regression test
57 | // Bitcoin Cash network. Not to be confused with the test network (version
58 | // 3), this network is sometimes simply called "testnet".
59 | var RegressionNetParams = chaincfg.Params{
60 | Net: TestNet,
61 | Name: "regtest",
62 | PubKeyHashAddrID: 111,
63 | ScriptHashAddrID: 196,
64 | PrivateKeyID: 239,
65 |
66 | // BIP32 hierarchical deterministic extended key magics
67 | HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xCF},
68 | HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94},
69 | }
70 |
71 | // mustRegister performs the same function as Register except it panics if there
72 | // is an error. This should only be called from package init functions.
73 | func mustRegister(params *chaincfg.Params) {
74 | if err := chaincfg.Register(params); err != nil &&
75 | err != chaincfg.ErrDuplicateNet {
76 | panic("failed to register network: " + err.Error())
77 | }
78 | }
79 |
80 | func init() {
81 | mustRegister(&MainNetParams)
82 | mustRegister(&TestNet3Params)
83 | mustRegister(&RegressionNetParams)
84 | }
85 |
86 | func GetParams(netName string) (*chaincfg.Params, error) {
87 | switch netName {
88 | case "mainnet", "main":
89 | return &MainNetParams, nil
90 | case "regtest", "simnet":
91 | return &RegressionNetParams, nil
92 | case "testnet3", "test", "testnet":
93 | return &TestNet3Params, nil
94 | }
95 |
96 | return nil, errors.Errorf("network '%s' is invalid or unsupported",
97 | netName)
98 | }
99 |
--------------------------------------------------------------------------------
/connectors/rpc/bitcoincash/validation.go:
--------------------------------------------------------------------------------
1 | package bitcoincash
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 | "github.com/btcsuite/btcutil"
6 | "github.com/go-errors/errors"
7 | cashAddr "github.com/schancel/cashaddr-converter/address"
8 | )
9 |
10 | // DecodeAddress validates bitcoin cash address according net,
11 | // and return btcutil compatible legacy bitcoin address. Supports cashaddr and
12 | // legacy addresses.
13 | //
14 | // NOTE: Regtest net treated as testnet 3.Ò
15 | func DecodeAddress(address, network string) (btcutil.Address, error) {
16 | netParams, err := GetParams(network)
17 | if err != nil {
18 | return nil, errors.Errorf("unable to get net params: %v", err)
19 | }
20 |
21 | // Check that address is valid, but if it return errors,
22 | // continue validation because it might be special "Cash Address" type.
23 | decodedAddress, err := btcutil.DecodeAddress(address, netParams)
24 | if err == nil {
25 | if decodedAddress.IsForNet(netParams) {
26 | return decodedAddress, nil
27 | } else {
28 | return nil, errors.New("address is not for specified network")
29 | }
30 | }
31 |
32 | cashAddress, err := cashAddr.NewFromString(address)
33 | if err != nil {
34 | return nil, errors.New("address neither legacy address nor cash addr")
35 | }
36 |
37 | legacyAddress, err := cashAddress.Legacy()
38 | if err != nil {
39 | return nil, errors.Errorf("unable convert to legacy address: %v", err)
40 | }
41 |
42 | address, err = legacyAddress.Encode()
43 | if err != nil {
44 | return nil, errors.Errorf("unable encode legacy address: %v", err)
45 | }
46 |
47 | decodedAddress, err = btcutil.DecodeAddress(address, netParams)
48 | if err == nil {
49 | if decodedAddress.IsForNet(netParams) {
50 | return decodedAddress, nil
51 | } else {
52 | return nil, errors.New("address is not for specified network")
53 | }
54 | }
55 |
56 | return decodedAddress, nil
57 | }
58 |
59 | func cashAddrNetToInt(networkType cashAddr.NetworkType) int {
60 | switch networkType {
61 | case cashAddr.MainNet:
62 | return 0
63 | case cashAddr.TestNet:
64 | return 1
65 | case cashAddr.RegTest:
66 | return 1
67 | }
68 | return 2
69 | }
70 |
71 | func bitcoinCashNetToInt(network *chaincfg.Params) int {
72 | switch network.Net {
73 | case Mainnet:
74 | return 0
75 | case TestNet3:
76 | return 1
77 | case TestNet:
78 | return 1
79 | }
80 | return 2
81 | }
82 |
--------------------------------------------------------------------------------
/connectors/rpc/dash/client.go:
--------------------------------------------------------------------------------
1 | package dash
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/bitlum/connector/common"
6 | "github.com/bitlum/connector/connectors/rpc"
7 | "github.com/bitlum/connector/connectors/rpc/bitcoin"
8 | "github.com/bitlum/go-bitcoind-rpc/btcjson"
9 | "github.com/bitlum/go-bitcoind-rpc/rpcclient"
10 | "github.com/davecgh/go-spew/spew"
11 | "github.com/go-errors/errors"
12 | )
13 |
14 | type ClientConfig bitcoin.ClientConfig
15 | type Client struct {
16 | *bitcoin.Client
17 | }
18 |
19 | // Runtime check to ensure that Client implements rpc.Client interface.
20 | var _ rpc.Client = (*Client)(nil)
21 |
22 | func NewClient(cfg ClientConfig) (*Client, error) {
23 | client, err := bitcoin.NewClient(bitcoin.ClientConfig(cfg))
24 | return &Client{Client: client}, err
25 | }
26 |
27 | type getDashBlockChainInfoResult struct {
28 | *btcjson.GetBlockChainInfoResult
29 |
30 | // Override initial btcjson field with interface in order to avoid json
31 | // unmarshal error, because in dash the format of this field is different.
32 | Bip9SoftForks interface{} `json:"bip9_softforks"`
33 | }
34 |
35 | func (c *Client) GetBlockChainInfo() (*rpc.BlockChainInfoResp, error) {
36 | res := c.Daemon.GetBlockChainInfoAsync()
37 | info, err := receiveDashInfo(res)
38 | if err != nil {
39 | c.Logger.Tracef("method: %v, error: %v", err)
40 | return nil, err
41 | }
42 |
43 | resp := &rpc.BlockChainInfoResp{
44 | Chain: info.Chain,
45 | }
46 |
47 | c.Logger.Tracef("method: %v, response: %v", common.GetFunctionName(),
48 | spew.Sdump(resp))
49 |
50 | return resp, nil
51 | }
52 |
53 | func receiveDashInfo(r rpcclient.FutureGetBlockChainInfoResult) (
54 | *getDashBlockChainInfoResult, error) {
55 |
56 | res, err := rpcclient.ReceiveFuture(r)
57 | if err != nil {
58 | return nil, err
59 | }
60 |
61 | var chainInfo getDashBlockChainInfoResult
62 | if err := json.Unmarshal(res, &chainInfo); err != nil {
63 | return nil, err
64 | }
65 | return &chainInfo, nil
66 | }
67 |
68 | func (c *Client) EstimateFee() (float64, error) {
69 | confTarget := uint32(2)
70 | res, err := c.Daemon.EstimateSmartFeeWithMode(confTarget, "")
71 | if err != nil {
72 | c.Logger.Tracef("method: %v, error: %v", common.GetFunctionName(), err)
73 | return 0, err
74 | }
75 |
76 | if res.Errors != nil {
77 | err := errors.New((*res.Errors)[0])
78 | c.Logger.Tracef("method: %v, error: %v", common.GetFunctionName(), err)
79 | return 0, err
80 | }
81 |
82 | if res.FeeRate == nil {
83 | err := errors.Errorf("fee rate is nil")
84 | c.Logger.Tracef("method: %v, error: %v", common.GetFunctionName(), err)
85 | return 0, err
86 | }
87 |
88 | if res.Blocks != int(confTarget) {
89 | err := errors.New("not enough data to make an estimation")
90 | c.Logger.Tracef("method: %v, error: %v", common.GetFunctionName(), err)
91 | return 0, err
92 | }
93 |
94 | feeRate := *res.FeeRate
95 | if feeRate <= 0 {
96 | err := errors.New("not enough data to make an estimation")
97 | c.Logger.Tracef("method: %v, error: %v", common.GetFunctionName(), err)
98 | return 0, err
99 | }
100 |
101 | c.Logger.Tracef("method: %v, response: %v", common.GetFunctionName(),
102 | spew.Sdump(feeRate))
103 |
104 | return feeRate, nil
105 | }
--------------------------------------------------------------------------------
/connectors/rpc/dash/params.go:
--------------------------------------------------------------------------------
1 | package dash
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 | "github.com/btcsuite/btcd/wire"
6 | "github.com/go-errors/errors"
7 | )
8 |
9 | // chainIDPrefix is created to distinguish different chains during
10 | // the process of registration with btcutil mustRegister function.
11 | //
12 | // NOTE: This is needed because of the fact how btcutil DecodeAddress works,
13 | // it couldn't proper decode address if its networks wasn't previously
14 | // registered.
15 | var chainIDPrefix wire.BitcoinNet = 2
16 |
17 | var (
18 | // Mainnet represents the main network.
19 | Mainnet = wire.MainNet + chainIDPrefix
20 |
21 | // TestNet represents the regression network.
22 | TestNet = wire.TestNet + chainIDPrefix
23 |
24 | // TestNet3 represents the test network.
25 | TestNet3 = wire.TestNet3 + chainIDPrefix
26 | )
27 |
28 | var MainNetParams = chaincfg.Params{
29 | Net: Mainnet,
30 | Name: "mainnet",
31 | PubKeyHashAddrID: 76, // addresses start with 'X'
32 | ScriptHashAddrID: 16, // script addresses start with '7'
33 | PrivateKeyID: 204, // private keys start with '7' or 'X'
34 |
35 | // BIP32 hierarchical deterministic extended key magics
36 | HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
37 | HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
38 | }
39 |
40 | var TestNet3Params = chaincfg.Params{
41 | Net: TestNet3,
42 | Name: "testnet3",
43 | PubKeyHashAddrID: 140, // addresses start with 'y'
44 | ScriptHashAddrID: 19, // script addresses start with '8' or '9'
45 | PrivateKeyID: 239, // private keys start with '9' or 'c' (Bitcoin defaults)
46 |
47 | // BIP32 hierarchical deterministic extended key magics
48 | HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xCF},
49 | HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94},
50 | }
51 |
52 | // RegressionNetParams defines the network parameters for the regression test
53 | // Dash network. Not to be confused with the test Dash network (version
54 | // 3), this network is sometimes simply called "testnet".
55 | var RegressionNetParams = chaincfg.Params{
56 | Net: TestNet,
57 | Name: "regtest",
58 | PubKeyHashAddrID: 140, // addresses start with 'y'
59 | ScriptHashAddrID: 19, // script addresses start with '8' or '9'
60 | PrivateKeyID: 239, // private keys start with '9' or 'c' (Bitcoin defaults)
61 |
62 | // BIP32 hierarchical deterministic extended key magics
63 | HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xCF},
64 | HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94},
65 | }
66 |
67 | // mustRegister performs the same function as Register except it panics if there
68 | // is an error. This should only be called from package init functions.
69 | func mustRegister(params *chaincfg.Params) {
70 | if err := chaincfg.Register(params); err != nil &&
71 | err != chaincfg.ErrDuplicateNet {
72 | panic("failed to register network: " + err.Error())
73 | }
74 | }
75 |
76 | func init() {
77 | mustRegister(&MainNetParams)
78 | mustRegister(&TestNet3Params)
79 | mustRegister(&RegressionNetParams)
80 | }
81 |
82 | func GetParams(netName string) (*chaincfg.Params, error) {
83 | switch netName {
84 | case "mainnet", "main":
85 | return &MainNetParams, nil
86 | case "regtest", "simnet":
87 | return &RegressionNetParams, nil
88 | case "testnet3", "test", "testnet":
89 | return &TestNet3Params, nil
90 | }
91 |
92 | return nil, errors.Errorf("network '%s' is "+
93 | "invalid or unsupported", netName)
94 | }
95 |
--------------------------------------------------------------------------------
/connectors/rpc/dash/validation.go:
--------------------------------------------------------------------------------
1 | package dash
2 |
3 | import (
4 | "github.com/go-errors/errors"
5 | "github.com/btcsuite/btcutil"
6 | )
7 |
8 | // DecodeAddress ensures that address is valid and belongs to the given
9 | // network, returns decoded address.
10 | func DecodeAddress(address, netName string) (btcutil.Address, error) {
11 | netParams, err := GetParams(netName)
12 | if err != nil {
13 | return nil, errors.Errorf("unable to get net params: %v", err)
14 | }
15 |
16 | decodedAddress, err := btcutil.DecodeAddress(address, netParams)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | if !decodedAddress.IsForNet(netParams) {
22 | return nil, errors.New("address is not for specified network")
23 | }
24 |
25 | return decodedAddress, nil
26 | }
27 |
--------------------------------------------------------------------------------
/connectors/rpc/ethereum/validation.go:
--------------------------------------------------------------------------------
1 | package ethereum
2 |
3 | import (
4 | "fmt"
5 | "regexp"
6 |
7 | eth "github.com/ethereum/go-ethereum/common"
8 | "github.com/go-errors/errors"
9 | )
10 |
11 | var re = regexp.MustCompile(fmt.Sprintf(`(?:0x)?[0-9a-fA-F]{%d}`,
12 | eth.AddressLength*2))
13 |
14 | // ValidateAddress validates Ethereum address.
15 | func ValidateAddress(address string) error {
16 | if !eth.IsHexAddress(address) || !re.MatchString(address) {
17 | return errors.Errorf("invalid hex address")
18 | }
19 | return nil
20 | }
21 |
--------------------------------------------------------------------------------
/connectors/rpc/ethereum/validation_test.go:
--------------------------------------------------------------------------------
1 | package ethereum
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestValidateAddress(t *testing.T) {
8 | type args struct {
9 | asset string
10 | net string
11 | addr string
12 | }
13 |
14 | tests := []struct {
15 | name string
16 | args args
17 | wantErr bool
18 | }{
19 | {
20 | name: "ETH empty net",
21 | args: args{"ETH", "", "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
22 | wantErr: false,
23 | },
24 | {
25 | name: "ETH mainnet",
26 | args: args{"ETH", "mainnet", "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
27 | wantErr: false,
28 | },
29 | {
30 | name: "ETH ropsten",
31 | args: args{"ETH", "ropsten", "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
32 | wantErr: false,
33 | },
34 | {
35 | name: "ETH kovan",
36 | args: args{"ETH", "kovan", "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
37 | wantErr: false,
38 | },
39 | {
40 | name: "ETH without prefix",
41 | args: args{"ETH", "", "de0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
42 | wantErr: false,
43 | },
44 | {
45 | name: "ETH invalid",
46 | args: args{"ETH", "", "0xdg0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"},
47 | wantErr: true,
48 | },
49 | {
50 | name: "ETH BTC mainnet address",
51 | args: args{"ETH", "", "bc1qn6f5cd9rpxtgavsxyk7lgyvgn75mj8tc56aenn3yvck7d0x6sc0qgxs65c"},
52 | wantErr: true,
53 | },
54 | {
55 | name: "ETH LTC mainnet address",
56 | args: args{"ETH", "", "tltc1ql00u9jm8qwzhv4e53hthz34t5744wh4pdkyuny5fp3feklm8cjgscue3nw"},
57 | wantErr: true,
58 | },
59 | {
60 | name: "ETH DASH mainnet address",
61 | args: args{"ETH", "", "yYQRhUDfzXn4b6acrnenZdDGRTpDUTqmQs"},
62 | wantErr: true,
63 | },
64 | {
65 | name: "ETH random",
66 | args: args{"ETH", "", "dGj3h7mvUfYuLGX2LoemYxsMyBQo90qQ20"},
67 | wantErr: true,
68 | },
69 | {
70 | name: "ETH empty",
71 | args: args{"ETH", "", ""},
72 | wantErr: true,
73 | },
74 | }
75 |
76 | for _, tt := range tests {
77 | t.Run(tt.name, func(t *testing.T) {
78 | defer func() {
79 | if r := recover(); r != nil {
80 | t.Errorf("unexpected panic: %v", r)
81 | }
82 | }()
83 | var err error
84 | if err = ValidateAddress(tt.args.addr); (err != nil) != tt.wantErr {
85 | t.Errorf("error = %v, wantErr = %v", err, tt.wantErr)
86 | }
87 | })
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/connectors/rpc/litecoin/client.go:
--------------------------------------------------------------------------------
1 | package litecoin
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors/rpc/bitcoin"
5 | "github.com/bitlum/connector/connectors/rpc"
6 | )
7 |
8 | type ClientConfig bitcoin.ClientConfig
9 |
10 | // Client is identical to bitcoin implementation.
11 | type Client struct {
12 | *bitcoin.Client
13 | }
14 |
15 | // Runtime check to ensure that Client implements rpc.Client interface.
16 | var _ rpc.Client = (*Client)(nil)
17 |
18 | func NewClient(cfg ClientConfig) (*Client, error) {
19 | client, err := bitcoin.NewClient(bitcoin.ClientConfig(cfg))
20 | return &Client{Client: client}, err
21 | }
22 |
--------------------------------------------------------------------------------
/connectors/rpc/litecoin/validation.go:
--------------------------------------------------------------------------------
1 | package litecoin
2 |
3 | import (
4 | "github.com/go-errors/errors"
5 | "github.com/btcsuite/btcd/chaincfg"
6 | "github.com/btcsuite/btcutil"
7 | )
8 |
9 | // DecodeAddress ensures that address is valid and belongs to the given
10 | // network, and return decoded address.
11 | func DecodeAddress(address, netName string) (btcutil.Address, error) {
12 | netParams, err := GetParams(netName)
13 | if err != nil {
14 | return nil, errors.Errorf("unable to get net params: %v", err)
15 | }
16 |
17 | decodedAddress, err := decode(address, netParams)
18 | if err != nil {
19 | // If there is error we shouldn't return it in mainnet straight away,
20 | // but instead because there is a possibility of address belong to the
21 | // legacy litecoin address type we should make another check.
22 | if netName == "mainnet" {
23 | legacyNetParams, err := GetParams("mainnet-legacy")
24 | if err != nil {
25 | return nil, errors.Errorf("unable to get legacy mainnet params: %v", err)
26 | }
27 |
28 | return decode(address, legacyNetParams)
29 | }
30 |
31 | return nil, err
32 | }
33 |
34 | // If validation was successful, than address is valid and we should
35 | // exit.
36 | return decodedAddress, nil
37 | }
38 |
39 | func decode(address string, network *chaincfg.Params) (btcutil.Address, error) {
40 | decodedAddress, err := btcutil.DecodeAddress(address, network)
41 | if err != nil {
42 | return nil, err
43 | }
44 |
45 | if !decodedAddress.IsForNet(network) {
46 | return nil, errors.New("address is not for specified network")
47 | }
48 |
49 | return decodedAddress, nil
50 | }
51 |
--------------------------------------------------------------------------------
/connectors/rpc/log.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/btcsuite/btclog"
5 | )
6 |
7 | // log is a logger that is initialized with no output filters. This
8 | // means the package will not perform any logging by default until the caller
9 | // requests it.
10 | var log btclog.Logger
11 |
12 | // The default amount of logging is none.
13 | func init() {
14 | DisableLog()
15 | }
16 |
17 | // DisableLog disables all library log output. Logging output is disabled
18 | // by default until UseLogger is called.
19 | func DisableLog() {
20 | log = btclog.Disabled
21 | }
22 |
23 | // UseLogger uses a specified Logger to output package logging info.
24 | // This should be used in preference to SetLogWriter if the caller is also
25 | // using btclog.
26 | func UseLogger(logger btclog.Logger) {
27 | log = logger
28 | }
29 |
30 | // logClosure is used to provide a closure over expensive logging operations
31 | // so don't have to be performed when the logging level doesn't warrant it.
32 | type logClosure func() string
33 |
34 | // String invokes the underlying function and returns the result.
35 | func (c logClosure) String() string {
36 | return c()
37 | }
38 |
39 | // newLogClosure returns a new closure over a function that returns a string
40 | // which itself provides a Stringer interface so that it can be used with the
41 | // logging system.
42 | func newLogClosure(c func() string) logClosure {
43 | return logClosure(c)
44 | }
45 |
--------------------------------------------------------------------------------
/connectors/storage.go:
--------------------------------------------------------------------------------
1 | package connectors
2 |
3 | import (
4 | "github.com/go-errors/errors"
5 | )
6 |
7 | // PaymentStorage is an external storage for payments, it is used by
8 | // connector to save payment as well as update its state.
9 | type PaymentsStore interface {
10 | // PaymentByID returns payment by id.
11 | PaymentByID(paymentID string) (*Payment, error)
12 |
13 | // PaymentByReceipt returns payment by receipt.
14 | PaymentByReceipt(receipt string) ([]*Payment, error)
15 |
16 | // SavePayment add payment to the store.
17 | SavePayment(payment *Payment) error
18 |
19 | // ListPayments return list of all payments.
20 | ListPayments(asset Asset, status PaymentStatus, direction PaymentDirection,
21 | media PaymentMedia, system PaymentSystem) ([]*Payment, error)
22 | }
23 |
24 | var PaymentNotFound = errors.New("payment not found")
25 |
26 | // StateStorage is used to keep data which is needed for connector to
27 | // properly synchronise and track transactions.
28 | //
29 | // NOTE: This storage should be persistent.
30 | type StateStorage interface {
31 | // PutLastSyncedHash is used to save last synchronised block hash.
32 | PutLastSyncedHash(hash []byte) error
33 |
34 | // LastSyncedHash is used to retrieve last synchronised block hash.
35 | LastSyncedHash() ([]byte, error)
36 | }
37 |
--------------------------------------------------------------------------------
/crpc/README.md:
--------------------------------------------------------------------------------
1 | The `rpc` package is using `gRPC` - open source remote procedure call (RPC)
2 | system initially developed at Google. It uses HTTP/2 for transport, Protocol
3 | Buffers as the interface description language, and provides features such as
4 | authentication, bidirectional streaming and flow control, blocking or
5 | non-blocking bindings, and cancellation and timeouts.
6 |
7 | With `rpc.proto` API schema defined we generate cross-platform clients for many
8 | languages, if API had changed, the generated clients also will be changed, and
9 | what you have to do is to update API by swapping old files with new one.
10 |
11 | If you want to connect to exchange server via `gRPC` from web browser, for
12 | example if you are developing browser extension which connects to our
13 | exchange, than you should use, `grpc-web-client` and one of the generated
14 | clients:
15 | * `js-web` - client generated for making requests to exchange server from
16 | browser using javascript. [Usage example]()
17 | * `ts-web` - client generated for making requests to exchange server from
18 | browser using typescript. [Usage example]()
19 |
20 | If you want to connect to exchange server via `gRPC` from your own server,
21 | for example if you are developing trading bot, you should use:
22 | * `go` - client generated for usage in golang. [Usage example]()
23 | * `js-node` - client generated for usage in nodejs. [Usage example]()
24 |
--------------------------------------------------------------------------------
/crpc/errors.go:
--------------------------------------------------------------------------------
1 | package crpc
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | const (
8 | ErrAssetNotSupported = iota + 1
9 | ErrNetworkNotSupported
10 | ErrInvalidArgument
11 |
12 | // ErrInternal...
13 | ErrInternal
14 | )
15 |
16 | type Error struct {
17 | code int
18 | errMsg string
19 | internal bool
20 | }
21 |
22 | func (e Error) Error() string {
23 | return e.errMsg
24 | }
25 |
26 | func newErrNetworkNotSupported(network, operation string) Error {
27 | return Error{
28 | code: ErrNetworkNotSupported,
29 | errMsg: fmt.Sprintf("%v: operation \"%v\" isn't supported for network %v",
30 | ErrNetworkNotSupported, operation, network),
31 | }
32 | }
33 |
34 | func newErrAssetNotSupported(asset, media string) Error {
35 | return Error{
36 | code: ErrAssetNotSupported,
37 | errMsg: fmt.Sprintf("%v: asset(%v) is not supported for media(%v)",
38 | ErrAssetNotSupported, asset, media),
39 | }
40 | }
41 |
42 | func newErrInternal(desc string) Error {
43 | return Error{
44 | code: ErrInternal,
45 | errMsg: fmt.Sprintf("%v: internal error: %v", ErrInternal, desc),
46 | }
47 | }
48 |
49 | func newErrInvalidArgument(argName string) Error {
50 | return Error{
51 | code: ErrInvalidArgument,
52 | errMsg: fmt.Sprintf("%v: invalid argument '%v'", ErrInvalidArgument,
53 | argName),
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/crpc/generate.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Generate the protos.
4 | protoc -I/usr/local/include -I. \
5 | -I$GOPATH/src \
6 | --go_out=plugins=grpc:. \
7 | rpc.proto
8 |
--------------------------------------------------------------------------------
/crpc/log.go:
--------------------------------------------------------------------------------
1 | package crpc
2 |
3 | import (
4 | "github.com/btcsuite/btclog"
5 | )
6 |
7 | // log is a logger that is initialized with no output filters. This
8 | // means the package will not perform any logging by default until the caller
9 | // requests it.
10 | var log btclog.Logger
11 |
12 | // The default amount of logging is none.
13 | func init() {
14 | DisableLog()
15 | }
16 |
17 | // DisableLog disables all library log output. Logging output is disabled
18 | // by default until UseLogger is called.
19 | func DisableLog() {
20 | log = btclog.Disabled
21 | }
22 |
23 | // UseLogger uses a specified Logger to output package logging info.
24 | // This should be used in preference to SetLogWriter if the caller is also
25 | // using btclog.
26 | func UseLogger(logger btclog.Logger) {
27 | log = logger
28 | }
29 |
30 | // logClosure is used to provide a closure over expensive logging operations
31 | // so don't have to be performed when the logging level doesn't warrant it.
32 | type logClosure func() string
33 |
34 | // String invokes the underlying function and returns the result.
35 | func (c logClosure) String() string {
36 | return c()
37 | }
38 |
39 | // newLogClosure returns a new closure over a function that returns a string
40 | // which itself provides a Stringer interface so that it can be used with the
41 | // logging system.
42 | func newLogClosure(c func() string) logClosure {
43 | return logClosure(c)
44 | }
45 |
--------------------------------------------------------------------------------
/db/inmemory/payments.go:
--------------------------------------------------------------------------------
1 | package inmemory
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "sort"
6 | "sync"
7 | )
8 |
9 | // MemoryPaymentsStore is a `PaymentStore` in-memory implementation,
10 | // which is able to periodically clean stored payments which are exceed
11 | // storing time.
12 | type MemoryPaymentsStore struct {
13 | paymentsMutex sync.RWMutex
14 | paymentsByID map[string]*connectors.Payment
15 | }
16 |
17 | // Runtime check to ensure that MemoryPaymentsStore implements
18 | // connectors.PaymentsStore interface.
19 | var _ connectors.PaymentsStore = (*MemoryPaymentsStore)(nil)
20 |
21 | // NewMemoryPaymentsStore creates new memory payment store with specified
22 | // payments storing time.
23 | func NewMemoryPaymentsStore() *MemoryPaymentsStore {
24 | return &MemoryPaymentsStore{
25 | paymentsByID: make(map[string]*connectors.Payment),
26 | }
27 | }
28 |
29 | // PaymentByID return payment by given id.
30 | func (s *MemoryPaymentsStore) PaymentByID(id string) (*connectors.Payment, error) {
31 | s.paymentsMutex.RLock()
32 | defer s.paymentsMutex.RUnlock()
33 |
34 | p, exists := s.paymentsByID[id]
35 | if !exists {
36 | return nil, connectors.PaymentNotFound
37 | }
38 | return p, nil
39 | }
40 |
41 | // PaymentByReceipt return payment by given receipt.
42 | func (s *MemoryPaymentsStore) PaymentByReceipt(receipt string) ([]*connectors.Payment, error) {
43 | s.paymentsMutex.RLock()
44 | defer s.paymentsMutex.RUnlock()
45 |
46 | var payments []*connectors.Payment
47 | for _, payment := range s.paymentsByID {
48 | if payment.Receipt == receipt {
49 | payments = append(payments, payment)
50 | }
51 | }
52 |
53 | sort.Slice(payments, func(i, j int) bool {
54 | return payments[i].UpdatedAt > payments[j].UpdatedAt
55 | })
56 |
57 | return payments, nil
58 | }
59 |
60 | // SavePayment adds payment to the store.
61 | func (s *MemoryPaymentsStore) SavePayment(p *connectors.Payment) error {
62 | s.paymentsMutex.Lock()
63 | defer s.paymentsMutex.Unlock()
64 |
65 | payment := &connectors.Payment{}
66 | *payment = *p
67 | s.paymentsByID[p.PaymentID] = payment
68 | return nil
69 | }
70 |
71 | // ListPayments return list of all payments.
72 | func (s *MemoryPaymentsStore) ListPayments(asset connectors.Asset,
73 | status connectors.PaymentStatus, direction connectors.PaymentDirection,
74 | media connectors.PaymentMedia, system connectors.PaymentSystem) ([]*connectors.Payment, error) {
75 |
76 | s.paymentsMutex.RLock()
77 | defer s.paymentsMutex.RUnlock()
78 |
79 | var payments []*connectors.Payment
80 | for _, payment := range s.paymentsByID {
81 | if asset != "" && payment.Asset != asset {
82 | continue
83 | }
84 |
85 | if status != "" && payment.Status != status {
86 | continue
87 | }
88 |
89 | if direction != "" && payment.Direction != direction {
90 | continue
91 | }
92 |
93 | if media != "" && payment.Media != media {
94 | continue
95 | }
96 |
97 | if system != "" && payment.System != system {
98 | continue
99 | }
100 |
101 | payments = append(payments, payment)
102 | }
103 |
104 | sort.Slice(payments, func(i, j int) bool {
105 | return payments[i].UpdatedAt > payments[j].UpdatedAt
106 | })
107 |
108 | return payments, nil
109 | }
110 |
--------------------------------------------------------------------------------
/db/sqlite/bitcoin_simnet_state.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "github.com/bitlum/connector/connectors/daemons/bitcoind_simple"
6 | "github.com/jinzhu/gorm"
7 | "time"
8 | )
9 |
10 | type BitcoinSimpleState struct {
11 | CreatedAt time.Time
12 | UpdatedAt time.Time
13 |
14 | Asset string `gorm:"primary_key"`
15 | TxCounter int
16 | }
17 |
18 | type BitcoinSimpleStateStorage struct {
19 | db *DB
20 | asset connectors.Asset
21 | }
22 |
23 | func NewBitcoinSimpleStateStorage(asset connectors.Asset,
24 | db *DB) *BitcoinSimpleStateStorage {
25 | return &BitcoinSimpleStateStorage{
26 | asset: asset,
27 | db: db,
28 | }
29 | }
30 |
31 | // Runtime check to ensure that ConnectorStateStorage implements
32 | // bitcoind_simple.StateStorage interface.
33 | var _ bitcoind_simple.StateStorage = (*BitcoinSimpleStateStorage)(nil)
34 |
35 | // PutLastSyncedTxCounter is used to save last synchronised confirmed tx
36 | // counter.
37 | //
38 | // NOTE: Part of the bitcoind_simple.StateStorage interface.
39 | func (s *BitcoinSimpleStateStorage) PutLastSyncedTxCounter(counter int) error {
40 | s.db.globalMutex.Lock()
41 | defer s.db.globalMutex.Unlock()
42 |
43 | return s.db.Save(&BitcoinSimpleState{
44 | Asset: string(s.asset),
45 | TxCounter: counter,
46 | }).Error
47 | }
48 |
49 | // LastTxCounter is used to retrieve last synchronised confirmed tx
50 | // counter.
51 | //
52 | // NOTE: Part of the bitcoind_simple.StateStorage interface.
53 | func (s *BitcoinSimpleStateStorage) LastTxCounter() (int, error) {
54 | s.db.globalMutex.Lock()
55 | defer s.db.globalMutex.Unlock()
56 |
57 | state := &BitcoinSimpleState{}
58 | err := s.db.Where("asset = ?", string(s.asset)).Find(state).Error
59 | if gorm.IsRecordNotFoundError(err) {
60 | return 0, nil
61 | } else if err != nil {
62 | return 0, err
63 | }
64 |
65 | return state.TxCounter, nil
66 | }
67 |
--------------------------------------------------------------------------------
/db/sqlite/connector_state.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "time"
6 | )
7 |
8 | type ConnectorState struct {
9 | CreatedAt time.Time
10 | UpdatedAt time.Time
11 |
12 | Asset string `gorm:"primary_key"`
13 | LastHash string
14 | }
15 |
16 | type ConnectorStateStorage struct {
17 | db *DB
18 | asset connectors.Asset
19 | }
20 |
21 | func NewConnectorStateStorage(asset connectors.Asset,
22 | db *DB) *ConnectorStateStorage {
23 | return &ConnectorStateStorage{
24 | asset: asset,
25 | db: db,
26 | }
27 | }
28 |
29 | // Runtime check to ensure that ConnectorStateStorage implements
30 | // connector.StateStorage interface.
31 | var _ connectors.StateStorage = (*ConnectorStateStorage)(nil)
32 |
33 | // PutLastSyncedHash is used to save last synchronised block hash.
34 | //
35 | // NOTE: Part of the bitcoind.Storage interface.
36 | func (s *ConnectorStateStorage) PutLastSyncedHash(hash []byte) error {
37 | s.db.globalMutex.Lock()
38 | defer s.db.globalMutex.Unlock()
39 |
40 | return s.db.Save(&ConnectorState{
41 | Asset: string(s.asset),
42 | LastHash: string(hash),
43 | }).Error
44 | }
45 |
46 | // LastSyncedHash is used to retrieve last synchronised block hash.
47 | //
48 | // NOTE: Part of the bitcoind.Storage interface.
49 | func (s *ConnectorStateStorage) LastSyncedHash() ([]byte, error) {
50 | s.db.globalMutex.Lock()
51 | defer s.db.globalMutex.Unlock()
52 |
53 | state := &ConnectorState{}
54 | if err := s.db.Where("asset = ?", string(s.asset)).
55 | Find(state).Error; err != nil {
56 | return nil, err
57 | }
58 |
59 | return []byte(state.LastHash), nil
60 | }
61 |
--------------------------------------------------------------------------------
/db/sqlite/connector_state_test.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "testing"
5 | "bytes"
6 | "github.com/bitlum/connector/connectors"
7 | )
8 |
9 | func TestPutLastHash(t *testing.T) {
10 | db, clear, err := MakeTestDB()
11 | if err != nil {
12 | t.Fatalf("unable to create test database: %v", err)
13 | }
14 | defer clear()
15 |
16 | btcStateStorage := NewConnectorStateStorage(connectors.BTC, db)
17 | err = btcStateStorage.PutLastSyncedHash([]byte("btc_hash"))
18 | if err != nil {
19 | t.Fatalf("unable to put hash: %v", err)
20 | }
21 |
22 | err = btcStateStorage.PutLastSyncedHash([]byte("btc_hash_next"))
23 | if err != nil {
24 | t.Fatalf("unable to put hash: %v", err)
25 | }
26 |
27 | btcHash, err := btcStateStorage.LastSyncedHash()
28 | if err != nil {
29 | t.Fatalf("unable to get hash: %v", err)
30 | }
31 |
32 | if !bytes.Equal(btcHash, []byte("btc_hash_next")) {
33 | t.Fatalf("wrong hash")
34 | }
35 |
36 | ethStateStorage := NewConnectorStateStorage(connectors.ETH, db)
37 | err = ethStateStorage.PutLastSyncedHash([]byte("eth_hash"))
38 | if err != nil {
39 | t.Fatalf("unable to put hash: %v", err)
40 | }
41 |
42 | err = ethStateStorage.PutLastSyncedHash([]byte("eth_hash_next"))
43 | if err != nil {
44 | t.Fatalf("unable to put hash: %v", err)
45 | }
46 |
47 | ethHash, err := ethStateStorage.LastSyncedHash()
48 | if err != nil {
49 | t.Fatalf("unable to get hash: %v", err)
50 | }
51 |
52 | if !bytes.Equal(ethHash, []byte("eth_hash_next")) {
53 | t.Fatalf("wrong hash")
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/db/sqlite/db.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/bitlum/graphql-go/errors"
5 | "github.com/jinzhu/gorm"
6 | _ "github.com/jinzhu/gorm/dialects/sqlite"
7 | "os"
8 | "path/filepath"
9 | "sync"
10 | )
11 |
12 | // db is the primary datastore.
13 | type DB struct {
14 | *gorm.DB
15 | dbPath string
16 |
17 | // globalMutex is used in order to avoid "database is locked" issue,
18 | // when two threads try to access database instead of waiting it just
19 | // return an error.
20 | globalMutex sync.Mutex
21 | }
22 |
23 | // Open opens an existing db. Any necessary schemas migrations due to
24 | // updates will take place as necessary.
25 | func Open(dbPath string, dbName string, shouldMigrate bool) (*DB, error) {
26 | path := filepath.Join(dbPath, dbName)
27 |
28 | if !fileExists(dbPath) {
29 | if err := os.MkdirAll(dbPath, 0700); err != nil {
30 | return nil, err
31 | }
32 | }
33 |
34 | gdb, err := gorm.Open("sqlite3", path)
35 | if err != nil {
36 | return nil, err
37 | }
38 |
39 | db := &DB{
40 | DB: gdb,
41 | dbPath: dbPath,
42 | }
43 |
44 | if shouldMigrate {
45 | if err := db.Migrate(); err != nil {
46 | return nil, errors.Errorf("unable to migrate: %v", err)
47 | }
48 | }
49 |
50 | return db, nil
51 | }
52 |
53 | // fileExists returns true if the file exists, and false otherwise.
54 | func fileExists(path string) bool {
55 | if _, err := os.Stat(path); err != nil {
56 | if os.IsNotExist(err) {
57 | return false
58 | }
59 | }
60 |
61 | return true
62 | }
63 |
--------------------------------------------------------------------------------
/db/sqlite/geth_accounts_test.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "testing"
5 | "github.com/davecgh/go-spew/spew"
6 | )
7 |
8 | func TestAccountsStorage(t *testing.T) {
9 | db, clear, err := MakeTestDB()
10 | if err != nil {
11 | t.Fatalf("unable to create test database: %v", err)
12 | }
13 | defer clear()
14 |
15 | storage := NewGethAccountsStorage(db)
16 |
17 | err = storage.AddAddressToAccount("address1", "account1")
18 | if err != nil {
19 | t.Fatalf("unable to add address: %v", err)
20 | }
21 |
22 | err = storage.AddAddressToAccount("address2", "account2")
23 | if err != nil {
24 | t.Fatalf("unable to add address: %v", err)
25 | }
26 |
27 | account, err := storage.GetAccountByAddress("address1")
28 | if err != nil {
29 | t.Fatalf("unable to get account: %v", err)
30 | }
31 |
32 | if account != "account1" {
33 | t.Fatalf("wrong account")
34 | }
35 |
36 | address, err := storage.GetLastAccountAddress("account1")
37 | if err != nil {
38 | t.Fatalf("unable to get address: %v", err)
39 | }
40 |
41 | if address != "address1" {
42 | t.Fatalf("wrong address")
43 | }
44 |
45 | addresses, err := storage.GetAddressesByAccount("account2")
46 | if err != nil {
47 | t.Fatalf("unable to get addresses: %v", err)
48 | }
49 |
50 | if len(addresses) != 1 {
51 | t.Fatalf("wrong len")
52 | }
53 |
54 | if addresses[0] != "address2" {
55 | t.Fatalf("wrong address")
56 | }
57 |
58 | allAddresses, err := storage.AllAddresses()
59 | if err != nil {
60 | t.Fatalf("unable to get all addreses: %v", err)
61 | }
62 |
63 | if len(allAddresses) != 2 {
64 | t.Fatalf("wrong len")
65 | }
66 |
67 | emptyAddress, err := storage.GetAccountByAddress("")
68 | if err != nil {
69 | t.Fatalf("unable to get address: %v", err)
70 | }
71 |
72 | spew.Dump(emptyAddress)
73 | if emptyAddress != "" {
74 | t.Fatalf("wrong address")
75 | }
76 | }
77 |
78 | func TestEthereumState(t *testing.T) {
79 | db, clear, err := MakeTestDB()
80 | if err != nil {
81 | t.Fatalf("unable to create test database: %v", err)
82 | }
83 | defer clear()
84 |
85 | s := NewGethAccountsStorage(db)
86 |
87 | if err := s.PutDefaultAddressNonce(10); err != nil {
88 | t.Fatalf("unable to put default address nonce: %v", err)
89 | }
90 |
91 | if nonce, err := s.DefaultAddressNonce(); err != nil {
92 | t.Fatalf("unable to put default address nonce: %v", err)
93 | } else {
94 | if nonce != 10 {
95 | t.Fatalf("wrong nonce")
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/db/sqlite/log.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/btcsuite/btclog"
5 | )
6 |
7 | // log is a logger that is initialized with no output filters. This
8 | // means the package will not perform any logging by default until the caller
9 | // requests it.
10 | var log btclog.Logger
11 |
12 | // The default amount of logging is none.
13 | func init() {
14 | DisableLog()
15 | }
16 |
17 | // DisableLog disables all library log output. Logging output is disabled
18 | // by default until UseLogger is called.
19 | func DisableLog() {
20 | log = btclog.Disabled
21 | }
22 |
23 | // UseLogger uses a specified Logger to output package logging info.
24 | // This should be used in preference to SetLogWriter if the caller is also
25 | // using btclog.
26 | func UseLogger(logger btclog.Logger) {
27 | log = logger
28 | }
29 |
30 | // logClosure is used to provide a closure over expensive logging operations
31 | // so don't have to be performed when the logging level doesn't warrant it.
32 | type logClosure func() string
33 |
34 | // String invokes the underlying function and returns the result.
35 | func (c logClosure) String() string {
36 | return c()
37 | }
38 |
39 | // newLogClosure returns a new closure over a function that returns a string
40 | // which itself provides a Stringer interface so that it can be used with the
41 | // logging system.
42 | func newLogClosure(c func() string) logClosure {
43 | return logClosure(c)
44 | }
45 |
--------------------------------------------------------------------------------
/db/sqlite/migrations.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "github.com/jinzhu/gorm"
6 | "gopkg.in/gormigrate.v1"
7 | )
8 |
9 | func (db *DB) Migrate() error {
10 | if err := db.DB.AutoMigrate(
11 | &EthereumState{},
12 | &ConnectorState{},
13 | &EthereumAddress{},
14 | &Payment{},
15 | &BitcoinSimpleState{},
16 | ).Error; err != nil {
17 | return err
18 | }
19 |
20 | return migrate(db.DB, allMigrations)
21 | }
22 |
23 | func migrate(gdb *gorm.DB, migrations []*gormigrate.Migration) error {
24 | return gormigrate.New(gdb, gormigrate.DefaultOptions, migrations).Migrate()
25 | }
26 |
27 | var allMigrations = []*gormigrate.Migration{
28 | addPaymentSystemType,
29 | }
30 |
31 | var addPaymentSystemType = &gormigrate.Migration{
32 | ID: "add_payment_system_type",
33 | Migrate: func(tx *gorm.DB) error {
34 | store := PaymentsStore{db: &DB{DB: tx}}
35 |
36 | // Previous internal payment direction now should be
37 | // moved in system field. All previous internal
38 | // payment where incoming.
39 | payments, err := store.ListPayments("", "",
40 | "", "", "")
41 | if err != nil {
42 | return err
43 | }
44 |
45 | for _, payment := range payments {
46 | // Internal transaction were tracked unproperly previously.
47 | if payment.Direction == "Internal" {
48 | err := tx.Delete(&Payment{}, "payment_id = ?",
49 | payment.PaymentID).Error
50 | if err != nil {
51 | return err
52 | }
53 |
54 | continue
55 | }
56 |
57 | // All other transactions were external
58 | oldID := payment.PaymentID
59 | err := tx.Delete(&Payment{}, "payment_id = ?", oldID).Error
60 | if err != nil {
61 | return err
62 | }
63 |
64 | payment.System = connectors.External
65 | payment.PaymentID, err = payment.GenPaymentID()
66 | if err != nil {
67 | return err
68 | }
69 |
70 | log.Infof("Payment migration (%v) => (%v)", oldID, payment.PaymentID)
71 | if err := store.SavePayment(payment); err != nil {
72 | return err
73 | }
74 | }
75 |
76 | return nil
77 | },
78 | }
79 |
--------------------------------------------------------------------------------
/db/sqlite/migrations_test.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "gopkg.in/gormigrate.v1"
5 | "testing"
6 | )
7 |
8 | func TestAddPaymentStatusMigration(t *testing.T) {
9 | db, err := Open("./", "test_db_add_status_field", false)
10 | if err != nil {
11 | t.Fatalf("unable create test db: %v", err)
12 | }
13 |
14 | tx := db.Begin()
15 | defer tx.Rollback()
16 |
17 | err = migrate(tx, []*gormigrate.Migration{addPaymentSystemType})
18 | if err != nil {
19 | t.Fatalf("unable migrate db: %v", err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/db/sqlite/payment_test.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "github.com/bitlum/connector/connectors"
5 | "github.com/shopspring/decimal"
6 | "reflect"
7 | "testing"
8 | )
9 |
10 | func TestPaymentsStorage(t *testing.T) {
11 | db, clear, err := MakeTestDB()
12 | if err != nil {
13 | t.Fatalf("unable to create test database: %v", err)
14 | }
15 | defer clear()
16 |
17 | store := PaymentsStore{db: db}
18 |
19 | paymentsBefore := []*connectors.Payment{
20 | {
21 |
22 | PaymentID: "1",
23 | UpdatedAt: 1,
24 | Status: connectors.Pending,
25 | System: connectors.Internal,
26 | Direction: connectors.Outgoing,
27 | Receipt: "receipt",
28 | Asset: connectors.BTC,
29 | Account: "account",
30 | Media: connectors.Blockchain,
31 | Amount: decimal.NewFromFloat(1.1),
32 | MediaFee: decimal.NewFromFloat(1.1),
33 | MediaID: "media_id",
34 | Detail: &connectors.BlockchainPendingDetails{
35 | ConfirmationsLeft: 3,
36 | Confirmations: 1,
37 | },
38 | },
39 | {
40 |
41 | PaymentID: "2",
42 | UpdatedAt: 2,
43 | Status: connectors.Completed,
44 | Direction: connectors.Incoming,
45 | System: connectors.External,
46 | Receipt: "receipt",
47 | Asset: connectors.ETH,
48 | Account: "account",
49 | Media: connectors.Lightning,
50 | Amount: decimal.NewFromFloat(1.1),
51 | MediaFee: decimal.NewFromFloat(1.1),
52 | MediaID: "media_id",
53 | Detail: &connectors.GeneratedTxDetails{
54 | RawTx: []byte("rawtx"),
55 | TxID: "123",
56 | },
57 | },
58 | }
59 |
60 | if err := store.SavePayment(paymentsBefore[0]); err != nil {
61 | t.Fatalf("unable to save payment: %v", err)
62 | }
63 |
64 | if err := store.SavePayment(paymentsBefore[1]); err != nil {
65 | t.Fatalf("unable to save payment: %v", err)
66 | }
67 |
68 | paymentsAfter, err := store.ListPayments("", "", "", "", "")
69 | if err != nil {
70 | t.Fatalf("unable to get payments: %v", err)
71 | }
72 |
73 | if !reflect.DeepEqual(paymentsBefore, paymentsAfter) {
74 | t.Fatalf("wrong data")
75 | }
76 |
77 | {
78 | payment, err := store.PaymentByID("1")
79 | if err != nil {
80 | t.Fatalf("unable to get payment by receipt: %v", err)
81 | }
82 |
83 | if !reflect.DeepEqual(payment, paymentsAfter[0]) {
84 | t.Fatalf("wrong data")
85 | }
86 | }
87 |
88 | {
89 | payments, err := store.PaymentByReceipt("receipt")
90 | if err != nil {
91 | t.Fatalf("unable to get payment by receipt: %v", err)
92 | }
93 |
94 | if !reflect.DeepEqual(payments, paymentsAfter) {
95 | t.Fatalf("wrong data")
96 | }
97 | }
98 |
99 | {
100 | payments, err := store.ListPayments(connectors.ETH, "", "", "", "")
101 | if err != nil {
102 | t.Fatalf("unable to list payments: %v", err)
103 | }
104 |
105 | if !reflect.DeepEqual(payments[0], paymentsAfter[1]) {
106 | t.Fatalf("wrong data")
107 | }
108 | }
109 |
110 | {
111 | payments, err := store.ListPayments("", connectors.Completed, "", "", "")
112 | if err != nil {
113 | t.Fatalf("unable to list payments: %v", err)
114 | }
115 |
116 | if !reflect.DeepEqual(payments[0], paymentsAfter[1]) {
117 | t.Fatalf("wrong data")
118 | }
119 | }
120 |
121 | {
122 | payments, err := store.ListPayments("", "", connectors.Outgoing, "", "")
123 | if err != nil {
124 | t.Fatalf("unable to list payments: %v", err)
125 | }
126 |
127 | if !reflect.DeepEqual(payments[0], paymentsAfter[0]) {
128 | t.Fatalf("wrong data")
129 | }
130 | }
131 |
132 | {
133 | payments, err := store.ListPayments("", "", "", connectors.Lightning, "")
134 | if err != nil {
135 | t.Fatalf("unable to list payments: %v", err)
136 | }
137 |
138 | if !reflect.DeepEqual(payments[0], paymentsAfter[1]) {
139 | t.Fatalf("wrong data")
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/db/sqlite/test_db_add_status_field:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitlum/payserver/d531a97c63b00c812b49d8a816c620b17f0b6ddb/db/sqlite/test_db_add_status_field
--------------------------------------------------------------------------------
/db/sqlite/utils.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | )
7 |
8 | // MakeTestDB creates a new instance of the ChannelDB for testing purposes. A
9 | // callback which cleans up the created temporary directories is also returned
10 | // and intended to be executed after the test completes.
11 | func MakeTestDB() (*DB, func(), error) {
12 | // First, create a temporary directory to be used for the duration of
13 | // this test.
14 | tempDirName, err := ioutil.TempDir("", "db")
15 | if err != nil {
16 | return nil, nil, err
17 | }
18 |
19 | db, err := Open(tempDirName, "sqlite.db", true)
20 | if err != nil {
21 | return nil, nil, err
22 | }
23 |
24 | cleanUp := func() {
25 | db.Close()
26 | os.RemoveAll(tempDirName)
27 | }
28 |
29 | return db, cleanUp, nil
30 | }
31 |
--------------------------------------------------------------------------------
/docker/mainnet/.env.empty:
--------------------------------------------------------------------------------
1 | # This file contains list of environment variables required to deploy
2 | # mainnet connector dockers. You should copy this file to `.env` and
3 | # fill the latter with right values
4 |
5 | # *_AUTH variables should contain authorization string generated by
6 | # `rpcauth.py` script from. You can get it from https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth
7 | # *_USER and *_PASSWORD variables should corresponds to *_AUTH string
8 |
9 | # *_VERSION variables should contain version in N.M.O format. This
10 | # variables are used to construct download link from official sources
11 | # of compiled binaries. So you should be sure that version you specifing
12 | # can be download. Check corresponding `Dockerfile` to get exact download
13 | # link.
14 |
15 | # *_REVISION variables specifies revision or branch which will be used.
16 | # As *_VERSION variables they are used to construct download link of
17 | # source code archive.
18 | # We encourage you to use revision a.k.a commit hash and do not use
19 | # branch name because docker-compose has cache and after first deploy
20 | # downloaded branch is freezed and further deploys will not download
21 | # new branch revisions. In contrast commit specifies exact
22 | # revision of source code so cache is working as should.
23 |
24 | EXTERNAL_IP=
25 | PRIVATE_IP=
26 |
27 | # `connector` docker container uses `github.com/bitlum/connector`. This
28 | # variable controls which revision (or branch) will be used.
29 | CONNECTOR_REVISION=
30 |
31 | # Force hash is used when connector need to skip syncing of blocks,
32 | # or roll back.
33 | CONNECTOR_BITCOIN_FORCE_HASH=
34 | CONNECTOR_LITECOIN_FORCE_HASH=
35 | CONNECTOR_DASH_FORCE_HASH=
36 | CONNECTOR_BITCOIN_CASH_FORCE_HASH=
37 | CONNECTOR_ETHEREUM_FORCE_HASH=
38 |
39 | BITCOIN_VERSION=
40 | BITCOIN_RPC_AUTH=
41 | BITCOIN_RPC_USER=
42 | BITCOIN_RPC_PASSWORD=
43 |
44 | # `bitcoin-lightning` docker container uses `lnd` from `github.com/lightningnetwork/lnd`
45 | # repo. This variable controls which revision (or branch) will be used.
46 | BITCOIN_LIGHTNING_REVISION=
47 |
48 | # `bitcoin-neutrino` docker container uses btcd from `github.com/btcsuite/btcd`
49 | # repo. This variable controls which revision (or branch) will be used.
50 | BITCOIN_NEUTRION_REVISION=
51 | BITCOIN_NEUTRINO_RPC_USER=
52 | BITCOIN_NEUTRINO_RPC_PASSWORD=
53 |
54 | BITCOIN_CASH_VERSION=
55 | BITCOIN_CASH_RPC_AUTH=
56 | BITCOIN_CASH_RPC_USER=
57 | BITCOIN_CASH_RPC_PASSWORD=
58 |
59 | DASH_VERSION=
60 | DASH_RPC_AUTH=
61 | DASH_RPC_USER=
62 | DASH_RPC_PASSWORD=
63 |
64 | # `ethereum` docker container uses `geth` from `github.com/bitlum/go-ethereum`
65 | # repo. This variable controls which revision (or branch) will be used.
66 | ETHEREUM_REVISION=
67 |
68 | LITECOIN_VERSION=
69 | LITECOIN_RPC_AUTH=
70 | LITECOIN_RPC_USER=
71 | LITECOIN_RPC_PASSWORD=
--------------------------------------------------------------------------------
/docker/mainnet/.gitignore:
--------------------------------------------------------------------------------
1 | .env
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-cash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_CASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.bitcoinabc.org/$BITCOIN_CASH_VERSION/linux/bitcoin-abc-${BITCOIN_CASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 9332
21 |
22 | # P2P port.
23 | EXPOSE 9333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder bitcoind /usr/local/bin/bitcoin-cashd
27 | COPY --from=builder bitcoin-cli /usr/local/bin/bitcoin-cash-cli
28 |
29 | # Default config used to initalize datadir volume at first or
30 | # cleaned deploy. It will be restored and used after each restart.
31 | COPY bitcoin-cash.mainnet.conf /root/default/bitcoin.conf
32 |
33 | # Entrypoint script used to init datadir if required and for
34 | # starting bitcoin cash daemon.
35 | COPY entrypoint.sh /root/
36 |
37 | # We are using exec syntax to enable gracefull shutdown. Check
38 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
39 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-cash/bitcoin-cash.mainnet.conf:
--------------------------------------------------------------------------------
1 | port=9333
2 |
3 | rpcbind=0.0.0.0
4 | rpcport=9332
5 |
6 | # Allow RPC for connector docker.
7 | rpcallowip=172.100.2.100
8 |
9 | txindex=1
10 | usecashaddr=0
11 |
12 | printtoconsole=1
13 |
14 | discover=1
15 | listen=1
16 | server=1
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-cash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec bitcoin-cashd $EXTERNAL_IP_OPT \
33 | --rpcauth=$BITCOIN_CASH_RPC_AUTH
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-lightning/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.11-alpine as builder
2 |
3 | ARG BITCOIN_LIGHTNING_REVISION
4 |
5 | # Install dependencies and install/build lnd.
6 | RUN apk add --no-cache --update alpine-sdk \
7 | git \
8 | make
9 |
10 | WORKDIR $GOPATH/src/github.com/lightningnetwork/lnd
11 |
12 | # Copy from repository to build from.
13 | RUN git clone https://github.com/lightningnetwork/lnd.git /go/src/github.com/lightningnetwork/lnd
14 |
15 | # Force Go to use the cgo based DNS resolver. This is required to ensure DNS
16 | # queries required to connect to linked containers succeed.
17 | ENV GODEBUG netdns=cgo
18 |
19 | RUN cd /go/src/github.com/lightningnetwork/lnd \
20 | && git checkout $BITCOIN_LIGHTNING_REVISION \
21 | && make build-itest \
22 | && mv lnd-itest /go/bin/lnd \
23 | && mv lncli-itest /go/bin/lncli
24 |
25 | # Start a new, final image to reduce size.
26 | FROM alpine as final
27 |
28 | # Expose lnd ports (server, rpc).
29 | EXPOSE 9735 10009
30 |
31 | # Copy the binaries and entrypoint from the builder image.
32 | COPY --from=builder /go/bin/lncli /bin/
33 | COPY --from=builder /go/bin/lnd /bin/
34 |
35 | # Default config used to initalize datadir volume at first or
36 | # cleaned deploy. It will be restored and used after each restart.
37 | COPY bitcoin-lightning.mainnet.conf /root/default/lnd.conf
38 |
39 | # Add bash.
40 | RUN apk add --no-cache \
41 | bash
42 |
43 | # Entrypoint script used to init datadir if required and for
44 | # starting lightning daemon.
45 | COPY entrypoint.sh /root/
46 |
47 | # We are using exec syntax to enable gracefull shutdown. Check
48 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
49 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-lightning/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make lnd data persistent.
4 | DATA_DIR=/root/.lnd
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/lnd.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/lnd.conf $CONFIG
22 |
23 | # If external IP defined we need to set corresponding run option
24 | if [ ! -z "$EXTERNAL_IP" ]; then
25 | echo "Setting external IP"
26 | EXTERNAL_IP_OPT="--externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | RPC_USER_OPT="--bitcoind.rpcuser="
30 |
31 | # We are using `exec` to enable gracefull shutdown of running daemon.
32 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
33 | exec lnd $EXTERNAL_IP_OPT \
34 | --bitcoind.rpcuser=$BITCOIN_RPC_USER \
35 | --bitcoind.rpcpass=$BITCOIN_RPC_PASSWORD
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-neutrino/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG BITCOIN_NEUTRINO_REVISION
4 |
5 | RUN go get -u github.com/Masterminds/glide
6 |
7 | WORKDIR $GOPATH/src/github.com/btcsuite/btcd
8 |
9 | RUN curl -L https://github.com/btcsuite/btcd/archive/$BITCOIN_NEUTRINO_REVISION.tar.gz \
10 | | tar xz --strip 1
11 |
12 | RUN glide install
13 |
14 | RUN go install -v . ./cmd/btcctl
15 |
16 |
17 |
18 | FROM ubuntu:18.04
19 |
20 | # P2P port.
21 | EXPOSE 18333
22 |
23 | # Copying required binaries from builder stage.
24 | COPY --from=builder /go/bin/btcd /go/bin/btcctl /usr/local/bin/
25 |
26 | # Default config used to initalize datadir volume at first or
27 | # cleaned deploy. It will be restored and used after each restart.
28 | COPY bitcoin-neutrino.mainnet.conf /root/default/btcd.conf
29 |
30 | # Entrypoint script used to init datadir if required and for
31 | # starting bitcoin daemon.
32 | COPY entrypoint.sh /root/
33 |
34 | # We are using exec syntax to enable gracefull shutdown. Check
35 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
36 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin-neutrino/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make lnd data persistent.
4 | DATA_DIR=/root/.btcd
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/btcd.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/btcd.conf $CONFIG
22 |
23 | # If external IP defined we need to set corresponding run option
24 | if [ ! -z "$EXTERNAL_IP" ]; then
25 | echo "Setting external IP"
26 | EXTERNAL_IP_OPT="--externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | RPC_USER_OPT="--bitcoind.rpcuser="
30 |
31 | # We are using `exec` to enable gracefull shutdown of running daemon.
32 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
33 | exec btcd $EXTERNAL_IP_OPT \
34 | --rpcuser=$BITCOIN_NEUTRINO_RPC_USER
35 | --rpcpass=$BITCOIN_NEUTRINO_RPC_PASSWORD
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 13332
21 |
22 | # P2P port.
23 | EXPOSE 13333
24 |
25 | # `zmqpubrawblock` and `zmqpubrawtx` port.
26 | EXPOSE 8334
27 |
28 | # Copying required binaries from builder stage.
29 | COPY --from=builder bitcoind bitcoin-cli /usr/local/bin/
30 |
31 | # Default config used to initalize datadir volume at first or
32 | # cleaned deploy. It will be restored and used after each restart.
33 | COPY bitcoin.mainnet.conf /root/default/bitcoin.conf
34 |
35 | # Entrypoint script used to init datadir if required and for
36 | # starting bitcoin daemon.
37 | COPY entrypoint.sh /root/
38 |
39 | # We are using exec syntax to enable gracefull shutdown. Check
40 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
41 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin/bitcoin.mainnet.conf:
--------------------------------------------------------------------------------
1 | port=8333
2 |
3 | rpcbind=0.0.0.0
4 | rpcport=8332
5 |
6 | # Allow RPC for connector docker.
7 | rpcallowip=172.100.2.100
8 |
9 | # Allow RPC for bitcoin-lightning docker.
10 | rpcallowip=172.100.2.101
11 |
12 | # Allow RPC for hub docker.
13 | rpcallowip=172.100.2.103
14 |
15 | # Increasing rcpthreads to workaround possible bug described here
16 | # https://github.com/lightningnetwork/lnd/issues/1174.
17 | rpcthreads=16
18 |
19 | # Tx index is required by lnd.
20 | txindex=1
21 |
22 | printtoconsole=1
23 |
24 | zmqpubrawblock=tcp://0.0.0.0:8334
25 | zmqpubrawtx=tcp://0.0.0.0:8335
26 |
27 | discover=1
28 | listen=1
29 | server=1
30 |
31 | # getaddressesbyaccount is deprecated and will be removed in V0.18.
32 | # but we need it right now.
33 | deprecatedrpc=accounts
34 | deprecatedrpc=signrawtransaction
--------------------------------------------------------------------------------
/docker/mainnet/bitcoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec bitcoind $EXTERNAL_IP_OPT \
33 | --rpcauth=$BITCOIN_RPC_AUTH
--------------------------------------------------------------------------------
/docker/mainnet/connector/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.12 AS builder
2 |
3 | WORKDIR $GOPATH/src/github.com/bitlum/connector/
4 |
5 | ARG CONNECTOR_REVISION
6 |
7 | RUN curl -L https://github.com/bitlum/connector/archive/$CONNECTOR_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN GO111MODULE=on go get
11 | RUN GO111MODULE=on go install . ./cmd/...
12 |
13 | FROM ubuntu:18.04
14 |
15 | RUN apt-get update && apt-get install -y \
16 | ca-certificates \
17 | curl \
18 | && rm -rf /var/lib/apt/lists/*
19 |
20 | # Copying required binaries from builder stage.
21 | COPY --from=builder /go/bin/connector /usr/local/bin
22 | COPY --from=builder /go/bin/pscli /usr/local/bin
23 |
24 | # Default config used to initalize datadir volume at first or
25 | # cleaned deploy. It will be restored and used after each restart.
26 | COPY connector.mainnet.conf /root/default/connector.conf
27 |
28 | # Entrypoint script used to init datadir if required and for
29 | # starting daemon
30 | COPY entrypoint.sh /root/
31 |
32 | # We are using exec syntax to enable gracefull shutdown. Check
33 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
34 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
35 |
--------------------------------------------------------------------------------
/docker/mainnet/connector/connector.mainnet.conf:
--------------------------------------------------------------------------------
1 | [Application Options]
2 | datadir=/root/.connector
3 | debuglevel=trace
4 | network=mainnet
5 |
6 | # RPC address to bind
7 | rpchost=0.0.0.0
8 | rpcport=9002
9 |
10 | [Bitcoin]
11 | bitcoin.disable=false
12 | bitcoin.minconfirmations=1
13 | bitcoin.syncdelay=5
14 | bitcoin.host=bitcoin.mainnet
15 | bitcoin.port=8332
16 |
17 | # From https://bitcoinfees.earn.com/ at 2018-07-02 fastest fee for byte
18 | # is 130. In connector we implemented unit as weight, so this parameter
19 | # is fee per weight.
20 | bitcoin.feeperunit=130
21 |
22 | [Bitcoincash]
23 | bitcoincash.disable=false
24 | bitcoincash.minconfirmations=1
25 | bitcoincash.syncdelay=5
26 | bitcoincash.host=bitcoin-cash.mainnet
27 | bitcoincash.port=9332
28 |
29 | # Didn't find any certain info. We will adjust this during tests and
30 | # further development. For now lets take big enough using
31 | # https://jochen-hoenicke.de/queue/#3,24h to be more than mosts.
32 | # At 2018-07-02 this was 20.
33 | bitcoincash.feeperunit=20
34 |
35 | [Dash]
36 | dash.disable=false
37 | dash.minconfirmations=1
38 | dash.syncdelay=5
39 | dash.host=dash.mainnet
40 | dash.port=10332
41 |
42 | # Didn't find any certain info. We will adjust this during tests and
43 | # further development. For now lets use https://asfi.co/ recommendation.
44 | # At 2018-07-02 this was 2566 satoshis per kilobyte or 2.5 satoshi per
45 | # byte.
46 | dash.feeperunit=4
47 |
48 | [Ethereum]
49 | ethereum.disable=false
50 | ethereum.minconfirmations=1
51 | ethereum.syncdelay=5
52 | ethereum.host=ethereum.mainnet
53 | ethereum.port=11332
54 |
55 | [Litecoin]
56 | litecoin.disable=false
57 | litecoin.minconfirmations=1
58 | litecoin.syncdelay=5
59 | litecoin.host=litecoin.mainnet
60 | litecoin.port=12332
61 |
62 | # Didn't find any certain info. We will adjust this during tests and
63 | # further development. For now lets take big enough using
64 | # https://jochen-hoenicke.de/queue/#4,24h to be more than mosts.
65 | # At 2018-07-02 this was 200.
66 | litecoin.feeperunit=200
67 |
68 | [Bitcoinlightning]
69 | bitcoinlightning.disable=false
70 | bitcoinlightning.tlscertpath=/root/.lnd/tls.cert
71 | bitcoinlightning.macaroonpath=/root/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
72 | # lnd RPC address
73 | bitcoinlightning.host=bitcoin-lightning.mainnet
74 | bitcoinlightning.port=10009
75 | # lnd P2P address
76 | bitcoinlightning.peerhost=connector.bitlum.io
77 | bitcoinlightning.peerport=97350
--------------------------------------------------------------------------------
/docker/mainnet/connector/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make connector data persistent.
4 | DATA_DIR=/root/.connector
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/connector.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/connector.conf $CONFIG
22 |
23 |
24 | # We are using `exec` to enable gracefull shutdown of running daemon.
25 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
26 | exec connector --config /root/.connector/connector.conf \
27 | --bitcoin.user=$BITCOIN_RPC_USER \
28 | --bitcoin.password=$BITCOIN_RPC_PASSWORD \
29 | --bitcoin.forcelasthash=$CONNECTOR_BITCOIN_FORCE_HASH \
30 | --bitcoincash.user=$BITCOIN_CASH_RPC_USER \
31 | --bitcoincash.password=$BITCOIN_CASH_RPC_PASSWORD \
32 | --bitcoincash.forcelasthash=$CONNECTOR_BITCOIN_CASH_FORCE_HASH \
33 | --dash.user=$DASH_RPC_USER \
34 | --dash.password=$DASH_RPC_PASSWORD \
35 | --dash.forcelasthash=$CONNECTOR_DASH_FORCE_HASH \
36 | --litecoin.user=$LITECOIN_RPC_USER \
37 | --litecoin.password=$LITECOIN_RPC_PASSWORD \
38 | --litecoin.forcelasthash=$CONNECTOR_LITECOIN_FORCE_HASH \
39 | --ethereum.forcelasthash=$CONNECTOR_ETHEREUM_FORCE_HASH
--------------------------------------------------------------------------------
/docker/mainnet/dash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG DASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl -L https://github.com/dashpay/dash/releases/download/v$DASH_VERSION/dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/dashd \
13 | */bin/dash-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 10332
21 |
22 | # P2P port.
23 | EXPOSE 10333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder dashd dash-cli /usr/local/bin/
27 |
28 | # Default config used to initalize datadir volume at first or
29 | # cleaned deploy. It will be restored and used after each restart.
30 | COPY dash.mainnet.conf /root/default/dash.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting dash daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/dash/dash.mainnet.conf:
--------------------------------------------------------------------------------
1 | port=10333
2 | rpcport=10332
3 |
4 | # Allow RPC for connector docker.
5 | rpcallowip=172.100.2.100
6 |
7 | txindex=1
8 |
9 | printtoconsole=1
10 |
11 | listen=1
12 | server=1
--------------------------------------------------------------------------------
/docker/mainnet/dash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.dashcore
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/dash.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/dash.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec dashd $EXTERNAL_IP_OPT \
33 | --rpcauth=$DASH_RPC_AUTH
--------------------------------------------------------------------------------
/docker/mainnet/ethereum/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG ETHEREUM_REVISION
4 |
5 | WORKDIR /ethereum
6 |
7 | RUN curl -L https://github.com/bitlum/go-ethereum/archive/$ETHEREUM_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN make geth
11 |
12 |
13 |
14 | FROM ubuntu:18.04
15 |
16 | # RPC port.
17 | EXPOSE 11332
18 |
19 | # RPC-WS port.
20 | EXPOSE 11331
21 |
22 | # P2P port.
23 | EXPOSE 30303
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder /ethereum/build/bin/geth /usr/local/bin/
27 |
28 | # Default config and genesis used to initalize datadir volume
29 | # at first or cleaned deploy.
30 | COPY ethereum.mainnet.conf /root/default/ethereum.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting bitcoin daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/ethereum/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This directory is expected to be volume to make blockchain and account
4 | # data persistent.
5 | DATA_DIR=/root/.ethereum
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/ethereum.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/ethereum.conf $CONFIG
23 |
24 | # At first deploy datadir keystore path should not exists which means
25 | # that account is not created, so we are creating it.
26 | KEYSTORE=$DATA_DIR/keystore
27 | if [ ! -d $KEYSTORE ] || [ ! "$(ls -A $KEYSTORE)" ]; then
28 | echo "Creating new account"
29 | geth --datadir $DATA_DIR --config $CONFIG account new --password /dev/null
30 | fi
31 |
32 | # If external IP defined when we need to set corresponding run option
33 | if [ ! -z "$EXTERNAL_IP" ]; then
34 | echo "Setting external IP"
35 | EXTERNAL_IP_OPT="--nat extip:$EXTERNAL_IP"
36 | fi
37 |
38 | # We are using `exec` to enable gracefull shutdown of running daemon.
39 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
40 | exec geth \
41 | --debug \
42 | --verbosity=4 \
43 | --datadir $DATA_DIR \
44 | --config $CONFIG \
45 | --cache 4086 \
46 | $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/mainnet/ethereum/ethereum.mainnet.conf:
--------------------------------------------------------------------------------
1 | [Eth]
2 | NetworkId = 1
3 | SyncMode = "fast"
4 | NoPruning = false
5 | LightPeers = 100
6 | DatabaseCache = 512
7 | TrieCleanCache = 256
8 | TrieDirtyCache = 256
9 | TrieTimeout = 3600000000000
10 | MinerGasFloor = 8000000
11 | MinerGasCeil = 8000000
12 | MinerGasPrice = 1000000000
13 | MinerRecommit = 3000000000
14 | MinerNoverify = false
15 | EnablePreimageRecording = false
16 | EWASMInterpreter = ""
17 | EVMInterpreter = ""
18 |
19 | [Eth.Ethash]
20 | CacheDir = "ethash"
21 | CachesInMem = 2
22 | CachesOnDisk = 3
23 | DatasetDir = "/root/.ethereum/ethash-dataset"
24 | DatasetsInMem = 1
25 | DatasetsOnDisk = 2
26 | PowMode = 0
27 |
28 | [Eth.TxPool]
29 | Locals = []
30 | NoLocals = false
31 | Journal = "transactions.rlp"
32 | Rejournal = 3600000000000
33 | PriceLimit = 1
34 | PriceBump = 10
35 | AccountSlots = 16
36 | GlobalSlots = 4096
37 | AccountQueue = 64
38 | GlobalQueue = 1024
39 | Lifetime = 10800000000000
40 |
41 | [Eth.GPO]
42 | Blocks = 20
43 | Percentile = 60
44 |
45 | [Shh]
46 | MaxMessageSize = 1048576
47 | MinimumAcceptedPOW = 2e-01
48 | RestrictConnectionBetweenLightClients = true
49 |
50 | [Node]
51 | DataDir = "/root/.ethereum"
52 |
53 | HTTPHost = "0.0.0.0"
54 | HTTPPort = 11332
55 | HTTPModules = ["net", "web3", "eth", "shh", "personal"]
56 | HTTPVirtualHosts = ["ethereum.mainnet"]
57 |
58 | # Disable USB
59 | NoUSB = true
60 |
61 | # Disable WS
62 | WSHost = ""
63 |
64 | [Node.P2P]
65 | MaxPeers = 25
66 | NoDiscovery = false
67 |
68 | BootstrapNodes = [
69 | "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
70 | "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303",
71 | "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303",
72 | "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303",
73 | "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
74 | "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303",
75 | ]
76 |
77 | BootstrapNodesV5 = [
78 | "enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305",
79 | "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308",
80 | "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309",
81 | ]
82 |
83 | StaticNodes = []
84 | TrustedNodes = []
85 | ListenAddr = ":30303"
86 | EnableMsgEvents = false
87 |
88 | [Node.HTTPTimeouts]
89 | ReadTimeout = 30000000000
90 | WriteTimeout = 30000000000
91 | IdleTimeout = 120000000000
--------------------------------------------------------------------------------
/docker/mainnet/fluentd/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM fluent/fluentd:stable
2 |
3 | # Install required fluentd plugins
4 | RUN gem install fluent-plugin-concat \
5 | fluent-plugin-rewrite-tag-filter \
6 | fluent-plugin-elasticsearch \
7 | fluent-plugin-prometheus --no-rdoc --no-ri
8 |
9 | # Copy fluentd configuration file to place where fluentd expect to have it
10 | COPY ./fluent.conf /fluentd/etc/fluent.conf
11 |
12 | # In order to add verboce tracing to fluentd daemon you need to rewrite
13 | # docker CMD directive with and add "-vv" flag.
--------------------------------------------------------------------------------
/docker/mainnet/fluentd/fluent.conf:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------------
2 | # Setup fluentd to accept docker logs on tcp port
3 | # ------------------------------------------------------------------------------
4 |
5 |
6 | @type forward
7 | bind 0.0.0.0
8 | port 24224
9 |
10 |
11 | # ------------------------------------------------------------------------------
12 | # Concatenate logs which where spited by docker daemon. As far as in this
13 | # containers is used one logger, the format is the same - date at the start,
14 | # which we use us indicator for merging logs back in one string.
15 | # ------------------------------------------------------------------------------
16 |
17 |
18 | @type concat
19 | key log
20 | multiline_start_regexp /^20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/
21 |
22 |
23 |
24 | @type concat
25 | key log
26 | multiline_start_regexp /^20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/
27 |
28 |
29 |
30 | @type concat
31 | key log
32 | multiline_start_regexp /^20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/
33 |
34 |
35 | # ------------------------------------------------------------------------------
36 | # Events parsing to get additional fields from string.
37 | # Use filter to replace field "log" of fluentd log entry with time, level,
38 | # subsystem, message.
39 | # ------------------------------------------------------------------------------
40 |
41 |
42 | @type parser
43 | format /(?
46 |
47 |
48 | @type parser
49 | format /(?
52 |
53 |
54 | @type parser
55 | format /(?
58 |
59 | # ------------------------------------------------------------------------------
60 | # Sending logs to storage
61 | # ------------------------------------------------------------------------------
62 |
63 | # Send all unparsed logs to elasticsearch too
64 |
65 | @type elasticsearch
66 | host 10.135.95.194
67 | port 9200
68 | logstash_format true
69 | logstash_prefix fluentd
70 | logstash_dateformat %Y%m%d
71 | include_tag_key true
72 | tag_key @log_name
73 |
74 | chunk_limit_size 32MB
75 | total_limit_size 2GB
76 | flush_mode interval
77 | flush_interval 5s
78 | flush_thread_count 2
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docker/mainnet/litecoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG LITECOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.litecoin.org/litecoin-$LITECOIN_VERSION/linux/litecoin-${LITECOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/litecoind \
13 | */bin/litecoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 12332
21 |
22 | # P2P port.
23 | EXPOSE 12333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder litecoind litecoin-cli /usr/local/bin/
27 |
28 | # Default config used to initalize datadir volume
29 | # at first or cleaned deploy.
30 | COPY litecoin.mainnet.conf /root/default/litecoin.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting litecoin daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/mainnet/litecoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.litecoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/litecoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/litecoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec litecoind $EXTERNAL_IP_OPT \
33 | --rpcauth=$LITECOIN_RPC_AUTH
--------------------------------------------------------------------------------
/docker/mainnet/litecoin/litecoin.mainnet.conf:
--------------------------------------------------------------------------------
1 | port=12333
2 |
3 | rpcbind=0.0.0.0
4 | rpcport=12332
5 |
6 | rpcallowip=172.100.2.100
7 |
8 | addresstype=legacy
9 |
10 | txindex=1
11 |
12 | printtoconsole=1
13 |
14 | discover=1
15 | listen=1
16 | server=1
--------------------------------------------------------------------------------
/docker/mainnet/logrotate.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for mainnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/logrotate.d/connector during deploy
4 | # following with restarting logrotate.
5 |
6 | /var/log/connector/*.log {
7 | # Rotate logs daily.
8 | daily
9 |
10 | # Keep last 30 days.
11 | rotate 180
12 |
13 | # If the log file is missing, go on to the next one without issuing an
14 | # error message.
15 | missingok
16 |
17 | # Do not rotate the log if it is empty.
18 | notifempty
19 |
20 | # Postpone compression of the previous log file to the next rotation cycle.
21 | delaycompress
22 |
23 | # Old versions of log files are compressed with gzip(1).
24 | compress
25 |
26 | # Signal rsyslog about rotation to start new log file.
27 | postrotate
28 | invoke-rc.d rsyslog rotate > /dev/null
29 | endscript
30 | }
--------------------------------------------------------------------------------
/docker/mainnet/rsyslog.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for mainnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/rsyslog.d/connector.conf during deploy
4 | # following with rsyslogd restarting.
5 |
6 | # Fix `\t` and other caharacters in log messages.
7 | global(
8 | parser.escapecontrolcharactertab="off"
9 | )
10 |
11 | # Raw log line message template
12 | template(name="outfmt" type="list") {
13 | property(name="msg" position.from="2")
14 | constant(value="\n")
15 | }
16 |
17 | # Template for log dynamic file name to put containers logs in separate
18 | # file in `/var/log/connector/`. E.g. for dash container log file is
19 | # `/var/log/connector/dash.simnet.primary.log`.
20 | template(name="dynaFile" type="list") {
21 | constant(value="/var/log/connector/")
22 | property(
23 | name="syslogtag"
24 | # We parsing syslogtag like `connector-docker/dash.simnet.perimary[123]`
25 | # to get `dash.simnet.perimary` substring.
26 | regex.expression="^connector-docker\\/\\(.\\+\\)\\["
27 | regex.submatch="1"
28 | )
29 | constant(value=".log")
30 | }
31 |
32 | # Put connector docker containers' logs in separate files and discard whem.
33 | if ($programname == "connector-docker") then {
34 | action(type="omfile" dynaFile="dynaFile" template="outfmt")
35 | stop
36 | }
37 |
--------------------------------------------------------------------------------
/docker/simnet/.env:
--------------------------------------------------------------------------------
1 | # This file contains list of environment variables required to deploy
2 | # simnet connector dockers.
3 |
4 | # *_VERSION variables should contain version in N.M.O format. This
5 | # variables are used to construct download link from official sources
6 | # of compiled binaries. So you should be sure that version you specifing
7 | # can be download. Check corresponding `Dockerfile` to get exact download
8 | # link.
9 |
10 | # *_REVISION variables specifies revision or branch which will be used.
11 | # As *_VERSION variables they are used to construct download link of
12 | # source code archive.
13 | # We encourage you to use revision a.k.a commit hash and do not use
14 | # branch name because docker-compose has cache and after first deploy
15 | # downloaded branch is freezed and further deploys will not download
16 | # new branch revisions. In contrast commit specifies exact
17 | # revision of source code so cache is working as should.
18 |
19 | # This variable intended to be specified outside of .env file.
20 | # You should specify them during deploy on specific droplet.
21 | # PRIVATE_IP=10.135.63.178
22 |
23 | BITCOIN_VERSION=0.17.0
24 |
25 | BITCOIN_CASH_VERSION=0.18.2
26 |
27 | BITCOIN_LIGHTNING_REVISION=c7ca387a9d92a1a2cd0f6e56b61ff04b9adc062d
28 |
29 | DASH_VERSION=0.12.3.3
30 |
31 | ETHEREUM_REVISION=4884b01a2bdfaf1dd96070a628d029ea9c854448
32 |
33 | LITECOIN_VERSION=0.16.3
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-cash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_CASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.bitcoinabc.org/$BITCOIN_CASH_VERSION/linux/bitcoin-abc-${BITCOIN_CASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # ROLE is bitcoin node role: primary or secondary.
20 | #
21 | # Primary role means that this node will init new blockchain if it not
22 | # exists during deploy or restart.
23 | #
24 | # Secondary rank means that this node will try to connect to primary
25 | # node and use blockchain of latter.
26 | ARG ROLE
27 |
28 | # RPC port.
29 | EXPOSE 9332
30 |
31 | # P2P port.
32 | EXPOSE 9333
33 |
34 | # Copying required binaries from builder stage.
35 | COPY --from=builder bitcoind /usr/local/bin/bitcoin-cashd
36 | COPY --from=builder bitcoin-cli /usr/local/bin/bitcoin-cash-cli
37 |
38 | # Default config used to initalize datadir volume at first or
39 | # cleaned deploy.
40 | COPY bitcoin.simnet.$ROLE.conf /root/default/bitcoin.conf
41 |
42 | # Entrypoint script used to init datadir if required and for
43 | # starting bitcoin cash daemon.
44 | COPY entrypoint.sh /root/
45 |
46 | # We are using exec syntax to enable gracefull shutdown. Check
47 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
48 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-cash/bitcoin.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=9333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=9332
7 |
8 | rpcallowip=0.0.0.0/0
9 | rpcuser=user
10 | rpcpassword=password
11 |
12 | dnsseed=0
13 | upnp=0
14 |
15 | txindex=1
16 | usecashaddr=0
17 |
18 | printtoconsole=1
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-cash/bitcoin.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=9333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=9332
7 |
8 | rpcallowip=0.0.0.0/0
9 | rpcuser=user
10 | rpcpassword=password
11 |
12 | dnsseed=0
13 | upnp=0
14 |
15 | txindex=1
16 | usecashaddr=0
17 |
18 | printtoconsole=1
19 |
20 | connect=bitcoin-cash.simnet.primary:9333
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-cash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | # We are using `exec` to enable gracefull shutdown of running daemon.
30 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
31 | exec bitcoin-cashd $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning-helper/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG BITCOIN_LIGHTNING_REVISION
4 |
5 | RUN go get -u github.com/golang/dep/cmd/dep
6 |
7 | WORKDIR $GOPATH/src/github.com/lightningnetwork/lnd
8 |
9 | # Instead of cloning lightningnetwork/lnd temproray use ourw version of lnd
10 | # daemon, but put in lightningnetwork/lnd directory so that all installation
11 | # scripts could work without being changed.
12 | RUN git clone https://github.com/bitlum/lnd.git .
13 |
14 | RUN git checkout $BITCOIN_LIGHTNING_REVISION
15 |
16 | RUN dep ensure -v
17 |
18 | RUN make install
19 |
20 |
21 |
22 | FROM python:2.7
23 |
24 | EXPOSE 80
25 |
26 | # Copying required binaries from builder stage.
27 | COPY --from=builder /go/bin/lncli /usr/local/bin/
28 |
29 | RUN pip install --no-cache-dir Flask
30 |
31 | COPY http-server.py /
32 |
33 | # We are using exec syntax to enable gracefull shutdown. Check
34 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
35 | ENTRYPOINT ["python", "http-server.py"]
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning-helper/http-server.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from flask import request
3 | from subprocess import Popen, PIPE
4 |
5 | app = Flask(__name__)
6 |
7 | rpcAddr = "bitcoin-lightning.simnet.primary:10009"
8 |
9 | macaroonFile = "/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon"
10 | tlsCertFile = "/root/.lnd/tls.cert"
11 |
12 | @app.route('/pay_invoice')
13 | def pay_invoice():
14 | invoice = request.args.get('invoice')
15 |
16 | bashCommand = "lncli --network=regtest --rpcserver={} --macaroonpath={} " \
17 | "--tlscertpath={} payinvoice --force --pay_req={}".format( \
18 | rpcAddr, macaroonFile, tlsCertFile, invoice)
19 |
20 | print(bashCommand)
21 | p = Popen(bashCommand, shell=True, stdout=PIPE)
22 | out = p.communicate()[0]
23 | return str(out)
24 |
25 | @app.route('/generate_invoice')
26 | def generate_invoice():
27 | amount = request.args.get('amount')
28 |
29 | bashCommand = "lncli --network=regtest --rpcserver={} --macaroonpath={} " \
30 | "--tlscertpath={} addinvoice --amt={}".format( \
31 | rpcAddr, macaroonFile, tlsCertFile, amount)
32 |
33 | print(bashCommand)
34 | p = Popen(bashCommand, shell=True, stdout=PIPE)
35 | out = p.communicate()[0]
36 | return str(out)
37 |
38 | if __name__ == '__main__':
39 | app.run(host="0.0.0.0", port="80")
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG BITCOIN_LIGHTNING_REVISION
4 |
5 | RUN go get -u github.com/golang/dep/cmd/dep
6 |
7 | WORKDIR $GOPATH/src/github.com/lightningnetwork/lnd
8 |
9 | # Instead of cloning lightningnetwork/lnd temproray use ourw version of lnd
10 | # daemon, but put in lightningnetwork/lnd directory so that all installation
11 | # scripts could work without being changed.
12 | RUN git clone https://github.com/bitlum/lnd.git .
13 |
14 | RUN git checkout $BITCOIN_LIGHTNING_REVISION
15 |
16 | RUN dep ensure -v
17 |
18 | RUN make install
19 |
20 |
21 |
22 | FROM ubuntu:18.04
23 |
24 | # ROLE is bitcoin node role: primary or secondary.
25 | #
26 | # Primary role means that this node will init new blockchain if it not
27 | # exists during deploy or restart.
28 | #
29 | # Secondary rank means that this node will try to connect to primary
30 | # node and use blockchain of latter.
31 | ARG ROLE
32 |
33 | # P2P port.
34 | EXPOSE 9735
35 |
36 | # RPC port.
37 | EXPOSE 10009
38 |
39 | # Copying required binaries from builder stage.
40 | COPY --from=builder /go/bin/lnd /go/bin/lncli /usr/local/bin/
41 |
42 | # Default config used to initalize datadir volume at first or
43 | # cleaned deploy.
44 | COPY bitcoin-lightning.simnet.$ROLE.conf /root/default/lnd.conf
45 |
46 | # Entrypoint script used to init datadir if required and for
47 | # starting bitcoin daemon.
48 | COPY entrypoint.sh /root/
49 |
50 | # We are using exec syntax to enable gracefull shutdown. Check
51 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
52 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning/bitcoin-lightning.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | [Application Options]
2 |
3 | maxpendingchannels=10
4 |
5 | listen=0.0.0.0:9735
6 | rpclisten=0.0.0.0:10009
7 |
8 | noseedbackup=1
9 |
10 | tlsextradomain=bitcoin-lightning.simnet.primary
11 | externalip=bitcoin-lightning.simnet.primary:9735
12 | debuglevel=trace
13 |
14 | [Bitcoin]
15 | bitcoin.active=1
16 | bitcoin.regtest=1
17 | bitcoin.node=bitcoind
18 |
19 | [Bitcoind]
20 | bitcoind.rpchost=bitcoin.simnet.primary:8332
21 | bitcoind.rpcuser=user
22 | bitcoind.rpcpass=password
23 | bitcoind.zmqpubrawblock=tcp://bitcoin.simnet.primary:8334
24 | bitcoind.zmqpubrawtx=tcp://bitcoin.simnet.primary:8335
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning/bitcoin-lightning.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | [Application Options]
2 |
3 | maxpendingchannels=10
4 |
5 | listen=0.0.0.0:9735
6 | rpclisten=0.0.0.0:10009
7 |
8 | noseedbackup=1
9 |
10 | tlsextradomain=bitcoin-lightning.simnet.secondary
11 | externalip=bitcoin-lightning.simnet.secondary:9735
12 | debuglevel=trace
13 |
14 | [Bitcoin]
15 | bitcoin.active=1
16 | bitcoin.regtest=1
17 | bitcoin.node=bitcoind
18 |
19 | [Bitcoind]
20 | bitcoind.rpchost=bitcoin.simnet.secondary:8332
21 | bitcoind.rpcuser=user
22 | bitcoind.rpcpass=password
23 | bitcoind.zmqpubrawblock=tcp://bitcoin.simnet.primary:8334
24 | bitcoind.zmqpubrawtx=tcp://bitcoin.simnet.primary:8335
--------------------------------------------------------------------------------
/docker/simnet/bitcoin-lightning/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make lnd data persistent.
4 | DATA_DIR=/root/.lnd
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/lnd.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/lnd.conf $CONFIG
22 |
23 | # If external IP defined we need to set corresponding run option.
24 | if [ ! -z $EXTERNAL_IP ]; then
25 | EXTERNAL_IP_OPT="--externalip=$EXTERNAL_IP"
26 | fi
27 |
28 | # We are using `exec` to enable gracefull shutdown of running daemon.
29 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
30 | exec lnd $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/bitcoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # ROLE is bitcoin node role: primary or secondary.
20 | #
21 | # Primary role means that this node will init new blockchain if it not
22 | # exists during deploy or restart.
23 | #
24 | # Secondary rank means that this node will try to connect to primary
25 | # node and use blockchain of latter.
26 | ARG ROLE
27 |
28 | # RPC port.
29 | EXPOSE 8332
30 |
31 | # P2P port.
32 | EXPOSE 8333
33 |
34 | # `zmqpubrawblock` and `zmqpubrawtx` port.
35 | EXPOSE 8334
36 |
37 | # Copying required binaries from builder stage.
38 | COPY --from=builder bitcoind bitcoin-cli /usr/local/bin/
39 |
40 | # Default config used to initalize datadir volume at first or
41 | # cleaned deploy.
42 | COPY bitcoin.simnet.$ROLE.conf /root/default/bitcoin.conf
43 |
44 | # Entrypoint script used to init datadir if required and for
45 | # starting bitcoin daemon.
46 | COPY entrypoint.sh /root/
47 |
48 | # We are using exec syntax to enable gracefull shutdown. Check
49 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
50 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/bitcoin/bitcoin.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | [regtest]
4 | port=8333
5 | rpcbind=0.0.0.0
6 | rpcport=8332
7 | rpcallowip=0.0.0.0/0
8 | rpcuser=user
9 | rpcpassword=password
10 |
11 | dnsseed=0
12 | upnp=0
13 |
14 | txindex=1
15 |
16 | printtoconsole=1
17 |
18 | zmqpubrawblock=tcp://0.0.0.0:8334
19 | zmqpubrawtx=tcp://0.0.0.0:8335
20 |
21 | # getaddressesbyaccount is deprecated and will be removed in V0.18.
22 | # but we need it right now.
23 | deprecatedrpc=accounts
24 | deprecatedrpc=signrawtransaction
--------------------------------------------------------------------------------
/docker/simnet/bitcoin/bitcoin.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=8333
4 |
5 | [regtest]
6 | rpcbind=0.0.0.0
7 | rpcport=8332
8 | rpcallowip=0.0.0.0/0
9 | rpcuser=user
10 | rpcpassword=password
11 |
12 | dnsseed=0
13 | upnp=0
14 |
15 | txindex=1
16 |
17 | printtoconsole=1
18 |
19 | zmqpubrawblock=tcp://0.0.0.0:8334
20 | zmqpubrawtx=tcp://0.0.0.0:8335
21 |
22 | connect=bitcoin.simnet.primary:8333
23 |
24 | # getaddressesbyaccount is deprecated and will be removed in V0.18.
25 | # but we need it right now.
26 | deprecatedrpc=accounts
27 | deprecatedrpc=signrawtransaction
--------------------------------------------------------------------------------
/docker/simnet/bitcoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | # We are using `exec` to enable gracefull shutdown of running daemon.
30 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
31 | exec bitcoind $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/blocks-generator/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_VERSION
4 | ARG BITCOIN_CASH_VERSION
5 | ARG DASH_VERSION
6 | ARG LITECOIN_VERSION
7 |
8 | RUN apt-get update && apt-get install -y \
9 | ca-certificates \
10 | curl \
11 | && rm -rf /var/lib/apt/lists/*
12 |
13 | RUN curl https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz \
14 | | tar xz --wildcards --strip 2 \
15 | */bin/bitcoin-cli
16 |
17 | RUN mkdir /bitcoin-cash
18 | RUN curl https://download.bitcoinabc.org/$BITCOIN_CASH_VERSION/linux/bitcoin-abc-${BITCOIN_CASH_VERSION}-x86_64-linux-gnu.tar.gz \
19 | | tar xz -C /bitcoin-cash --wildcards --strip 2 \
20 | */bin/bitcoin-cli
21 |
22 | RUN curl -L https://github.com/dashpay/dash/releases/download/v$DASH_VERSION/dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz \
23 | | tar xz --wildcards --strip 2 \
24 | */bin/dash-cli
25 |
26 | RUN curl https://download.litecoin.org/litecoin-$LITECOIN_VERSION/linux/litecoin-${LITECOIN_VERSION}-x86_64-linux-gnu.tar.gz \
27 | | tar xz --wildcards --strip 2 \
28 | */bin/litecoin-cli
29 |
30 |
31 | FROM ubuntu:18.04
32 |
33 | # Blocks generation period in seconds
34 | ARG PERIOD=10
35 |
36 | # Setting runtime environment variables whhich will be used in `entrypoint.sh`
37 | ENV PERIOD $PERIOD
38 |
39 | # Copying CLI binaries from already builded blockchain containers. They
40 | # should be builded already because this container depends on them.
41 | COPY --from=builder bitcoin-cli /usr/local/bin/
42 | COPY --from=builder bitcoin-cash/bitcoin-cli /usr/local/bin/bitcoin-cash-cli
43 | COPY --from=builder dash-cli /usr/local/bin/
44 | COPY --from=builder litecoin-cli /usr/local/bin/
45 |
46 | # Entrypoint script used for periodically blocks generation
47 | COPY entrypoint.sh /root/
48 |
49 | # We are using exec syntax to enable gracefull shutdown. Check
50 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
51 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/blocks-generator/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | RPC_USER="user"
4 | RPC_PASSWORD="password"
5 |
6 | BTC_RPC_HOST="bitcoin.simnet.primary"
7 | BTC_RPC_PORT=8332
8 | BTC_OPTS="\
9 | --rpcconnect=$BTC_RPC_HOST --rpcport=$BTC_RPC_PORT \
10 | --rpcuser=$RPC_USER --rpcpassword=$RPC_PASSWORD \
11 | --regtest"
12 |
13 | BCH_RPC_HOST="bitcoin-cash.simnet.primary"
14 | BCH_RPC_PORT=9332
15 | BCH_OPTS="\
16 | --rpcconnect=$BCH_RPC_HOST --rpcport=$BCH_RPC_PORT \
17 | --rpcuser=$RPC_USER --rpcpassword=$RPC_PASSWORD \
18 | --regtest"
19 |
20 | DASH_RPC_HOST="dash.simnet.primary"
21 | DASH_RPC_PORT=10332
22 | DASH_OPTS="\
23 | --rpcconnect=$DASH_RPC_HOST --rpcport=$DASH_RPC_PORT \
24 | --rpcuser=$RPC_USER --rpcpassword=$RPC_PASSWORD \
25 | --regtest"
26 |
27 | LTC_RPC_HOST="litecoin.simnet.primary"
28 | LTC_RPC_PORT=12332
29 | LTC_OPTS="\
30 | --rpcconnect=$LTC_RPC_HOST --rpcport=$LTC_RPC_PORT \
31 | --rpcuser=$RPC_USER --rpcpassword=$RPC_PASSWORD \
32 | --regtest"
33 |
34 | # We need to wait until all primary blockchain nodes are started.
35 | echo "$(date '+%Y-%m-%d %H:%M:%S') Waiting for all primary blockchains nodes are started"
36 | sleep 60
37 |
38 | # Initial blocks generation.
39 | echo "$(date '+%Y-%m-%d %H:%M:%S') Initial blocks generation"
40 | bitcoin-cli $BTC_OPTS generate 400
41 | bitcoin-cash-cli $BCH_OPTS generate 100
42 | dash-cli $DASH_OPTS generate 100
43 | litecoin-cli $LTC_OPTS generate 100
44 |
45 | # Proper way to catch shutdown signal and stop infinite loop. In this
46 | # solution we consider blockchains' cli runs are not long running process
47 | # so we need to stop sleep only.
48 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
49 | shutdown() {
50 | kill -s SIGTERM $!
51 | exit 0
52 | }
53 | trap shutdown SIGINT SIGTERM
54 |
55 | # Periodically block generation.
56 | while true
57 | do
58 | echo "$(date '+%Y-%m-%d %H:%M:%S') Periodical blocks generation"
59 | bitcoin-cli $BTC_OPTS generate 1
60 | bitcoin-cash-cli $BCH_OPTS generate 1
61 | dash-cli $DASH_OPTS generate 1
62 | litecoin-cli $LTC_OPTS generate 1
63 |
64 | # Wait for next period.
65 | sleep $PERIOD &
66 | wait $!
67 | done
--------------------------------------------------------------------------------
/docker/simnet/connector/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04
2 |
3 | RUN apt-get update && apt-get install -y \
4 | ca-certificates \
5 | curl \
6 | && rm -rf /var/lib/apt/lists/*
7 |
8 | # This implies that service has to be built locally first, and putted
9 | # in the docker directory, for running docker build.
10 | COPY bin/connector /usr/local/bin
11 | COPY bin/pscli /usr/local/bin
12 |
13 | # Default config used to initalize datadir volume at first or
14 | # cleaned deploy.
15 | COPY connector.simnet.conf /root/default/connector.conf
16 |
17 | # Entrypoint script used to init datadir if required and for
18 | # starting dash daemon
19 | COPY entrypoint.sh /root/
20 |
21 | # We are using exec syntax to enable gracefull shutdown. Check
22 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
23 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/connector/connector.simnet.conf:
--------------------------------------------------------------------------------
1 | [Application Options]
2 | datadir=/root/.connector
3 | debuglevel=trace
4 | network=simnet
5 |
6 | # RPC address to bind
7 | rpchost=0.0.0.0
8 | rpcport=9002
9 |
10 | [Bitcoin]
11 | bitcoin.disable=false
12 | bitcoin.minconfirmations=1
13 | bitcoin.syncdelay=5
14 | bitcoin.host=bitcoin.simnet.secondary
15 | bitcoin.port=8332
16 | bitcoin.user=user
17 | bitcoin.password=password
18 | bitcoin.feeperunit=11
19 |
20 | [Bitcoincash]
21 | bitcoincash.disable=true
22 | bitcoincash.minconfirmations=1
23 | bitcoincash.syncdelay=5
24 | bitcoincash.host=bitcoin-cash.simnet.secondary
25 | bitcoincash.port=9332
26 | bitcoincash.user=user
27 | bitcoincash.password=password
28 | bitcoincash.feeperunit=11
29 |
30 | [Dash]
31 | dash.disable=true
32 | dash.minconfirmations=1
33 | dash.syncdelay=5
34 | dash.host=dash.simnet.secondary
35 | dash.port=10332
36 | dash.user=user
37 | dash.password=password
38 | dash.feeperunit=11
39 |
40 | [Ethereum]
41 | ethereum.disable=true
42 | ethereum.minconfirmations=1
43 | ethereum.syncdelay=5
44 | ethereum.host=ethereum.simnet.secondary
45 | ethereum.port=11332
46 | ethereum.password=
47 |
48 | [Litecoin]
49 | litecoin.disable=true
50 | litecoin.minconfirmations=1
51 | litecoin.syncdelay=5
52 | litecoin.host=litecoin.simnet.secondary
53 | litecoin.port=12332
54 | litecoin.user=user
55 | litecoin.password=password
56 | litecoin.feeperunit=11
57 |
58 | [Bitcoinlightning]
59 | bitcoinlightning.disable=true
60 | bitcoinlightning.tlscertpath=/root/.lnd/tls.cert
61 | bitcoinlightning.macaroonpath=/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon
62 | # lnd RPC address
63 | bitcoinlightning.host=bitcoin-lightning.simnet.secondary
64 | bitcoinlightning.port=10009
65 | # lnd P2P address
66 | bitcoinlightning.peerhost=simnet.connector.bitlum.io
67 | bitcoinlightning.peerport=9735
68 |
69 | [Prometheus]
70 | prometheus.port=9998
--------------------------------------------------------------------------------
/docker/simnet/connector/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make connector data persistent.
4 | DATA_DIR=/root/.connector
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/connector.conf
11 |
12 | # At first deploy datadir should not exists, we creating it.
13 | if [ ! -d $DATA_DIR ]; then
14 | mkdir $DATA_DIR
15 | fi
16 |
17 | # We always restoring default config shipped with docker.
18 | echo "Restoring default config"
19 | cp $DEFAULTS_DIR/connector.conf $CONFIG
20 |
21 | # We are using `exec` to enable gracefull shutdown of running daemon.
22 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
23 | exec connector --config /root/.connector/connector.conf
--------------------------------------------------------------------------------
/docker/simnet/dash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG DASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl -L https://github.com/dashpay/dash/releases/download/v$DASH_VERSION/dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/dashd \
13 | */bin/dash-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # ROLE is bitcoin node role: primary or secondary.
20 | #
21 | # Primary role means that this node will init new blockchain if it not
22 | # exists during deploy or restart.
23 | #
24 | # Secondary rank means that this node will try to connect to primary
25 | # node and use blockchain of latter.
26 | ARG ROLE
27 |
28 | # RPC port.
29 | EXPOSE 10332
30 |
31 | # P2P port.
32 | EXPOSE 10333
33 |
34 | # Copying required binaries from builder stage.
35 | COPY --from=builder dashd dash-cli /usr/local/bin/
36 |
37 | # Default config used to initalize datadir volume at first or
38 | # cleaned deploy.
39 | COPY dash.simnet.$ROLE.conf /root/default/dash.conf
40 |
41 | # Entrypoint script used to init datadir if required and for
42 | # starting dash daemon.
43 | COPY entrypoint.sh /root/
44 |
45 | # We are using exec syntax to enable gracefull shutdown. Check
46 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
47 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/dash/dash.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=10333
4 | rpcport=10332
5 |
6 | rpcallowip=0.0.0.0/0
7 | rpcuser=user
8 | rpcpassword=password
9 |
10 | dnsseed=0
11 | upnp=0
12 | litemode=1
13 |
14 | txindex=1
15 |
16 | printtoconsole=1
--------------------------------------------------------------------------------
/docker/simnet/dash/dash.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=10333
4 | rpcport=10332
5 |
6 | rpcallowip=0.0.0.0/0
7 | rpcuser=user
8 | rpcpassword=password
9 |
10 | dnsseed=0
11 | upnp=0
12 | litemode=1
13 |
14 | txindex=1
15 |
16 | printtoconsole=1
17 |
18 | connect=dash.simnet.primary:10333
--------------------------------------------------------------------------------
/docker/simnet/dash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.dashcore
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/dash.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/dash.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | # We are using `exec` to enable gracefull shutdown of running daemon.
30 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
31 | exec dashd $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/ethereum-bootnode/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG ETHEREUM_REVISION
4 |
5 | WORKDIR /ethereum
6 |
7 | RUN curl -L https://github.com/bitlum/go-ethereum/archive/$ETHEREUM_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN make all
11 |
12 |
13 |
14 | FROM ubuntu:18.04
15 |
16 | # P2P port
17 | EXPOSE 30301
18 |
19 | # Copying required binaries from builder stage
20 | COPY --from=builder /ethereum/build/bin/bootnode /usr/local/bin/
21 |
22 | # Entrypoint script used to init datadir if required and for
23 | # starting bootnode daemon
24 | COPY entrypoint.sh /root/
25 |
26 | # We are using exec syntax to enable gracefull shutdown. Check
27 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
28 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/ethereum-bootnode/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Docker container public IP
4 | HOST_ADDR=`awk 'END{print $1}' /etc/hosts`
5 |
6 | # Bootnode bind address
7 | BIND_ADDR="$HOST_ADDR:30301"
8 |
9 | # This path is expected to be volume to be able to share bootnode keys
10 | DATA_DIR=/bootnode
11 |
12 | # This file will contain bootnode key
13 | KEY_FILE=$DATA_DIR/bootnode.key
14 |
15 | # This file will contain enode URL
16 | ENODE_FILE=$DATA_DIR/enode.url
17 |
18 | # If data directory doesn't exists this means that volume is not mounted
19 | # or mounted incorrectly, so we must fail.
20 | if [ ! -d $DATA_DIR ]; then
21 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
22 | exit 1
23 | fi
24 |
25 | if [ ! -f $KEY_FILE ]; then
26 | echo "Generation bootnode key"
27 | bootnode --genkey=$KEY_FILE
28 | fi
29 |
30 | if [ ! -f $ENODE_FILE ]; then
31 | echo "Computing enode URL"
32 | ENODE_KEY=`bootnode --nodekey=$KEY_FILE -writeaddress`
33 | ENODE_URL="enode://$ENODE_KEY@$BIND_ADDR"
34 | echo "Writing enode URL to file"
35 | echo $ENODE_URL > $ENODE_FILE
36 | fi
37 |
38 | # If external IP defined when we need to set corresponding run option
39 | if [ ! -z "$EXTERNAL_IP" ]; then
40 | EXTERNAL_IP_OPT="--nat extip:$EXTERNAL_IP"
41 | fi
42 |
43 | # Start bootnode with computed key file on defined bind address and
44 | # with optional nat external IP.
45 | echo "Starting bootnode"
46 | # We are using `exec` to enable gracefull shutdown of running daemon.
47 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
48 | exec bootnode --nodekey=$KEY_FILE --addr=$BIND_ADDR $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/ethereum/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG ETHEREUM_REVISION
4 |
5 | WORKDIR /ethereum
6 |
7 | RUN curl -L https://github.com/bitlum/go-ethereum/archive/$ETHEREUM_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN make geth
11 |
12 |
13 |
14 | FROM ubuntu:18.04
15 |
16 | # ROLE is bitcoin node role: primary or secondary.
17 | #
18 | # Primary role means that this node will init new blockchain if it not
19 | # exists during deploy or restart.
20 | #
21 | # Secondary rank means that this node will try to connect to primary
22 | # node and use blockchain of latter.
23 | ARG ROLE
24 |
25 | # RPC port.
26 | EXPOSE 11332
27 |
28 | # RPC-WS port.
29 | EXPOSE 11331
30 |
31 | # P2P port.
32 | EXPOSE 11333
33 |
34 | # Copying required binaries from builder stage.
35 | COPY --from=builder /ethereum/build/bin/geth /usr/local/bin/
36 |
37 | # Default config and genesis used to initalize datadir volume
38 | # at first or cleaned deploy.
39 | COPY ethereum.simnet.$ROLE.conf /root/default/ethereum.conf
40 | COPY genesis.json /root/default/
41 |
42 | # Entrypoint script used to init datadir if required and for
43 | # starting bitcoin daemon.
44 | COPY entrypoint.sh /root/
45 |
46 | # We are using exec syntax to enable gracefull shutdown. Check
47 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
48 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/ethereum/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This directory is expected to be volume to make blockchain and account
4 | # data persistent.
5 | DATA_DIR=/root/.ethereum
6 |
7 | BOOTNODE_DIR=/bootnode
8 |
9 | # This path is expected to have default data used to init environment
10 | # at first deploy such as config and genesis.
11 | DEFAULTS_DIR=/root/default
12 |
13 | CONFIG=$DATA_DIR/ethereum.conf
14 | GENESIS=$DATA_DIR/genesis.conf
15 | ENODE=$BOOTNODE_DIR/enode.url
16 |
17 | # If data directory doesn't exists this means that volume is not mounted
18 | # or mounted incorrectly, so we must fail.
19 | if [ ! -d $DATA_DIR ]; then
20 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
21 | exit 1
22 | fi
23 |
24 | # If bootnode directory doesn't exists this means that volume is not
25 | # mounted or mounted incorrectly, so we must fail.
26 | if [ ! -d $BOOTNODE_DIR ]; then
27 | echo "Bootnode directory '$BOOTNODE_DIR' doesn't exists. Check your volume configuration."
28 | exit 2
29 | fi
30 |
31 | if [ ! -f $ENODE ]; then
32 | echo "Bootnode directory '$BOOTNODE_DIR' doesn't contain 'enode.url' file. Does 'ethereum-bootnode' container successfully started?"
33 | exit 3
34 | fi
35 |
36 | # We always restoring default config shipped with docker.
37 | echo "Restoring default config"
38 | cp $DEFAULTS_DIR/ethereum.conf $CONFIG
39 |
40 | # At first deploy genesis in datadir should not exists so we
41 | # copying from default genesis shipped with docker.
42 | if [ ! -f $GENESIS ]; then
43 | echo "Copying default genesis"
44 | cp $DEFAULTS_DIR/genesis.json $GENESIS
45 | fi
46 |
47 | # At first deploy datadir keystore path should not exists which means
48 | # that account is not created, so we are creating it.
49 | KEYSTORE=$DATA_DIR/keystore
50 | if [ ! -d $KEYSTORE ] || [ ! "$(ls -A $KEYSTORE)" ]; then
51 | echo "Creating new account"
52 | geth --datadir $DATA_DIR --config $CONFIG account new --password /dev/null
53 | fi
54 |
55 | # At first deploy datadir geth path should not exists which means
56 | # that blockchain is not inited, so we are initing it.
57 | GETH=$DATA_DIR/geth
58 | if [ ! -d $GETH ] || [ ! "$(ls -A $GETH)" ]; then
59 | echo "Initialising genesis block"
60 | geth --datadir $DATA_DIR --config $CONFIG init $GENESIS
61 | fi
62 |
63 | # Set mine option to enable blocks mining if required.
64 | if [ "$MINE" -eq "1" ]; then
65 | MINE_OPT="--mine"
66 | fi
67 |
68 | # If external IP defined when we need to set corresponding run option
69 | if [ ! -z "$EXTERNAL_IP" ]; then
70 | EXTERNAL_IP_OPT="--nat extip:$EXTERNAL_IP"
71 | fi
72 |
73 | # We are using `exec` to enable gracefull shutdown of running daemon.
74 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
75 | exec geth \
76 | --datadir $DATA_DIR \
77 | --config $CONFIG \
78 | --bootnodes `cat $ENODE` \
79 | $MINE_OPT \
80 | $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/ethereum/ethereum.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | # Note: this config doesn't contain the genesis block.
2 |
3 | [Eth]
4 | NetworkId = 876546875
5 | SyncMode = "fast"
6 | LightPeers = 20
7 | DatabaseCache = 128
8 | GasPrice = 18000000000
9 | EnablePreimageRecording = false
10 |
11 | [Eth.Ethash]
12 | CacheDir = "ethash"
13 | CachesInMem = 2
14 | CachesOnDisk = 3
15 | DatasetDir = "/root/.ethereum/ethash.dagdir"
16 | DatasetsInMem = 1
17 | DatasetsOnDisk = 2
18 |
19 | [Eth.TxPool]
20 | NoLocals = false
21 | Journal = "transactions.rlp"
22 | Rejournal = 3600000000000
23 | PriceLimit = 1
24 | PriceBump = 10
25 | AccountSlots = 16
26 | GlobalSlots = 4096
27 | AccountQueue = 64
28 | GlobalQueue = 1024
29 | Lifetime = 10800000000000
30 |
31 | [Eth.GPO]
32 | Blocks = 10
33 | Percentile = 50
34 |
35 | [Shh]
36 | MaxMessageSize = 1048576
37 | MinimumAcceptedPOW = 2e-01
38 |
39 | [Node]
40 | DataDir = ""
41 | NoUSB = true
42 | IPCPath = "/root/.ethereum/geth.ipc"
43 | HTTPHost = "0.0.0.0"
44 | HTTPPort = 11332
45 | HTTPModules = ["net", "web3", "eth", "shh", "personal"]
46 | HTTPVirtualHosts = ["*"]
47 | WSHost = "0.0.0.0"
48 | WSPort = 11331
49 | WSModules = ["net", "web3", "eth", "shh"]
50 | WSOrigins = ["*"]
51 |
52 | [Node.P2P]
53 | MaxPeers = 10
54 | NoDiscovery = false
55 | BootstrapNodes = []
56 | BootstrapNodesV5 = []
57 | StaticNodes = []
58 | TrustedNodes = []
59 | ListenAddr = ":11333"
60 | EnableMsgEvents = false
61 |
62 | [Dashboard]
63 | Host = "localhost"
64 | Port = 8080
65 | Refresh = 3000000000
66 |
--------------------------------------------------------------------------------
/docker/simnet/ethereum/ethereum.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | # Note: this config doesn't contain the genesis block.
2 |
3 | [Eth]
4 | NetworkId = 876546875
5 | SyncMode = "fast"
6 | LightPeers = 20
7 | DatabaseCache = 128
8 | GasPrice = 18000000000
9 | EnablePreimageRecording = false
10 |
11 | [Eth.Ethash]
12 | CacheDir = "ethash"
13 | CachesInMem = 2
14 | CachesOnDisk = 3
15 | DatasetDir = "/root/.ethereum/ethash.dagdir"
16 | DatasetsInMem = 1
17 | DatasetsOnDisk = 2
18 |
19 | [Eth.TxPool]
20 | NoLocals = false
21 | Journal = "transactions.rlp"
22 | Rejournal = 3600000000000
23 | PriceLimit = 1
24 | PriceBump = 10
25 | AccountSlots = 16
26 | GlobalSlots = 4096
27 | AccountQueue = 64
28 | GlobalQueue = 1024
29 | Lifetime = 10800000000000
30 |
31 | [Eth.GPO]
32 | Blocks = 10
33 | Percentile = 50
34 |
35 | [Shh]
36 | MaxMessageSize = 1048576
37 | MinimumAcceptedPOW = 2e-01
38 |
39 | [Node]
40 | DataDir = ""
41 | NoUSB = true
42 | IPCPath = "/root/.ethereum/geth.ipc"
43 | HTTPHost = "0.0.0.0"
44 | HTTPPort = 11332
45 | HTTPModules = ["net", "web3", "eth", "shh", "personal"]
46 | HTTPVirtualHosts = ["*"]
47 | WSHost = "0.0.0.0"
48 | WSPort = 11331
49 | WSModules = ["net", "web3", "eth", "shh"]
50 | WSOrigins = ["*"]
51 |
52 | [Node.P2P]
53 | MaxPeers = 10
54 | NoDiscovery = false
55 | BootstrapNodes = []
56 | BootstrapNodesV5 = []
57 | StaticNodes = []
58 | TrustedNodes = []
59 | ListenAddr = ":11333"
60 | EnableMsgEvents = false
61 |
62 | [Dashboard]
63 | Host = "localhost"
64 | Port = 8080
65 | Refresh = 3000000000
66 |
--------------------------------------------------------------------------------
/docker/simnet/ethereum/genesis.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "chainId": 876546875,
4 | "homesteadBlock": 0,
5 | "eip155Block": 0,
6 | "eip158Block": 0,
7 | "minimumDifficulty": 16384
8 | },
9 | "difficulty": "0x4000",
10 | "gasLimit": "2100000",
11 | "alloc": {}
12 | }
--------------------------------------------------------------------------------
/docker/simnet/litecoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG LITECOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.litecoin.org/litecoin-$LITECOIN_VERSION/linux/litecoin-${LITECOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/litecoind \
13 | */bin/litecoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # ROLE is bitcoin node role: primary or secondary.
20 | #
21 | # Primary role means that this node will init new blockchain if it not
22 | # exists during deploy or restart.
23 | #
24 | # Secondary rank means that this node will try to connect to primary
25 | # node and use blockchain of latter.
26 | ARG ROLE
27 |
28 | # RPC port.
29 | EXPOSE 12332
30 |
31 | # P2P port.
32 | EXPOSE 12333
33 |
34 | # Copying required binaries from builder stage.
35 | COPY --from=builder litecoind litecoin-cli /usr/local/bin/
36 |
37 | # Default config used to initalize datadir volume
38 | # at first or cleaned deploy.
39 | COPY litecoin.simnet.$ROLE.conf /root/default/litecoin.conf
40 |
41 | # Entrypoint script used to init datadir if required and for
42 | # starting litecoin daemon.
43 | COPY entrypoint.sh /root/
44 |
45 | # We are using exec syntax to enable gracefull shutdown. Check
46 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
47 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/simnet/litecoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.litecoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/litecoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/litecoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | # We are using `exec` to enable gracefull shutdown of running daemon.
30 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
31 | exec litecoind $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/simnet/litecoin/litecoin.simnet.primary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=12333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=12332
7 |
8 | rpcallowip=0.0.0.0/0
9 | rpcuser=user
10 | rpcpassword=password
11 |
12 | addresstype=legacy
13 |
14 | dnsseed=0
15 | upnp=0
16 |
17 | txindex=1
18 |
19 | printtoconsole=1
--------------------------------------------------------------------------------
/docker/simnet/litecoin/litecoin.simnet.secondary.conf:
--------------------------------------------------------------------------------
1 | regtest=1
2 |
3 | port=12333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=12332
7 |
8 | rpcallowip=0.0.0.0/0
9 | rpcuser=user
10 | rpcpassword=password
11 |
12 | addresstype=legacy
13 |
14 | dnsseed=0
15 | upnp=0
16 |
17 | txindex=1
18 |
19 | printtoconsole=1
20 |
21 | connect=litecoin.simnet.primary:12333
--------------------------------------------------------------------------------
/docker/simnet/logrotate.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for simnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/logrotate.d/connector during deploy
4 | # following with restarting logrotate.
5 |
6 | /var/log/connector/*.log {
7 | # Rotate logs daily.
8 | daily
9 |
10 | # Keep last 7 days.
11 | rotate 7
12 |
13 | # If the log file is missing, go on to the next one without issuing an
14 | # error message.
15 | missingok
16 |
17 | # Do not rotate the log if it is empty.
18 | notifempty
19 |
20 | # Signal rsyslog about rotation to start new log file.
21 | postrotate
22 | invoke-rc.d rsyslog rotate > /dev/null
23 | endscript
24 | }
--------------------------------------------------------------------------------
/docker/simnet/rsyslog.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for simnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/rsyslog.d/connector.conf during deploy
4 | # following with rsyslogd restarting.
5 |
6 | # Fix `\t` and other caharacters in log messages.
7 | global(
8 | parser.escapecontrolcharactertab="off"
9 | )
10 |
11 | # Raw log line message template
12 | template(name="outfmt" type="list") {
13 | property(name="msg" position.from="2")
14 | constant(value="\n")
15 | }
16 |
17 | # Template for log dynamic file name to put containers logs in separate
18 | # file in `/var/log/connector/`. E.g. for dash container log file is
19 | # `/var/log/connector/dash.simnet.primary.log`.
20 | template(name="dynaFile" type="list") {
21 | constant(value="/var/log/connector/")
22 | property(
23 | name="syslogtag"
24 | # We parsing syslogtag like `connector-docker/dash.simnet.perimary[123]`
25 | # to get `dash.simnet.perimary` substring.
26 | regex.expression="^connector-docker\\/\\(.\\+\\)\\["
27 | regex.submatch="1"
28 | )
29 | constant(value=".log")
30 | }
31 |
32 | # Put connector docker containers' logs in separate files and discard whem.
33 | if ($programname == "connector-docker") then {
34 | action(type="omfile" dynaFile="dynaFile" template="outfmt")
35 | stop
36 | }
37 |
--------------------------------------------------------------------------------
/docker/testnet/.env.empty:
--------------------------------------------------------------------------------
1 | # This file contains list of environment variables required to deploy
2 | # testnet connector dockers. You should copy this file to `.env` and
3 | # fill the latter with right values
4 |
5 | # *_AUTH variables should contain authorization string generated by
6 | # `rpcauth.py` script from. You can get it from https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth
7 | # *_USER and *_PASSWORD variables should corresponds to *_AUTH string
8 |
9 | # *_VERSION variables should contain version in N.M.O format. This
10 | # variables are used to construct download link from official sources
11 | # of compiled binaries. So you should be sure that version you specifing
12 | # can be download. Check corresponding `Dockerfile` to get exact download
13 | # link.
14 |
15 | # *_REVISION variables specifies revision or branch which will be used.
16 | # As *_VERSION variables they are used to construct download link of
17 | # source code archive.
18 | # We encourage you to use revision a.k.a commit hash and do not use
19 | # branch name because docker-compose has cache and after first deploy
20 | # downloaded branch is freezed and further deploys will not download
21 | # new branch revisions. In contrast commit specifies exact
22 | # revision of source code so cache is working as should.
23 |
24 | # This three variables intended to be specified outside of .env file.
25 | # You should specify them during deploy on specific droplet.
26 | EXTERNAL_IP=
27 | PRIVATE_IP=
28 |
29 | # `connector` docker container uses `github.com/bitlum/connector`. This
30 | # variable controls which revision (or branch) will be used.
31 | CONNECTOR_REVISION=
32 |
33 | # Force hash is used when connector need to skip syncing of blocks,
34 | # or roll back.
35 | CONNECTOR_BITCOIN_FORCE_HASH=
36 | CONNECTOR_LITECOIN_FORCE_HASH=
37 | CONNECTOR_DASH_FORCE_HASH=
38 | CONNECTOR_BITCOIN_CASH_FORCE_HASH=
39 | CONNECTOR_ETHEREUM_FORCE_HASH=
40 |
41 | BITCOIN_VERSION=
42 | BITCOIN_RPC_AUTH=
43 | BITCOIN_RPC_USER=
44 | BITCOIN_RPC_PASSWORD=
45 |
46 | # `bitcoin-lightning` docker container uses `lnd` from `github.com/bitlum/lnd`
47 | # repo. This variable controls which revision (or branch) will be used.
48 | BITCOIN_LIGHTNING_REVISION=
49 |
50 | # `bitcoin-neutrino` docker container uses btcd from `github.com/btcsuite/btcd`
51 | # repo. This variable controls which revision (or branch) will be used.
52 | BITCOIN_NEUTRION_REVISION=
53 | BITCOIN_NEUTRINO_RPC_USER=
54 | BITCOIN_NEUTRINO_RPC_PASSWORD=
55 |
56 | BITCOIN_CASH_VERSION=
57 | BITCOIN_CASH_RPC_AUTH=
58 | BITCOIN_CASH_RPC_USER=
59 | BITCOIN_CASH_RPC_PASSWORD=
60 |
61 | DASH_VERSION=
62 | DASH_RPC_AUTH=
63 | DASH_RPC_USER=
64 | DASH_RPC_PASSWORD=
65 |
66 | # `ethereum` docker container uses `geth` from `github.com/bitlum/go-ethereum`
67 | # repo. This variable controls which revision (or branch) will be used.
68 | ETHEREUM_REVISION=
69 | # This password will be used to create new ethereum account and to unlock
70 | # existing ethereum accounts. You should generate strong and long password.
71 | ETHEREUM_ACCOUNT_PASSWORD=
72 |
73 | LITECOIN_VERSION=
74 | LITECOIN_RPC_AUTH=
75 | LITECOIN_RPC_USER=
76 | LITECOIN_RPC_PASSWORD=
--------------------------------------------------------------------------------
/docker/testnet/.gitignore:
--------------------------------------------------------------------------------
1 | .env
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-cash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_CASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.bitcoinabc.org/$BITCOIN_CASH_VERSION/linux/bitcoin-abc-${BITCOIN_CASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 9332
21 |
22 | # P2P port.
23 | EXPOSE 9333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder bitcoind /usr/local/bin/bitcoin-cashd
27 | COPY --from=builder bitcoin-cli /usr/local/bin/bitcoin-cash-cli
28 |
29 | # Default config used to initalize datadir volume at first or
30 | # cleaned deploy. It will be restored and used after each restart.
31 | COPY bitcoin-cash.testnet.conf /root/default/bitcoin.conf
32 |
33 | # Entrypoint script used to init datadir if required and for
34 | # starting bitcoin cash daemon.
35 | COPY entrypoint.sh /root/
36 |
37 | # We are using exec syntax to enable gracefull shutdown. Check
38 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
39 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-cash/bitcoin-cash.testnet.conf:
--------------------------------------------------------------------------------
1 | testnet=1
2 |
3 | port=9333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=9332
7 |
8 | rpcallowip=172.100.1.100
9 |
10 | txindex=1
11 | usecashaddr=0
12 |
13 | printtoconsole=1
14 |
15 | discover=1
16 | listen=1
17 | server=1
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-cash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec bitcoin-cashd $EXTERNAL_IP_OPT \
33 | --rpcauth=$BITCOIN_CASH_RPC_AUTH
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-lightning/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.11-alpine as builder
2 |
3 | ARG BITCOIN_LIGHTNING_REVISION
4 |
5 | # Install dependencies and install/build lnd.
6 | RUN apk add --no-cache --update alpine-sdk \
7 | git \
8 | make
9 |
10 | WORKDIR $GOPATH/src/github.com/lightningnetwork/lnd
11 |
12 | # Copy from repository to build from.
13 | RUN git clone https://github.com/lightningnetwork/lnd.git /go/src/github.com/lightningnetwork/lnd
14 |
15 | # Force Go to use the cgo based DNS resolver. This is required to ensure DNS
16 | # queries required to connect to linked containers succeed.
17 | ENV GODEBUG netdns=cgo
18 |
19 | RUN cd /go/src/github.com/lightningnetwork/lnd \
20 | && git checkout $BITCOIN_LIGHTNING_REVISION \
21 | && make build-itest \
22 | && mv lnd-itest /go/bin/lnd \
23 | && mv lncli-itest /go/bin/lncli
24 |
25 | # Install delve - debugger for the Go programming language, in order to be
26 | # able to attach to the lnd process and understand why it stuck.
27 | RUN go get -u github.com/derekparker/delve/cmd/dlv
28 |
29 | # Start a new, final image to reduce size.
30 | FROM alpine as final
31 |
32 | # Expose lnd ports (server, rpc).
33 | EXPOSE 9735 10009
34 |
35 | # Add bash.
36 | RUN apk add --no-cache \
37 | bash
38 |
39 | # Copying required binaries from builder stage.
40 | COPY --from=builder /go/bin/lnd /usr/local/bin/
41 | COPY --from=builder /go/bin/lncli /usr/local/bin/
42 | COPY --from=builder /go/bin/dlv /usr/local/bin/
43 |
44 | # Default config used to initalize datadir volume at first or
45 | # cleaned deploy. It will be restored and used after each restart.
46 | COPY bitcoin-lightning.testnet.conf /root/default/lnd.conf
47 |
48 | # Entrypoint script used to init datadir if required and for
49 | # starting bitcoin daemon.
50 | COPY entrypoint.sh /root/
51 |
52 | # We are using exec syntax to enable gracefull shutdown. Check
53 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
54 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-lightning/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make lnd data persistent.
4 | DATA_DIR=/root/.lnd
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/lnd.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/lnd.conf $CONFIG
22 |
23 | # If external IP defined we need to set corresponding run option
24 | if [ ! -z "$EXTERNAL_IP" ]; then
25 | echo "Setting external IP"
26 | EXTERNAL_IP_OPT="--externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | RPC_USER_OPT="--bitcoind.rpcuser="
30 |
31 | # We are using `exec` to enable gracefull shutdown of running daemon.
32 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
33 | exec lnd $EXTERNAL_IP_OPT \
34 | --bitcoind.rpcuser=$BITCOIN_RPC_USER \
35 | --bitcoind.rpcpass=$BITCOIN_RPC_PASSWORD
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-neutrino/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG BITCOIN_NEUTRINO_REVISION
4 |
5 | RUN go get -u github.com/Masterminds/glide
6 |
7 | WORKDIR $GOPATH/src/github.com/btcsuite/btcd
8 |
9 | RUN curl -L https://github.com/btcsuite/btcd/archive/$BITCOIN_NEUTRINO_REVISION.tar.gz \
10 | | tar xz --strip 1
11 |
12 | RUN glide install
13 |
14 | RUN go install -v . ./cmd/btcctl
15 |
16 |
17 |
18 | FROM ubuntu:18.04
19 |
20 | # P2P port.
21 | EXPOSE 18333
22 |
23 | # Copying required binaries from builder stage.
24 | COPY --from=builder /go/bin/btcd /go/bin/btcctl /usr/local/bin/
25 |
26 | # Default config used to initalize datadir volume at first or
27 | # cleaned deploy. It will be restored and used after each restart.
28 | COPY bitcoin-neutrino.testnet.conf /root/default/btcd.conf
29 |
30 | # Entrypoint script used to init datadir if required and for
31 | # starting bitcoin daemon.
32 | COPY entrypoint.sh /root/
33 |
34 | # We are using exec syntax to enable gracefull shutdown. Check
35 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
36 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/bitcoin-neutrino/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make lnd data persistent.
4 | DATA_DIR=/root/.btcd
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/btcd.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [ ! -d $DATA_DIR ]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp $DEFAULTS_DIR/btcd.conf $CONFIG
22 |
23 | # If external IP defined we need to set corresponding run option
24 | if [ ! -z "$EXTERNAL_IP" ]; then
25 | echo "Setting external IP"
26 | EXTERNAL_IP_OPT="--externalip=$EXTERNAL_IP"
27 | fi
28 |
29 | RPC_USER_OPT="--bitcoind.rpcuser="
30 |
31 | # We are using `exec` to enable gracefull shutdown of running daemon.
32 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
33 | exec btcd $EXTERNAL_IP_OPT \
34 | --rpcuser=$BITCOIN_NEUTRINO_RPC_USER
35 | --rpcpass=$BITCOIN_NEUTRINO_RPC_PASSWORD
--------------------------------------------------------------------------------
/docker/testnet/bitcoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG BITCOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/bitcoind \
13 | */bin/bitcoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 13332
21 |
22 | # P2P port.
23 | EXPOSE 13333
24 |
25 | # `zmqpubrawblock` and `zmqpubrawtx` port.
26 | EXPOSE 8334
27 |
28 | # Copying required binaries from builder stage.
29 | COPY --from=builder bitcoind bitcoin-cli /usr/local/bin/
30 |
31 | # Default config used to initalize datadir volume at first or
32 | # cleaned deploy. It will be restored and used after each restart.
33 | COPY bitcoin.testnet.conf /root/default/bitcoin.conf
34 |
35 | # Entrypoint script used to init datadir if required and for
36 | # starting bitcoin daemon.
37 | COPY entrypoint.sh /root/
38 |
39 | # We are using exec syntax to enable gracefull shutdown. Check
40 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
41 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/bitcoin/bitcoin.testnet.conf:
--------------------------------------------------------------------------------
1 | testnet=1
2 | discover=1
3 | listen=1
4 | server=1
5 | txindex=1
6 | printtoconsole=1
7 |
8 |
9 | [test]
10 | port=8333
11 | rpcbind=0.0.0.0
12 | rpcport=8332
13 | rpcallowip=0.0.0.0/0
14 |
15 | # Increasing rcpthreads to workaround possible bug described here
16 | # https://github.com/lightningnetwork/lnd/issues/1174.
17 | rpcthreads=16
18 |
19 | zmqpubrawblock=tcp://0.0.0.0:8334
20 | zmqpubrawtx=tcp://0.0.0.0:8335
21 |
22 | # getaddressesbyaccount is deprecated and will be removed in V0.18.
23 | # but we need it right now.
24 | deprecatedrpc=accounts
25 | deprecatedrpc=signrawtransaction
--------------------------------------------------------------------------------
/docker/testnet/bitcoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.bitcoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/bitcoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/bitcoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec bitcoind $EXTERNAL_IP_OPT \
33 | --rpcauth=$BITCOIN_RPC_AUTH
--------------------------------------------------------------------------------
/docker/testnet/connector/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.12 AS builder
2 |
3 | WORKDIR $GOPATH/src/github.com/bitlum/connector/
4 |
5 | ARG CONNECTOR_REVISION
6 |
7 | RUN curl -L https://github.com/bitlum/connector/archive/$CONNECTOR_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN GO111MODULE=on go get
11 | RUN GO111MODULE=on go install . ./cmd/...
12 |
13 | FROM ubuntu:18.04
14 |
15 | RUN apt-get update && apt-get install -y \
16 | ca-certificates \
17 | curl \
18 | && rm -rf /var/lib/apt/lists/*
19 |
20 | # Copying required binaries from builder stage.
21 | COPY --from=builder /go/bin/connector /usr/local/bin
22 | COPY --from=builder /go/bin/pscli /usr/local/bin
23 |
24 | # Default config used to initalize datadir volume at first or
25 | # cleaned deploy. It will be restored and used after each restart.
26 | COPY connector.testnet.conf /root/default/connector.conf
27 |
28 | # Entrypoint script used to init datadir if required and for
29 | # starting dash daemon
30 | COPY entrypoint.sh /root/
31 |
32 | # We are using exec syntax to enable gracefull shutdown. Check
33 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
34 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
35 |
--------------------------------------------------------------------------------
/docker/testnet/connector/connector.testnet.conf:
--------------------------------------------------------------------------------
1 | [Application Options]
2 | datadir=/root/.connector
3 | debuglevel=trace
4 | network=testnet
5 |
6 | # RPC address to bind
7 | rpchost=0.0.0.0
8 | rpcport=9002
9 |
10 | [Bitcoin]
11 | bitcoin.disable=false
12 | bitcoin.minconfirmations=1
13 | bitcoin.syncdelay=5
14 | bitcoin.host=bitcoin.testnet
15 | bitcoin.port=8332
16 | bitcoin.feeperunit=350
17 |
18 | [Bitcoincash]
19 | bitcoincash.disable=false
20 | bitcoincash.minconfirmations=1
21 | bitcoincash.syncdelay=5
22 | bitcoincash.host=bitcoin-cash.testnet
23 | bitcoincash.port=9332
24 | bitcoincash.feeperunit=350
25 |
26 | [Dash]
27 | dash.disable=false
28 | dash.minconfirmations=1
29 | dash.syncdelay=5
30 | dash.host=dash.testnet
31 | dash.port=10332
32 | dash.feeperunit=350
33 |
34 | [Ethereum]
35 | ethereum.disable=false
36 | ethereum.minconfirmations=1
37 | ethereum.syncdelay=5
38 | ethereum.host=ethereum.testnet
39 | ethereum.port=11332
40 |
41 | [Litecoin]
42 | litecoin.disable=false
43 | litecoin.minconfirmations=1
44 | litecoin.syncdelay=5
45 | litecoin.host=litecoin.testnet
46 | litecoin.port=12332
47 | litecoin.feeperunit=350
48 |
49 | [Bitcoinlightning]
50 | bitcoinlightning.disable=false
51 | bitcoinlightning.tlscertpath=/root/.lnd/tls.cert
52 | bitcoinlightning.macaroonpath=/root/.lnd/data/chain/bitcoin/testnet/admin.macaroon
53 | # lnd RPC address
54 | bitcoinlightning.host=bitcoin-lightning.testnet
55 | bitcoinlightning.port=10009
56 | # lnd P2P address
57 | bitcoinlightning.peerhost=testnet.connector.bitlum.io
58 | bitcoinlightning.peerport=9735
--------------------------------------------------------------------------------
/docker/testnet/connector/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make connector data persistent.
4 | DATA_DIR=/root/.connector
5 |
6 | # This path is expected to have default data used to init environment
7 | # at first deploy such as config.
8 | DEFAULTS_DIR=/root/default
9 |
10 | CONFIG=$DATA_DIR/connector.conf
11 |
12 | # If data directory doesn't exists this means that volume is not mounted
13 | # or mounted incorrectly, so we must fail.
14 | if [[ ! -d ${DATA_DIR} ]]; then
15 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
16 | exit 1
17 | fi
18 |
19 | # We always restoring default config shipped with docker.
20 | echo "Restoring default config"
21 | cp ${DEFAULTS_DIR}/connector.conf ${CONFIG}
22 |
23 | # We are using `exec` to enable gracefull shutdown of running daemon.
24 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
25 | exec connector --config /root/.connector/connector.conf \
26 | --bitcoin.user=${BITCOIN_RPC_USER} \
27 | --bitcoin.password=${BITCOIN_RPC_PASSWORD} \
28 | --bitcoin.forcelasthash=${CONNECTOR_BITCOIN_FORCE_HASH} \
29 | --bitcoincash.user=${BITCOIN_CASH_RPC_USER} \
30 | --bitcoincash.password=${BITCOIN_CASH_RPC_PASSWORD} \
31 | --bitcoincash.forcelasthash=${CONNECTOR_BITCOIN_CASH_FORCE_HASH} \
32 | --dash.user=${DASH_RPC_USER} \
33 | --dash.password=${DASH_RPC_PASSWORD} \
34 | --dash.forcelasthash=${CONNECTOR_DASH_FORCE_HASH} \
35 | --ethereum.password=${ETHEREUM_ACCOUNT_PASSWORD} \
36 | --ethereum.forcelasthash=${CONNECTOR_ETHEREUM_FORCE_HASH}
37 | --litecoin.user=${LITECOIN_RPC_USER} \
38 | --litecoin.password=${LITECOIN_RPC_PASSWORD} \
39 | --litecoin.forcelasthash=${CONNECTOR_LITECOIN_FORCE_HASH}
40 |
--------------------------------------------------------------------------------
/docker/testnet/dash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG DASH_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl -L https://github.com/dashpay/dash/releases/download/v$DASH_VERSION/dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/dashd \
13 | */bin/dash-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 10332
21 |
22 | # P2P port.
23 | EXPOSE 10333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder dashd dash-cli /usr/local/bin/
27 |
28 | # Default config used to initalize datadir volume at first or
29 | # cleaned deploy. It will be restored and used after each restart.
30 | COPY dash.testnet.conf /root/default/dash.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting dash daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/dash/dash.testnet.conf:
--------------------------------------------------------------------------------
1 | testnet=1
2 |
3 | port=10333
4 | rpcport=10332
5 |
6 | rpcallowip=172.100.1.100
7 |
8 | txindex=1
9 |
10 | printtoconsole=1
11 |
12 | listen=1
13 | server=1
--------------------------------------------------------------------------------
/docker/testnet/dash/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.dashcore
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/dash.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/dash.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec dashd $EXTERNAL_IP_OPT \
33 | --rpcauth=$DASH_RPC_AUTH
--------------------------------------------------------------------------------
/docker/testnet/ethereum/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.10.3 AS builder
2 |
3 | ARG ETHEREUM_REVISION
4 |
5 | WORKDIR /ethereum
6 |
7 | RUN curl -L https://github.com/bitlum/go-ethereum/archive/$ETHEREUM_REVISION.tar.gz \
8 | | tar xz --strip 1
9 |
10 | RUN make geth
11 |
12 |
13 |
14 | FROM ubuntu:18.04
15 |
16 | # RPC port.
17 | EXPOSE 11332
18 |
19 | # RPC-WS port.
20 | EXPOSE 11331
21 |
22 | # P2P port.
23 | EXPOSE 11333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder /ethereum/build/bin/geth /usr/local/bin/
27 |
28 | # Default config and genesis used to initalize datadir volume
29 | # at first or cleaned deploy.
30 | COPY ethereum.testnet.conf /root/default/ethereum.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting bitcoin daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/ethereum/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This directory is expected to be volume to make blockchain and account
4 | # data persistent.
5 | DATA_DIR=/root/.ethereum
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/ethereum.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/ethereum.conf $CONFIG
23 |
24 | # At first deploy datadir keystore path should not exists which means
25 | # that account is not created, so we are creating it.
26 | KEYSTORE=$DATA_DIR/keystore
27 | if [ ! -d $KEYSTORE ] || [ ! "$(ls -A $KEYSTORE)" ]; then
28 | echo "Creating new account"
29 | geth --datadir $DATA_DIR --config $CONFIG account new --password /dev/null
30 | fi
31 |
32 | # If external IP defined when we need to set corresponding run option
33 | if [ ! -z "$EXTERNAL_IP" ]; then
34 | echo "Setting external IP"
35 | EXTERNAL_IP_OPT="--nat extip:$EXTERNAL_IP"
36 | fi
37 |
38 | # We are using `exec` to enable gracefull shutdown of running daemon.
39 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
40 | exec geth \
41 | --datadir $DATA_DIR \
42 | --config $CONFIG \
43 | $EXTERNAL_IP_OPT
--------------------------------------------------------------------------------
/docker/testnet/ethereum/ethereum.testnet.conf:
--------------------------------------------------------------------------------
1 | [Eth]
2 | NetworkId = 4
3 | SyncMode = "fast"
4 | NoPruning = false
5 | LightPeers = 100
6 | DatabaseCache = 512
7 | TrieCleanCache = 256
8 | TrieDirtyCache = 256
9 | TrieTimeout = 3600000000000
10 | MinerGasFloor = 8000000
11 | MinerGasCeil = 8000000
12 | MinerGasPrice = 1000000000
13 | MinerRecommit = 3000000000
14 | MinerNoverify = false
15 | EnablePreimageRecording = false
16 | EWASMInterpreter = ""
17 | EVMInterpreter = ""
18 |
19 | [Eth.Ethash]
20 | CacheDir = "ethash"
21 | CachesInMem = 2
22 | CachesOnDisk = 3
23 | DatasetDir = "/root/.ethereum/ethash.dagdir"
24 | DatasetsInMem = 1
25 | DatasetsOnDisk = 2
26 | PowMode = 0
27 |
28 | [Eth.TxPool]
29 | Locals = []
30 | NoLocals = false
31 | Journal = "transactions.rlp"
32 | Rejournal = 3600000000000
33 | PriceLimit = 1
34 | PriceBump = 10
35 | AccountSlots = 16
36 | GlobalSlots = 4096
37 | AccountQueue = 64
38 | GlobalQueue = 1024
39 | Lifetime = 10800000000000
40 |
41 | [Eth.GPO]
42 | Blocks = 20
43 | Percentile = 60
44 |
45 | [Shh]
46 | MaxMessageSize = 1048576
47 | MinimumAcceptedPOW = 2e-01
48 | RestrictConnectionBetweenLightClients = true
49 |
50 | [Node]
51 | DataDir = ""
52 | NoUSB = true
53 | IPCPath = "/root/.ethereum/geth.ipc"
54 | HTTPHost = "0.0.0.0"
55 | HTTPPort = 11332
56 | HTTPModules = ["net", "web3", "eth", "shh", "personal"]
57 | HTTPVirtualHosts = ["ethereum.testnet"]
58 | WSHost = "0.0.0.0"
59 | WSPort = 11331
60 | WSModules = ["net", "web3", "eth", "shh"]
61 | WSOrigins = ["ethereum.testnet"]
62 |
63 | [Node.P2P]
64 | MaxPeers = 25
65 | NoDiscovery = false
66 | BootstrapNodes = ["enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"]
67 | BootstrapNodesV5 = ["enode://06051a5573c81934c9554ef2898eb13b33a34b94cf36b202b69fde139ca17a85051979867720d4bdae4323d4943ddf9aeeb6643633aa656e0be843659795007a@35.177.226.168:30303", "enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30304", "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30306", "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30307"]
68 | StaticNodes = []
69 | TrustedNodes = []
70 | ListenAddr = ":11333"
71 | EnableMsgEvents = false
72 |
73 | [Node.HTTPTimeouts]
74 | ReadTimeout = 30000000000
75 | WriteTimeout = 30000000000
76 | IdleTimeout = 120000000000
--------------------------------------------------------------------------------
/docker/testnet/litecoin/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:18.04 AS builder
2 |
3 | ARG LITECOIN_VERSION
4 |
5 | RUN apt-get update && apt-get install -y \
6 | ca-certificates \
7 | curl \
8 | && rm -rf /var/lib/apt/lists/*
9 |
10 | RUN curl https://download.litecoin.org/litecoin-$LITECOIN_VERSION/linux/litecoin-${LITECOIN_VERSION}-x86_64-linux-gnu.tar.gz \
11 | | tar xz --wildcards --strip 2 \
12 | */bin/litecoind \
13 | */bin/litecoin-cli
14 |
15 |
16 |
17 | FROM ubuntu:18.04
18 |
19 | # RPC port.
20 | EXPOSE 12332
21 |
22 | # P2P port.
23 | EXPOSE 12333
24 |
25 | # Copying required binaries from builder stage.
26 | COPY --from=builder litecoind litecoin-cli /usr/local/bin/
27 |
28 | # Default config used to initalize datadir volume
29 | # at first or cleaned deploy.
30 | COPY litecoin.testnet.conf /root/default/litecoin.conf
31 |
32 | # Entrypoint script used to init datadir if required and for
33 | # starting litecoin daemon.
34 | COPY entrypoint.sh /root/
35 |
36 | # We are using exec syntax to enable gracefull shutdown. Check
37 | # http://veithen.github.io/2014/11/16/sigterm-propagation.html.
38 | ENTRYPOINT ["bash", "/root/entrypoint.sh"]
--------------------------------------------------------------------------------
/docker/testnet/litecoin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This path is expected to be volume to make blockchain and accounts
4 | # data persistent.
5 | DATA_DIR=/root/.litecoin
6 |
7 | # This path is expected to have default data used to init environment
8 | # at first deploy such as config and genesis.
9 | DEFAULTS_DIR=/root/default
10 |
11 | CONFIG=$DATA_DIR/litecoin.conf
12 |
13 | # If data directory doesn't exists this means that volume is not mounted
14 | # or mounted incorrectly, so we must fail.
15 | if [ ! -d $DATA_DIR ]; then
16 | echo "Data directory '$DATA_DIR' doesn't exists. Check your volume configuration."
17 | exit 1
18 | fi
19 |
20 | # We always restoring default config shipped with docker.
21 | echo "Restoring default config"
22 | cp $DEFAULTS_DIR/litecoin.conf $CONFIG
23 |
24 | # If external IP defined when we need to set corresponding run option
25 | if [ ! -z "$EXTERNAL_IP" ]; then
26 | echo "Setting external IP"
27 | EXTERNAL_IP_OPT="-externalip=$EXTERNAL_IP"
28 | fi
29 |
30 | # We are using `exec` to enable gracefull shutdown of running daemon.
31 | # Check http://veithen.github.io/2014/11/16/sigterm-propagation.html.
32 | exec litecoind $EXTERNAL_IP_OPT \
33 | --rpcauth=$LITECOIN_RPC_AUTH
--------------------------------------------------------------------------------
/docker/testnet/litecoin/litecoin.testnet.conf:
--------------------------------------------------------------------------------
1 | testnet=1
2 |
3 | port=12333
4 |
5 | rpcbind=0.0.0.0
6 | rpcport=12332
7 |
8 | rpcallowip=172.100.1.100
9 |
10 | addresstype=legacy
11 |
12 | txindex=1
13 |
14 | printtoconsole=1
15 |
16 | discover=1
17 | listen=1
18 | server=1
--------------------------------------------------------------------------------
/docker/testnet/logrotate.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for testnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/logrotate.d/connector during deploy
4 | # following with restarting logrotate.
5 |
6 | /var/log/connector/*.log {
7 | # Rotate logs daily.
8 | daily
9 |
10 | # Keep last 30 days.
11 | rotate 30
12 |
13 | # If the log file is missing, go on to the next one without issuing an
14 | # error message.
15 | missingok
16 |
17 | # Do not rotate the log if it is empty.
18 | notifempty
19 |
20 | # Postpone compression of the previous log file to the next rotation cycle.
21 | delaycompress
22 |
23 | # Old versions of log files are compressed with gzip(1).
24 | compress
25 |
26 | # Signal rsyslog about rotation to start new log file.
27 | postrotate
28 | invoke-rc.d rsyslog rotate > /dev/null
29 | endscript
30 | }
--------------------------------------------------------------------------------
/docker/testnet/rsyslog.conf:
--------------------------------------------------------------------------------
1 | # This is rsyslog configuration for testnet connector dockers' host machine.
2 |
3 | # It intended to be placed at /etc/rsyslog.d/connector.conf during deploy
4 | # following with rsyslogd restarting.
5 |
6 | # Fix `\t` and other caharacters in log messages.
7 | global(
8 | parser.escapecontrolcharactertab="off"
9 | )
10 |
11 | # Raw log line message template
12 | template(name="outfmt" type="list") {
13 | property(name="msg" position.from="2")
14 | constant(value="\n")
15 | }
16 |
17 | # Template for log dynamic file name to put containers logs in separate
18 | # file in `/var/log/connector/`. E.g. for dash container log file is
19 | # `/var/log/connector/dash.simnet.primary.log`.
20 | template(name="dynaFile" type="list") {
21 | constant(value="/var/log/connector/")
22 | property(
23 | name="syslogtag"
24 | # We parsing syslogtag like `connector-docker/dash.simnet.perimary[123]`
25 | # to get `dash.simnet.perimary` substring.
26 | regex.expression="^connector-docker\\/\\(.\\+\\)\\["
27 | regex.submatch="1"
28 | )
29 | constant(value=".log")
30 | }
31 |
32 | # Put connector docker containers' logs in separate files and discard whem.
33 | if ($programname == "connector-docker") then {
34 | action(type="omfile" dynaFile="dynaFile" template="outfmt")
35 | stop
36 | }
37 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/bitlum/connector
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/bitlum/btcd v0.0.0-20180201152249-072770c03d1d
7 | github.com/bitlum/btcutil v0.0.0-20171119084920-ff69c1bdcd79 // indirect
8 | github.com/bitlum/go-bitcoind-rpc v0.0.0-20181122191953-5503508ef045
9 | github.com/bitlum/graphql-go v0.0.0-20171223131140-e232a94bee37
10 | github.com/boltdb/bolt v1.3.1 // indirect
11 | github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c
12 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
13 | github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38
14 | github.com/btcsuite/btcwallet v0.0.0-20190319010515-89ab2044f962
15 | github.com/btcsuite/go-flags v0.0.0-20150116065318-6c288d648c1c
16 | github.com/davecgh/go-spew v1.1.1
17 | github.com/ethereum/go-ethereum v1.8.23
18 | github.com/go-errors/errors v1.0.1
19 | github.com/golang/protobuf v1.3.1
20 | github.com/jarcoal/httpmock v1.0.0 // indirect
21 | github.com/jinzhu/gorm v1.9.2
22 | github.com/jrick/logrotate v1.0.0
23 | github.com/lightningnetwork/lnd v0.5.1-beta.0.20190322040823-c7ca387a9d92
24 | github.com/ltcsuite/ltcd v0.0.0-20190215003858-73a737535028
25 | github.com/mr-tron/base58 v1.1.1 // indirect
26 | github.com/onrik/ethrpc v0.0.0-20190305112807-6b8e9c0e9a8f
27 | github.com/pkg/errors v0.8.1
28 | github.com/prometheus/client_golang v0.9.2
29 | github.com/schancel/cashaddr-converter v0.0.0-20181111022653-4769e7add95a
30 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
31 | github.com/tidwall/gjson v1.2.1 // indirect
32 | github.com/tidwall/match v1.0.1 // indirect
33 | github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51 // indirect
34 | github.com/urfave/cli v1.18.0
35 | golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53
36 | google.golang.org/grpc v1.19.1
37 | gopkg.in/gormigrate.v1 v1.4.0
38 | gopkg.in/macaroon.v2 v2.1.0
39 | )
40 |
--------------------------------------------------------------------------------
/metrics/labels.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | // Severity defines how severe the problem we faced, depending on this we
4 | // farther react on the problem differently. For example in Prometheus
5 | // metric backend implementation we setup the alert rules on the server
6 | // which notifies as about the problem.
7 | type Severity string
8 |
9 | var (
10 | // HighSeverity high level of error importance.
11 | HighSeverity Severity = "high"
12 |
13 | // MiddleSeverity middle level of err importance.
14 | MiddleSeverity Severity = "middle"
15 |
16 | // LowSeverity low level of error importance.
17 | LowSeverity Severity = "low"
18 | )
19 |
--------------------------------------------------------------------------------
/metrics/log.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "github.com/btcsuite/btclog"
5 | )
6 |
7 | // log is a logger that is initialized with no output filters. This
8 | // means the package will not perform any logging by default until the caller
9 | // requests it.
10 | var log btclog.Logger
11 |
12 | // The default amount of logging is none.
13 | func init() {
14 | DisableLog()
15 | }
16 |
17 | // DisableLog disables all library log output. Logging output is disabled
18 | // by default until UseLogger is called.
19 | func DisableLog() {
20 | log = btclog.Disabled
21 | }
22 |
23 | // UseLogger uses a specified Logger to output package logging info.
24 | // This should be used in preference to SetLogWriter if the caller is also
25 | // using btclog.
26 | func UseLogger(logger btclog.Logger) {
27 | log = logger
28 | }
29 |
30 | // logClosure is used to provide a closure over expensive logging operations
31 | // so don't have to be performed when the logging level doesn't warrant it.
32 | type logClosure func() string
33 |
34 | // String invokes the underlying function and returns the result.
35 | func (c logClosure) String() string {
36 | return c()
37 | }
38 |
39 | // newLogClosure returns a new closure over a function that returns a string
40 | // which itself provides a Stringer interface so that it can be used with the
41 | // logging system.
42 | func newLogClosure(c func() string) logClosure {
43 | return logClosure(c)
44 | }
45 |
--------------------------------------------------------------------------------
/metrics/rpc/backend.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "github.com/prometheus/client_golang/prometheus"
5 | "github.com/bitlum/graphql-go/errors"
6 | "github.com/bitlum/connector/metrics"
7 | )
8 |
9 | const (
10 | // subsystem is used as the second part in the name of the metric,
11 | // after the metrics namespace.
12 | subsystem = "rpc"
13 |
14 | // requestLabel is used to distinguish different RPC requests during the
15 | // process of metric analysis and alert rule constructing.
16 | requestLabel = "request"
17 |
18 | // severityLabel is used to distinguish different error codes by its
19 | // level of importance.
20 | severityLabel = "severity"
21 | )
22 |
23 | // MetricsBackend is a system which is responsible for receiving and storing
24 | // the connector metrics.
25 | type MetricsBackend interface {
26 | AddError(request, severity string)
27 | }
28 |
29 | // EmptyBackend is used as an empty metrics backend in order to avoid
30 | type EmptyBackend struct{}
31 |
32 | func (b *EmptyBackend) AddError(request, severity string) {}
33 |
34 | // PrometheusBackend is the main subsystem metrics implementation. Uses
35 | // prometheus metrics singletons defined above.
36 | //
37 | // WARN: Method name should be taken from limited set.
38 | // Don't use dynamic naming, it may cause dramatic increase of the amount of
39 | // data on the metric server.
40 | //
41 | // NOTE: Non-pointer receiver made by intent to avoid conflict in the system
42 | // with parallel metrics report.
43 | type PrometheusBackend struct {
44 | errorsTotal *prometheus.CounterVec
45 | }
46 |
47 | // AddError increases error counter for the given method name.
48 | //
49 | // WARN: Error code name should be taken from limited set.
50 | // Don't use dynamic naming, it may cause dramatic increase of the amount of
51 | // data on the metric server.
52 | //
53 | // NOTE: Non-pointer receiver made by intent to avoid conflict in the system
54 | // with parallel metrics report.
55 | func (m PrometheusBackend) AddError(method, severity string) {
56 | m.errorsTotal.With(
57 | prometheus.Labels{
58 | requestLabel: method,
59 | severityLabel: severity,
60 | },
61 | ).Add(1)
62 | }
63 |
64 | // InitMetricsBackend creates subsystem metrics for specified
65 | // net. Creates and tries to register metrics singletons. If register was
66 | // already done, than function not returning error.
67 | func InitMetricsBackend(net string) (MetricsBackend, error) {
68 | backend := PrometheusBackend{}
69 |
70 | backend.errorsTotal = prometheus.NewCounterVec(
71 | prometheus.CounterOpts{
72 | Namespace: metrics.Namespace,
73 | Subsystem: subsystem,
74 | Name: "errors_total",
75 | Help: "Total requests which processing ended with error",
76 | ConstLabels: prometheus.Labels{
77 | metrics.NetLabel: net,
78 | },
79 | },
80 | []string{
81 | requestLabel,
82 | severityLabel,
83 | },
84 | )
85 |
86 | if err := prometheus.Register(backend.errorsTotal); err != nil {
87 | // Skip returning error if we re-registered metric.
88 | if _, ok := err.(prometheus.AlreadyRegisteredError); !ok {
89 | return backend, errors.Errorf(
90 | "unable to register 'errorsTotal' metric: " +
91 | err.Error())
92 | }
93 | }
94 |
95 | return backend, nil
96 | }
97 |
--------------------------------------------------------------------------------
/metrics/server.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/prometheus/client_golang/prometheus/promhttp"
7 | "time"
8 | )
9 |
10 | const (
11 | // Namespace is used to distinguish this metrics from other. Namespace is
12 | // global for the project, for that reason ensure that name not interfere
13 | // with other namespaces on your prometheus server.
14 | Namespace = "connector"
15 |
16 | // NetLabel is used to distinguish different blockchain network names in
17 | // which service is working e.g simnet, testnet, mainnet, during the
18 | // process of metric analysis and alert rule constructing.
19 | NetLabel = "net"
20 | )
21 |
22 | func StartServer(addr string) *http.Server {
23 |
24 | handler := http.NewServeMux()
25 | handler.Handle("/metrics", promhttp.Handler())
26 |
27 | server := &http.Server{Addr: addr, Handler: handler}
28 |
29 | go func() {
30 | log.Infof("Starting metrics http server on `%s`", addr)
31 |
32 | for {
33 | switch err := server.ListenAndServe(); err {
34 | case http.ErrServerClosed:
35 | log.Infof("Metrics http server shutdown")
36 | return
37 | default:
38 | log.Errorf("Metrics http server error: %v", err)
39 |
40 | time.Sleep(5 * time.Second)
41 |
42 | log.Infof("Trying to start metrics http"+
43 | " server on '%s' one more time", addr)
44 | }
45 | }
46 | }()
47 |
48 | return server
49 | }
50 |
--------------------------------------------------------------------------------
/signal.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | "os/signal"
7 | )
8 |
9 | // Heavily inspired by https://github.com/btcsuite/btcd/blob/master/signal.go
10 |
11 | // interruptChannel is used to receive SIGINT (Ctrl+C) signals.
12 | var interruptChannel chan os.Signal
13 |
14 | // shutdownRequestChannel is used to request the daemon to shutdown gracefully,
15 | // similar to when receiveing SIGINT.
16 | var shutdownRequestChannel = make(chan struct{})
17 |
18 | // addHandlerChannel is used to add an interrupt handler to the list of handlers
19 | // to be invoked on SIGINT (Ctrl+C) signals and shutdown.
20 | var addHandlerChannel = make(chan func())
21 |
22 | // mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the
23 | // interruptChannel and shutdown requests on the shutdownRequestChannel, and
24 | // invokes the registered interruptCallbacks accordingly. It also listens for
25 | // callback registration.
26 | // It must be run as a goroutine.
27 | func mainInterruptHandler(shutdownChannel chan struct{}) {
28 | // interruptCallbacks is a list of callbacks to invoke when a
29 | // SIGINT (Ctrl+C) or a shutdown request is received.
30 | var interruptCallbacks []func()
31 |
32 | // isShutdown is a flag which is used to indicate whether or not
33 | // the shutdown signal has already been received and hence any future
34 | // attempts to add a new interrupt handler should invoke them
35 | // immediately.
36 | var isShutdown bool
37 |
38 | // shutdown invokes the registered interrupt handlers, then signals the
39 | // shutdownChannel.
40 | shutdown := func() {
41 | // Ignore more than one shutdown signal.
42 | if isShutdown {
43 | log.Println("Already shutting down...")
44 | return
45 | }
46 | isShutdown = true
47 | log.Println("Shutting down...")
48 |
49 | // Run handlers in LIFO order.
50 | for i := range interruptCallbacks {
51 | idx := len(interruptCallbacks) - 1 - i
52 | callback := interruptCallbacks[idx]
53 | callback()
54 | }
55 |
56 | // Signal the main goroutine to shutdown.
57 | go func() {
58 | shutdownChannel <- struct{}{}
59 | }()
60 | }
61 |
62 | for {
63 | select {
64 | case <-interruptChannel:
65 | log.Println("Received SIGINT (Ctrl+C).")
66 | shutdown()
67 |
68 | case <-shutdownRequestChannel:
69 | log.Println("Received shutdown request.")
70 | shutdown()
71 |
72 | case handler := <-addHandlerChannel:
73 | // The shutdown signal has already been received, so
74 | // just invoke any new handlers immediately.
75 | if isShutdown {
76 | handler()
77 | }
78 |
79 | interruptCallbacks = append(interruptCallbacks, handler)
80 | }
81 | }
82 | }
83 |
84 | // addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) or a
85 | // shutdown request is received.
86 | func addInterruptHandler(shutdownChannel chan struct{}, handler func()) {
87 | // Create the channel and start the main interrupt handler which invokes
88 | // all other callbacks and exits if not already done.
89 | if interruptChannel == nil {
90 | interruptChannel = make(chan os.Signal, 1)
91 | signal.Notify(interruptChannel, os.Interrupt)
92 | go mainInterruptHandler(shutdownChannel)
93 | }
94 |
95 | addHandlerChannel <- handler
96 | }
97 |
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | // fileExists reports whether the named file or directory exists.
8 | // This function is taken from https://github.com/btcsuite/btcd
9 | func fileExists(name string) bool {
10 | if _, err := os.Stat(name); err != nil {
11 | if os.IsNotExist(err) {
12 | return false
13 | }
14 | }
15 | return true
16 | }
17 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
4 |
5 | import (
6 | "bytes"
7 | "fmt"
8 | "strings"
9 | )
10 |
11 | const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
12 |
13 | // These constants define the application version and follow the semantic
14 | // versioning 2.0.0 spec (http://semver.org/).
15 | const (
16 | appMajor uint = 0
17 | appMinor uint = 1
18 | appPatch uint = 0
19 |
20 | // appPreRelease MUST only contain characters from semanticAlphabet
21 | // per the semantic versioning spec.
22 | appPreRelease = "alpha"
23 | )
24 |
25 | // appBuild is defined as a variable so it can be overridden during the build
26 | // process with '-ldflags "-X main.appBuild foo' if needed. It MUST only
27 | // contain characters from semanticAlphabet per the semantic versioning spec.
28 | var appBuild string
29 |
30 | // version returns the application version as a properly formed string per the
31 | // semantic versioning 2.0.0 spec (http://semver.org/).
32 | func version() string {
33 | // Start with the major, minor, and patch versions.
34 | version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
35 |
36 | // Append pre-release version if there is one. The hyphen called for
37 | // by the semantic versioning spec is automatically appended and should
38 | // not be contained in the pre-release string. The pre-release version
39 | // is not appended if it contains invalid characters.
40 | preRelease := normalizeVerString(appPreRelease)
41 | if preRelease != "" {
42 | version = fmt.Sprintf("%s-%s", version, preRelease)
43 | }
44 |
45 | // Append build metadata if there is any. The plus called for
46 | // by the semantic versioning spec is automatically appended and should
47 | // not be contained in the build metadata string. The build metadata
48 | // string is not appended if it contains invalid characters.
49 | build := normalizeVerString(appBuild)
50 | if build != "" {
51 | version = fmt.Sprintf("%s+%s", version, build)
52 | }
53 |
54 | return version
55 | }
56 |
57 | // normalizeVerString returns the passed string stripped of all characters which
58 | // are not valid according to the semantic versioning guidelines for pre-release
59 | // version and build metadata strings. In particular they MUST only contain
60 | // characters in semanticAlphabet.
61 | func normalizeVerString(str string) string {
62 | var result bytes.Buffer
63 | for _, r := range str {
64 | if strings.ContainsRune(semanticAlphabet, r) {
65 | result.WriteRune(r)
66 | }
67 | }
68 | return result.String()
69 | }
70 |
--------------------------------------------------------------------------------