2021-06-29 17:02:21 +00:00
package server
2021-04-18 16:39:15 +00:00
import (
"context"
2021-07-23 12:31:59 +00:00
"fmt"
2021-04-18 16:39:15 +00:00
"io"
"net/http"
"os"
"path/filepath"
"runtime/pprof"
2021-08-16 09:45:10 +00:00
"strings"
2021-04-18 16:39:15 +00:00
"time"
2021-07-23 12:31:59 +00:00
"github.com/cosmos/cosmos-sdk/codec"
2021-10-06 11:41:42 +00:00
"github.com/cosmos/cosmos-sdk/crypto/keyring"
2021-07-23 12:31:59 +00:00
2021-04-18 16:39:15 +00:00
"github.com/spf13/cobra"
2021-08-19 16:55:13 +00:00
2021-04-18 16:39:15 +00:00
"google.golang.org/grpc"
2021-07-23 12:31:59 +00:00
abciserver "github.com/tendermint/tendermint/abci/server"
2021-04-18 16:39:15 +00:00
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
2021-07-23 12:31:59 +00:00
tmos "github.com/tendermint/tendermint/libs/os"
2021-04-18 16:39:15 +00:00
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/p2p"
pvm "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/rpc/client/local"
dbm "github.com/tendermint/tm-db"
2021-07-23 12:31:59 +00:00
"github.com/cosmos/cosmos-sdk/server/rosetta"
crgserver "github.com/cosmos/cosmos-sdk/server/rosetta/lib/server"
2021-04-18 16:39:15 +00:00
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/api"
2021-07-23 12:31:59 +00:00
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
2021-04-18 16:39:15 +00:00
servergrpc "github.com/cosmos/cosmos-sdk/server/grpc"
"github.com/cosmos/cosmos-sdk/server/types"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
2021-06-16 16:27:50 +00:00
ethlog "github.com/ethereum/go-ethereum/log"
2021-08-19 16:55:13 +00:00
"github.com/tharsis/ethermint/log"
2021-10-01 14:49:22 +00:00
ethdebug "github.com/tharsis/ethermint/rpc/ethereum/namespaces/debug"
2021-07-28 09:47:29 +00:00
"github.com/tharsis/ethermint/server/config"
2021-08-16 09:45:10 +00:00
srvflags "github.com/tharsis/ethermint/server/flags"
2021-04-18 16:39:15 +00:00
)
// StartCmd runs the service passed in, either stand-alone or in-process with
// Tendermint.
func StartCmd ( appCreator types . AppCreator , defaultNodeHome string ) * cobra . Command {
cmd := & cobra . Command {
Use : "start" ,
Short : "Run the full node" ,
Long : ` Run the full node application with Tendermint in or out of process . By
default , the application will run with Tendermint in process .
Pruning options can be provided via the ' -- pruning ' flag or alternatively with ' -- pruning - keep - recent ' ,
' pruning - keep - every ' , and ' pruning - interval ' together .
For ' -- pruning ' the options are as follows :
default : the last 100 states are kept in addition to every 500 th state ; pruning at 10 block intervals
nothing : all historic states will be saved , nothing will be deleted ( i . e . archiving node )
everything : all saved states will be deleted , storing only the current state ; pruning at 10 block intervals
custom : allow pruning options to be manually specified through ' pruning - keep - recent ' , ' pruning - keep - every ' , and ' pruning - interval '
Node halting configurations exist in the form of two flags : ' -- halt - height ' and ' -- halt - time ' . During
the ABCI Commit phase , the node will check if the current block height is greater than or equal to
the halt - height or if the current block time is greater than or equal to the halt - time . If so , the
node will attempt to gracefully shutdown and the block will not be committed . In addition , the node
will not be able to commit subsequent blocks .
For profiling and benchmarking purposes , CPU profiling can be enabled via the ' -- cpu - profile ' flag
which accepts a path for the resulting pprof file .
` ,
PreRunE : func ( cmd * cobra . Command , _ [ ] string ) error {
serverCtx := server . GetServerContextFromCmd ( cmd )
// Bind flags to the Context's Viper so the app construction can set
// options accordingly.
2021-06-08 07:07:11 +00:00
err := serverCtx . Viper . BindPFlags ( cmd . Flags ( ) )
if err != nil {
return err
}
2021-04-18 16:39:15 +00:00
2021-06-08 07:07:11 +00:00
_ , err = server . GetPruningOptionsFromFlags ( serverCtx . Viper )
2021-04-18 16:39:15 +00:00
return err
} ,
RunE : func ( cmd * cobra . Command , _ [ ] string ) error {
serverCtx := server . GetServerContextFromCmd ( cmd )
2021-07-23 12:31:59 +00:00
clientCtx , err := client . GetClientQueryContext ( cmd )
if err != nil {
return err
}
2021-08-16 09:45:10 +00:00
withTM , _ := cmd . Flags ( ) . GetBool ( srvflags . WithTendermint )
2021-07-23 12:31:59 +00:00
if ! withTM {
serverCtx . Logger . Info ( "starting ABCI without Tendermint" )
return startStandAlone ( serverCtx , appCreator )
}
2021-04-18 16:39:15 +00:00
2021-10-06 11:41:42 +00:00
serverCtx . Logger . Info ( "Unlocking keyring" )
// fire unlock precess for keyring
keyringBackend , _ := cmd . Flags ( ) . GetString ( flags . FlagKeyringBackend )
if keyringBackend == keyring . BackendFile {
_ , err = clientCtx . Keyring . List ( )
if err != nil {
return err
}
}
2021-04-18 16:39:15 +00:00
serverCtx . Logger . Info ( "starting ABCI with Tendermint" )
// amino is needed here for backwards compatibility of REST routes
2021-07-23 12:31:59 +00:00
err = startInProcess ( serverCtx , clientCtx , appCreator )
errCode , ok := err . ( server . ErrorCode )
if ! ok {
return err
}
serverCtx . Logger . Debug ( fmt . Sprintf ( "received quit signal: %d" , errCode . Code ) )
return nil
2021-04-18 16:39:15 +00:00
} ,
}
cmd . Flags ( ) . String ( flags . FlagHome , defaultNodeHome , "The application home directory" )
2021-08-16 09:45:10 +00:00
cmd . Flags ( ) . Bool ( srvflags . WithTendermint , true , "Run abci app embedded in-process with tendermint" )
cmd . Flags ( ) . String ( srvflags . Address , "tcp://0.0.0.0:26658" , "Listen address" )
cmd . Flags ( ) . String ( srvflags . Transport , "socket" , "Transport protocol: socket, grpc" )
cmd . Flags ( ) . String ( srvflags . TraceStore , "" , "Enable KVStore tracing to an output file" )
2021-10-20 19:14:39 +00:00
cmd . Flags ( ) . String ( server . FlagMinGasPrices , "" , "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photon;0.0001stake)" )
2021-08-16 09:45:10 +00:00
cmd . Flags ( ) . IntSlice ( server . FlagUnsafeSkipUpgrades , [ ] int { } , "Skip a set of upgrade heights to continue the old binary" )
cmd . Flags ( ) . Uint64 ( server . FlagHaltHeight , 0 , "Block height at which to gracefully halt the chain and shutdown the node" )
cmd . Flags ( ) . Uint64 ( server . FlagHaltTime , 0 , "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node" )
cmd . Flags ( ) . Bool ( server . FlagInterBlockCache , true , "Enable inter-block caching" )
cmd . Flags ( ) . String ( srvflags . CPUProfile , "" , "Enable CPU profiling and write to the provided file" )
cmd . Flags ( ) . Bool ( server . FlagTrace , false , "Provide full stack traces for errors in ABCI Log" )
cmd . Flags ( ) . String ( server . FlagPruning , storetypes . PruningOptionDefault , "Pruning strategy (default|nothing|everything|custom)" )
cmd . Flags ( ) . Uint64 ( server . FlagPruningKeepRecent , 0 , "Number of recent heights to keep on disk (ignored if pruning is not 'custom')" )
cmd . Flags ( ) . Uint64 ( server . FlagPruningKeepEvery , 0 , "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')" )
cmd . Flags ( ) . Uint64 ( server . FlagPruningInterval , 0 , "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')" )
cmd . Flags ( ) . Uint ( server . FlagInvCheckPeriod , 0 , "Assert registered invariants every N blocks" )
cmd . Flags ( ) . Uint64 ( server . FlagMinRetainBlocks , 0 , "Minimum block height offset during ABCI commit to prune Tendermint blocks" )
cmd . Flags ( ) . Bool ( srvflags . GRPCEnable , true , "Define if the gRPC server should be enabled" )
cmd . Flags ( ) . String ( srvflags . GRPCAddress , serverconfig . DefaultGRPCAddress , "the gRPC server address to listen on" )
cmd . Flags ( ) . Bool ( srvflags . GRPCWebEnable , true , "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)" )
cmd . Flags ( ) . String ( srvflags . GRPCWebAddress , serverconfig . DefaultGRPCWebAddress , "The gRPC-Web server address to listen on" )
cmd . Flags ( ) . Bool ( srvflags . JSONRPCEnable , true , "Define if the gRPC server should be enabled" )
cmd . Flags ( ) . StringSlice ( srvflags . JSONRPCAPI , config . GetDefaultAPINamespaces ( ) , "Defines a list of JSON-RPC namespaces that should be enabled" )
cmd . Flags ( ) . String ( srvflags . JSONRPCAddress , config . DefaultJSONRPCAddress , "the JSON-RPC server address to listen on" )
cmd . Flags ( ) . String ( srvflags . JSONWsAddress , config . DefaultJSONRPCWsAddress , "the JSON-RPC WS server address to listen on" )
2021-10-20 19:14:39 +00:00
cmd . Flags ( ) . Uint64 ( srvflags . JSONRPCGasCap , config . DefaultGasCap , "Sets a cap on gas that can be used in eth_call/estimateGas unit is aphoton (0=infinite)" )
cmd . Flags ( ) . Float64 ( srvflags . JSONRPCTxFeeCap , config . DefaultTxFeeCap , "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 photon)" )
2021-10-13 11:03:49 +00:00
cmd . Flags ( ) . Int32 ( srvflags . JSONRPCFilterCap , config . DefaultFilterCap , "Sets the global cap for total number of filters that can be created" )
2021-08-16 09:45:10 +00:00
cmd . Flags ( ) . String ( srvflags . EVMTracer , config . DefaultEVMTracer , "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)" )
2021-09-28 11:33:54 +00:00
cmd . Flags ( ) . String ( srvflags . TLSCertPath , "" , "the cert.pem file path for the server TLS configuration" )
cmd . Flags ( ) . String ( srvflags . TLSKeyPath , "" , "the key.pem file path for the server TLS configuration" )
2021-08-16 09:45:10 +00:00
cmd . Flags ( ) . Uint64 ( server . FlagStateSyncSnapshotInterval , 0 , "State sync snapshot interval" )
cmd . Flags ( ) . Uint32 ( server . FlagStateSyncSnapshotKeepRecent , 2 , "State sync snapshot to keep" )
2021-04-18 16:39:15 +00:00
// add support for all Tendermint-specific command line options
tcmd . AddNodeFlags ( cmd )
return cmd
}
2021-07-23 12:31:59 +00:00
func startStandAlone ( ctx * server . Context , appCreator types . AppCreator ) error {
2021-08-16 09:45:10 +00:00
addr := ctx . Viper . GetString ( srvflags . Address )
transport := ctx . Viper . GetString ( srvflags . Transport )
2021-07-23 12:31:59 +00:00
home := ctx . Viper . GetString ( flags . FlagHome )
db , err := openDB ( home )
if err != nil {
return err
}
2021-09-09 08:05:26 +00:00
defer func ( ) {
if err := db . Close ( ) ; err != nil {
ctx . Logger . With ( "error" , err ) . Error ( "error closing db" )
}
} ( )
2021-07-23 12:31:59 +00:00
2021-08-16 09:45:10 +00:00
traceWriterFile := ctx . Viper . GetString ( srvflags . TraceStore )
2021-07-23 12:31:59 +00:00
traceWriter , err := openTraceWriter ( traceWriterFile )
if err != nil {
return err
}
app := appCreator ( ctx . Logger , db , traceWriter , ctx . Viper )
svr , err := abciserver . NewServer ( addr , transport , app )
if err != nil {
return fmt . Errorf ( "error creating listener: %v" , err )
}
svr . SetLogger ( ctx . Logger . With ( "server" , "abci" ) )
err = svr . Start ( )
if err != nil {
tmos . Exit ( err . Error ( ) )
}
defer func ( ) {
if err = svr . Stop ( ) ; err != nil {
tmos . Exit ( err . Error ( ) )
}
} ( )
// Wait for SIGINT or SIGTERM signal
return server . WaitForQuitSignals ( )
}
// legacyAminoCdc is used for the legacy REST API
2021-04-18 16:39:15 +00:00
func startInProcess ( ctx * server . Context , clientCtx client . Context , appCreator types . AppCreator ) error {
cfg := ctx . Config
home := cfg . RootDir
2021-07-12 18:39:35 +00:00
logger := ctx . Logger
2021-07-23 12:31:59 +00:00
var cpuProfileCleanup func ( )
2021-08-16 09:45:10 +00:00
if cpuProfile := ctx . Viper . GetString ( srvflags . CPUProfile ) ; cpuProfile != "" {
2021-10-13 10:52:05 +00:00
fp , err := ethdebug . ExpandHome ( cpuProfile )
if err != nil {
ctx . Logger . Debug ( "failed to get filepath for the CPU profile file" , "error" , err . Error ( ) )
return err
}
f , err := os . Create ( fp )
2021-07-23 12:31:59 +00:00
if err != nil {
return err
}
ctx . Logger . Info ( "starting CPU profiler" , "profile" , cpuProfile )
if err := pprof . StartCPUProfile ( f ) ; err != nil {
return err
}
cpuProfileCleanup = func ( ) {
ctx . Logger . Info ( "stopping CPU profiler" , "profile" , cpuProfile )
pprof . StopCPUProfile ( )
f . Close ( )
}
}
2021-04-18 16:39:15 +00:00
2021-08-16 09:45:10 +00:00
traceWriterFile := ctx . Viper . GetString ( srvflags . TraceStore )
2021-04-18 16:39:15 +00:00
db , err := openDB ( home )
if err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "failed to open DB" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
return err
}
2021-09-09 08:05:26 +00:00
defer func ( ) {
if err := db . Close ( ) ; err != nil {
ctx . Logger . With ( "error" , err ) . Error ( "error closing db" )
}
} ( )
2021-04-18 16:39:15 +00:00
traceWriter , err := openTraceWriter ( traceWriterFile )
if err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "failed to open trace writer" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
return err
}
2021-07-23 12:31:59 +00:00
config := config . GetConfig ( ctx . Viper )
2021-08-16 09:45:10 +00:00
2021-07-23 12:31:59 +00:00
if err := config . ValidateBasic ( ) ; err != nil {
2021-08-16 09:45:10 +00:00
if strings . Contains ( err . Error ( ) , "set min gas price in app.toml or flag or env variable" ) {
ctx . Logger . Error (
"WARNING: The minimum-gas-prices config in app.toml is set to the empty string. " +
"This defaults to 0 in the current version, but will error in the next version " +
"(SDK v0.44). Please explicitly put the desired minimum-gas-prices in your app.toml." ,
)
} else {
return err
}
2021-07-23 12:31:59 +00:00
}
2021-04-18 16:39:15 +00:00
app := appCreator ( ctx . Logger , db , traceWriter , ctx . Viper )
nodeKey , err := p2p . LoadOrGenNodeKey ( cfg . NodeKeyFile ( ) )
if err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "failed load or gen node key" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
return err
}
genDocProvider := node . DefaultGenesisDocProviderFunc ( cfg )
tmNode , err := node . NewNode (
cfg ,
pvm . LoadOrGenFilePV ( cfg . PrivValidatorKeyFile ( ) , cfg . PrivValidatorStateFile ( ) ) ,
nodeKey ,
proxy . NewLocalClientCreator ( app ) ,
genDocProvider ,
node . DefaultDBProvider ,
node . DefaultMetricsProvider ( cfg . Instrumentation ) ,
2021-07-23 12:31:59 +00:00
ctx . Logger . With ( "server" , "node" ) ,
2021-04-18 16:39:15 +00:00
)
if err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "failed init node" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
return err
}
if err := tmNode . Start ( ) ; err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "failed start tendermint server" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
return err
}
2021-07-23 12:31:59 +00:00
// Add the tx service to the gRPC router. We only need to register this
// service if API or gRPC is enabled, and avoid doing so in the general
// case, because it spawns a new local tendermint RPC client.
if config . API . Enable || config . GRPC . Enable {
clientCtx = clientCtx . WithClient ( local . New ( tmNode ) )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
app . RegisterTxService ( clientCtx )
app . RegisterTendermintService ( clientCtx )
}
2021-04-18 16:39:15 +00:00
var apiSrv * api . Server
2021-07-23 12:31:59 +00:00
if config . API . Enable {
genDoc , err := genDocProvider ( )
2021-04-18 16:39:15 +00:00
if err != nil {
2021-06-25 09:18:37 +00:00
return err
2021-04-18 16:39:15 +00:00
}
2021-07-23 12:31:59 +00:00
clientCtx := clientCtx .
WithHomeDir ( home ) .
WithChainID ( genDoc . ChainID )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
apiSrv = api . New ( clientCtx , ctx . Logger . With ( "server" , "api" ) )
app . RegisterAPIRoutes ( apiSrv , config . API )
2021-04-18 16:39:15 +00:00
errCh := make ( chan error )
2021-07-23 12:31:59 +00:00
go func ( ) {
if err := apiSrv . Start ( config . Config ) ; err != nil {
2021-04-18 16:39:15 +00:00
errCh <- err
}
} ( )
select {
case err := <- errCh :
return err
2021-07-23 12:31:59 +00:00
case <- time . After ( types . ServerStartTime ) : // assume server started successfully
2021-04-18 16:39:15 +00:00
}
2021-07-23 12:31:59 +00:00
}
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
var (
grpcSrv * grpc . Server
grpcWebSrv * http . Server
)
if config . GRPC . Enable {
grpcSrv , err = servergrpc . StartGRPCServer ( clientCtx , app , config . GRPC . Address )
if err != nil {
return err
}
if config . GRPCWeb . Enable {
grpcWebSrv , err = servergrpc . StartGRPCWeb ( grpcSrv , config . Config )
if err != nil {
ctx . Logger . Error ( "failed to start grpc-web http server: " , err )
return err
}
}
2021-04-18 16:39:15 +00:00
}
2021-07-23 12:31:59 +00:00
var rosettaSrv crgserver . Server
if config . Rosetta . Enable {
offlineMode := config . Rosetta . Offline
if ! config . GRPC . Enable { // If GRPC is not enabled rosetta cannot work in online mode, so it works in offline mode.
offlineMode = true
}
conf := & rosetta . Config {
Blockchain : config . Rosetta . Blockchain ,
Network : config . Rosetta . Network ,
TendermintRPC : ctx . Config . RPC . ListenAddress ,
GRPCEndpoint : config . GRPC . Address ,
Addr : config . Rosetta . Address ,
Retries : config . Rosetta . Retries ,
Offline : offlineMode ,
}
conf . WithCodec ( clientCtx . InterfaceRegistry , clientCtx . Codec . ( * codec . ProtoCodec ) )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
rosettaSrv , err = rosetta . ServerFromConfig ( conf )
if err != nil {
return err
}
errCh := make ( chan error )
2021-04-18 16:39:15 +00:00
go func ( ) {
2021-07-23 12:31:59 +00:00
if err := rosettaSrv . Start ( ) ; err != nil {
2021-04-18 16:39:15 +00:00
errCh <- err
}
} ( )
select {
case err := <- errCh :
return err
2021-07-23 12:31:59 +00:00
case <- time . After ( types . ServerStartTime ) : // assume server started successfully
2021-04-18 16:39:15 +00:00
}
}
2021-08-19 16:55:13 +00:00
ethlog . Root ( ) . SetHandler ( log . NewHandler ( logger ) )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
var (
httpSrv * http . Server
httpSrvDone chan struct { }
)
2021-08-16 09:45:10 +00:00
if config . JSONRPC . Enable {
2021-07-23 12:31:59 +00:00
genDoc , err := genDocProvider ( )
2021-04-18 16:39:15 +00:00
if err != nil {
return err
}
2021-07-23 12:31:59 +00:00
clientCtx := clientCtx . WithChainID ( genDoc . ChainID )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
tmEndpoint := "/websocket"
tmRPCAddr := cfg . RPC . ListenAddress
2021-08-16 09:45:10 +00:00
httpSrv , httpSrvDone , err = StartJSONRPC ( ctx , clientCtx , tmRPCAddr , tmEndpoint , config )
2021-07-23 12:31:59 +00:00
if err != nil {
return err
2021-04-18 16:39:15 +00:00
}
}
2021-07-23 12:31:59 +00:00
defer func ( ) {
2021-04-18 16:39:15 +00:00
if tmNode . IsRunning ( ) {
_ = tmNode . Stop ( )
}
if cpuProfileCleanup != nil {
cpuProfileCleanup ( )
}
2021-07-23 12:31:59 +00:00
if apiSrv != nil {
_ = apiSrv . Close ( )
}
if grpcSrv != nil {
grpcSrv . Stop ( )
if grpcWebSrv != nil {
grpcWebSrv . Close ( )
}
}
2021-04-18 16:39:15 +00:00
if httpSrv != nil {
shutdownCtx , cancelFn := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
defer cancelFn ( )
if err := httpSrv . Shutdown ( shutdownCtx ) ; err != nil {
2021-07-12 18:39:35 +00:00
logger . Error ( "HTTP server shutdown produced a warning" , "error" , err . Error ( ) )
2021-04-18 16:39:15 +00:00
} else {
2021-07-12 18:39:35 +00:00
logger . Info ( "HTTP server shut down, waiting 5 sec" )
2021-04-18 16:39:15 +00:00
select {
case <- time . Tick ( 5 * time . Second ) :
case <- httpSrvDone :
}
}
}
2021-07-12 18:39:35 +00:00
logger . Info ( "Bye!" )
2021-07-23 12:31:59 +00:00
} ( )
2021-04-18 16:39:15 +00:00
2021-07-23 12:31:59 +00:00
// Wait for SIGINT or SIGTERM signal
return server . WaitForQuitSignals ( )
2021-04-18 16:39:15 +00:00
}
func openDB ( rootDir string ) ( dbm . DB , error ) {
dataDir := filepath . Join ( rootDir , "data" )
return sdk . NewLevelDB ( "application" , dataDir )
}
func openTraceWriter ( traceWriterFile string ) ( w io . Writer , err error ) {
if traceWriterFile == "" {
return
}
return os . OpenFile (
traceWriterFile ,
os . O_WRONLY | os . O_APPEND | os . O_CREATE ,
2021-09-05 11:03:06 +00:00
0 o666 ,
2021-04-18 16:39:15 +00:00
)
}