From e7052401bc05ecdee45612df362c055517d2fd3c Mon Sep 17 00:00:00 2001 From: jenson Date: Fri, 10 Jan 2020 17:21:29 +0800 Subject: [PATCH 1/3] remove fmt println ; add from and nonce to audit log --- chain/app/evm/evm.go | 4 +++- gemmill/angine.go | 15 +++++++-------- gemmill/modules/go-autofile/group.go | 13 +++++++------ gemmill/modules/go-common/errors.go | 26 ++++++++++++++++++++++---- gemmill/modules/go-log/zap.go | 2 +- gemmill/state/state.go | 13 ------------- 6 files changed, 40 insertions(+), 33 deletions(-) diff --git a/chain/app/evm/evm.go b/chain/app/evm/evm.go index 59c9e01..b147924 100755 --- a/chain/app/evm/evm.go +++ b/chain/app/evm/evm.go @@ -400,7 +400,9 @@ func (app *EVMApp) CheckTx(bs []byte) error { return err } from, _ := etypes.Sender(app.Signer, tx) - + defer func() { + log.Audit().Info("check tx",zap.String("from",from.String()),zap.Uint64("nonce",tx.Nonce())) + }() app.stateMtx.Lock() defer app.stateMtx.Unlock() // Last but not least check for nonce errors diff --git a/gemmill/angine.go b/gemmill/angine.go index f45d372..1b523d4 100644 --- a/gemmill/angine.go +++ b/gemmill/angine.go @@ -150,7 +150,7 @@ func NewAngine(app types.Application, tune *Tunes) (angine *Angine, err error) { logger, err := getLogger(conf) if err != nil { - fmt.Println("failed to get logger: ", err) + err = fmt.Errorf("failed to get logger: %v", err) return nil, err } log.SetLog(logger) @@ -163,13 +163,13 @@ func NewAngine(app types.Application, tune *Tunes) (angine *Angine, err error) { crypto.NodeInit(crypto.CryptoType) privValidator, err := types.LoadPrivValidator(conf.GetString("priv_validator_file")) if err != nil { - fmt.Println("LoadPrivValidator error: ", err) + err = fmt.Errorf("LoadPrivValidator error: %v", err) return nil, err } refuseList = refuse_list.NewRefuseList(dbBackend, dbDir) p2psw, err := prepareP2P(conf, genesis, privValidator, refuseList) if err != nil { - fmt.Println("prepare p2p error: ", err) + err = fmt.Errorf("prepare p2p error: %v", err) return nil, err } @@ -225,13 +225,13 @@ func (a *Angine) OnRecvExchangeData(data *p2p.ExchangeData) error { othGenesis, err := types.GenesisDocFromJSONRet(data.GenesisJSON) if err != nil { // TODO log err - fmt.Println("oth genesis err:", err) + log.Warn("oth genesis err:", zap.Error(err)) return err } a.p2pSwitch.GetExchangeData().GenesisJSON = data.GenesisJSON if err = a.buildState(othGenesis); err != nil { // TODO log err - fmt.Println("build state err:", err) + log.Warn("build state err:", zap.Error(err)) return err } if a.stateMachine == nil { @@ -245,7 +245,7 @@ func (a *Angine) OnRecvExchangeData(data *p2p.ExchangeData) error { func (a *Angine) buildState(genesis *types.GenesisDoc) error { stateM, err := getOrMakeState(a.conf, a.dbs["state"], genesis) if err != nil { - fmt.Println("getOrMakeState error: ", err) + log.Warn("getOrMakeState error: ", zap.Error(err)) return err } @@ -572,7 +572,7 @@ func (e *Angine) Start() error { } if _, err := (*e.eventSwitch).Start(); err != nil { - fmt.Println("fail to start event switch, error: ", err) + log.Warn("fail to start event switch, error: ", zap.Error(err)) return err } @@ -1008,7 +1008,6 @@ func (ang *Angine) InitPlugins() { if err != nil { // querydb failure is something that we can bear with log.Error("[QueryCachePlugin Init]", zap.Error(err)) - fmt.Println(err) } params.StateDB = querydb p.Init(params) diff --git a/gemmill/modules/go-autofile/group.go b/gemmill/modules/go-autofile/group.go index 2b4af9a..3133938 100644 --- a/gemmill/modules/go-autofile/group.go +++ b/gemmill/modules/go-autofile/group.go @@ -19,7 +19,6 @@ import ( "errors" "fmt" "io" - "log" "os" "path" "path/filepath" @@ -30,6 +29,8 @@ import ( "time" gcmn "github.com/dappledger/AnnChain/gemmill/modules/go-common" + "github.com/dappledger/AnnChain/gemmill/modules/go-log" + "go.uber.org/zap" ) /* @@ -216,18 +217,18 @@ func (g *Group) checkTotalSizeLimit() { } if index == gInfo.MaxIndex { // Special degenerate case, just do nothing. - log.Println("WARNING: Group's head " + g.Head.Path + "may grow without bound") + log.Warn("WARNING: Group's head " + g.Head.Path + "may grow without bound") return } pathToRemove := filePathForIndex(g.Head.Path, index, gInfo.MaxIndex) fileInfo, err := os.Stat(pathToRemove) if err != nil { - log.Println("WARNING: Failed to fetch info for file @" + pathToRemove) + log.Warn("WARNING: Failed to fetch info for file @" + pathToRemove) continue } err = os.Remove(pathToRemove) if err != nil { - log.Println(err) + log.Warn("remove file err",zap.Error(err)) return } totalSize -= fileInfo.Size() @@ -245,7 +246,7 @@ func (g *Group) RotateFile() { err := os.Rename(g.Head.Path, dstPath) if err != nil { if strings.Contains(err.Error(), "The process cannot access the file because it is being used by another process") { - log.Printf("Rename old(%s) to new(%s) error:%s\n", g.Head.Path, dstPath, err.Error()) + log.Infof("Rename old(%s) to new(%s) error:%s\n", g.Head.Path, dstPath, err.Error()) err = g.Head.closeFile() if err != nil { panic(err) @@ -255,7 +256,7 @@ func (g *Group) RotateFile() { panic(err) } } else { - log.Printf("Rename old(%s) to new(%s) OK\n", g.Head.Path, dstPath) + log.Warnf("Rename old(%s) to new(%s) OK\n", g.Head.Path, dstPath) break } } diff --git a/gemmill/modules/go-common/errors.go b/gemmill/modules/go-common/errors.go index 6e2c2af..f26623a 100644 --- a/gemmill/modules/go-common/errors.go +++ b/gemmill/modules/go-common/errors.go @@ -16,6 +16,9 @@ package common import ( "fmt" + "runtime/debug" + + "github.com/dappledger/AnnChain/gemmill/modules/go-log" ) type StackError struct { @@ -31,29 +34,44 @@ func (se StackError) Error() string { return se.String() } +func panicLog(err interface{}, stack []byte) { + se := StackError { + Err:err, + Stack:stack, + } + log.Warn(se.String()) +} //-------------------------------------------------------------------------------------------------- // panic wrappers // A panic resulting from a sanity check means there is a programmer error // and some gaurantee is not satisfied. func PanicSanity(v interface{}) { - panic(Fmt("Paniced on a Sanity Check: %v", v)) + msg:= Fmt("Paniced on a Sanity Check: %v", v) + panicLog(msg,debug.Stack()) + panic(msg) } // A panic here means something has gone horribly wrong, in the form of data corruption or // failure of the operating system. In a correct/healthy system, these should never fire. // If they do, it's indicative of a much more serious problem. func PanicCrisis(v interface{}) { - panic(Fmt("Paniced on a Crisis: %v", v)) + msg:=Fmt("Paniced on a Crisis: %v", v) + panicLog(msg,debug.Stack()) + panic(msg) } // Indicates a failure of consensus. Someone was malicious or something has // gone horribly wrong. These should really boot us into an "emergency-recover" mode func PanicConsensus(v interface{}) { - panic(Fmt("Paniced on a Consensus Failure: %v", v)) + msg := Fmt("Paniced on a Consensus Failure: %v", v) + panicLog(msg,debug.Stack()) + panic(msg) } // For those times when we're not sure if we should panic func PanicQ(v interface{}) { - panic(Fmt("Paniced questionably: %v", v)) + msg := Fmt("Paniced questionably: %v", v) + panicLog(msg,debug.Stack()) + panic(msg) } diff --git a/gemmill/modules/go-log/zap.go b/gemmill/modules/go-log/zap.go index 68e4411..687cd08 100644 --- a/gemmill/modules/go-log/zap.go +++ b/gemmill/modules/go-log/zap.go @@ -222,4 +222,4 @@ func Debugf(template string, args ...interface{}) { return } slogger.Debugf(template, args...) -} +} \ No newline at end of file diff --git a/gemmill/state/state.go b/gemmill/state/state.go index 125379d..7467c76 100644 --- a/gemmill/state/state.go +++ b/gemmill/state/state.go @@ -16,9 +16,7 @@ package state import ( "bytes" - "fmt" "io/ioutil" - "reflect" "sync" "time" @@ -74,17 +72,6 @@ type State struct { ReceiptsHash []byte } -func (s *State) CheckPubkeyPtr() { - for _, v := range s.GenesisDoc.Validators { - fmt.Println("genes:", reflect.TypeOf(v.PubKey)) - } - for _, v := range s.Validators.Validators { - fmt.Println("vldts:", reflect.TypeOf(v.PubKey)) - } - for _, v := range s.LastValidators.Validators { - fmt.Println("last vldts:", reflect.TypeOf(v.PubKey)) - } -} func LoadState(db dbm.DB) *State { return loadState(db, stateKey) From 1c3cade53b9e1dda4828f3922eafd977b9962241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E6=B4=AA=E6=9C=88?= Date: Mon, 13 Jan 2020 16:03:46 +0800 Subject: [PATCH 2/3] add getPendingNonce interface --- chain/app/evm/evm.go | 17 +++++++++++++++++ chain/app/evm/tx_pool.go | 23 +++++++++++++++++++++++ chain/app/evm/tx_sort.go | 13 +++++++++++++ chain/types/types.go | 5 +++-- gemmill/mempool/mempool.go | 4 ++++ gemmill/types/tx_pool.go | 4 +++- 6 files changed, 63 insertions(+), 3 deletions(-) diff --git a/chain/app/evm/evm.go b/chain/app/evm/evm.go index 59c9e01..73ec38e 100755 --- a/chain/app/evm/evm.go +++ b/chain/app/evm/evm.go @@ -515,6 +515,8 @@ func (app *EVMApp) Query(query []byte) (res gtypes.Result) { res = app.queryKey(load) case rtypes.QueryType_Key_Prefix: res = app.queryKeyWithPrefix(load) + case rtypes.QueryType_Pending_Nonce: + res = app.queryPendingNonce(load) default: res = gtypes.NewError(gtypes.CodeType_BaseInvalidInput, "unimplemented query") } @@ -606,6 +608,21 @@ func makeETHHeader(header *gtypes.Header) *etypes.Header { } } +func (app *EVMApp) queryPendingNonce(addrBytes []byte) gtypes.Result { + if len(addrBytes) != 20 { + return gtypes.NewError(gtypes.CodeType_BaseInvalidInput, "Invalid address") + } + nonce, err := app.GetTxPool().GetPendingMaxNonce(addrBytes) + if err != nil { + return gtypes.NewError(gtypes.CodeType_UnknownRequest, err.Error()) + } + data, err := rlp.EncodeToBytes(nonce) + if err != nil { + log.Warn("query error", zap.Error(err)) + } + return gtypes.NewResultOK(data, "") +} + func (app *EVMApp) queryNonce(addrBytes []byte) gtypes.Result { if len(addrBytes) != 20 { return gtypes.NewError(gtypes.CodeType_BaseInvalidInput, "Invalid address") diff --git a/chain/app/evm/tx_pool.go b/chain/app/evm/tx_pool.go index d30ad6d..98482ac 100644 --- a/chain/app/evm/tx_pool.go +++ b/chain/app/evm/tx_pool.go @@ -177,6 +177,29 @@ func (tp *ethTxPool) ReceiveTx(rawTx types.Tx) error { return nil } +func (tp *ethTxPool) GetPendingMaxNonce(bytAddress []byte) (uint64, error) { + tp.Lock() + defer tp.Unlock() + address := common.BytesToAddress(bytAddress) + txWSMap, ok := tp.waiting[address] + if ok && txWSMap != nil { + pendingMaxNonce := tp.unSafeGetPendingMaxNonce(address) + if pendingMaxNonce != txWSMap.MinNonce() { + return pendingMaxNonce, nil + } + return txWSMap.MaxNonce() + 1, nil + } + return tp.unSafeGetPendingMaxNonce(address), nil +} + +func (tp *ethTxPool) unSafeGetPendingMaxNonce(address common.Address) uint64 { + txSMap, ok := tp.pending[address] + if ok && txSMap != nil { + return txSMap.MaxNonce() + 1 + } + return tp.safeGetNonce(address) +} + // receive and handle adminOp txs func (tp *ethTxPool) handleAdminOP(tx types.Tx) error { tp.Lock() diff --git a/chain/app/evm/tx_sort.go b/chain/app/evm/tx_sort.go index 6e70a38..519baf9 100644 --- a/chain/app/evm/tx_sort.go +++ b/chain/app/evm/tx_sort.go @@ -128,6 +128,19 @@ func (m *txSortedMap) MaxNonce() uint64 { return maxNonce } +// return min nonce in txSortedMap, call from empty m will cause a panic. +func (m *txSortedMap) MinNonce() uint64 { + var sortedTxs types.Transactions + if m.cache != nil { + sortedTxs = m.cache + } else { + sortedTxs = m.Flatten() + } + + minNonce := (sortedTxs)[0].Nonce() + return minNonce +} + // Remove deletes a transaction from the maintained map, returning whether the // transaction was found. func (m *txSortedMap) Remove(nonce uint64) bool { diff --git a/chain/types/types.go b/chain/types/types.go index c14272e..0cbbe68 100644 --- a/chain/types/types.go +++ b/chain/types/types.go @@ -37,7 +37,7 @@ type ( KVs []*KV - QueryType = byte + QueryType = byte ) const ( @@ -53,6 +53,7 @@ const ( QueryTypeContractByHeight QueryType = 10 QueryType_Key QueryType = 11 QueryType_Key_Prefix QueryType = 12 + QueryType_Pending_Nonce QueryType = 13 ) -var KVTxType = []byte("kvTx-") \ No newline at end of file +var KVTxType = []byte("kvTx-") diff --git a/gemmill/mempool/mempool.go b/gemmill/mempool/mempool.go index 38daad4..3b6b019 100644 --- a/gemmill/mempool/mempool.go +++ b/gemmill/mempool/mempool.go @@ -64,6 +64,10 @@ func NewMempool(conf *viper.Viper) *Mempool { return mempool } +func (mem *Mempool) GetPendingMaxNonce(byt []byte) (uint64, error) { + return 0, errors.New("raft consense please use GetNonce() interface") +} + func (mem *Mempool) RegisterFilter(filter types.IFilter) { mem.txFilters = append(mem.txFilters, filter) } diff --git a/gemmill/types/tx_pool.go b/gemmill/types/tx_pool.go index e8b0623..8af8b11 100644 --- a/gemmill/types/tx_pool.go +++ b/gemmill/types/tx_pool.go @@ -15,8 +15,9 @@ package types import ( - "github.com/dappledger/AnnChain/gemmill/modules/go-clist" "sync/atomic" + + "github.com/dappledger/AnnChain/gemmill/modules/go-clist" ) type TxPool interface { @@ -29,6 +30,7 @@ type TxPool interface { TxsFrontWait() *clist.CElement Flush() RegisterFilter(filter IFilter) + GetPendingMaxNonce([]byte) (uint64, error) } // A transaction that successfully ran From c7901db1e098edac3fcf197ad8d36367dfd900dd Mon Sep 17 00:00:00 2001 From: jenson Date: Mon, 13 Jan 2020 18:43:28 +0800 Subject: [PATCH 3/3] add account and nonce to audit log --- chain/app/evm/evm.go | 38 +++++++++++++++------------ chain/core/routes.go | 32 ++++++++++++++--------- gemmill/angine.go | 6 ++--- gemmill/rpc/server/handlers.go | 43 +++++++++++++++++++++++++------ gemmill/rpc/server/http_server.go | 10 ++++++- gemmill/types/application.go | 4 ++- 6 files changed, 92 insertions(+), 41 deletions(-) diff --git a/chain/app/evm/evm.go b/chain/app/evm/evm.go index 23fa562..f7435d6 100755 --- a/chain/app/evm/evm.go +++ b/chain/app/evm/evm.go @@ -393,30 +393,34 @@ func (app *EVMApp) OnCommit(height, round int64, block *gtypes.Block) (interface }, nil } -func (app *EVMApp) CheckTx(bs []byte) error { +func (app *EVMApp) GetAddressFromTx(tx *etypes.Transaction) (from common.Address, err error) { + from, err = etypes.Sender(app.Signer, tx) + return +} + +func (app *EVMApp) CheckTx(bs []byte) (from common.Address,nonce uint64, err error) { tx := &etypes.Transaction{} - err := rlp.DecodeBytes(bs, tx) + err = rlp.DecodeBytes(bs, &tx) if err != nil { - return err + return } - from, _ := etypes.Sender(app.Signer, tx) - defer func() { - log.Audit().Info("check tx",zap.String("from",from.String()),zap.Uint64("nonce",tx.Nonce())) - }() + from, _ = etypes.Sender(app.Signer, tx) app.stateMtx.Lock() defer app.stateMtx.Unlock() // Last but not least check for nonce errors - nonce := tx.Nonce() + nonce = tx.Nonce() getNonce := app.state.GetNonce(from) if getNonce > nonce { txhash := gtypes.Tx(bs).Hash() - return fmt.Errorf("nonce(%d) different with getNonce(%d), transaction already exists %v", nonce, getNonce, hex.EncodeToString(txhash)) + err = fmt.Errorf("nonce(%d) different with getNonce(%d), transaction already exists %v", nonce, getNonce, hex.EncodeToString(txhash)) + return } // Transactor should have enough funds to cover the costs // cost == V + GP * GL if app.state.GetBalance(from).Cmp(tx.Cost()) < 0 { - return fmt.Errorf("not enough funds") + err = fmt.Errorf("not enough funds") + return } txType := common.Bytes2Hex(tx.Data()) @@ -424,18 +428,20 @@ func (app *EVMApp) CheckTx(bs []byte) error { if strings.HasPrefix(txType, common.Bytes2Hex(rtypes.KVTxType)) { txData := tx.Data()[len(rtypes.KVTxType):] kvData := &rtypes.KV{} - if err := rlp.DecodeBytes(txData, kvData); err != nil { - return fmt.Errorf("rlp decode to kv error %s", err.Error()) + if err = rlp.DecodeBytes(txData, kvData); err != nil { + err = fmt.Errorf("rlp decode to kv error %s", err.Error()) + return } if len(kvData.Key) > MaxKey || len(kvData.Value) > MaxValue { - return fmt.Errorf("key or value too big,MaxKey:%v,MaxValue:%v", MaxKey, MaxValue) + err = fmt.Errorf("key or value too big,MaxKey:%v,MaxValue:%v", MaxKey, MaxValue) + return } if ok, _ := app.stateDb.Has(append(KvPrefix, kvData.Key...)); ok { - return fmt.Errorf("duplicate key :%v", kvData.Key) + err = fmt.Errorf("duplicate key :%v", kvData.Key) + return } } - - return nil + return } func (app *EVMApp) SaveReceipts() ([]byte, error) { diff --git a/chain/core/routes.go b/chain/core/routes.go index d622397..531606a 100644 --- a/chain/core/routes.go +++ b/chain/core/routes.go @@ -210,28 +210,36 @@ func (h *rpcHandler) UnsafeFlushMempool() (*gtypes.ResultUnsafeFlushMempool, err return >ypes.ResultUnsafeFlushMempool{}, nil } -func (h *rpcHandler) BroadcastTx(tx []byte) (*gtypes.ResultBroadcastTx, error) { - if err := h.node.Application.CheckTx(tx); err != nil { - return nil, err +func (h *rpcHandler) BroadcastTx(tx []byte) (result *gtypes.ResultBroadcastTx, logFields map[string]string, err error) { + logFields = make(map[string]string) + from, nonce, err := h.node.Application.CheckTx(tx) + logFields["account"] = from.String() + logFields["nonce"] = fmt.Sprintf("%d", nonce) + if err != nil { + return } - if err := h.node.Angine.BroadcastTx(tx); err != nil { - return nil, err + if err = h.node.Angine.BroadcastTx(tx); err != nil { + return } hash := gtypes.Tx(tx).Hash() - return >ypes.ResultBroadcastTx{TxHash: hexutil.Encode(hash), Code: 0}, nil + return >ypes.ResultBroadcastTx{TxHash: hexutil.Encode(hash), Code: 0}, logFields, nil } -func (h *rpcHandler) BroadcastTxCommit(tx []byte) (*gtypes.ResultBroadcastTxCommit, error) { - if err := h.node.Application.CheckTx(tx); err != nil { - return nil, err +func (h *rpcHandler) BroadcastTxCommit(tx []byte) (result *gtypes.ResultBroadcastTxCommit, logFields map[string]string, err error) { + logFields = make(map[string]string) + from, nonce, err := h.node.Application.CheckTx(tx) + logFields["account"] = from.String() + logFields["nonce"] = fmt.Sprintf("%d", nonce) + if err != nil { + return } - if err := h.node.Angine.BroadcastTxCommit(tx); err != nil { - return nil, err + if err = h.node.Angine.BroadcastTxCommit(tx); err != nil { + return } hash := gtypes.Tx(tx).Hash() - return >ypes.ResultBroadcastTxCommit{TxHash: hexutil.Encode(hash), Code: 0}, nil + return >ypes.ResultBroadcastTxCommit{TxHash: hexutil.Encode(hash), Code: 0}, logFields, nil } func (h *rpcHandler) QueryTx(query []byte) (*gtypes.ResultNumLimitTx, error) { diff --git a/gemmill/angine.go b/gemmill/angine.go index 1b523d4..48562df 100644 --- a/gemmill/angine.go +++ b/gemmill/angine.go @@ -222,14 +222,14 @@ func (a *Angine) OnRecvExchangeData(data *p2p.ExchangeData) error { // TODO wait ... return errors.New("no genesis file found in other node") } - othGenesis, err := types.GenesisDocFromJSONRet(data.GenesisJSON) + otherGenesis, err := types.GenesisDocFromJSONRet(data.GenesisJSON) if err != nil { // TODO log err - log.Warn("oth genesis err:", zap.Error(err)) + log.Warn("other genesis err:", zap.Error(err)) return err } a.p2pSwitch.GetExchangeData().GenesisJSON = data.GenesisJSON - if err = a.buildState(othGenesis); err != nil { + if err = a.buildState(otherGenesis); err != nil { // TODO log err log.Warn("build state err:", zap.Error(err)) return err diff --git a/gemmill/rpc/server/handlers.go b/gemmill/rpc/server/handlers.go index 7983670..f6928f9 100644 --- a/gemmill/rpc/server/handlers.go +++ b/gemmill/rpc/server/handlers.go @@ -49,6 +49,11 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) { mux.HandleFunc("/", makeJSONRPCHandler(funcMap)) } +const ( + MinReturnParamNum = iota + 2 + MaxReturnParamNum = MinReturnParamNum + 1 +) + //------------------------------------- // function introspection @@ -89,6 +94,10 @@ func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc { func funcArgTypes(f interface{}) []reflect.Type { t := reflect.TypeOf(f) n := t.NumIn() + numOut:= t.NumOut() + if numOut > MaxReturnParamNum || numOut < MinReturnParamNum { + gcmn.PanicSanity(fmt.Sprintf("rpc functions param len is not in range %d ", numOut)) + } typez := make([]reflect.Type, n) for i := 0; i < n; i++ { typez[i] = t.In(i) @@ -153,7 +162,10 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { returns := rpcFunc.f.Call(args) // log.Debugw("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) - result, err := unreflectResult(returns) + result, logFields, err := unreflectResult(returns) + if rww, ok := w.(*ResponseWriterWrapper); ok { + rww.SetLogFields(logFields) + } if err != nil { WriteRPCResponseHTTP(w, gtypes.NewRPCResponse(request.ID, result, fmt.Sprintf("Error unreflecting result: %v", err.Error()))) return @@ -232,7 +244,10 @@ func makeHTTPHandler(rpcFunc *RPCFunc) func(http.ResponseWriter, *http.Request) } returns := rpcFunc.f.Call(args) log.Debugw("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) - result, err := unreflectResult(returns) + result, logFields, err := unreflectResult(returns) + if rww, ok := w.(*ResponseWriterWrapper); ok { + rww.SetLogFields(logFields) + } if err != nil { WriteRPCResponseHTTP(w, gtypes.NewRPCResponse("", nil, fmt.Sprintf("Error unreflecting result: %v", err.Error()))) return @@ -493,7 +508,7 @@ func (wsc *wsConnection) readRoutine() { } returns := rpcFunc.f.Call(args) log.Infow("WSJSONRPC", "method", request.Method, "args", args, "returns", returns) - result, err := unreflectResult(returns) + result, _, err := unreflectResult(returns) if err != nil { wsc.WriteRPCResponse(gtypes.NewRPCResponse(request.ID, nil, err.Error())) continue @@ -580,18 +595,30 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ // rpc.websocket //----------------------------------------------------------------------------- -// NOTE: assume returns is result struct and error. If error is not nil, return it -func unreflectResult(returns []reflect.Value) (interface{}, error) { - errV := returns[1] +// NOTE: assume returns is result struct and error or result struct and logfields and error. If error is not nil, return it +func unreflectResult(returns []reflect.Value) (result interface{}, logFields map[string]string, err error) { + if len(returns) > MaxReturnParamNum || len(returns) < MinReturnParamNum { + gcmn.PanicSanity(fmt.Sprintf("returned params num is tot the range %v ", len(returns))) + } + if len(returns) == MaxReturnParamNum { + i := returns[MaxReturnParamNum-2].Interface() + if i != nil { + logFields, _ = i.(map[string]string) + } + } + + //errV := returns[1] + errV := returns[len(returns)-1] if errV.Interface() != nil { - return nil, fmt.Errorf("%v", errV.Interface()) + err = fmt.Errorf("%v", errV.Interface()) } rv := returns[0] // the result is a registered interface, // we need a pointer to it so we can marshal with type byte rvp := reflect.New(rv.Type()) rvp.Elem().Set(rv) - return rvp.Interface(), nil + result = rvp.Interface() + return } // writes a list of available rpc endpoints as an html page diff --git a/gemmill/rpc/server/http_server.go b/gemmill/rpc/server/http_server.go index 85f8fa2..d1fa825 100644 --- a/gemmill/rpc/server/http_server.go +++ b/gemmill/rpc/server/http_server.go @@ -83,7 +83,7 @@ func WriteRPCResponseHTTP(w http.ResponseWriter, res gtypes.RPCResponse) { if res.Error != "" { rww.recordErr(errors.New(res.Error)) } else { - if res.Result!=nil { + if res.Result != nil { rww.recordResponse(*res.Result) } } @@ -175,6 +175,9 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler { if len(rww.responseContent) > 0 { fields = append(fields, zap.ByteString("response_content", rww.responseContent)) } + for k,v:= range rww.logFields { + fields = append(fields,zap.String(k,v)) + } log.Audit().Info("rpc got response ", fields...) rww.Flush() }) @@ -189,6 +192,7 @@ type ResponseWriterWrapper struct { err error requestContent []byte responseContent []byte + logFields map[string]string } func (w *ResponseWriterWrapper) WriteHeader(status int) { @@ -241,3 +245,7 @@ func (w *ResponseWriterWrapper) recordRequest(data []byte) { } w.requestContent = data } + +func (w *ResponseWriterWrapper) SetLogFields(fields map[string]string) { + w.logFields = fields +} diff --git a/gemmill/types/application.go b/gemmill/types/application.go index 37c362e..a379d1e 100644 --- a/gemmill/types/application.go +++ b/gemmill/types/application.go @@ -17,6 +17,8 @@ package types import ( "bytes" "errors" + + "github.com/dappledger/AnnChain/eth/common" "github.com/dappledger/AnnChain/gemmill/go-wire" gcmn "github.com/dappledger/AnnChain/gemmill/modules/go-common" "github.com/dappledger/AnnChain/gemmill/modules/go-db" @@ -32,7 +34,7 @@ type TxPoolApplication interface { type Application interface { GetAngineHooks() Hooks CompatibleWithAngine() - CheckTx([]byte) error + CheckTx(bs []byte) (from common.Address,nonce uint64, err error) Query([]byte) Result Info() ResultInfo Start() error