diff --git a/.gitignore b/.gitignore index d2482dea1fddf517c65e3d27012dfaffb6fcb809..32cda3d9e9fe1a96791e20a1b713a57dfacd6241 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor *.swp debug .idea +.vscode diff --git a/client/cmd/status.go b/client/cmd/status.go index 45e8096285b9b031062988bd8255d240e25ec834..08807956dc1012a49aea2b49e1a50c8ac975e974 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -20,17 +20,20 @@ import ( "github.com/spf13/cobra" "github.com/eris-ltd/eris-db/client/methods" + "github.com/eris-ltd/eris-db/util" ) - -func buildStatusCommand() *cobra.Command{ +func buildStatusCommand() *cobra.Command { statusCmd := &cobra.Command{ Use: "status", Short: "eris-client status returns the current status from a chain.", Long: `eris-client status returns the current status from a chain. `, Run: func(cmd *cobra.Command, args []string) { - methods.Status(clientDo) + err := methods.Status(clientDo) + if err != nil { + util.Fatalf("Could not get status: %s", err) + } }, } statusCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)") @@ -45,4 +48,3 @@ func buildStatusCommand() *cobra.Command{ return statusCmd } - diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go index f2dc52e6eebd8821eed601b1b69df000df0a2b4c..a527577481c7fec56dc87ac25383cbfef6b7f3f7 100644 --- a/client/cmd/transaction.go +++ b/client/cmd/transaction.go @@ -17,14 +17,12 @@ package commands import ( - "os" "strings" "github.com/spf13/cobra" - log "github.com/eris-ltd/eris-logger" - "github.com/eris-ltd/eris-db/client/methods" + "github.com/eris-ltd/eris-db/util" ) func buildTransactionCommand() *cobra.Command { @@ -33,9 +31,8 @@ func buildTransactionCommand() *cobra.Command { transactionCmd := &cobra.Command{ Use: "tx", Short: "eris-client tx formulates and signs a transaction to a chain", - Long: `eris-client tx formulates and signs a transaction to a chain. -`, - Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, + Long: "eris-client tx formulates and signs a transaction to a chain.", + Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, } addTransactionPersistentFlags(transactionCmd) @@ -46,7 +43,10 @@ func buildTransactionCommand() *cobra.Command { Short: "eris-client tx send --amt <amt> --to <addr>", Long: "eris-client tx send --amt <amt> --to <addr>", Run: func(cmd *cobra.Command, args []string) { - methods.Send(clientDo) + err := methods.Send(clientDo) + if err != nil { + util.Fatalf("Could not complete send: %s", err) + } }, PreRun: assertParameters, } @@ -75,7 +75,10 @@ func buildTransactionCommand() *cobra.Command { Short: "eris-client tx call --amt <amt> --fee <fee> --gas <gas> --to <contract addr> --data <data>", Long: "eris-client tx call --amt <amt> --fee <fee> --gas <gas> --to <contract addr> --data <data>", Run: func(cmd *cobra.Command, args []string) { - methods.Call(clientDo) + err := methods.Call(clientDo) + if err != nil { + util.Fatalf("Could not complete call: %s", err) + } }, PreRun: assertParameters, } @@ -180,26 +183,21 @@ func defaultAddress() string { func assertParameters(cmd *cobra.Command, args []string) { if clientDo.ChainidFlag == "" { - log.Fatal(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`) - os.Exit(1) + util.Fatalf(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`) } if !strings.HasPrefix(clientDo.NodeAddrFlag, "tcp://") && !strings.HasPrefix(clientDo.NodeAddrFlag, "unix://") { // TODO: [ben] go-rpc will deprecate reformatting; also it is bad practice to auto-correct for this; - log.Warn(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`) + // TODO: [Silas] I've made this fatal, but I'm inclined to define the default as tcp:// and normalise as with http + // below + util.Fatalf(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`) } if !strings.HasPrefix(clientDo.SignAddrFlag, "http://") { // NOTE: [ben] we preserve the auto-correction here as it is a simple http request-response to the key server. + // TODO: [Silas] we don't have logging here to log that we've done this. I'm inclined to either urls without a scheme + // and be quiet about it, or to make non-compliance fatal clientDo.SignAddrFlag = "http://" + clientDo.SignAddrFlag - log.WithFields(log.Fields{ - "signing address": clientDo.SignAddrFlag, - }).Warn(`Please use fully formed listening address for the key server; adding http:// prefix`) } - log.WithFields(log.Fields{ - "signing address": clientDo.SignAddrFlag, - "node address": clientDo.NodeAddrFlag, - "chain id": clientDo.ChainidFlag, - }).Debug("Asserted parameters") } diff --git a/client/methods/call.go b/client/methods/call.go index abe2fe91a28dc927bf5714e4caa63bc4d0e8c3fb..1bdcbdfeceea4f9c41663c244d76cddf392a6591 100644 --- a/client/methods/call.go +++ b/client/methods/call.go @@ -17,7 +17,7 @@ package methods import ( - log "github.com/eris-ltd/eris-logger" + "fmt" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/client/rpc" @@ -25,23 +25,31 @@ import ( "github.com/eris-ltd/eris-db/keys" ) -func Call(do *definitions.ClientDo) { +func Call(do *definitions.ClientDo) error { // construct two clients to call out to keys server and // blockchain node. - erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag) - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) + logger, err := loggerFromClientDo(do, "Call") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger) + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) // form the call transaction callTransaction, err := rpc.Call(erisNodeClient, erisKeyClient, do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag, do.GasFlag, do.FeeFlag, do.DataFlag) if err != nil { - log.Fatalf("Failed on forming Call Transaction: %s", err) - return + return fmt.Errorf("Failed on forming Call Transaction: %s", err) } // TODO: [ben] we carry over the sign bool, but always set it to true, // as we move away from and deprecate the api that allows sending unsigned // transactions and relying on (our) receiving node to sign it. - unpackSignAndBroadcast( - rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, - erisKeyClient, callTransaction, true, do.BroadcastFlag, do.WaitFlag)) + txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient, + callTransaction, true, do.BroadcastFlag, do.WaitFlag) + + if err != nil { + return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err) + } + unpackSignAndBroadcast(txResult, logger) + return nil } diff --git a/client/methods/helpers.go b/client/methods/helpers.go index 93c34ee51b25a2e227d08894f480f340ffb0d2a3..13c7138d26f1a9ddcc15a7e38fe505b6df43acef 100644 --- a/client/methods/helpers.go +++ b/client/methods/helpers.go @@ -17,33 +17,40 @@ package methods import ( - "fmt" - "os" - - log "github.com/eris-ltd/eris-logger" - "github.com/eris-ltd/eris-db/client/rpc" + "github.com/eris-ltd/eris-db/core" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/lifecycle" + "github.com/eris-ltd/eris-db/logging/loggers" ) -func unpackSignAndBroadcast(result *rpc.TxResult, err error) { - if err != nil { - log.Fatalf("Failed on signing (and broadcasting) transaction: %s", err) - os.Exit(1) - } +func unpackSignAndBroadcast(result *rpc.TxResult, logger loggers.InfoTraceLogger) { if result == nil { // if we don't provide --sign or --broadcast return } - printResult := log.Fields{ - "transaction hash": fmt.Sprintf("%X", result.Hash), - } + + logger = logger.With("transaction hash", result.Hash) + if result.Address != nil { - printResult["Contract Address"] = fmt.Sprintf("%X", result.Address) + logger = logger.With("Contract Address", result.Address) } + if result.Return != nil { - printResult["Block Hash"] = fmt.Sprintf("%X", result.BlockHash) - printResult["Return Value"] = fmt.Sprintf("%X", result.Return) - printResult["Exception"] = fmt.Sprintf("%s", result.Exception) + logger = logger.With("Block Hash", result.BlockHash, + "Return Value", result.Return, + "Exception", result.Exception, + ) + } + + logging.InfoMsg(logger, "SignAndBroadcast result") +} + +func loggerFromClientDo(do *definitions.ClientDo, scope string) (loggers.InfoTraceLogger, error) { + lc, err := core.LoadLoggingConfigFromClientDo(do) + if err != nil { + return nil, err } - log.WithFields(printResult).Warn("Result") + return logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(lc), scope), nil } diff --git a/client/methods/send.go b/client/methods/send.go index af393e029f454e43a953aeae759c45575fd8c6ec..65c2db37e5584c6e11205097aa0d3c01c45343bb 100644 --- a/client/methods/send.go +++ b/client/methods/send.go @@ -17,7 +17,7 @@ package methods import ( - log "github.com/eris-ltd/eris-logger" + "fmt" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/client/rpc" @@ -25,22 +25,29 @@ import ( "github.com/eris-ltd/eris-db/keys" ) -func Send(do *definitions.ClientDo) { +func Send(do *definitions.ClientDo) error { // construct two clients to call out to keys server and // blockchain node. - erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag) - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) + logger, err := loggerFromClientDo(do, "Send") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger) + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) // form the send transaction sendTransaction, err := rpc.Send(erisNodeClient, erisKeyClient, do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag) if err != nil { - log.Fatalf("Failed on forming Send Transaction: %s", err) - return + fmt.Errorf("Failed on forming Send Transaction: %s", err) } // TODO: [ben] we carry over the sign bool, but always set it to true, // as we move away from and deprecate the api that allows sending unsigned // transactions and relying on (our) receiving node to sign it. - unpackSignAndBroadcast( - rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, - erisKeyClient, sendTransaction, true, do.BroadcastFlag, do.WaitFlag)) + txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient, + sendTransaction, true, do.BroadcastFlag, do.WaitFlag) + if err != nil { + return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err) + } + unpackSignAndBroadcast(txResult, logger) + return nil } diff --git a/client/methods/status.go b/client/methods/status.go index ba2470e7645ba0759445daed1042211c8f184401..dd5bf72523bc9ba5277fadf10fb6e9bf2c1d7209 100644 --- a/client/methods/status.go +++ b/client/methods/status.go @@ -19,35 +19,35 @@ package methods import ( "fmt" - log "github.com/eris-ltd/eris-logger" - "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/definitions" ) -func Status(do *definitions.ClientDo) { - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) +func Status(do *definitions.ClientDo) error { + logger, err := loggerFromClientDo(do, "Status") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) genesisHash, validatorPublicKey, latestBlockHash, latestBlockHeight, latestBlockTime, err := erisNodeClient.Status() if err != nil { - log.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err) - return + return fmt.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err) } chainName, chainId, genesisHashfromChainId, err := erisNodeClient.ChainId() if err != nil { - log.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err) - return + return fmt.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err) } - log.WithFields(log.Fields{ - "chain": do.NodeAddrFlag, - "genesisHash": fmt.Sprintf("%X", genesisHash), - "chainName": chainName, - "chainId": chainId, - "genesisHash from chainId": fmt.Sprintf("%X", genesisHashfromChainId), - "validator public key": fmt.Sprintf("%X", validatorPublicKey), - "latest block hash": fmt.Sprintf("%X", latestBlockHash), - "latest block height": latestBlockHeight, - "latest block time": latestBlockTime, - }).Info("status") + logger.Info("chain", do.NodeAddrFlag, + "genesisHash", fmt.Sprintf("%X", genesisHash), + "chainName", chainName, + "chainId", chainId, + "genesisHash from chainId", fmt.Sprintf("%X", genesisHashfromChainId), + "validator public key", fmt.Sprintf("%X", validatorPublicKey), + "latest block hash", fmt.Sprintf("%X", latestBlockHash), + "latest block height", latestBlockHeight, + "latest block time", latestBlockTime, + ) + return nil } diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go index 12cdd76cf9b15f9575898224889a1528eb8a58b7..197818639d86bdfb8138a24682ca7eb56993c054 100644 --- a/client/mock/client_mock.go +++ b/client/mock/client_mock.go @@ -24,6 +24,7 @@ import ( consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/logging/loggers" ) // NOTE [ben] Compiler check to ensure ErisMockClient successfully implements @@ -117,3 +118,7 @@ func (mock *MockNodeClient) GetName(name string) (owner []byte, data string, exp func (mock *MockNodeClient) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) { return 0, nil, nil, nil } + +func (mock *MockNodeClient) Logger() loggers.InfoTraceLogger { + return loggers.NewNoopInfoTraceLogger() +} diff --git a/client/node_client.go b/client/node_client.go index 34275c8b2deacc6a10d18d18a496297dd3b44430..302bb968767c1393a8a1f578a7e970dc720c4a29 100644 --- a/client/node_client.go +++ b/client/node_client.go @@ -21,15 +21,12 @@ import ( // "strings" "github.com/tendermint/go-rpc/client" - // Note [ben]: this is included to silence the logger from tendermint/go-rpc/client - // see func init() - tendermint_log "github.com/tendermint/log15" - - log "github.com/eris-ltd/eris-logger" acc "github.com/eris-ltd/eris-db/account" consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client" tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" @@ -48,6 +45,9 @@ type NodeClient interface { DumpStorage(address []byte) (storage *core_types.Storage, err error) GetName(name string) (owner []byte, data string, expirationBlock int, err error) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) + + // Logging context for this NodeClient + Logger() loggers.InfoTraceLogger } type NodeWebsocketClient interface { @@ -58,37 +58,31 @@ type NodeWebsocketClient interface { Close() } -// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements +// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements // eris-db/client.NodeClient -var _ NodeClient = (*ErisNodeClient)(nil) +var _ NodeClient = (*erisNodeClient)(nil) // Eris-Client is a simple struct exposing the client rpc methods - -type ErisNodeClient struct { +type erisNodeClient struct { broadcastRPC string + logger loggers.InfoTraceLogger } // ErisKeyClient.New returns a new eris-keys client for provided rpc location // Eris-keys connects over http request-responses -func NewErisNodeClient(rpcString string) *ErisNodeClient { - return &ErisNodeClient{ +func NewErisNodeClient(rpcString string, logger loggers.InfoTraceLogger) *erisNodeClient { + return &erisNodeClient{ broadcastRPC: rpcString, + logger: logging.WithScope(logger, "ErisNodeClient"), } } -// Note [Ben]: This is a hack to silence Tendermint logger from tendermint/go-rpc -// it needs to be initialised before go-rpc, hence it's placement here. -func init() { - h := tendermint_log.LvlFilterHandler(tendermint_log.LvlWarn, tendermint_log.StdoutHandler) - tendermint_log.Root().SetHandler(h) -} - //------------------------------------------------------------------------------------ // broadcast to blockchain node // NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate // protocol version for moving towards rpc/v1 -func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) { +func (erisNodeClient *erisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) { client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC) receipt, err := tendermint_client.BroadcastTx(client, tx) if err != nil { @@ -97,7 +91,7 @@ func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) return &receipt, nil } -func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { +func (erisNodeClient *erisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { var wsAddr string // TODO: clean up this inherited mess on dealing with the address prefixes. nodeAddr := erisNodeClient.broadcastRPC @@ -115,16 +109,17 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node // } // wsAddr = "ws://" + wsAddr wsAddr = nodeAddr - log.WithFields(log.Fields{ - "websocket address": wsAddr, - "endpoint": "/websocket", - }).Debug("Subscribing to websocket address") + logging.TraceMsg(erisNodeClient.logger, "Subscribing to websocket address", + "websocket address", wsAddr, + "endpoint", "/websocket", + ) wsClient := rpcclient.NewWSClient(wsAddr, "/websocket") if _, err = wsClient.Start(); err != nil { return nil, err } - derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{ + derivedErisNodeWebsocketClient := &erisNodeWebsocketClient{ tendermintWebsocket: wsClient, + logger: logging.WithScope(erisNodeClient.logger, "ErisNodeWebsocketClient"), } return derivedErisNodeWebsocketClient, nil } @@ -134,7 +129,7 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node // Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash // the block height and the latest block time. -func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) { +func (erisNodeClient *erisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) res, err := tendermint_client.Status(client) if err != nil { @@ -152,7 +147,7 @@ func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPub return } -func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) { +func (erisNodeClient *erisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) chainIdResult, err := tendermint_client.ChainId(client) if err != nil { @@ -170,7 +165,7 @@ func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, Gene // QueryContract executes the contract code at address with the given data // NOTE: there is no check on the caller; -func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { +func (erisNodeClient *erisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data) if err != nil { @@ -182,7 +177,7 @@ func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress } // QueryContractCode executes the contract code at address with the given data but with provided code -func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { +func (erisNodeClient *erisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) // TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only // have a single address that is the contract to query. @@ -196,7 +191,7 @@ func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []by } // GetAccount returns a copy of the account -func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) { +func (erisNodeClient *erisNodeClient) GetAccount(address []byte) (*acc.Account, error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) account, err := tendermint_client.GetAccount(client, address) if err != nil { @@ -213,7 +208,7 @@ func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, } // DumpStorage returns the full storage for an account. -func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) { +func (erisNodeClient *erisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) resultStorage, err := tendermint_client.DumpStorage(client, address) if err != nil { @@ -231,7 +226,7 @@ func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core //-------------------------------------------------------------------------------------------- // Name registry -func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) { +func (erisNodeClient *erisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) entryResult, err := tendermint_client.GetName(client, name) if err != nil { @@ -248,7 +243,7 @@ func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data s //-------------------------------------------------------------------------------------------- -func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int, +func (erisNodeClient *erisNodeClient) ListValidators() (blockHeight int, bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) validatorsResult, err := tendermint_client.ListValidators(client) @@ -263,3 +258,7 @@ func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int, unbondingValidators = validatorsResult.UnbondingValidators return } + +func (erisNodeClient *erisNodeClient) Logger() loggers.InfoTraceLogger { + return erisNodeClient.logger +} diff --git a/client/rpc/client.go b/client/rpc/client.go index b82527ef72e961c05ce876034ed6863f9088495b..77ed13b528cf10853ab790088a456e777ecf910c 100644 --- a/client/rpc/client.go +++ b/client/rpc/client.go @@ -21,11 +21,8 @@ import ( "fmt" "strconv" - log "github.com/eris-ltd/eris-logger" - ptypes "github.com/eris-ltd/eris-db/permission/types" - "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/keys" "github.com/eris-ltd/eris-db/txs" @@ -268,16 +265,14 @@ type TxResult struct { } // Preserve -func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) { +func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, + broadcast, wait bool) (txResult *TxResult, err error) { var inputAddr []byte if sign { inputAddr, tx, err = signTx(keyClient, chainID, tx) if err != nil { return nil, err } - log.WithFields(log.Fields{ - "transaction": string(account.SignBytes(chainID, tx)), - }).Debug("Signed transaction") } if broadcast { @@ -296,23 +291,19 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke // if broadcast threw an error, just return return } - log.Debug("Waiting for transaction to be confirmed.") confirmation := <-confirmationChannel if confirmation.Error != nil { - log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error) - err = confirmation.Error + err = fmt.Errorf("Encountered error waiting for event: %s", confirmation.Error) return } if confirmation.Exception != nil { - log.Errorf("Encountered Exception from chain: %s\n", confirmation.Exception) - err = confirmation.Exception + err = fmt.Errorf("Encountered Exception from chain: %s", confirmation.Exception) return } txResult.BlockHash = confirmation.BlockHash txResult.Exception = "" eventDataTx, ok := confirmation.Event.(*txs.EventDataTx) if !ok { - log.Errorf("Received wrong event type.") err = fmt.Errorf("Received wrong event type.") return } diff --git a/client/rpc/client_util.go b/client/rpc/client_util.go index 3e995a8acf9a9d28366d7b00aa85013457f733f5..df8ffe3f0fe77cd927014794878b6ada6806f421 100644 --- a/client/rpc/client_util.go +++ b/client/rpc/client_util.go @@ -21,13 +21,12 @@ import ( "fmt" "strconv" - log "github.com/eris-ltd/eris-logger" - "github.com/tendermint/go-crypto" acc "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/keys" + "github.com/eris-ltd/eris-db/logging" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" ) @@ -101,10 +100,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return } else if pubkey != "" { if addr != "" { - log.WithFields(log.Fields{ - "public key": pubkey, - "address": addr, - }).Info("you have specified both a pubkey and an address. the pubkey takes precedent") + logging.InfoMsg(nodeClient.Logger(), "Both a public key and an address have been specified. The public key takes precedent.", + "public_key", pubkey, + "address", addr, + ) } pubKeyBytes, err = hex.DecodeString(pubkey) if err != nil { @@ -151,10 +150,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return pub, amt, nonce, err2 } nonce = int64(account.Sequence) + 1 - log.WithFields(log.Fields{ - "nonce": nonce, - "account address": fmt.Sprintf("%X", addrBytes), - }).Debug("Fetch nonce from node") + logging.TraceMsg(nodeClient.Logger(), "Fetch nonce from node", + "nonce", nonce, + "account address", addrBytes, + ) } else { nonce, err = strconv.ParseInt(nonceS, 10, 64) if err != nil { diff --git a/client/websocket_client.go b/client/websocket_client.go index f58f8050dff4bf9c50addd8457b346b90f728d5b..429958aad169eab380d832f83a7b70e868ebde08 100644 --- a/client/websocket_client.go +++ b/client/websocket_client.go @@ -24,8 +24,8 @@ import ( "github.com/tendermint/go-rpc/client" "github.com/tendermint/go-wire" - log "github.com/eris-ltd/eris-logger" - + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ctypes "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" ) @@ -41,29 +41,30 @@ type Confirmation struct { Error error } -// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements +// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements // eris-db/client.NodeClient -var _ NodeWebsocketClient = (*ErisNodeWebsocketClient)(nil) +var _ NodeWebsocketClient = (*erisNodeWebsocketClient)(nil) -type ErisNodeWebsocketClient struct { +type erisNodeWebsocketClient struct { // TODO: assert no memory leak on closing with open websocket tendermintWebsocket *rpcclient.WSClient + logger loggers.InfoTraceLogger } // Subscribe to an eventid -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Subscribe(eventid string) error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Subscribe(eventid string) error { // TODO we can in the background listen to the subscription id and remember it to ease unsubscribing later. return erisNodeWebsocketClient.tendermintWebsocket.Subscribe(eventid) } // Unsubscribe from an eventid -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Unsubscribe(subscriptionId string) error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Unsubscribe(subscriptionId string) error { return erisNodeWebsocketClient.tendermintWebsocket.Unsubscribe(subscriptionId) } // Returns a channel that will receive a confirmation with a result or the exception that // has been confirmed; or an error is returned and the confirmation channel is nil. -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) { // check no errors are reported on the websocket if err := erisNodeWebsocketClient.assertNoErrors(); err != nil { return nil, err @@ -88,7 +89,8 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t result := new(ctypes.ErisDBResult) if wire.ReadJSONPtr(result, resultBytes, &err); err != nil { // keep calm and carry on - log.Errorf("[eris-client] Failed to unmarshal json bytes for websocket event: %s", err) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to unmarshal json bytes for websocket event", + "error", err) continue } @@ -97,36 +99,41 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t // Received confirmation of subscription to event streams // TODO: collect subscription IDs, push into channel and on completion // unsubscribe - log.Infof("[eris-client] received confirmation for event (%s) with subscription id (%s).", - subscription.Event, subscription.SubscriptionId) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Received confirmation for event", + "event", subscription.Event, + "subscription_id", subscription.SubscriptionId) continue } event, ok := (*result).(*ctypes.ResultEvent) if !ok { // keep calm and carry on - log.Warnf("[eris-client] Failed to cast to ResultEvent for websocket event: %s", *result) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to cast to ResultEvent for websocket event", + "event", event.Event) continue } blockData, ok := event.Data.(txs.EventDataNewBlock) if ok { latestBlockHash = blockData.Block.Hash() - log.WithFields(log.Fields{ - "new block": blockData.Block, - "latest hash": latestBlockHash, - }).Debug("Registered new block") + logging.TraceMsg(erisNodeWebsocketClient.logger, "Registered new block", + "block", blockData.Block, + "latest_block_hash", latestBlockHash, + ) continue } // we don't accept events unless they came after a new block (ie. in) if latestBlockHash == nil { - log.Infof("[eris-client] no first block has been registered, so ignoring event: %s", event.Event) + logging.InfoMsg(erisNodeWebsocketClient.logger, "First block has not been registered so ignoring event", + "event", event.Event) continue } if event.Event != eid { - log.Warnf("[eris-client] received unsolicited event! Got %s, expected %s\n", event.Event, eid) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Received unsolicited event", + "event_received", event.Event, + "event_expected", eid) continue } @@ -143,10 +150,9 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t } if !bytes.Equal(txs.TxHash(chainId, data.Tx), txs.TxHash(chainId, tx)) { - log.WithFields(log.Fields{ + logging.TraceMsg(erisNodeWebsocketClient.logger, "Received different event", // TODO: consider re-implementing TxID again, or other more clear debug - "received transaction event": txs.TxHash(chainId, data.Tx), - }).Debug("Received different event") + "received transaction event", txs.TxHash(chainId, data.Tx)) continue } @@ -188,13 +194,13 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t return confirmationChannel, nil } -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Close() { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Close() { if erisNodeWebsocketClient.tendermintWebsocket != nil { erisNodeWebsocketClient.tendermintWebsocket.Stop() } } -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) assertNoErrors() error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) assertNoErrors() error { if erisNodeWebsocketClient.tendermintWebsocket != nil { select { case err := <-erisNodeWebsocketClient.tendermintWebsocket.ErrorsCh: diff --git a/cmd/serve.go b/cmd/serve.go index 49bc665be308c0a5bbe23deaeba3e7d9f9c97730..29319b1fd815498dae5ac82659211afacc8280c8 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -111,7 +111,7 @@ func NewCoreFromDo(do *definitions.Do) (*core.Core, error) { } // Create a root logger to pass through to dependencies - logger := logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(*loggerConfig), "Serve") + logger := logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(loggerConfig), "Serve") // Capture all logging from tendermint/tendermint and tendermint/go-* // dependencies lifecycle.CaptureTendermintLog15Output(logger) diff --git a/docs/generator.go b/docs/generator.go index 2d34fb2a358ecdb4991e8ce002851d440846d8c1..bd06bb6e22887d0fff1576abce2020709e751454 100644 --- a/docs/generator.go +++ b/docs/generator.go @@ -7,7 +7,7 @@ import ( "strings" "text/template" - "github.com/eris-ltd/common/go/docs" + docs "github.com/eris-ltd/eris-db/docs/generator" commands "github.com/eris-ltd/eris-db/cmd" clientCommands "github.com/eris-ltd/eris-db/client/cmd" diff --git a/docs/generator/generator.go b/docs/generator/generator.go new file mode 100644 index 0000000000000000000000000000000000000000..0818121324e0b6dec2152c7a517c3ea196372210 --- /dev/null +++ b/docs/generator/generator.go @@ -0,0 +1,333 @@ +package generator + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "strconv" + "text/template" +) + +const FrontMatter = `--- + +layout: single +type: docs +title: "Documentation | {{ .Description }} | {{ $name }}" + +---` + +type Entry struct { + Title string + Template *template.Template + Specifications []*Entry + Examples []*Entry + Description string + FileName string + CmdEntryPoint string + URL string + BaseURL string +} + +func GenerateFileName(dir, s string) string { + return (dir + strings.Replace(strings.ToLower(s), " ", "_", -1) + ".md") +} + +func GenerateTitleFromFileName(file string) string { + file = strings.Replace(file, "_", " ", -1) + file = strings.Replace(file, "-", " ", -1) + return strings.Title(strings.Replace(file, ".md", "", 1)) +} + +func GenerateFileNameFromGlob(dir, s string) string { + return (dir + strings.Replace(filepath.Base(s), " ", "_", -1)) +} + +func GenerateURLFromFileName(s string) string { + s = strings.Replace(s, "./", "/", 1) + return strings.Replace(s, ".md", "/", -1) +} + +func GenerateCommandsTemplate() (*template.Template, error) { + handle_link := func(s string) string { + return (strings.Replace(s, ".md", "/", -1)) + } + + handle_file := func(s1, s2 string) string { + return strings.Replace((s1 + " " + s2 + ".md"), " ", "_", -1) + } + + funcMap := template.FuncMap{ + "title": strings.Title, + "replace": strings.Replace, + "chomp": strings.TrimSpace, + "handle_file": handle_file, + "handle_link": handle_link, + } + + var templateText = `{{- $name := .Command.CommandPath -}}` + FrontMatter + ` + +# {{ $name }} + +{{ title .Command.Short }} + +{{ if .Command.Runnable }}## Usage + +` + "```bash\n{{ .Command.UseLine }}\n```" + `{{ end }} + +{{ if ne .Command.Long "" }}## Synopsis + +{{ .Command.Long }} +{{ end }} +{{ $flags := .Command.NonInheritedFlags }} +{{ if $flags.HasFlags }}## Options + +` + "```bash\n {{ $flags.FlagUsages | chomp }}\n```" + `{{ end }} +{{ $global_flags := .Command.InheritedFlags }} +{{ if $global_flags.HasFlags }}## Options inherited from parent commands + +` + "```bash\n {{ $global_flags.FlagUsages | chomp }}\n```" + `{{ end }} + +{{ if .Command.HasSubCommands }}# Subcommands +{{ range .Command.Commands }} +{{ if ne .Deprecated "" }} +* [{{ $name }} {{ .Name }}]({{ .BaseURL }}{{ handle_file $name .Name | handle_link }}) - {{ .Short }} +{{ end }} +{{ end }} +{{ end }} + +{{ if .Command.HasParent }}{{ $parent := .Command.Parent }}## See Also +* [{{ $parent.CommandPath }}]({{ .BaseURL }}{{ handle_file $parent.CommandPath "" | handle_link }}) - {{ $parent.Short }} +{{ end }} + +{{ if ne .Command.Example "" }}# Quick Tips + +` + "```bash\n{{ .Command.Example }}\n```" + `{{ end }} + +{{ if ne (len .Entry.Examples) 0 }}# Examples +{{ range .Entry.Examples }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} + +{{ if ne (len .Entry.Specifications) 0 }}# Specifications +{{ range .Entry.Specifications }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} +` + + return template.New("docGenerator").Funcs(funcMap).Parse(templateText) +} + +func GenerateEntries(dir, render_dir, description string) ([]*Entry, error) { + var entries []*Entry + + if _, err := os.Stat(render_dir); os.IsNotExist(err) { + err = os.MkdirAll(render_dir, 0755) + if err != nil { + panic(err) + } + } + + files := CollectEntries(dir) + + for _, file := range files { + this_entry, err := GenerateEntry(file, dir, render_dir, description) + if err != nil { + return nil, err + } else { + entries = append(entries, this_entry) + } + } + + return entries, nil +} + +func CollectEntries(dir string) ([]string) { + var newFiles []string + + files, err := filepath.Glob(dir + "/*") + if err != nil { + panic(err) + } + + for _, file := range files { + f_info, err := os.Stat(file) + + if err != nil { + panic(err) + } + + if f_info.IsDir() { + newFiles = append(newFiles, CollectEntries(file)...) + } else { + if filepath.Ext(file) == ".md" { + newFiles = append(newFiles, file) + } + } + } + + return newFiles +} + +func GenerateEntry(file, dir, render_dir, description string) (*Entry, error) { + var err error + + this_entry := &Entry{ + FileName: GenerateFileNameFromGlob(render_dir, file), + Title: GenerateTitleFromFileName(filepath.Base(file)), + Description: description, + } + + this_entry.URL = GenerateURLFromFileName(this_entry.FileName) + + txt, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + // Get template from docs generator + this_entry.Template, err = GenerateEntriesTemplate(txt) + if err != nil { + return nil, err + } + + return this_entry, nil +} + +func GenerateEntriesTemplate(txt []byte) (*template.Template, error) { + handle_link := func(s string) string { + return (strings.Replace(s, ".md", "/", -1)) + } + + handle_file := func(s1 string) string { + return strings.Replace((s1 + ".md"), " ", "_", -1) + } + + insert_definition := func(file string, struc string) string { + txt, err := ioutil.ReadFile(filepath.Join("definitions", file)) + if err != nil { + panic(err) + } + finder := regexp.MustCompile(fmt.Sprintf(`(?ms:^type %s struct {.*?^})`, struc)) + return ("```go\n" + string(finder.Find(txt)) + "\n```") + } + + insert_bash_lines := func(file string, linesToRead string) string { + var lines []byte + var line []byte + var start int + var stop int + + fileInfo, err := os.Open(filepath.Join("docs", "tests", file)) + if err != nil { + panic(err) + } + defer fileInfo.Close() + + start, err = strconv.Atoi(strings.Split(linesToRead, "-")[0]) + if strings.Contains(linesToRead, "-") { + stop, err = strconv.Atoi(strings.Split(linesToRead, "-")[1]) + } else { + stop = start + } + if err != nil { + panic(err) + } + + r := bufio.NewReader(fileInfo) + for i := 1; ; i++ { + line, err = r.ReadBytes('\n') + if err != nil { + break + } + if i >= start && i <= stop { + lines = append(lines, line...) + } + } + if err != io.EOF { + panic(err) + } + + return ("```bash\n" + string(lines) + "```") + } + + insert_file := func(file string) string { + file = filepath.Join("docs", "tests", file) + ext := filepath.Ext(file) + switch ext { + case ".sol": + ext = ".javascript" + case ".yml": + ext = ".yaml" + } + + ext = strings.Replace(ext, ".", "", 1) + + txtB, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + + txt := string(txtB) + if !strings.HasSuffix(txt, "\n") { + txt = txt + "\n" + } + + return ("```" + ext + "\n" + txt + "```") // TODO: add auto-curl text + } + + funcMap := template.FuncMap{ + "title": strings.Title, + "replace": strings.Replace, + "chomp": strings.TrimSpace, + "handle_file": handle_file, + "handle_link": handle_link, + "insert_definition": insert_definition, + "insert_bash_lines": insert_bash_lines, + "insert_file": insert_file, + } + + var templateText = `{{- $name := .Title -}}` + FrontMatter + ` + +` + string(txt) + ` + +## Commands + +* [{{ .CmdEntryPoint }}]({{ .BaseURL }}{{ handle_file .CmdEntryPoint | handle_link }}) + +{{ if ne (len .Examples) 0 }}# Examples +{{ range .Examples }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} + +{{ if ne (len .Specifications) 0 }}# Specifications +{{ range .Specifications }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} +` + + return template.New("entryGenerator").Funcs(funcMap).Parse(templateText) +} + +func RenderEntry(this_entry *Entry) error { + out_file, err := os.Create(this_entry.FileName) + if err != nil { + return err + } + defer out_file.Close() + + err = this_entry.Template.Execute(out_file, this_entry) + if err != nil { + return err + } + + return nil +} \ No newline at end of file diff --git a/event/event_cache.go b/event/event_cache.go index 3f230ae599fb99e96fa7726b37130409d11ed993..08db30116ea20225cb27875fb79fd103b0165070 100644 --- a/event/event_cache.go +++ b/event/event_cache.go @@ -70,7 +70,7 @@ func reap(es *EventSubscriptions) { defer es.mtx.Unlock() for id, sub := range es.subs { if time.Since(sub.ts) > reaperThreshold { - // Seems like Go is ok with evts.. + // Seems like Go is ok with this.. delete(es.subs, id) es.eventEmitter.Unsubscribe(id) } @@ -116,7 +116,7 @@ func (this *EventSubscriptions) Poll(subId string) ([]interface{}, error) { func (this *EventSubscriptions) Remove(subId string) error { this.mtx.Lock() defer this.mtx.Unlock() - // TODO Check evts. + // TODO Check this. _, ok := this.subs[subId] if !ok { return fmt.Errorf("Subscription not active. ID: " + subId) diff --git a/event/event_cache_test.go b/event/event_cache_test.go index 0ac32fb23ee6d4af3d320ba16169b807bd1fa6f0..35ce6d7ed0cbdc45c3b838d831b993fddfbbb42b 100644 --- a/event/event_cache_test.go +++ b/event/event_cache_test.go @@ -131,7 +131,7 @@ func TestSubReaping(t *testing.T) { // Test that event subscriptions can be added and removed manually. func TestSubManualClose(t *testing.T) { NUM_SUBS := 100 - // Keep the reaper out of evts. + // Keep the reaper out of this. reaperThreshold = 10000 * time.Millisecond reaperTimeout = 10000 * time.Millisecond @@ -178,7 +178,7 @@ func TestSubManualClose(t *testing.T) { // Test that the system doesn't fail under high pressure. func TestSubFlooding(t *testing.T) { NUM_SUBS := 100 - // Keep the reaper out of evts. + // Keep the reaper out of this. reaperThreshold = 10000 * time.Millisecond reaperTimeout = 10000 * time.Millisecond // Crank it up. Now pressure is 10 times higher on each sub. diff --git a/event/events.go b/event/events.go index 87647623f1fa73476ac175f29612afa8e7ffb695..ec790a9ed8b1d120a46ecf1642bec840756fcbdd 100644 --- a/event/events.go +++ b/event/events.go @@ -30,13 +30,13 @@ import ( tm_types "github.com/tendermint/tendermint/types" ) -// TODO: [Silas] evts is a compatibility layer between our event types and +// TODO: [Silas] this is a compatibility layer between our event types and // TODO: go-events. Our ultimate plan is to replace go-events with our own pub-sub // TODO: code that will better allow us to manage and multiplex events from different // TODO: subsystems // Oh for a sum type -// We are using evts as a marker interface for the +// We are using this as a marker interface for the type anyEventData interface{} type EventEmitter interface { @@ -144,7 +144,7 @@ func GenerateSubId() (string, error) { } func mapToOurEventData(eventData anyEventData) (txs.EventData, error) { - // TODO: [Silas] avoid evts with a better event pub-sub system of our own + // TODO: [Silas] avoid this with a better event pub-sub system of our own // TODO: that maybe involves a registry of events switch eventData := eventData.(type) { case txs.EventData: diff --git a/glide.lock b/glide.lock index 34cea95404a3b7a0d4ff200facba76ad4cd14890..3d3222867ddd50d4f9b9d5432a7c265af17d35a7 100644 --- a/glide.lock +++ b/glide.lock @@ -23,11 +23,6 @@ imports: version: f0aeabca5a127c4078abb8c8d64298b147264b55 - name: github.com/davecgh/go-spew version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d -- name: github.com/eris-ltd/common - version: 8ca15f5455104403db4202c995e2f6e161654c02 - subpackages: - - go/docs - - go/common - name: github.com/eris-ltd/eris-keys version: 114ebc77443db9a153692233294e48bc7e184215 - name: github.com/fsnotify/fsnotify @@ -231,8 +226,6 @@ imports: version: 46a701a619de90c65a78c04d1a58bf02585e9701 subpackages: - term -- name: github.com/eris-ltd/eris-logger - version: ea48a395d6ecc0eccc67a26da9fc7a6106fabb84 - name: github.com/streadway/simpleuuid version: 6617b501e485b77e61b98cd533aefff9e258b5a7 devImports: [] diff --git a/glide.yaml b/glide.yaml index 9ad7b4d4edc758ee21952e15cf611acc9d47fc12..6b1ab4221fd9d4dfccf5d67eaea6e9ed43438e48 100644 --- a/glide.yaml +++ b/glide.yaml @@ -17,7 +17,6 @@ import: - package: golang.org/x/net subpackages: - http2 -- package: github.com/eris-ltd/common - package: github.com/go-kit/kit version: ^0.3.0 - package: github.com/eapache/channels diff --git a/keys/key_client.go b/keys/key_client.go index e841f50abd71f68ce4bdd9fc838acf764e5f3b1f..f10093b831bf9e78c09c41c1fe3e6d2831d5d352 100644 --- a/keys/key_client.go +++ b/keys/key_client.go @@ -19,6 +19,8 @@ package keys import ( "encoding/hex" "fmt" + "github.com/eris-ltd/eris-db/logging/loggers" + "github.com/eris-ltd/eris-db/logging" ) type KeyClient interface { @@ -29,31 +31,33 @@ type KeyClient interface { PublicKey(address []byte) (publicKey []byte, err error) } -// NOTE [ben] Compiler check to ensure ErisKeyClient successfully implements +// NOTE [ben] Compiler check to ensure erisKeyClient successfully implements // eris-db/keys.KeyClient -var _ KeyClient = (*ErisKeyClient)(nil) +var _ KeyClient = (*erisKeyClient)(nil) -type ErisKeyClient struct { +type erisKeyClient struct { rpcString string + logger loggers.InfoTraceLogger } -// ErisKeyClient.New returns a new eris-keys client for provided rpc location +// erisKeyClient.New returns a new eris-keys client for provided rpc location // Eris-keys connects over http request-responses -func NewErisKeyClient(rpcString string) *ErisKeyClient { - return &ErisKeyClient{ +func NewErisKeyClient(rpcString string, logger loggers.InfoTraceLogger) *erisKeyClient { + return &erisKeyClient{ rpcString: rpcString, + logger: logging.WithScope(logger, "ErisKeysClient"), } } // Eris-keys client Sign requests the signature from ErisKeysClient over rpc for the given // bytes to be signed and the address to sign them with. -func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte) (signature []byte, err error) { +func (erisKeys *erisKeyClient) Sign(signBytesString string, signAddress []byte) (signature []byte, err error) { args := map[string]string{ "msg": signBytesString, "hash": signBytesString, // TODO:[ben] backwards compatibility "addr": fmt.Sprintf("%X", signAddress), } - sigS, err := RequestResponse(erisKeys.rpcString, "sign", args) + sigS, err := RequestResponse(erisKeys.rpcString, "sign", args, erisKeys.logger) if err != nil { return } @@ -66,11 +70,11 @@ func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte) // Eris-keys client PublicKey requests the public key associated with an address from // the eris-keys server. -func (erisKeys *ErisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) { +func (erisKeys *erisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) { args := map[string]string{ "addr": fmt.Sprintf("%X", address), } - pubS, err := RequestResponse(erisKeys.rpcString, "pub", args) + pubS, err := RequestResponse(erisKeys.rpcString, "pub", args, erisKeys.logger) if err != nil { return } diff --git a/keys/key_client_util.go b/keys/key_client_util.go index e9e5f4783d51ce2df4e590cea008535ed5f1a4d7..5fd1cbce41f55d1348584aeb29d55dacf7fcc1d8 100644 --- a/keys/key_client_util.go +++ b/keys/key_client_util.go @@ -26,7 +26,8 @@ import ( "io/ioutil" "net/http" - log "github.com/eris-ltd/eris-logger" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ) // Eris-Keys server connects over http request-response structures @@ -36,17 +37,17 @@ type HTTPResponse struct { Error string } -func RequestResponse(addr, method string, args map[string]string) (string, error) { - b, err := json.Marshal(args) +func RequestResponse(addr, method string, args map[string]string, logger loggers.InfoTraceLogger) (string, error) { + body, err := json.Marshal(args) if err != nil { return "", err } endpoint := fmt.Sprintf("%s/%s", addr, method) - log.WithFields(log.Fields{ - "key server endpoint": endpoint, - "request body": string(b), - }).Debugf("Eris-client: Sending request body to key server") - req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b)) + logging.TraceMsg(logger, "Sending request to key server", + "key_server_endpoint", endpoint, + "request_body", string(body), + ) + req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body)) if err != nil { return "", err } @@ -58,11 +59,11 @@ func RequestResponse(addr, method string, args map[string]string) (string, error if errS != "" { return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS) } - log.WithFields(log.Fields{ - "endpoint": endpoint, - "request body": string(b), - "response": res, - }).Debugf("Received response from key server") + logging.TraceMsg(logger, "Received response from key server", + "endpoint", endpoint, + "request body", string(body), + "response", res, + ) return res, nil } diff --git a/logging/lifecycle/lifecycle.go b/logging/lifecycle/lifecycle.go index 026c07025942d27e6f8da19f52d4ed57eebf1d26..ce0faef40ace4decfb9f71a09126d3d22ce4e41d 100644 --- a/logging/lifecycle/lifecycle.go +++ b/logging/lifecycle/lifecycle.go @@ -1,26 +1,28 @@ package lifecycle // No package in ./logging/... should depend on lifecycle - import ( "os" + "time" + "github.com/eris-ltd/eris-db/logging" "github.com/eris-ltd/eris-db/logging/adapters/stdlib" tmLog15adapter "github.com/eris-ltd/eris-db/logging/adapters/tendermint_log15" "github.com/eris-ltd/eris-db/logging/loggers" "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" - tmLog15 "github.com/tendermint/log15" "github.com/streadway/simpleuuid" - "time" + tmLog15 "github.com/tendermint/log15" ) +// Lifecycle provides a canonical source for eris loggers. Components should use the functions here +// to set up their root logger and capture any other logging output. + // Obtain a logger from a LoggingConfig -func NewLoggerFromLoggingConfig(LoggingConfig logging.LoggingConfig) loggers.InfoTraceLogger { - infoLogger := kitlog.NewLogfmtLogger(os.Stderr) - traceLogger := kitlog.NewLogfmtLogger(os.Stderr) - return logging.WithMetadata(loggers.NewInfoTraceLogger(infoLogger, traceLogger)) +func NewLoggerFromLoggingConfig(LoggingConfig *logging.LoggingConfig) loggers.InfoTraceLogger { + return NewStdErrLogger() } func NewStdErrLogger() loggers.InfoTraceLogger { @@ -29,8 +31,12 @@ func NewStdErrLogger() loggers.InfoTraceLogger { return NewLogger(logger, logger) } +// Provided a standard eris logger that outputs to the supplied underlying info and trace +// loggers func NewLogger(infoLogger, traceLogger kitlog.Logger) loggers.InfoTraceLogger { - infoTraceLogger := loggers.NewInfoTraceLogger(infoLogger, traceLogger) + infoTraceLogger := loggers.NewInfoTraceLogger( + loggers.ErisFormatLogger(infoLogger), + loggers.ErisFormatLogger(traceLogger)) // Create a random ID based on start time uuid, _ := simpleuuid.NewTime(time.Now()) var runId string @@ -43,10 +49,10 @@ func NewLogger(infoLogger, traceLogger kitlog.Logger) loggers.InfoTraceLogger { func CaptureTendermintLog15Output(infoTraceLogger loggers.InfoTraceLogger) { tmLog15.Root().SetHandler( tmLog15adapter.InfoTraceLoggerAsLog15Handler(infoTraceLogger. - With(structure.ComponentKey, "tendermint_log15"))) + With(structure.CapturedLoggingSourceKey, "tendermint_log15"))) } func CaptureStdlibLogOutput(infoTraceLogger loggers.InfoTraceLogger) { stdlib.CaptureRootLogger(infoTraceLogger. - With(structure.ComponentKey, "stdlib_log")) + With(structure.CapturedLoggingSourceKey, "stdlib_log")) } diff --git a/logging/loggers/eris_format_logger.go b/logging/loggers/eris_format_logger.go new file mode 100644 index 0000000000000000000000000000000000000000..20cdd8d653ad1ef09076c24e547d7f0f89e58297 --- /dev/null +++ b/logging/loggers/eris_format_logger.go @@ -0,0 +1,39 @@ +package loggers + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/logging/structure" + + kitlog "github.com/go-kit/kit/log" +) + +// Logger that implements some formatting conventions for eris-db and eris-client +// This is intended for applying consistent value formatting before the final 'output' logger; +// we should avoid prematurely formatting values here if it is useful to let the output logger +// decide how it wants to display values. Ideal candidates for 'early' formatting here are types that +// we control and generic output loggers are unlikely to know about. +type erisFormatLogger struct { + logger kitlog.Logger +} + +var _ kitlog.Logger = &erisFormatLogger{} + +func (efl *erisFormatLogger) Log(keyvals ...interface{}) error { + return efl.logger.Log(structure.MapKeyValues(keyvals, erisFormatKeyValueMapper)...) +} + +func erisFormatKeyValueMapper(key, value interface{}) (interface{}, interface{}) { + switch key { + default: + switch v := value.(type) { + case []byte: + return key, fmt.Sprintf("%X", v) + } + } + return key, value +} + +func ErisFormatLogger(logger kitlog.Logger) *erisFormatLogger { + return &erisFormatLogger{logger: logger} +} diff --git a/logging/structure/structure.go b/logging/structure/structure.go index 8a81777a2bf3105645f03933724b27d01136b850..96aaa3f54bebb7dbac007b63a630b8ed787853a8 100644 --- a/logging/structure/structure.go +++ b/logging/structure/structure.go @@ -17,6 +17,8 @@ const ( ChannelKey = "channel" // Log message (string) MessageKey = "message" + // Captured logging source (like tendermint_log15, stdlib_log) + CapturedLoggingSourceKey = "captured_logging_source" // Top-level component (choose one) name ComponentKey = "component" // Vector-valued scope @@ -131,3 +133,14 @@ func KeyFromValue(val interface{}) string { return reflect.TypeOf(val).Name() } } + +// Maps key values pairs with a function (key, value) -> (new key, new value) +func MapKeyValues(keyvals []interface{}, fn func(interface{}, interface{}) (interface{}, interface{})) []interface{} { + mappedKeyvals := make([]interface{}, len(keyvals)) + for i := 0; i < 2*(len(keyvals)/2); i += 2 { + key := keyvals[i] + val := keyvals[i+1] + mappedKeyvals[i], mappedKeyvals[i+1]= fn(key, val) + } + return mappedKeyvals +} \ No newline at end of file diff --git a/manager/eris-mint/state/execution.go b/manager/eris-mint/state/execution.go index ceefbbd8368a2491e1a743f87b823fd40558935e..9d9fd85bd3f85fc9d0cd2c50402b86a3376d98cf 100644 --- a/manager/eris-mint/state/execution.go +++ b/manager/eris-mint/state/execution.go @@ -203,6 +203,12 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*acm.Account, out return accounts, nil } +// Since all ethereum accounts implicitly exist we sometimes lazily create an Account object to represent them +// only when needed. Sometimes we need to create an unknown Account knowing only its address (which is expected to +// be a deterministic hash of its associated public key) and not its public key. When we eventually receive a +// transaction acting on behalf of that account we will be given a public key that we can check matches the address. +// If it does then we will associate the public key with the stub account already registered in the system once and +// for all time. func checkInputPubKey(acc *acm.Account, in *txs.TxInput) error { if acc.PubKey == nil { if in.PubKey == nil {