diff --git a/cmd/fred/fred.go b/cmd/fred/fred.go index 1210db5..47c149c 100644 --- a/cmd/fred/fred.go +++ b/cmd/fred/fred.go @@ -63,12 +63,6 @@ type fredConfig struct { // support lightning or not to support lightning? LightningSupport bool `long:"lightning" description:"Whether or not to support lightning on the exchange"` - // database information - DBUsername string `long:"dbuser" description:"database username"` - DBPassword string `long:"dbpassword" description:"database password"` - DBHost string `long:"dbhost" description:"Host for the database connection"` - DBPort uint16 `long:"dbport" description:"Port for the database connection"` - // Auction server options AuctionTime uint64 `long:"auctiontime" description:"Time it should take to generate a timelock puzzle protected order"` } @@ -91,12 +85,6 @@ var ( // Yes we want lightning defaultLightningSupport = true - // default database stuff - defaultDBUsername = "opencx" - defaultDBPassword = "testpass" - defaultDBHost = "localhost" - defaultDBPort = uint16(3306) - // default auction options defaultAuctionTime = uint64(30000) ) @@ -120,24 +108,19 @@ func main() { Litport: defaultLitport, AuthenticatedRPC: defaultAuthenticatedRPC, LightningSupport: defaultLightningSupport, - DBUsername: defaultDBUsername, - DBPassword: defaultDBPassword, - DBHost: defaultDBHost, - DBPort: defaultDBPort, AuctionTime: defaultAuctionTime, } // Check and load config params key := opencxSetup(&conf) - var db *cxdbsql.DB - if db, err = cxdbsql.CreateDBConnection(conf.DBUsername, conf.DBPassword, conf.DBHost, conf.DBPort); err != nil { - logging.Fatalf("Error initializing Database: \n%s", err) - } - // Generate the coin list based on the parameters we know coinList := generateCoinList(&conf) + // init db + var db *cxdbsql.DB + db = new(cxdbsql.DB) + // Setup DB Client if err = db.SetupClient(coinList); err != nil { log.Fatalf("Error setting up sql client: \n%s", err) diff --git a/cmd/opencxd/opencxd.go b/cmd/opencxd/opencxd.go index 4929bf2..e3533d3 100644 --- a/cmd/opencxd/opencxd.go +++ b/cmd/opencxd/opencxd.go @@ -21,7 +21,7 @@ type opencxConfig struct { // stuff for files and directories LogFilename string `long:"logFilename" description:"Filename for output log file"` - OpencxHomeDir string `long:"dir" description:"Location of the root directory relative to home directory"` + OpencxHomeDir string `long:"dir" description:"Location of the root directory for opencxd"` // stuff for ports Rpcport uint16 `short:"p" long:"rpcport" description:"Set RPC port to connect to"` @@ -61,12 +61,6 @@ type opencxConfig struct { // support lightning or not to support lightning? LightningSupport bool `long:"lightning" description:"Whether or not to support lightning on the exchange"` - - // database information - DBUsername string `long:"dbuser" description:"database username"` - DBPassword string `long:"dbpassword" description:"database password"` - DBHost string `long:"dbhost" description:"Host for the database connection"` - DBPort uint16 `long:"dbport" description:"Port for the database connection"` } var ( @@ -86,12 +80,6 @@ var ( // Yes we want lightning defaultLightningSupport = true - - // default database stuff - defaultDBUsername = "opencx" - defaultDBPassword = "testpass" - defaultDBHost = "localhost" - defaultDBPort = uint16(3306) ) // newConfigParser returns a new command line flags parser. @@ -113,23 +101,17 @@ func main() { Litport: defaultLitport, AuthenticatedRPC: defaultAuthenticatedRPC, LightningSupport: defaultLightningSupport, - DBUsername: defaultDBUsername, - DBPassword: defaultDBPassword, - DBHost: defaultDBHost, - DBPort: defaultDBPort, } // Check and load config params key := opencxSetup(&conf) - var db *cxdbsql.DB - if db, err = cxdbsql.CreateDBConnection(conf.DBUsername, conf.DBPassword, conf.DBHost, conf.DBPort); err != nil { - logging.Fatalf("Error initializing Database: \n%s", err) - } - // Generate the coin list based on the parameters we know coinList := generateCoinList(&conf) + var db *cxdbsql.DB + db = new(cxdbsql.DB) + // Setup DB Client if err = db.SetupClient(coinList); err != nil { log.Fatalf("Error setting up sql client: \n%s", err) diff --git a/cxbenchmark/setup.go b/cxbenchmark/setup.go index 9dfe2c7..d7936ca 100644 --- a/cxbenchmark/setup.go +++ b/cxbenchmark/setup.go @@ -104,14 +104,11 @@ func registerClient(client *benchclient.BenchClient) (err error) { } // createFullServer creates a server after starting the database with a bunch of parameters -func createFullServer(dbuser string, dbpass string, dbhost string, dbport uint16, coinList []*coinparam.Params, serverhost string, serverport uint16, privkey *koblitz.PrivateKey, authrpc bool) (ocxServer *cxserver.OpencxServer, offChan chan bool, err error) { +func createFullServer(coinList []*coinparam.Params, serverhost string, serverport uint16, privkey *koblitz.PrivateKey, authrpc bool) (ocxServer *cxserver.OpencxServer, offChan chan bool, err error) { // Create db connection var db *cxdbsql.DB - if db, err = cxdbsql.CreateDBConnection(dbuser, dbpass, dbhost, dbport); err != nil { - err = fmt.Errorf("Error initializing Database: \n%s", err) - return - } + db = new(cxdbsql.DB) // Setup DB Client if err = db.SetupClient(coinList); err != nil { @@ -119,9 +116,6 @@ func createFullServer(dbuser string, dbpass string, dbhost string, dbport uint16 return } - // first add closeDB to the stack of things we're deferring until we're out of the scope of this function - // functionCloser(closeFunc, db.DBHandler.Close) - // Anyways, here's where we set the server ocxServer = cxserver.InitServer(db, "", serverport, coinList) @@ -188,7 +182,7 @@ func functionCloser(original func() error, additionalCloser func() error) { // createDefaultParamServerWithKey creates a server with a bunch of default params minus privkey and authrpc func createDefaultParamServerWithKey(privkey *koblitz.PrivateKey, authrpc bool) (server *cxserver.OpencxServer, offChan chan bool, err error) { - return createFullServer("opencx", "testpass", "localhost", uint16(3306), []*coinparam.Params{&coinparam.RegressionNetParams, &coinparam.VertcoinRegTestParams, &coinparam.LiteRegNetParams}, "localhost", uint16(12346), privkey, authrpc) + return createFullServer([]*coinparam.Params{&coinparam.RegressionNetParams, &coinparam.VertcoinRegTestParams, &coinparam.LiteRegNetParams}, "localhost", uint16(12346), privkey, authrpc) } // prepareBalances adds tons of money to both accounts diff --git a/cxdb/cxdbsql/auctionqueries_test.go b/cxdb/cxdbsql/auctionqueries_test.go index a05b97a..98cbdb3 100644 --- a/cxdb/cxdbsql/auctionqueries_test.go +++ b/cxdb/cxdbsql/auctionqueries_test.go @@ -55,10 +55,7 @@ func constCoinParams() (params []*coinparam.Params) { func createOpencxUser() (err error) { var dbconn *DB - if dbconn, err = CreateDBConnection(rootUser, rootPass, defaultHost, defaultPort); err != nil { - err = fmt.Errorf("Error creating db connection to create testing user: %s", err) - return - } + dbconn = new(DB) // create open string for db openString := fmt.Sprintf("%s:%s@%s(%s)/", dbconn.dbUsername, dbconn.dbPassword, dbconn.dbAddr.Network(), dbconn.dbAddr.String()) @@ -90,10 +87,7 @@ func TestPlaceAuctionGoodParams(t *testing.T) { } var dbConn *DB - if dbConn, err = CreateDBConnection(testingUser, testingPass, defaultHost, defaultPort); err != nil { - t.Skipf("Could not create db connection for test (error), so skipping: %s", err) - return - } + dbConn = new(DB) if err = dbConn.SetupClient(constCoinParams()); err != nil { t.Errorf("Error setting up db client for test: %s", err) @@ -117,10 +111,7 @@ func TestPlaceAuctionBadParams(t *testing.T) { } var dbConn *DB - if dbConn, err = CreateDBConnection(testingUser, testingPass, defaultHost, defaultPort); err != nil { - t.Skipf("Could not create db connection for test (error), so skipping: %s", err) - return - } + dbConn = new(DB) if err = dbConn.SetupClient([]*coinparam.Params{}); err != nil { t.Errorf("Error setting up db client for test: %s", err) @@ -144,10 +135,7 @@ func TestPlaceAuctionOrderbookChanges(t *testing.T) { } var dbConn *DB - if dbConn, err = CreateDBConnection(testingUser, testingPass, defaultHost, defaultPort); err != nil { - t.Skipf("Could not create db connection for test (error), so skipping: %s", err) - return - } + dbConn = new(DB) if err = dbConn.SetupClient(constCoinParams()); err != nil { t.Errorf("Error setting up db client for test: %s", err) diff --git a/cxdb/cxdbsql/db.go b/cxdb/cxdbsql/db.go index d3c1287..47a9208 100644 --- a/cxdb/cxdbsql/db.go +++ b/cxdb/cxdbsql/db.go @@ -20,34 +20,53 @@ type dbsqlConfig struct { // Filename of config file where this stuff can be set as well ConfigFile string - // database information + // database home dir + DBHomeDir string `long:"dir" description:"Location of the root directory for the sql db info and config"` + + // database info required to establish connection DBUsername string `long:"dbuser" description:"database username"` DBPassword string `long:"dbpassword" description:"database password"` DBHost string `long:"dbhost" description:"Host for the database connection"` DBPort uint16 `long:"dbport" description:"Port for the database connection"` + + // database schema names + BalanceSchemaName string `long:"balanceschema" description:"Name of balance schema"` + DepositSchemaName string `long:"depositschema" description:"Name of deposit schema"` + PendingDepositSchemaName string `long:"penddepschema" description:"Name of pending deposit schema"` + PuzzleSchemaName string `long:"puzzleschema" description:"Name of schema for puzzle orderbooks"` + AuctionSchemaName string `long:"auctionschema" description:"Name of schema for auction ID"` + AuctionOrderSchemaName string `long:"auctionorderschema" description:"Name of schema for auction orderbook"` + OrderSchemaName string `long:"orderschema" description:"Name of schema for limit orderbook"` + PeerSchemaName string `long:"peerschema" description:"Name of schema for peer storage"` + + // database table names + PuzzleTableName string `long:"puzzletable" description:"Name of table for puzzle orderbooks"` + AuctionOrderTableName string `long:"auctionordertable" description:"Name of table for auction orders"` + PeerTableName string `long:"peertable" description:"Name of table for peer storage"` } // Let these be turned into config things at some point var ( - defaultConfigFilename = "sqldb.conf" - defaultDBHomeDirName = os.Getenv("HOME") + "/.opencx/" - defaultHomeDir = os.Getenv("HOME") - defaultDBPort = uint16(12345) - defaultDBHost = "localhost" - defaultAuthenticatedRPC = true + defaultConfigFilename = "sqldb.conf" + defaultHomeDir = os.Getenv("HOME") + defaultDBHomeDirName = defaultHomeDir + "/.opencx/db/" + defaultDBPort = uint16(3306) + defaultDBHost = "localhost" + defaultDBUser = "opencx" + defaultDBPass = "testpass" // definitely move this to a config file - balanceSchema = "balances" - depositSchema = "deposit" - pendingDepositSchema = "pending_deposits" - puzzleSchema = "puzzle" - puzzleTable = "puzzles" - auctionSchema = "auctions" - auctionOrderSchema = "auctionorder" - auctionOrderTable = "auctionorders" - orderSchema = "orders" - peerSchema = "peers" - peerTableName = "opencxpeers" + defaultBalanceSchema = "balances" + defaultDepositSchema = "deposit" + defaultPendingDepositSchema = "pending_deposits" + defaultPuzzleSchema = "puzzle" + defaultPuzzleTable = "puzzles" + defaultAuctionSchema = "auctions" + defaultAuctionOrderSchema = "auctionorder" + defaultAuctionOrderTable = "auctionorders" + defaultOrderSchema = "orders" + defaultPeerSchema = "peers" + defaultPeerTable = "opencxpeers" ) // newConfigParser returns a new command line flags parser. @@ -136,36 +155,77 @@ func (db *DB) GetPairs() (pairArray []*match.Pair) { return } -// CreateDBConnection initializes the db so the client is ready to be set up. We use TCP by default -func CreateDBConnection(username string, password string, host string, port uint16) (dbconn *DB, err error) { - var dbAddr net.Addr - if dbAddr, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(host, fmt.Sprintf("%d", port))); err != nil { - err = fmt.Errorf("Error resolving database address: \n%s", err) - return - } +func (db *DB) SetOptionsFromConfig(conf *dbsqlConfig) { - dbconn = &DB{ - dbAddr: dbAddr, - dbUsername: username, - dbPassword: password, - } return } // SetupClient sets up the mysql client and driver func (db *DB) SetupClient(coinList []*coinparam.Params) (err error) { - db.gPriceMap = make(map[string]float64) - db.balanceSchema = balanceSchema - db.depositSchema = depositSchema - db.pendingDepositSchema = pendingDepositSchema - db.orderSchema = orderSchema - db.peerSchema = peerSchema - db.peerTableName = peerTableName - db.puzzleSchema = puzzleSchema - db.puzzleTable = puzzleTable - db.auctionSchema = auctionSchema - db.auctionOrderSchema = auctionOrderSchema - db.auctionOrderTable = auctionOrderTable + + // Set defaults + conf := dbsqlConfig{ + // home dir + DBHomeDir: defaultDBHomeDirName, + + // user / pass / net stuff + DBUsername: defaultDBUser, + DBPassword: defaultDBPass, + DBHost: defaultDBHost, + DBPort: defaultDBPort, + + // schemas + BalanceSchemaName: defaultBalanceSchema, + DepositSchemaName: defaultDepositSchema, + PendingDepositSchemaName: defaultPendingDepositSchema, + PuzzleSchemaName: defaultPuzzleSchema, + AuctionSchemaName: defaultAuctionSchema, + AuctionOrderSchemaName: defaultAuctionOrderSchema, + OrderSchemaName: defaultOrderSchema, + PeerSchemaName: defaultPeerSchema, + + // tables + PuzzleTableName: defaultPuzzleTable, + AuctionOrderTableName: defaultAuctionOrderTable, + PeerTableName: defaultPeerTable, + } + + // create and parse config file + dbConfigSetup(&conf) + + *db = DB{ + dbUsername: conf.DBUsername, + dbPassword: conf.DBPassword, + + // schemas + balanceSchema: conf.BalanceSchemaName, + depositSchema: conf.DepositSchemaName, + pendingDepositSchema: conf.PendingDepositSchemaName, + orderSchema: conf.OrderSchemaName, + peerSchema: conf.PeerSchemaName, + puzzleSchema: conf.PuzzleSchemaName, + auctionSchema: conf.AuctionSchemaName, + auctionOrderSchema: conf.AuctionOrderSchemaName, + + // tables + puzzleTable: conf.PuzzleTableName, + auctionOrderTable: conf.AuctionOrderTableName, + peerTableName: conf.PeerTableName, + + // Initialize price map and mutex + gPriceMap: make(map[string]float64), + priceMapMtx: new(sync.Mutex), + + // Set the coinlist based on the param we passed in + coinList: coinList, + } + + // Resolve address for host and port, then set that as the network address + if db.dbAddr, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.DBHost, fmt.Sprintf("%d", conf.DBPort))); err != nil { + err = fmt.Errorf("Error resolving database address: \n%s", err) + return + } + // Create users and schemas and assign permissions to opencx if err = db.rootInitSchemas(); err != nil { err = fmt.Errorf("Root could not initialize schemas: \n%s", err) @@ -175,22 +235,16 @@ func (db *DB) SetupClient(coinList []*coinparam.Params) (err error) { // open db handle openString := fmt.Sprintf("%s:%s@%s(%s)/", db.dbUsername, db.dbPassword, db.dbAddr.Network(), db.dbAddr.String()) - var dbHandle *sql.DB - if dbHandle, err = sql.Open("mysql", openString); err != nil { + if db.DBHandler, err = sql.Open("mysql", openString); err != nil { err = fmt.Errorf("Error opening database: \n%s", err) return } - db.DBHandler = dbHandle - db.coinList = coinList - // Get all the pairs if db.pairsArray, err = match.GenerateAssetPairs(coinList); err != nil { return } - // DEBUGGING - // Get all the assets for i, asset := range db.coinList { logging.Debugf("Asset %d: %s\n", i, asset.Name) diff --git a/cxdb/cxdbsql/dbconfig.go b/cxdb/cxdbsql/dbconfig.go new file mode 100644 index 0000000..70efcc6 --- /dev/null +++ b/cxdb/cxdbsql/dbconfig.go @@ -0,0 +1,75 @@ +package cxdbsql + +import ( + "bufio" + "os" + "path/filepath" + + "github.com/jessevdk/go-flags" + "github.com/mit-dci/opencx/logging" +) + +// createDefaultConfigFile creates a config file -- only call this if the +// config file isn't already there +func createDefaultConfigFile(destinationPath string) error { + + dest, err := os.OpenFile(filepath.Join(destinationPath, defaultConfigFilename), + os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer dest.Close() + + writer := bufio.NewWriter(dest) + defaultArgs := []byte("\n") + _, err = writer.Write(defaultArgs) + if err != nil { + return err + } + writer.Flush() + return nil +} + +func dbConfigSetup(conf *dbsqlConfig) { + // Pre-parse the command line options to see if an alternative config + // file or the version flag was specified. Config file will be read later + // and cli options would be parsed again below + + parser := newConfigParser(conf, flags.Default) + + // create home directory + _, err := os.Stat(conf.DBHomeDir) + if os.IsNotExist(err) { + if err = os.MkdirAll(conf.DBHomeDir, 0700); err != nil { + logging.Fatalf("Could not make dirs needed for home dir %s", conf.DBHomeDir) + } + + logging.Infof("Creating a new db home directory at %s", conf.DBHomeDir) + + if err = createDefaultConfigFile(conf.DBHomeDir); err != nil { + logging.Fatalf("Error creating a default config file: %s", conf.DBHomeDir) + } + } else if err != nil { + logging.Fatalf("Error while creating a directory: %s", err) + } + + if _, err := os.Stat(filepath.Join(conf.DBHomeDir, defaultConfigFilename)); os.IsNotExist(err) { + // if there is no config file found over at the directory, create one + logging.Infof("Creating a new default db config file at %s", conf.DBHomeDir) + + // source of error + if err = createDefaultConfigFile(filepath.Join(conf.DBHomeDir)); err != nil { + logging.Fatal(err) + } + } + conf.ConfigFile = filepath.Join(conf.DBHomeDir, defaultConfigFilename) + // lets parse the config file provided, if any + if err = flags.NewIniParser(parser).ParseFile(conf.ConfigFile); err != nil { + // If the error isn't a path error then we care about it + if _, ok := err.(*os.PathError); !ok { + logging.Fatalf("Non-path error encountered when parsing config file: %s", err) + } + } + + return +}