diff --git a/cleanupDB.sh b/cleanupDB.sh index ff4110925..f77b5ca7e 100755 --- a/cleanupDB.sh +++ b/cleanupDB.sh @@ -6,6 +6,6 @@ cd ${PROJECT_DIR} && find . -name '*.db' -exec rm -vf {} \; cd ${PROJECT_DIR} && find . -name '*.db-shm' -exec rm -vf {} \; cd ${PROJECT_DIR} && find . -name '*.db-wal' -exec rm -vf {} \; cd ${PROJECT_DIR} && find . -name 'db.meta' -exec rm -vf {} \; -cd ${PROJECT_DIR} && find . -name 'public.keystore' -exec rm -vf {} \; -cd ${PROJECT_DIR} && find . -name '*.public.keystore' -exec rm -vf {} \; +cd ${PROJECT_DIR} && find . -name 'public.keystore*' -exec rm -vf {} \; +cd ${PROJECT_DIR} && find . -name '*.public.keystore*' -exec rm -vf {} \; cd ${PROJECT_DIR} && find . -type d -name '*.ldb' -prune -exec rm -vrf {} \; diff --git a/client/config.go b/client/config.go index b9bf5e936..0decccf26 100644 --- a/client/config.go +++ b/client/config.go @@ -25,6 +25,7 @@ import ( const ( paramUseLeader = "use_leader" paramUseFollower = "use_follower" + paramMirror = "mirror" ) // Config is a configuration parsed from a DSN string. @@ -40,6 +41,9 @@ type Config struct { // UseFollower use follower nodes to do queries UseFollower bool + + // Mirror option forces client to query from mirror server + Mirror string } // NewConfig creates a new config with default value. @@ -64,6 +68,9 @@ func (cfg *Config) FormatDSN() string { newQuery.Add(paramUseLeader, strconv.FormatBool(cfg.UseLeader)) } } + if cfg.Mirror != "" { + newQuery.Add(paramMirror, cfg.Mirror) + } u.RawQuery = newQuery.Encode() return u.String() @@ -90,6 +97,7 @@ func ParseDSN(dsn string) (cfg *Config, err error) { if !cfg.UseLeader && !cfg.UseFollower { cfg.UseLeader = true } + cfg.Mirror = q.Get(paramMirror) return cfg, nil } diff --git a/client/config_test.go b/client/config_test.go index a9b49d0b5..be893317f 100644 --- a/client/config_test.go +++ b/client/config_test.go @@ -91,4 +91,13 @@ func TestConfig(t *testing.T) { UseFollower: true, }) }) + + Convey("test format and parse dsn with mirror option", t, func() { + cfg, err := ParseDSN("covenantsql://db?mirror=happy") + So(err, ShouldBeNil) + So(cfg.Mirror, ShouldEqual, "happy") + So(cfg.FormatDSN(), ShouldEqual, "covenantsql://db?mirror=happy") + cfg.Mirror = "" + So(cfg.FormatDSN(), ShouldEqual, "covenantsql://db") + }) } diff --git a/client/conn.go b/client/conn.go index e4b0a1ac6..351a69e80 100644 --- a/client/conn.go +++ b/client/conn.go @@ -55,7 +55,7 @@ type conn struct { type pconn struct { parent *conn ackCh chan *types.Ack - pCaller *rpc.PersistentCaller + pCaller rpc.PCaller } func newConn(cfg *Config) (c *conn, err error) { @@ -84,39 +84,48 @@ func newConn(cfg *Config) (c *conn, err error) { return nil, errors.WithMessage(err, "cacheGetPeers failed") } - if cfg.UseLeader { + if cfg.Mirror != "" { c.leader = &pconn{ parent: c, - pCaller: rpc.NewPersistentCaller(peers.Leader), + pCaller: rpc.NewRawCaller(cfg.Mirror), + } + + // no ack workers required, mirror mode does not support ack worker + } else { + if cfg.UseLeader { + c.leader = &pconn{ + parent: c, + pCaller: rpc.NewPersistentCaller(peers.Leader), + } } - } - // choose a random follower node - if cfg.UseFollower && len(peers.Servers) > 1 { - for { - node := peers.Servers[randSource.Intn(len(peers.Servers))] - if node != peers.Leader { - c.follower = &pconn{ - parent: c, - pCaller: rpc.NewPersistentCaller(node), + // choose a random follower node + if cfg.UseFollower && len(peers.Servers) > 1 { + for { + node := peers.Servers[randSource.Intn(len(peers.Servers))] + if node != peers.Leader { + c.follower = &pconn{ + parent: c, + pCaller: rpc.NewPersistentCaller(node), + } + break } - break } } - } - if c.leader == nil && c.follower == nil { - return nil, errors.New("no follower peers found") - } + if c.leader == nil && c.follower == nil { + return nil, errors.New("no follower peers found") + } - if c.leader != nil { - if err := c.leader.startAckWorkers(2); err != nil { - return nil, errors.WithMessage(err, "leader startAckWorkers failed") + if c.leader != nil { + if err := c.leader.startAckWorkers(2); err != nil { + return nil, errors.WithMessage(err, "leader startAckWorkers failed") + } } - } - if c.follower != nil { - if err := c.follower.startAckWorkers(2); err != nil { - return nil, errors.WithMessage(err, "follower startAckWorkers failed") + if c.follower != nil { + if err := c.follower.startAckWorkers(2); err != nil { + return nil, errors.WithMessage(err, "follower startAckWorkers failed") + } } } @@ -133,13 +142,19 @@ func (c *pconn) startAckWorkers(workerCount int) (err error) { } func (c *pconn) stopAckWorkers() { - close(c.ackCh) + if c.ackCh != nil { + select { + case <-c.ackCh: + default: + close(c.ackCh) + } + } } func (c *pconn) ackWorker() { var ( oneTime sync.Once - pc *rpc.PersistentCaller + pc rpc.PCaller err error ) @@ -150,10 +165,10 @@ ackWorkerLoop: break ackWorkerLoop } oneTime.Do(func() { - pc = rpc.NewPersistentCaller(c.pCaller.TargetID) + pc = c.pCaller.New() }) if err = ack.Sign(c.parent.privKey); err != nil { - log.WithField("target", pc.TargetID).WithError(err).Error("failed to sign ack") + log.WithField("target", pc.Target()).WithError(err).Error("failed to sign ack") continue } @@ -375,7 +390,7 @@ func (c *conn) sendQuery(ctx context.Context, queryType types.QueryType, queries "type": queryType.String(), "connID": connID, "seqNo": seqNo, - "target": uc.pCaller.TargetID, + "target": uc.pCaller.Target(), "source": c.localNodeID, }).WithError(err).Debug("send query") }() @@ -415,15 +430,17 @@ func (c *conn) sendQuery(ctx context.Context, queryType types.QueryType, queries // build ack func() { defer trace.StartRegion(ctx, "ackEnqueue").End() - uc.ackCh <- &types.Ack{ - Header: types.SignedAckHeader{ - AckHeader: types.AckHeader{ - Response: response.Header.ResponseHeader, - ResponseHash: response.Header.Hash(), - NodeID: c.localNodeID, - Timestamp: getLocalTime(), + if uc.ackCh != nil { + uc.ackCh <- &types.Ack{ + Header: types.SignedAckHeader{ + AckHeader: types.AckHeader{ + Response: response.Header.ResponseHeader, + ResponseHash: response.Header.Hash(), + NodeID: c.localNodeID, + Timestamp: getLocalTime(), + }, }, - }, + } } }() diff --git a/cmd/cql-minerd/integration_test.go b/cmd/cql-minerd/integration_test.go index 38046c5c3..44745276f 100644 --- a/cmd/cql-minerd/integration_test.go +++ b/cmd/cql-minerd/integration_test.go @@ -596,6 +596,37 @@ func TestFullProcess(t *testing.T) { err = db.Close() So(err, ShouldBeNil) + // test query from follower node + dsnCfgMix := *dsnCfg + dsnCfgMix.UseLeader = true + dsnCfgMix.UseFollower = true + dbMix, err := sql.Open("covenantsql", dsnCfgMix.FormatDSN()) + So(err, ShouldBeNil) + defer dbMix.Close() + + result = 0 + err = dbMix.QueryRow("SELECT * FROM test LIMIT 1").Scan(&result) + So(err, ShouldBeNil) + So(result, ShouldEqual, 4) + + _, err = dbMix.Exec("INSERT INTO test VALUES(2)") + So(err, ShouldBeNil) + + // test query from follower only + dsnCfgFollower := *dsnCfg + dsnCfgFollower.UseLeader = false + dsnCfgFollower.UseFollower = true + dbFollower, err := sql.Open("covenantsql", dsnCfgFollower.FormatDSN()) + So(err, ShouldBeNil) + defer dbFollower.Close() + + err = dbFollower.QueryRow("SELECT * FROM test LIMIT 1").Scan(&result) + So(err, ShouldBeNil) + So(result, ShouldEqual, 4) + + _, err = dbFollower.Exec("INSERT INTO test VALUES(2)") + So(err, ShouldNotBeNil) + // TODO(lambda): Drop database }) } diff --git a/cmd/cql-observer/node.go b/cmd/cql-observer/node.go deleted file mode 100644 index c49738de7..000000000 --- a/cmd/cql-observer/node.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2019 The CovenantSQL Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "fmt" - "syscall" - - "golang.org/x/crypto/ssh/terminal" - - "github.com/CovenantSQL/CovenantSQL/conf" - "github.com/CovenantSQL/CovenantSQL/crypto/kms" - "github.com/CovenantSQL/CovenantSQL/route" - "github.com/CovenantSQL/CovenantSQL/utils/log" -) - -func initNode() (err error) { - var masterKey []byte - if !conf.GConf.IsTestMode { - fmt.Print("Type in Master key to continue:") - masterKey, err = terminal.ReadPassword(syscall.Stdin) - if err != nil { - fmt.Printf("Failed to read Master key: %v", err) - } - fmt.Println("") - } - - if err = kms.InitLocalKeyPair(conf.GConf.PrivateKeyFile, masterKey); err != nil { - log.WithError(err).Error("init local key pair failed") - return - } - - log.Info("init routes") - - // init kms routing - route.InitKMS(conf.GConf.PubKeyStoreFile) - - return -} diff --git a/cmd/cql/internal/adapter.go b/cmd/cql/internal/adapter.go index d54fad032..a0e2ca446 100644 --- a/cmd/cql/internal/adapter.go +++ b/cmd/cql/internal/adapter.go @@ -18,7 +18,6 @@ package internal import ( "context" - "net/http" "time" "github.com/CovenantSQL/CovenantSQL/sqlchain/adapter" @@ -26,14 +25,13 @@ import ( ) var ( - adapterAddr string // adapter listen addr - - adapterHTTPServer *http.Server + adapterAddr string // adapter listen addr + adapterUseMirrorAddr string ) // CmdAdapter is cql adapter command entity. var CmdAdapter = &Command{ - UsageLine: "cql adapter [-config file] [-tmp-path path] [-bg-log-level level] address", + UsageLine: "cql adapter [-config file] [-tmp-path path] [-bg-log-level level] [-mirror addr] address", Short: "start a SQLChain adapter", Long: ` Adapter command serves a SQLChain adapter @@ -44,13 +42,14 @@ e.g. func init() { CmdAdapter.Run = runAdapter + CmdAdapter.Flag.StringVar(&adapterUseMirrorAddr, "mirror", "", "mirror server for adapter to query") addCommonFlags(CmdAdapter) addBgServerFlag(CmdAdapter) } -func startAdapterServer(adapterAddr string) func() { - adapterHTTPServer, err := adapter.NewHTTPAdapter(adapterAddr, configFile) +func startAdapterServer(adapterAddr string, adapterUseMirrorAddr string) func() { + adapterHTTPServer, err := adapter.NewHTTPAdapter(adapterAddr, configFile, adapterUseMirrorAddr) if err != nil { ConsoleLog.WithError(err).Error("init adapter failed") SetExitStatus(1) @@ -84,7 +83,7 @@ func runAdapter(cmd *Command, args []string) { } adapterAddr = args[0] - cancelFunc := startAdapterServer(adapterAddr) + cancelFunc := startAdapterServer(adapterAddr, adapterUseMirrorAddr) ExitIfErrors() defer cancelFunc() diff --git a/cmd/cql/internal/base.go b/cmd/cql/internal/base.go index 4d9b2a432..7ccf52fd7 100644 --- a/cmd/cql/internal/base.go +++ b/cmd/cql/internal/base.go @@ -98,7 +98,8 @@ func (c *Command) Runnable() bool { var atExitFuncs []func() -func atExit(f func()) { +// AtExit will register function to be executed before exit. +func AtExit(f func()) { atExitFuncs = append(atExitFuncs, f) } diff --git a/cmd/cql/internal/cfg.go b/cmd/cql/internal/cfg.go index 42ee87822..6e9f5d214 100644 --- a/cmd/cql/internal/cfg.go +++ b/cmd/cql/internal/cfg.go @@ -71,6 +71,8 @@ func configInit() { Exit() } + ConsoleLog.Info("init config success") + // TODO(leventeliu): discover more specific confirmation duration from config. We don't have // enough informations from config to do that currently, so just use a fixed and long enough // duration. diff --git a/cmd/cql/internal/console.go b/cmd/cql/internal/console.go index c125c233f..9e63e7bc3 100644 --- a/cmd/cql/internal/console.go +++ b/cmd/cql/internal/console.go @@ -365,7 +365,7 @@ func runConsole(cmd *Command, args []string) { } if adapterAddr != "" { - cancelFunc := startAdapterServer(adapterAddr) + cancelFunc := startAdapterServer(adapterAddr, "") defer cancelFunc() } diff --git a/cmd/cql/internal/drop.go b/cmd/cql/internal/drop.go index cc7561994..4562bf36a 100644 --- a/cmd/cql/internal/drop.go +++ b/cmd/cql/internal/drop.go @@ -54,7 +54,7 @@ func runDrop(cmd *Command, args []string) { // drop database if _, err := client.ParseDSN(dsn); err != nil { - // not a dsn + // not a dsn/dbid ConsoleLog.WithField("db", dsn).WithError(err).Error("Not a valid dsn") SetExitStatus(1) return diff --git a/cmd/cql/internal/mirror.go b/cmd/cql/internal/mirror.go new file mode 100644 index 000000000..baa925fa9 --- /dev/null +++ b/cmd/cql/internal/mirror.go @@ -0,0 +1,97 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package internal + +import ( + "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/sqlchain/mirror" + "github.com/CovenantSQL/CovenantSQL/utils" +) + +var ( + mirrorDatabase string // mirror database id + mirrorAddr string // mirror server rpc addr + + mirrorService *mirror.Service +) + +// CmdMirror is cql mirror command. +var CmdMirror = &Command{ + UsageLine: "cql mirror [-config file] [-tmp-path path] [-bg-log-level level] dsn/dbid address", + Short: "start a SQLChain database mirror", + Long: ` +Mirror command subscribes database updates and serves a read-only database mirror. +e.g. + cql mirror database_id 127.0.0.1:9389 +`, +} + +func init() { + CmdMirror.Run = runMirror + + addCommonFlags(CmdMirror) + addBgServerFlag(CmdMirror) +} + +func startMirrorServer(mirrorDatabase string, mirrorAddr string) func() { + var err error + mirrorService, err = mirror.StartMirror(mirrorDatabase, mirrorAddr) + if err != nil { + ConsoleLog.WithError(err).Error("start mirror failed") + SetExitStatus(1) + return nil + } + + ConsoleLog.Infof("mirror server started on %s", mirrorAddr) + // TODO(): print sample command for cql to connect + + return func() { + mirror.StopMirror(mirrorService) + ConsoleLog.Info("mirror stopped") + } +} + +func runMirror(cmd *Command, args []string) { + configInit() + bgServerInit() + + if len(args) != 2 { + ConsoleLog.Error("Mirror command need database_id/dsn and listen address as parameters") + SetExitStatus(1) + return + } + + dsn := args[0] + mirrorAddr = args[1] + + cfg, err := client.ParseDSN(dsn) + if err != nil { + // not a dsn/dbid + ConsoleLog.WithField("db", dsn).WithError(err).Error("Not a valid dsn") + SetExitStatus(1) + return + } + + mirrorDatabase = cfg.DatabaseID + + cancelFunc := startMirrorServer(mirrorDatabase, mirrorAddr) + ExitIfErrors() + defer cancelFunc() + + ConsoleLog.Printf("Ctrl + C to stop mirror server on %s\n", mirrorAddr) + <-utils.WaitForExit() +} diff --git a/cmd/cql/main.go b/cmd/cql/main.go index 72e2972fc..8809408ab 100644 --- a/cmd/cql/main.go +++ b/cmd/cql/main.go @@ -38,6 +38,7 @@ func init() { internal.CmdBalance, internal.CmdTransfer, internal.CmdGrant, + internal.CmdMirror, internal.CmdExplorer, internal.CmdAdapter, internal.CmdVersion, @@ -46,7 +47,6 @@ func init() { } func main() { - internal.Version = version // set random diff --git a/cmd/cql/main_test.go b/cmd/cql/main_test.go index ef97d55c7..fa2c6b32a 100644 --- a/cmd/cql/main_test.go +++ b/cmd/cql/main_test.go @@ -18,9 +18,15 @@ package main -import "testing" +import ( + "testing" + + "github.com/CovenantSQL/CovenantSQL/cmd/cql/internal" +) func TestMain(m *testing.M) { - defer m.Run() + internal.AtExit(func() { + m.Run() + }) main() } diff --git a/rpc/rawcaller.go b/rpc/rawcaller.go new file mode 100644 index 000000000..da735d793 --- /dev/null +++ b/rpc/rawcaller.go @@ -0,0 +1,130 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rpc + +import ( + "io" + "net" + "net/rpc" + "strings" + "sync" + + "github.com/pkg/errors" +) + +// PCaller defines generic interface shared with PersistentCaller and RawCaller. +type PCaller interface { + Call(method string, request interface{}, reply interface{}) (err error) + Close() + Target() string + New() PCaller // returns new instance of current caller +} + +// RawCaller defines a raw rpc caller without any encryption. +type RawCaller struct { + targetAddr string + client *Client + sync.RWMutex +} + +// NewRawCaller creates the raw rpc caller to target node. +func NewRawCaller(targetAddr string) *RawCaller { + return &RawCaller{ + targetAddr: targetAddr, + } +} + +func (c *RawCaller) isClientValid() bool { + c.RLock() + defer c.RUnlock() + + return c.client != nil +} + +func (c *RawCaller) resetClient() (err error) { + c.Lock() + defer c.Unlock() + + if c.client != nil { + c.client.Close() + c.client = nil + } + + var conn net.Conn + if conn, err = net.Dial("tcp", c.targetAddr); err != nil { + err = errors.Wrapf(err, "dial to target %s failed", c.targetAddr) + return + } + + if c.client, err = InitClientConn(conn); err != nil { + c.client = nil + err = errors.Wrapf(err, "init client to target %s failed", c.targetAddr) + return + } + + return +} + +// Call issues client rpc call. +func (c *RawCaller) Call(method string, args interface{}, reply interface{}) (err error) { + if !c.isClientValid() { + if err = c.resetClient(); err != nil { + return + } + } + + c.RLock() + err = c.client.Call(method, args, reply) + c.RUnlock() + + if err != nil { + if err == io.EOF || + err == io.ErrUnexpectedEOF || + err == io.ErrClosedPipe || + err == rpc.ErrShutdown || + strings.Contains(strings.ToLower(err.Error()), "shut down") || + strings.Contains(strings.ToLower(err.Error()), "broken pipe") { + // if got EOF, retry once + reconnectErr := c.resetClient() + if reconnectErr != nil { + err = errors.Wrap(reconnectErr, "reconnect failed") + } + } + err = errors.Wrapf(err, "call %s failed", method) + } + return +} + +// Close release underlying connection resources. +func (c *RawCaller) Close() { + c.Lock() + defer c.Unlock() + if c.client != nil { + c.client.Close() + c.client = nil + } +} + +// Target returns the request target for logging purpose. +func (c *RawCaller) Target() string { + return c.targetAddr +} + +// New returns brand new caller. +func (c *RawCaller) New() PCaller { + return NewRawCaller(c.targetAddr) +} diff --git a/rpc/rawcaller_test.go b/rpc/rawcaller_test.go new file mode 100644 index 000000000..2dbb7edcf --- /dev/null +++ b/rpc/rawcaller_test.go @@ -0,0 +1,76 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rpc + +import ( + "net" + "testing" + + "github.com/pkg/errors" + . "github.com/smartystreets/goconvey/convey" +) + +type testService struct{} + +func (s *testService) Test(req *int, resp *int) (err error) { + *resp = *req + 1 + return +} + +func (s *testService) TestFailed(req *int, resp *interface{}) (err error) { + return errors.New("failed") +} + +func (s *testService) TestReconnect(req *int, resp *interface{}) (err error) { + return errors.New("shut down") +} + +func TestRawCaller(t *testing.T) { + Convey("test raw caller methods", t, func() { + s := NewServer() + err := s.RegisterService("Test", &testService{}) + So(err, ShouldBeNil) + l, err := net.Listen("tcp", ":0") + So(err, ShouldBeNil) + s.SetListener(l) + go s.Serve() + defer s.Stop() + c := NewRawCaller(l.Addr().String()) + defer c.Close() + var resp int + err = c.Call("Test.Test", 1, &resp) + So(err, ShouldBeNil) + So(resp, ShouldEqual, 2) + err = c.Call("Test.TestFailed", 1, nil) + So(err, ShouldNotBeNil) + So(errors.Cause(err).Error(), ShouldEqual, "failed") + err = c.Call("Test.TestReconnect", 1, nil) + So(err, ShouldNotBeNil) + So(err.Error(), ShouldContainSubstring, "shut down") + So(c.Target(), ShouldEqual, l.Addr().String()) + err = c.Call("Test.Test", 2, &resp) + So(err, ShouldBeNil) + So(resp, ShouldEqual, 3) + + // test new client + c2 := c.New() + defer c2.Close() + err = c2.Call("Test.Test", 4, &resp) + So(err, ShouldBeNil) + So(resp, ShouldEqual, 5) + }) +} diff --git a/rpc/rpcutil.go b/rpc/rpcutil.go index 2c1e47d51..7f53cd972 100644 --- a/rpc/rpcutil.go +++ b/rpc/rpcutil.go @@ -68,6 +68,16 @@ func NewPersistentCaller(target proto.NodeID) *PersistentCaller { } } +// Target returns the request target for logging purpose. +func (c *PersistentCaller) Target() string { + return string(c.TargetID) +} + +// New returns brand new persistent caller. +func (c *PersistentCaller) New() PCaller { + return NewPersistentCaller(c.TargetID) +} + func (c *PersistentCaller) initClient(isAnonymous bool) (err error) { c.Lock() defer c.Unlock() diff --git a/rpc/rpcutil_test.go b/rpc/rpcutil_test.go index 57963d432..2e5b66de5 100644 --- a/rpc/rpcutil_test.go +++ b/rpc/rpcutil_test.go @@ -225,6 +225,14 @@ func TestNewPersistentCaller(t *testing.T) { Node: *node1, } + if client.Target() != string(conf.GConf.BP.NodeID) { + t.Fatal("persistent caller target not equal") + } + + if client.New() == nil { + t.Fatal("new persistent caller failed") + } + respA := new(proto.PingResp) err = client.Call("DHT.Ping", reqA, respA) if err != nil { diff --git a/sqlchain/adapter/config/config.go b/sqlchain/adapter/config/config.go index 8a89bcdb7..a78b40248 100644 --- a/sqlchain/adapter/config/config.go +++ b/sqlchain/adapter/config/config.go @@ -59,6 +59,7 @@ type Config struct { WriteCertificates []*x509.Certificate `yaml:"-"` // storage config + MirrorServer string `yaml:"Mirror"` // use mirror server for queries StorageDriver string `yaml:"StorageDriver"` // sqlite3 or covenantsql StorageRoot string `yaml:"StorageRoot"` StorageInstance storage.Storage `yaml:"-"` @@ -160,7 +161,7 @@ func LoadConfig(configPath string) (config *Config, err error) { // load storage switch config.StorageDriver { case "covenantsql": - config.StorageInstance = storage.NewCovenantSQLStorage() + config.StorageInstance = storage.NewCovenantSQLStorage(config.MirrorServer) case "sqlite3": storageRoot := filepath.Join(workingRoot, config.StorageRoot) if config.StorageInstance, err = storage.NewSQLite3Storage(storageRoot); err != nil { diff --git a/sqlchain/adapter/server.go b/sqlchain/adapter/server.go index 4c0611e3a..abfeb13d0 100644 --- a/sqlchain/adapter/server.go +++ b/sqlchain/adapter/server.go @@ -34,7 +34,7 @@ type HTTPAdapter struct { } // NewHTTPAdapter creates adapter to service. -func NewHTTPAdapter(listenAddr string, configFile string) (adapter *HTTPAdapter, err error) { +func NewHTTPAdapter(listenAddr string, configFile string, adapterUseMirrorAddr string) (adapter *HTTPAdapter, err error) { adapter = new(HTTPAdapter) // load config file @@ -46,6 +46,9 @@ func NewHTTPAdapter(listenAddr string, configFile string) (adapter *HTTPAdapter, if listenAddr != "" { cfg.ListenAddr = listenAddr } + if adapterUseMirrorAddr != "" { + cfg.MirrorServer = adapterUseMirrorAddr + } // init server handler := handlers.CORS( handlers.AllowedHeaders([]string{"Content-Type"}), diff --git a/sqlchain/adapter/storage/covenantsql.go b/sqlchain/adapter/storage/covenantsql.go index fb1772d9d..c92d1f365 100644 --- a/sqlchain/adapter/storage/covenantsql.go +++ b/sqlchain/adapter/storage/covenantsql.go @@ -23,11 +23,13 @@ import ( ) // CovenantSQLStorage defines the covenantsql database abstraction. -type CovenantSQLStorage struct{} +type CovenantSQLStorage struct { + mirrorServerAddr string +} // NewCovenantSQLStorage returns new covenantsql storage handler. -func NewCovenantSQLStorage() (s *CovenantSQLStorage) { - s = &CovenantSQLStorage{} +func NewCovenantSQLStorage(mirrorServerAddr string) (s *CovenantSQLStorage) { + s = &CovenantSQLStorage{mirrorServerAddr: mirrorServerAddr} return } @@ -116,6 +118,9 @@ func (s *CovenantSQLStorage) Exec(dbID string, query string, args ...interface{} func (s *CovenantSQLStorage) getConn(dbID string) (db *sql.DB, err error) { cfg := client.NewConfig() cfg.DatabaseID = dbID + if s.mirrorServerAddr != "" { + cfg.Mirror = s.mirrorServerAddr + } return sql.Open("covenantsql", cfg.FormatDSN()) } diff --git a/sqlchain/mirror/mirror_test.go b/sqlchain/mirror/mirror_test.go new file mode 100644 index 000000000..7980ebc77 --- /dev/null +++ b/sqlchain/mirror/mirror_test.go @@ -0,0 +1,318 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mirror + +import ( + "context" + "database/sql" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "sync" + "syscall" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" + + "github.com/CovenantSQL/CovenantSQL/client" + "github.com/CovenantSQL/CovenantSQL/conf" + "github.com/CovenantSQL/CovenantSQL/test" + "github.com/CovenantSQL/CovenantSQL/utils" + "github.com/CovenantSQL/CovenantSQL/utils/log" +) + +var ( + baseDir = utils.GetProjectSrcDir() + testWorkingDir = FJ(baseDir, "./test/") + logDir = FJ(testWorkingDir, "./log/") +) + +var nodeCmds []*utils.CMD + +var FJ = filepath.Join + +func startNodes() { + // wait for ports to be available + var err error + ctx := context.Background() + err = utils.WaitForPorts(ctx, "127.0.0.1", []int{ + 5120, + 5121, + 5122, + }, time.Millisecond*200) + + if err != nil { + log.Fatalf("wait for port ready timeout: %v", err) + } + + var cmd *utils.CMD + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cqld.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_0/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/leader-mirror.cover.out"), + }, + "leader", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cqld.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_1/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower1-mirror.cover.out"), + }, + "follower1", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cqld.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_2/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower2-mirror.cover.out"), + }, + "follower2", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + err = utils.WaitToConnect(ctx, "127.0.0.1", []int{ + 5120, + 5121, + 5122, + }, time.Second) + if err != nil { + log.Fatalf("wait for port ready timeout: %v", err) + } + + ctx, cancel = context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + err = utils.WaitForPorts(ctx, "127.0.0.1", []int{ + 5144, + 5145, + 5146, + }, time.Millisecond*200) + if err != nil { + log.Fatalf("wait for port ready timeout: %v", err) + } + + time.Sleep(10 * time.Second) + // start 3miners + os.RemoveAll(FJ(testWorkingDir, "./mirror/node_miner_0/data")) + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cql-minerd.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_miner_0/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner0-mirror.cover.out"), + }, + "miner0", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } + + os.RemoveAll(FJ(testWorkingDir, "./mirror/node_miner_1/data")) + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cql-minerd.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_miner_1/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner1-mirror.cover.out"), + }, + "miner1", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } + + os.RemoveAll(FJ(testWorkingDir, "./mirror/node_miner_2/data")) + if cmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cql-minerd.test"), + []string{"-config", FJ(testWorkingDir, "./mirror/node_miner_2/config.yaml"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner2-mirror.cover.out"), + }, + "miner2", testWorkingDir, logDir, false, + ); err == nil { + nodeCmds = append(nodeCmds, cmd) + } else { + log.Errorf("start node failed: %v", err) + } +} + +func stopNodes() { + var wg sync.WaitGroup + + for _, nodeCmd := range nodeCmds { + wg.Add(1) + go func(thisCmd *utils.CMD) { + defer wg.Done() + thisCmd.Cmd.Process.Signal(syscall.SIGTERM) + thisCmd.Cmd.Wait() + grepRace := exec.Command("/bin/sh", "-c", "grep -a -A 50 'DATA RACE' "+thisCmd.LogPath) + out, _ := grepRace.Output() + if len(out) > 2 { + log.Fatal(string(out)) + } + }(nodeCmd) + } + + wg.Wait() +} + +func waitForMirrorComplete(ctx context.Context, dbID string, tick time.Duration, stableDuration time.Duration) (err error) { + progressFile := FJ(testWorkingDir, "./mirror/node_mirror/"+dbID+progressFileSuffix) + lastProgress := 0 + lastUpdate := time.Now() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(tick): + progressData, _ := ioutil.ReadFile(progressFile) + progressCount, _ := strconv.Atoi(string(progressData)) + if progressCount > lastProgress { + lastUpdate = time.Now() + } + if progressCount > 5 || (progressCount > 0 && time.Now().Sub(lastUpdate) > stableDuration) { + // mirror synced + return + } + } + } +} + +func TestFullProcess(t *testing.T) { + log.SetLevel(log.DebugLevel) + + Convey("test mirror full process", t, func() { + var ( + err error + ) + + startNodes() + defer stopNodes() + + err = client.Init(FJ(testWorkingDir, "./mirror/node_c/config.yaml"), []byte("")) + So(err, ShouldBeNil) + + // wait bp chain service to start + ctx, ccl1 := context.WithTimeout(context.Background(), 1*time.Minute) + defer ccl1() + err = test.WaitBPChainService(ctx, 3*time.Second) + So(err, ShouldBeNil) + + // create + meta := client.ResourceMeta{} + meta.Node = 1 + _, dsn, err := client.Create(meta) + So(err, ShouldBeNil) + dsnCfg, err := client.ParseDSN(dsn) + So(err, ShouldBeNil) + + log.Infof("the created database dsn is %v", dsn) + + db, err := sql.Open("covenantsql", dsn) + So(err, ShouldBeNil) + defer db.Close() + + // wait for creation + ctx, ccl2 := context.WithTimeout(context.Background(), 5*time.Minute) + defer ccl2() + err = client.WaitDBCreation(ctx, dsn) + So(err, ShouldBeNil) + + _, err = db.Exec("CREATE TABLE test (test int)") + So(err, ShouldBeNil) + + _, err = db.Exec("INSERT INTO test VALUES(?)", 4) + So(err, ShouldBeNil) + + row := db.QueryRow("SELECT * FROM test LIMIT 1") + + var result int + err = row.Scan(&result) + So(err, ShouldBeNil) + So(result, ShouldEqual, 4) + + // run mirror node + utils.RemoveAll(FJ(testWorkingDir, "./mirror/node_mirror/"+dsnCfg.DatabaseID+"*")) + defer utils.RemoveAll(FJ(testWorkingDir, "./mirror/node_mirror/"+dsnCfg.DatabaseID+"*")) + + var mirrorCmd *utils.CMD + mirrorCmd, err = utils.RunCommandNB( + FJ(baseDir, "./bin/cql.test"), + []string{"-test.coverprofile", FJ(baseDir, "./cmd/cql/mirror.cover.out"), + "mirror", + "-config", FJ(testWorkingDir, "./mirror/node_mirror/config.yaml"), + "-no-password", + "-bg-log-level", "debug", + dsnCfg.DatabaseID, + "127.0.0.1:5663", + }, + "mirror", testWorkingDir, logDir, true, + ) + So(err, ShouldBeNil) + defer func() { + mirrorCmd.Cmd.Process.Signal(syscall.SIGTERM) + mirrorCmd.Cmd.Wait() + }() + + defer func() { + _ = mirrorCmd.Cmd.Process.Signal(syscall.SIGTERM) + _ = mirrorCmd.Cmd.Wait() + }() + + err = utils.WaitToConnect(context.Background(), "127.0.0.1", []int{5663}, 200*time.Millisecond) + So(err, ShouldBeNil) + + time.Sleep(time.Second) + + // check subscription status, wait for 5 period + ctx, ccl3 := context.WithTimeout(context.Background(), 10*conf.GConf.SQLChainPeriod) + defer ccl3() + err = waitForMirrorComplete(ctx, dsnCfg.DatabaseID, conf.GConf.SQLChainPeriod, 2*conf.GConf.SQLChainPeriod) + So(err, ShouldBeNil) + + // mirror synced, query using mirror + dsnCfg.Mirror = "127.0.0.1:5663" + mirrorDSN := dsnCfg.FormatDSN() + log.Infof("the mirror dsn is %v", mirrorDSN) + + dbMirror, err := sql.Open("covenantsql", mirrorDSN) + So(err, ShouldBeNil) + defer dbMirror.Close() + + // test read query + row = dbMirror.QueryRow("SELECT * FROM test LIMIT 1") + err = row.Scan(&result) + So(err, ShouldBeNil) + So(result, ShouldEqual, 4) + + // test write query, must not success + _, err = dbMirror.Exec("INSERT INTO test VALUES(?)", 5) + So(err, ShouldNotBeNil) + }) +} diff --git a/sqlchain/mirror/server.go b/sqlchain/mirror/server.go new file mode 100644 index 000000000..f3578f912 --- /dev/null +++ b/sqlchain/mirror/server.go @@ -0,0 +1,60 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mirror + +import ( + "net" + + "github.com/pkg/errors" + + "github.com/CovenantSQL/CovenantSQL/rpc" +) + +func createServer(listenAddr string) (s *rpc.Server, err error) { + var l net.Listener + if l, err = net.Listen("tcp", listenAddr); err != nil { + err = errors.Wrap(err, "listen rpc server failed") + return + } + + s = rpc.NewServer() + s.SetListener(l) + + return +} + +// StartMirror starts the mirror server and start mirror database. +func StartMirror(database string, listenAddr string) (service *Service, err error) { + var server *rpc.Server + if server, err = createServer(listenAddr); err != nil { + return + } + + if service, err = NewService(database, server); err != nil { + return + } + + // start mirror + err = service.start() + + return +} + +// StopMirror stops the mirror server. +func StopMirror(service *Service) { + service.stop() +} diff --git a/sqlchain/mirror/service.go b/sqlchain/mirror/service.go new file mode 100644 index 000000000..0f44b6b2e --- /dev/null +++ b/sqlchain/mirror/service.go @@ -0,0 +1,265 @@ +/* + * Copyright 2019 The CovenantSQL Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mirror + +import ( + "database/sql" + "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + + "github.com/CovenantSQL/CovenantSQL/conf" + "github.com/CovenantSQL/CovenantSQL/proto" + "github.com/CovenantSQL/CovenantSQL/route" + "github.com/CovenantSQL/CovenantSQL/rpc" + "github.com/CovenantSQL/CovenantSQL/types" + "github.com/CovenantSQL/CovenantSQL/utils/log" + "github.com/CovenantSQL/CovenantSQL/worker" + x "github.com/CovenantSQL/CovenantSQL/xenomint" + xs "github.com/CovenantSQL/CovenantSQL/xenomint/sqlite" +) + +const ( + progressFileSuffix = ".progress" + dbFileSuffix = ".db3" +) + +var ( + // ErrNotReadQuery represents invalid query type for mirror service to respond. + ErrNotReadQuery = errors.New("only read query is supported") +) + +// Service defines a database mirror service handler. +type Service struct { + server *rpc.Server + dbID proto.DatabaseID + upstream proto.NodeID + progress int32 + strg *xs.SQLite3 + st *x.State + stopCh chan struct{} + wg sync.WaitGroup +} + +// NewService returns new mirror service handler. +func NewService(database string, server *rpc.Server) (s *Service, err error) { + var ( + dbProgressPath = filepath.Join(conf.GConf.WorkingRoot, database+progressFileSuffix) + dbPath = filepath.Join(conf.GConf.WorkingRoot, database+dbFileSuffix) + progress int32 + ) + + // load current progress + if rawProgress, err := ioutil.ReadFile(dbProgressPath); err != nil { + // not exists + progress = 0 + log.WithError(err).Warning("read progress file failed") + } else if rawIntProgress, err := strconv.ParseUint(string(rawProgress), 10, 32); err != nil { + // progress not valid + progress = 0 + log.WithError(err).Warning("parse progress file failed") + } else { + progress = int32(rawIntProgress) + } + + s = &Service{ + server: server, + dbID: proto.DatabaseID(database), + progress: progress, + stopCh: make(chan struct{}), + } + + if s.strg, err = xs.NewSqlite(dbPath); err != nil { + err = errors.Wrap(err, "open database file failed") + return + } + + s.st = x.NewState(sql.LevelDefault, proto.NodeID(""), s.strg) + + // register myself + if err = server.RegisterService(route.DBRPCName, s); err != nil { + err = errors.Wrap(err, "register rpc failed") + return + } + + return +} + +func (s *Service) start() (err error) { + // query sqlchain profile for peers info + var ( + req = new(types.QuerySQLChainProfileReq) + resp = new(types.QuerySQLChainProfileResp) + ) + + req.DBID = s.dbID + + if err = rpc.RequestBP(route.MCCQuerySQLChainProfile.String(), req, resp); err != nil { + err = errors.Wrap(err, "get peers for database failed") + return + } else if len(resp.Profile.Miners) == 0 { + err = errors.Wrap(err, "get empty peers for database") + return + } + + s.upstream = resp.Profile.Miners[0].NodeID + + // start subscriptions + s.wg.Add(2) + go s.run() + go func() { + defer s.wg.Done() + s.server.Serve() + }() + + return +} + +func (s *Service) run() { + defer s.wg.Done() + + var nextTick time.Duration + + for { + select { + case <-s.stopCh: + return + case <-time.After(nextTick): + if err := s.pull(s.getProgress()); err != nil { + nextTick = conf.GConf.SQLChainPeriod + } else { + nextTick /= 10 + } + } + } +} + +func (s *Service) pull(count int32) (err error) { + var ( + req = new(worker.ObserverFetchBlockReq) + resp = new(worker.ObserverFetchBlockResp) + next int32 + ) + + defer func() { + lf := log.WithFields(log.Fields{ + "req_count": count, + "count": resp.Count, + }) + + if err != nil { + lf.WithError(err).Debug("sync block failed") + } else { + if resp.Block != nil { + lf = lf.WithField("block", resp.Block.BlockHash()) + } else { + lf = lf.WithField("block", nil) + } + + lf.WithField("next", next).Debug("sync block success") + } + }() + + req.DatabaseID = s.dbID + req.Count = count + + if err = rpc.NewCaller().CallNode(s.upstream, route.DBSObserverFetchBlock.String(), req, resp); err != nil { + return + } + + if resp.Block == nil { + err = errors.New("nil block, try later") + return + } + + if err = s.saveBlock(resp.Block); err != nil { + err = errors.New("save block failed") + return + } + + if count < 0 { + next = resp.Count + 1 + } else { + next = count + 1 + } + + if atomic.CompareAndSwapInt32(&s.progress, count, next) { + s.saveProgress() + } + + return +} + +func (s *Service) saveBlock(b *types.Block) (err error) { + // save block + return s.st.ReplayBlock(b) +} + +func (s *Service) getProgress() int32 { + return atomic.LoadInt32(&s.progress) +} + +func (s *Service) saveProgress() { + progressFile := filepath.Join(conf.GConf.WorkingRoot, string(s.dbID)+progressFileSuffix) + _ = ioutil.WriteFile(progressFile, []byte(fmt.Sprintf("%d", s.getProgress())), 0644) +} + +func (s *Service) stop() { + if s.stopCh != nil { + select { + case <-s.stopCh: + default: + close(s.stopCh) + } + } + s.server.Stop() + s.wg.Wait() +} + +// Query mocks DBS.Query for mirrored database. +func (s *Service) Query(req *types.Request, res *types.Response) (err error) { + if req.Header.QueryType != types.ReadQuery { + // only read query is supported + err = ErrNotReadQuery + return + } + + if req.Header.DatabaseID != s.dbID { + // database instance not matched + err = worker.ErrNotExists + return + } + + var r *types.Response + if _, r, err = s.st.Query(req, false); err != nil { + return + } + *res = *r + return +} + +// Ack mocks DBS.Ack for mirrored database. +func (s *Service) Ack(ack *types.Ack, _ *types.AckResponse) (err error) { + // no-op + return +} diff --git a/sqlchain/observer/api.go b/sqlchain/observer/api.go index 24a8ca4b2..71ae2ccb5 100644 --- a/sqlchain/observer/api.go +++ b/sqlchain/observer/api.go @@ -688,7 +688,8 @@ func startAPI(service *Service, listenAddr string, version string) (server *http api := &explorerAPI{ service: service, } - v1Router := router.PathPrefix(apiProxyPrefix + "/v1").Subrouter() + apiRouter := router.PathPrefix(apiProxyPrefix).Subrouter() + v1Router := apiRouter.PathPrefix("/v1").Subrouter() v1Router.HandleFunc("/ack/{db}/{hash}", api.GetAck).Methods("GET") v1Router.HandleFunc("/offset/{db}/{offset:[0-9]+}", func(writer http.ResponseWriter, request *http.Request) { @@ -700,9 +701,9 @@ func startAPI(service *Service, listenAddr string, version string) (server *http v1Router.HandleFunc("/count/{db}/{count:[0-9]+}", api.GetBlockByCount).Methods("GET") v1Router.HandleFunc("/height/{db}/{height:[0-9]+}", api.GetBlockByHeight).Methods("GET") v1Router.HandleFunc("/head/{db}", api.GetHighestBlock).Methods("GET") - v2Router := router.PathPrefix(apiProxyPrefix + "/v2").Subrouter() + v2Router := apiRouter.PathPrefix("/v2").Subrouter() v2Router.HandleFunc("/head/{db}", api.GetHighestBlockV2).Methods("GET") - v3Router := router.PathPrefix(apiProxyPrefix + "/v3").Subrouter() + v3Router := apiRouter.PathPrefix("/v3").Subrouter() v3Router.HandleFunc("/response/{db}/{hash}", api.GetResponse).Methods("GET") v3Router.HandleFunc("/block/{db}/{hash}", api.GetBlockV3).Methods("GET") v3Router.HandleFunc("/count/{db}/{count:[0-9]+}", api.GetBlockByCountV3).Methods("GET") diff --git a/sqlchain/observer/config_test.go b/sqlchain/observer/config_test.go index 30326a664..2b6bd150a 100644 --- a/sqlchain/observer/config_test.go +++ b/sqlchain/observer/config_test.go @@ -1,5 +1,3 @@ -// +build !testbinary - /* * Copyright 2018 The CovenantSQL Authors. * diff --git a/sqlchain/observer/observation_test.go b/sqlchain/observer/observation_test.go index 284b3a919..ccd88c672 100644 --- a/sqlchain/observer/observation_test.go +++ b/sqlchain/observer/observation_test.go @@ -1,5 +1,3 @@ -// +build !testbinary - /* * Copyright 2018 The CovenantSQL Authors. * @@ -91,7 +89,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cqld.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_0/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/leader.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/leader-observer.cover.out"), }, "leader", testWorkingDir, logDir, false, ); err == nil { @@ -102,7 +100,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cqld.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_1/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower1.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower1-observer.cover.out"), }, "follower1", testWorkingDir, logDir, false, ); err == nil { @@ -113,7 +111,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cqld.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_2/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower2.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/follower2-observer.cover.out"), }, "follower2", testWorkingDir, logDir, false, ); err == nil { @@ -150,7 +148,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cql-minerd.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_miner_0/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner0.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner0-observer.cover.out"), }, "miner0", testWorkingDir, logDir, false, ); err == nil { @@ -163,7 +161,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cql-minerd.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_miner_1/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner1.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner1-observer.cover.out"), }, "miner1", testWorkingDir, logDir, false, ); err == nil { @@ -176,7 +174,7 @@ func startNodes() { if cmd, err = utils.RunCommandNB( FJ(baseDir, "./bin/cql-minerd.test"), []string{"-config", FJ(testWorkingDir, "./observation/node_miner_2/config.yaml"), - "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner2.cover.out"), + "-test.coverprofile", FJ(baseDir, "./cmd/cql/miner2-observer.cover.out"), }, "miner2", testWorkingDir, logDir, false, ); err == nil { @@ -500,7 +498,7 @@ func TestFullProcess(t *testing.T) { So(err, ShouldBeNil) defer func() { - observerCmd.Cmd.Process.Signal(os.Interrupt) + observerCmd.Cmd.Process.Signal(syscall.SIGTERM) observerCmd.Cmd.Wait() }() @@ -715,7 +713,7 @@ func TestFullProcess(t *testing.T) { _, err = client.Drop(dsn2) So(err, ShouldBeNil) - observerCmd.Cmd.Process.Signal(os.Interrupt) + observerCmd.Cmd.Process.Signal(syscall.SIGTERM) observerCmd.Cmd.Wait() // start observer again diff --git a/sqlchain/observer/worker.go b/sqlchain/observer/worker.go index 05d4388a6..918d0640e 100644 --- a/sqlchain/observer/worker.go +++ b/sqlchain/observer/worker.go @@ -53,7 +53,6 @@ func (w *subscribeWorker) run() { var nextTick time.Duration for { - select { case <-w.stopCh: return diff --git a/test/mirror/node_0/config.yaml b/test/mirror/node_0/config.yaml new file mode 100644 index 000000000..fed82017e --- /dev/null +++ b/test/mirror/node_0/config.yaml @@ -0,0 +1,125 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5122" +ThisNodeID: "00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +MinNodeIDDifficulty: 2 +DNSSeed: + EnforcedDNSSEC: false + DNSServers: + - 1.1.1.1 + - 202.46.34.74 + - 202.46.34.75 + - 202.46.34.76 + +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z + BaseAccounts: + - Address: ba0ba731c7a76ccef2c1170f42038f7e228dfb474ef0190dfe35d9a37911ed37 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 1a7b0959bbd0d0ec529278a61c0056c277bffe75b2646e1699b46b10a90210be + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9235bc4130a2ed4e6c35ea189dab35198ebb105640bedb97dd5269cc80863b16 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9e1618775cceeb19f110e04fbc6c5bca6c8e4e9b116e193a42fe69bf602e7bcd + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: e4e1628477a17c969f3f915f4bc7c059c3fbcbaf37855bc55a811465ea2480af + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_0/private.key b/test/mirror/node_0/private.key new file mode 100644 index 000000000..449618c0a --- /dev/null +++ b/test/mirror/node_0/private.key @@ -0,0 +1,2 @@ +WAð8#|TZԓ`mF}~e ʆ?~ *E%vpo*a߂ç_Bľ@8 +MC2 \ No newline at end of file diff --git a/test/mirror/node_1/config.yaml b/test/mirror/node_1/config.yaml new file mode 100644 index 000000000..c1e448194 --- /dev/null +++ b/test/mirror/node_1/config.yaml @@ -0,0 +1,125 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5121" +ThisNodeID: "00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +MinNodeIDDifficulty: 2 +DNSSeed: + EnforcedDNSSEC: false + DNSServers: + - 1.1.1.1 + - 202.46.34.74 + - 202.46.34.75 + - 202.46.34.76 + +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z + BaseAccounts: + - Address: ba0ba731c7a76ccef2c1170f42038f7e228dfb474ef0190dfe35d9a37911ed37 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 1a7b0959bbd0d0ec529278a61c0056c277bffe75b2646e1699b46b10a90210be + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9235bc4130a2ed4e6c35ea189dab35198ebb105640bedb97dd5269cc80863b16 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9e1618775cceeb19f110e04fbc6c5bca6c8e4e9b116e193a42fe69bf602e7bcd + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: e4e1628477a17c969f3f915f4bc7c059c3fbcbaf37855bc55a811465ea2480af + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_1/private.key b/test/mirror/node_1/private.key new file mode 100644 index 000000000..449618c0a --- /dev/null +++ b/test/mirror/node_1/private.key @@ -0,0 +1,2 @@ +WAð8#|TZԓ`mF}~e ʆ?~ *E%vpo*a߂ç_Bľ@8 +MC2 \ No newline at end of file diff --git a/test/mirror/node_2/config.yaml b/test/mirror/node_2/config.yaml new file mode 100644 index 000000000..7b20dc22a --- /dev/null +++ b/test/mirror/node_2/config.yaml @@ -0,0 +1,125 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5120" +ThisNodeID: "000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +MinNodeIDDifficulty: 2 +DNSSeed: + EnforcedDNSSEC: false + DNSServers: + - 1.1.1.1 + - 202.46.34.74 + - 202.46.34.75 + - 202.46.34.76 + +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z + BaseAccounts: + - Address: ba0ba731c7a76ccef2c1170f42038f7e228dfb474ef0190dfe35d9a37911ed37 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 1a7b0959bbd0d0ec529278a61c0056c277bffe75b2646e1699b46b10a90210be + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9235bc4130a2ed4e6c35ea189dab35198ebb105640bedb97dd5269cc80863b16 + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: 9e1618775cceeb19f110e04fbc6c5bca6c8e4e9b116e193a42fe69bf602e7bcd + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 + - Address: e4e1628477a17c969f3f915f4bc7c059c3fbcbaf37855bc55a811465ea2480af + StableCoinBalance: 1000000000 + CovenantCoinBalance: 1000000000 +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_2/private.key b/test/mirror/node_2/private.key new file mode 100644 index 000000000..449618c0a --- /dev/null +++ b/test/mirror/node_2/private.key @@ -0,0 +1,2 @@ +WAð8#|TZԓ`mF}~e ʆ?~ *E%vpo*a߂ç_Bľ@8 +MC2 \ No newline at end of file diff --git a/test/mirror/node_c/config.yaml b/test/mirror/node_c/config.yaml new file mode 100644 index 000000000..2222031a6 --- /dev/null +++ b/test/mirror/node_c/config.yaml @@ -0,0 +1,109 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5120" +ThisNodeID: "00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +MinNodeIDDifficulty: 2 +DNSSeed: + EnforcedDNSSEC: false + DNSServers: + - 1.1.1.1 + - 202.46.34.74 + - 202.46.34.75 + - 202.46.34.76 + +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_c/private.key b/test/mirror/node_c/private.key new file mode 100644 index 000000000..f563980c1 Binary files /dev/null and b/test/mirror/node_c/private.key differ diff --git a/test/mirror/node_miner_0/config.yaml b/test/mirror/node_miner_0/config.yaml new file mode 100644 index 000000000..4968cf123 --- /dev/null +++ b/test/mirror/node_miner_0/config.yaml @@ -0,0 +1,105 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5144" +ThisNodeID: "000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z +Miner: + IsTestMode: true + RootDir: "./data" + MaxReqTimeGap: "2s" + ProvideServiceInterval: "3s" +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_miner_0/private.key b/test/mirror/node_miner_0/private.key new file mode 100644 index 000000000..12e7d3d80 --- /dev/null +++ b/test/mirror/node_miner_0/private.key @@ -0,0 +1 @@ +8s_/W-7IyH_DyTG*M9C#8p%x>SߪRLmPB>{:̜뢷|| \ No newline at end of file diff --git a/test/mirror/node_miner_1/config.yaml b/test/mirror/node_miner_1/config.yaml new file mode 100644 index 000000000..28a4cece9 --- /dev/null +++ b/test/mirror/node_miner_1/config.yaml @@ -0,0 +1,105 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5145" +ThisNodeID: "000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z +Miner: + IsTestMode: true + RootDir: "./data" + MaxReqTimeGap: "2s" + ProvideServiceInterval: "3s" +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_miner_1/private.key b/test/mirror/node_miner_1/private.key new file mode 100644 index 000000000..44e8915e6 --- /dev/null +++ b/test/mirror/node_miner_1/private.key @@ -0,0 +1,2 @@ +s]](o3R +D5*9C 7ZinƋSp*SS5^ޑax>Xо2#IxRw+Ŕ \ No newline at end of file diff --git a/test/mirror/node_miner_2/config.yaml b/test/mirror/node_miner_2/config.yaml new file mode 100644 index 000000000..45ab4520c --- /dev/null +++ b/test/mirror/node_miner_2/config.yaml @@ -0,0 +1,105 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5146" +ThisNodeID: "000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z +Miner: + IsTestMode: true + RootDir: "./data" + MaxReqTimeGap: "2s" + ProvideServiceInterval: "3s" +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 00000f3b43288fe99831eb533ab77ec455d13e11fc38ec35a42d4edd17aa320d + Nonce: + a: 22403 + b: 0 + c: 0 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_miner_2/private.key b/test/mirror/node_miner_2/private.key new file mode 100644 index 000000000..adb437e75 --- /dev/null +++ b/test/mirror/node_miner_2/private.key @@ -0,0 +1 @@ +6 i.i%8pVVrLBKb: 1;(fF &y췥 RW3?CA;e"K2 \ No newline at end of file diff --git a/test/mirror/node_mirror/config.yaml b/test/mirror/node_mirror/config.yaml new file mode 100644 index 000000000..08aa15797 --- /dev/null +++ b/test/mirror/node_mirror/config.yaml @@ -0,0 +1,109 @@ +IsTestMode: true +WorkingRoot: "./" +PubKeyStoreFile: "public.keystore" +PrivateKeyFile: "private.key" +DHTFileName: "dht.db" +ListenAddr: "127.0.0.1:5120" +ThisNodeID: "0000003b5316520d2ecf1adac937301d00a019171d899cb3949a067593559acd" +QPS: 1000 +BillingBlockCount: 3600 +ChainBusPeriod: 1s +BPPeriod: 3s +BPTick: 1s +SQLChainPeriod: 3s +SQLChainTick: 1s +SQLChainTTL: 10 +MinProviderDeposit: 1000000 +ValidDNSKeys: + koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==: cloudflare.com + mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==: cloudflare.com + oJMRESz5E4gYzS/q6XDrvU1qMPYIjCWzJaOau8XNEZeqCYKD5ar0IRd8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==: cloudflare.com +MinNodeIDDifficulty: 2 +DNSSeed: + EnforcedDNSSEC: false + DNSServers: + - 1.1.1.1 + - 202.46.34.74 + - 202.46.34.75 + - 202.46.34.76 + +BlockProducer: + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + NodeID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + ChainFileName: "chain.db" + BPGenesisInfo: + Version: 1 + BlockHash: f745ca6427237aac858dd3c7f2df8e6f3c18d0f1c164e07a1c6b8eebeba6b154 + Producer: 0000000000000000000000000000000000000000000000000000000000000001 + MerkleRoot: 0000000000000000000000000000000000000000000000000000000000000001 + ParentHash: 0000000000000000000000000000000000000000000000000000000000000001 + Timestamp: 2018-08-13T21:59:59.12Z +KnownNodes: +- ID: 00000bef611d346c0cbe1beaa76e7f0ed705a194fdf9ac3a248ec70e9c198bf9 + Nonce: + a: 313283 + b: 0 + c: 0 + d: 0 + Addr: 127.0.0.1:5122 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Leader +- ID: 00000381d46fd6cf7742d7fb94e2422033af989c0e348b5781b3219599a3af35 + Nonce: + a: 478373 + b: 0 + c: 0 + d: 2305843009893772025 + Addr: 127.0.0.1:5121 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 000000172580063ded88e010556b0aca2851265be8845b1ef397e8fce6ab5582 + Nonce: + a: 259939 + b: 0 + c: 0 + d: 2305843012544226372 + Addr: 127.0.0.1:5120 + PublicKey: "02c76216704d797c64c58bc11519fb68582e8e63de7e5b3b2dbbbe8733efe5fd24" + Role: Follower +- ID: 0000003b5316520d2ecf1adac937301d00a019171d899cb3949a067593559acd + Nonce: + a: 829487 + b: 0 + c: 1485583208 + d: 0 + Addr: "" + PublicKey: 02ec784ca599f21ef93fe7abdc68d78817ab6c9b31f2324d15ea174d9da498b4c4 + Role: Client +- ID: 000005aa62048f85da4ae9698ed59c14ec0d48a88a07c15a32265634e7e64ade + Nonce: + a: 567323 + b: 0 + c: 0 + d: 3104982049 + Addr: 127.0.0.1:5144 + PublicKey: 0367aa51809a7c1dc0f82c02452fec9557b3e1d10ce7c919d8e73d90048df86d20 + Role: Miner +- ID: 000005f4f22c06f76c43c4f48d5a7ec1309cc94030cbf9ebae814172884ac8b5 + Nonce: + a: 240524 + b: 0 + c: 0 + d: 2305843010430351476 + Addr: 127.0.0.1:5145 + PublicKey: 02914bca0806f040dd842207c44474ab41ecd29deee7f2d355789c5c78d448ca16 + Role: Miner +- ID: 000003f49592f83d0473bddb70d543f1096b4ffed5e5f942a3117e256b7052b8 + Nonce: + a: 606016 + b: 0 + c: 0 + d: 13835058056920509601 + Addr: 127.0.0.1:5146 + PublicKey: 03ae859eac5b72ee428c7a85f10b2ce748d9de5e480aefbb70f6597dfa8b2175e5 + Role: Miner diff --git a/test/mirror/node_mirror/private.key b/test/mirror/node_mirror/private.key new file mode 100644 index 000000000..f563980c1 Binary files /dev/null and b/test/mirror/node_mirror/private.key differ