diff --git a/go.mod b/go.mod index 4d5541eb..94ccc7ce 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,12 @@ module github.com/xuperchain/xupercore -go 1.14 +go 1.16 require ( - github.com/ChainSafe/go-schnorrkel v0.0.0-20200626160457-b38283118816 // indirect + github.com/agiledragon/gomonkey/v2 v2.9.0 github.com/aws/aws-sdk-go v1.32.4 github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d github.com/dgraph-io/badger/v3 v3.2103.1 - github.com/docker/go-connections v0.4.1-0.20180821093606-97c2040d34df // indirect github.com/docker/go-units v0.4.0 github.com/emirpasic/gods v1.12.1-0.20201118132343-79df803e554c github.com/fsouza/go-dockerclient v1.6.0 @@ -15,7 +14,6 @@ require ( github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.4.3 github.com/golang/snappy v0.0.3 - github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/hashicorp/golang-lru v0.5.4 github.com/hyperledger/burrow v0.30.5 @@ -45,4 +43,10 @@ require ( google.golang.org/grpc v1.35.0 ) +require ( + github.com/ChainSafe/go-schnorrkel v0.0.0-20200626160457-b38283118816 // indirect + github.com/docker/go-connections v0.4.1-0.20180821093606-97c2040d34df // indirect + github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa // indirect +) + replace github.com/hyperledger/burrow => github.com/xuperchain/burrow v0.30.6-0.20211229032028-fbee6a05ab0f diff --git a/go.sum b/go.sum index 614e9a17..7f737610 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/agiledragon/gomonkey/v2 v2.9.0 h1:PDiKKybR596O6FHW+RVSG0Z7uGCBNbmbUXh3uCNQ7Hc= +github.com/agiledragon/gomonkey/v2 v2.9.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= github.com/alecthomas/jsonschema v0.0.0-20190122210438-a6952de1bbe6/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= diff --git a/kernel/contract/context.go b/kernel/contract/context.go index 87aa864e..aea8b687 100644 --- a/kernel/contract/context.go +++ b/kernel/contract/context.go @@ -26,6 +26,11 @@ type Response struct { Body []byte `json:"body"` } +// HasError checked by status +func (r *Response) HasError() bool { + return r.Status >= StatusErrorThreshold +} + // ContextConfig define the config of context type ContextConfig struct { State StateSandbox diff --git a/kernel/contract/context_test.go b/kernel/contract/context_test.go new file mode 100644 index 00000000..1a5efb3e --- /dev/null +++ b/kernel/contract/context_test.go @@ -0,0 +1,50 @@ +package contract + +import "testing" + +func TestResponse_HasError(t *testing.T) { + type fields struct { + Status int + Message string + Body []byte + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "no error", + fields: fields{ + Status: StatusOK, + }, + want: false, + }, + { + name: "threshold error", + fields: fields{ + Status: StatusErrorThreshold, + }, + want: true, + }, + { + name: "normal error", + fields: fields{ + Status: StatusError, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Response{ + Status: tt.fields.Status, + Message: tt.fields.Message, + Body: tt.fields.Body, + } + if got := r.HasError(); got != tt.want { + t.Errorf("Response.HasError() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/kernel/engines/xuperos/asyncworker/asyncworker_test.go b/kernel/engines/xuperos/asyncworker/asyncworker_test.go index cdf17024..185c541f 100644 --- a/kernel/engines/xuperos/asyncworker/asyncworker_test.go +++ b/kernel/engines/xuperos/asyncworker/asyncworker_test.go @@ -2,7 +2,6 @@ package asyncworker import ( "encoding/json" - "io/ioutil" "os" "path/filepath" "testing" @@ -222,7 +221,7 @@ type TestHelper struct { } func NewTestHelper() (*TestHelper, error) { - basedir, err := ioutil.TempDir("", "asyncworker-test") + basedir, err := os.MkdirTemp("", "asyncworker-test") if err != nil { return nil, err } diff --git a/kernel/engines/xuperos/chain.go b/kernel/engines/xuperos/chain.go index 85cc59d0..712bc6fb 100644 --- a/kernel/engines/xuperos/chain.go +++ b/kernel/engines/xuperos/chain.go @@ -115,24 +115,14 @@ func (t *Chain) Context() *common.ChainCtx { } // 交易预执行 -func (t *Chain) PreExec(ctx xctx.XContext, reqs []*protos.InvokeRequest, initiator string, authRequires []string) (*protos.InvokeResponse, error) { - if ctx == nil || ctx.GetLog() == nil { - return nil, common.ErrParameter - } - - reservedRequests, err := t.ctx.State.GetReservedContractRequests(reqs, true) - if err != nil { - t.log.Error("PreExec get reserved contract request error", "error", err) - return nil, common.ErrParameter.More("%v", err) - } +func (t *Chain) PreExec(ctx xctx.XContext, reqs []*protos.InvokeRequest, initiator string, + authRequires []string) (*protos.InvokeResponse, error) { - transContractName, transAmount, err := tx.ParseContractTransferRequest(reqs) + reqCtx, err := t.reqContext(ctx, reqs) if err != nil { - return nil, common.ErrParameter.More("%v", err) + return nil, err } - - reqs = append(reservedRequests, reqs...) - if len(reqs) <= 0 { + if len(reqCtx.requests) <= 0 { return &protos.InvokeResponse{}, nil } @@ -146,7 +136,7 @@ func (t *Chain) PreExec(ctx xctx.XContext, reqs []*protos.InvokeRequest, initiat return nil, common.ErrContractNewSandboxFailed } - contextConfig := &contract.ContextConfig{ + contractCtx := &contract.ContextConfig{ State: sandbox, Initiator: initiator, AuthRequire: authRequires, @@ -154,18 +144,46 @@ func (t *Chain) PreExec(ctx xctx.XContext, reqs []*protos.InvokeRequest, initiat ChainName: t.ctx.BCName, } - gasPrice := t.ctx.State.GetMeta().GetGasPrice() - gasUsed := int64(0) - responseBodes := make([][]byte, 0, len(reqs)) - requests := make([]*protos.InvokeRequest, 0, len(reqs)) - responses := make([]*protos.ContractResponse, 0, len(reqs)) - for i, req := range reqs { + invokeResp, err := t.preExecWithReservedReqs(reqCtx, contractCtx) + if err != nil { + return nil, err + } + + err = sandbox.Flush() + if err != nil { + return nil, err + } + rwSet := sandbox.RWSet() + utxoRWSet := sandbox.UTXORWSet() + + invokeResp.Inputs = xmodel.GetTxInputs(rwSet.RSet) + invokeResp.Outputs = xmodel.GetTxOutputs(rwSet.WSet) + invokeResp.UtxoInputs = utxoRWSet.Rset + invokeResp.UtxoOutputs = utxoRWSet.WSet + + return invokeResp, nil +} + +func (t *Chain) preExecWithReservedReqs(reqCtx *reqContext, + contractCtx *contract.ContextConfig) (*protos.InvokeResponse, error) { + + var ( + log = reqCtx.logger + reqCnt = len(reqCtx.requests) + invokeResp = &protos.InvokeResponse{ + Response: make([][]byte, 0, reqCnt), + Requests: make([]*protos.InvokeRequest, 0, reqCnt), + Responses: make([]*protos.ContractResponse, 0, reqCnt), + } + ) + + for idx, req := range reqCtx.requests { if req == nil { continue } if req.ModuleName == "" && req.ContractName == "" && req.MethodName == "" { - ctx.GetLog().Warn("PreExec req empty", "req", req) + log.Warn("PreExec req empty", "req", req) continue } if req.ModuleName == "" { @@ -175,91 +193,83 @@ func (t *Chain) PreExec(ctx xctx.XContext, reqs []*protos.InvokeRequest, initiat if err != nil { return nil, err } - contextConfig.Module = desc.GetContractType() + contractCtx.Module = desc.GetContractType() } else { - contextConfig.Module = req.ModuleName + contractCtx.Module = req.ModuleName } - beginTime := time.Now() + contractCtx.ContractName = req.ContractName + contractCtx.TransferAmount = req.Amount + isReservedReq := reqCtx.IsReservedReq(idx) - contextConfig.ContractName = req.ContractName - if transContractName == req.ContractName { - contextConfig.TransferAmount = transAmount.String() - } else { - contextConfig.TransferAmount = "" - } - - context, err := t.ctx.Contract.NewContext(contextConfig) + err := t.preExecOnce(contractCtx, req, isReservedReq, invokeResp, log) if err != nil { - ctx.GetLog().Error("PreExec NewContext error", "error", err, "contractName", req.ContractName) - if i < len(reservedRequests) && strings.HasSuffix(err.Error(), "not found") { - requests = append(requests, req) - continue - } - return nil, common.ErrContractNewCtxFailed.More("%v", err) - } - - resp, err := context.Invoke(req.MethodName, req.Args) - if err != nil { - // TODO: deal with error - _ = context.Release() - ctx.GetLog().Error("PreExec Invoke error", "error", err, "contractName", req.ContractName) - metrics.ContractInvokeCounter.WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "InvokeError").Inc() - return nil, common.ErrContractInvokeFailed.More("%v", err) - } - - if resp.Status >= 400 && i < len(reservedRequests) { - // TODO: deal with error - _ = context.Release() - ctx.GetLog().Error("PreExec Invoke error", "status", resp.Status, "contractName", req.ContractName) - metrics.ContractInvokeCounter.WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "InvokeError").Inc() - return nil, common.ErrContractInvokeFailed.More("%v", resp.Message) + return nil, err } + } + return invokeResp, nil +} - metrics.ContractInvokeCounter.WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "OK").Inc() - resourceUsed := context.ResourceUsed() - if i >= len(reservedRequests) { - gasUsed += resourceUsed.TotalGas(gasPrice) - } +// preExecOnce preExec one request with metrics +func (t *Chain) preExecOnce(contractCtx *contract.ContextConfig, req *protos.InvokeRequest, isReservedReq bool, invokeResp *protos.InvokeResponse, log logs.Logger) error { - // request - request := *req - request.ResourceLimits = contract.ToPbLimits(resourceUsed) - requests = append(requests, &request) + gasPrice := t.ctx.State.GetMeta().GetGasPrice() + beginTime := time.Now() - // response - response := &protos.ContractResponse{ - Status: int32(resp.Status), - Message: resp.Message, - Body: resp.Body, + context, err := t.ctx.Contract.NewContext(contractCtx) + if err != nil { + log.Error("PreExec NewContext error", "error", err, "contractName", req.ContractName) + if isReservedReq && strings.HasSuffix(err.Error(), "not found") { + invokeResp.Requests = append(invokeResp.Requests, req) + return nil } - responses = append(responses, response) - responseBodes = append(responseBodes, resp.Body) - - // TODO: deal with error - _ = context.Release() - metrics.ContractInvokeHistogram.WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName).Observe(time.Since(beginTime).Seconds()) + return common.ErrContractNewCtxFailed.More("%v", err) } + defer func() { _ = context.Release() }() // TODO: deal with error - err = sandbox.Flush() + resp, err := context.Invoke(req.MethodName, req.Args) if err != nil { - return nil, err - } - rwSet := sandbox.RWSet() - utxoRWSet := sandbox.UTXORWSet() - - invokeResponse := &protos.InvokeResponse{ - GasUsed: gasUsed, - Response: responseBodes, - Inputs: xmodel.GetTxInputs(rwSet.RSet), - Outputs: xmodel.GetTxOutputs(rwSet.WSet), - Requests: requests, - Responses: responses, - UtxoInputs: utxoRWSet.Rset, - UtxoOutputs: utxoRWSet.WSet, - } - - return invokeResponse, nil + log.Error("PreExec Invoke error", "error", err, "contractName", req.ContractName) + metrics.ContractInvokeCounter. + WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "InvokeError"). + Inc() + return common.ErrContractInvokeFailed.More("%v", err) + } + + if resp.HasError() && isReservedReq { + log.Error("PreExec Invoke error", "status", resp.Status, "contractName", req.ContractName) + metrics.ContractInvokeCounter. + WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "InvokeError"). + Inc() + return common.ErrContractInvokeFailed.More("%v", resp.Message) + } + + metrics.ContractInvokeCounter. + WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName, "OK"). + Inc() + resourceUsed := context.ResourceUsed() + if !isReservedReq { + invokeResp.GasUsed += resourceUsed.TotalGas(gasPrice) + } + + // request + request := *req + request.ResourceLimits = contract.ToPbLimits(resourceUsed) + invokeResp.Requests = append(invokeResp.Requests, &request) + + // response + response := &protos.ContractResponse{ + Status: int32(resp.Status), + Message: resp.Message, + Body: resp.Body, + } + invokeResp.Responses = append(invokeResp.Responses, response) + invokeResp.Response = append(invokeResp.Response, resp.Body) + + metrics.ContractInvokeHistogram. + WithLabelValues(t.ctx.BCName, req.ModuleName, req.ContractName, req.MethodName). + Observe(time.Since(beginTime).Seconds()) + return nil } // 提交交易到交易池(xuperos引擎同时更新到状态机和交易池) @@ -474,3 +484,47 @@ func (t *Chain) CreateParaChain() error { } return nil } + +// reqContext is context for PreExec requests +type reqContext struct { + + // requests contains [reservedReqs..., preExecReqs...] + requests []*protos.InvokeRequest + reservedReqCnt int + + logger logs.Logger +} + +// IsReservedReq judges reserved request by index +func (c *reqContext) IsReservedReq(index int) bool { + return index < c.reservedReqCnt +} + +// reqContext creates context with chain reserved requests and PreExec requests +func (t *Chain) reqContext(ctx xctx.XContext, reqs []*protos.InvokeRequest) (*reqContext, error) { + reqCtx := new(reqContext) + var err error + + // check logger + if ctx == nil || ctx.GetLog() == nil { + return nil, common.ErrParameter + } + reqCtx.logger = ctx.GetLog() + + // check transfer info from PreExec requests + _, _, err = tx.ParseContractTransferRequest(reqs) + if err != nil { + return nil, common.ErrParameter.More("%v", err) + } + + // pack reserved requests and PreExec requests + reservedRequests, err := t.ctx.State.GetReservedContractRequests(reqs, true) + if err != nil { + t.log.Error("PreExec get reserved contract request error", "error", err) + return nil, common.ErrParameter.More("%v", err) + } + reqCtx.reservedReqCnt = len(reservedRequests) + reqCtx.requests = append(reservedRequests, reqs...) + + return reqCtx, nil +} diff --git a/kernel/engines/xuperos/chain_test.go b/kernel/engines/xuperos/chain_test.go index e44a6df8..dd65e99a 100644 --- a/kernel/engines/xuperos/chain_test.go +++ b/kernel/engines/xuperos/chain_test.go @@ -4,13 +4,28 @@ import ( "encoding/hex" "fmt" "math/big" + "math/rand" + "os" + "os/exec" + "path/filepath" + "reflect" + "runtime" + "strconv" "testing" "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/patrickmn/go-cache" + + "github.com/xuperchain/xupercore/bcs/ledger/xledger/state" "github.com/xuperchain/xupercore/bcs/ledger/xledger/state/utxo/txhash" lpb "github.com/xuperchain/xupercore/bcs/ledger/xledger/xldgpb" "github.com/xuperchain/xupercore/kernel/common/xaddress" + xctx "github.com/xuperchain/xupercore/kernel/common/xcontext" "github.com/xuperchain/xupercore/kernel/engines/xuperos/common" + "github.com/xuperchain/xupercore/kernel/engines/xuperos/miner" + "github.com/xuperchain/xupercore/lib/logs" + "github.com/xuperchain/xupercore/lib/timer" "github.com/xuperchain/xupercore/lib/utils" "github.com/xuperchain/xupercore/protos" ) @@ -89,31 +104,30 @@ func mockTransferTx(chain common.Chain) (*lpb.Transaction, error) { return tx, nil } -func TestChain_SubmitTx(t *testing.T) { +func TestChain_SubmitTx_case_transfer(t *testing.T) { + patch := setup(t) + defer patch.Reset() + engine, err := MockEngine("p2pv2/node1/conf/env.yaml") if err != nil { - t.Logf("%v", err) - return + t.Fatalf("%v", err) } - // go engine.Run() - // defer engine.Exit() + go engine.Run() + defer engine.Exit() chain, err := engine.Get("xuper") if err != nil { - t.Errorf("get chain error: %v", err) - return + t.Fatalf("get chain error: %v", err) } tx, err := mockTransferTx(chain) if err != nil { - t.Errorf("mock tx error: %v", err) - return + t.Fatalf("mock tx error: %v", err) } err = chain.SubmitTx(chain.Context(), tx) if err != nil { - t.Errorf("submit tx error: %v", err) - return + t.Fatalf("submit tx error: %v", err) } } @@ -196,30 +210,381 @@ func mockContractTx(chain common.Chain) (*lpb.Transaction, error) { return tx, nil } -func TestChain_PreExec(t *testing.T) { +func TestChain_SubmitTx_case_contract(t *testing.T) { + patch := setup(t) + defer patch.Reset() + engine, err := MockEngine("p2pv2/node1/conf/env.yaml") if err != nil { - t.Logf("%v", err) - return + t.Fatalf("%v", err) } - // go engine.Run() - // defer engine.Exit() + go engine.Run() + defer engine.Exit() chain, err := engine.Get("xuper") if err != nil { - t.Errorf("get chain error: %v", err) + t.Fatalf("get chain error: %v", err) return } tx, err := mockContractTx(chain) if err != nil { - t.Errorf("mock tx error: %v", err) - return + t.Fatalf("mock tx error: %v", err) } err = chain.SubmitTx(chain.Context(), tx) if err != nil { - t.Errorf("submit tx error: %v", err) + t.Fatalf("submit tx error: %v", err) + } +} + +func setup(t *testing.T) *gomonkey.Patches { + if runtime.GOOS == "darwin" { + t.Skip() + } + mockLookPath := func(arg string) (string, error) { + if arg == "wasm2c" { + wasm2cPath := filepath.Join(filepath.Dir(os.Args[0]), "wasm2c") + fmt.Println(filepath.Dir(os.Args[0])) + return filepath.Abs(wasm2cPath) + } + return exec.LookPath(arg) + } + patch := gomonkey. + ApplyFunc(exec.LookPath, mockLookPath). + ApplyMethod(reflect.TypeOf(new(state.State)), + "GetReservedContractRequests", + mockGetReservedContractRequests) + return patch +} + +func TestChain_PreExec(t *testing.T) { + patch := setup(t) + defer patch.Reset() + + engine, err := MockEngine("p2pv2/node1/conf/env.yaml") + if err != nil { + t.Fatalf("%v", err) + } + go engine.Run() + defer engine.Exit() + + chain, err := engine.Get("xuper") + if err != nil { + t.Fatalf("get chain error: %v", err) return } + + type fields struct { + log logs.Logger + miner *miner.Miner + relyAgent common.ChainRelyAgent + txIdCache *cache.Cache + } + type args struct { + ctx xctx.XContext + reqs []*protos.InvokeRequest + initiator string + authRequires []string + } + tests := []struct { + name string + fields fields + args args + // we don't care much about the specific content returned, so simply judge gasUsed + want *protos.InvokeResponse + wantErr bool + }{ + { + name: "req without reserved contract", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("kernel")}, + }, + want: &protos.InvokeResponse{GasUsed: 1000}, + }, + { + name: "req with abnormal reserved contract", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("req with abnormal reserved contract")}, + }, + wantErr: true, + }, + { + name: "req with normal reserved contract", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("req with normal reserved contract")}, + }, + want: &protos.InvokeResponse{GasUsed: 1000}, + }, + { + name: "abnormal req with normal reserved contract", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("abnormal req with normal reserved contract")}, + }, + wantErr: true, + }, + { + name: "contract method with invalid args", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("invalid args")}, + }, + wantErr: true, + }, + { + name: "method not exist", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("method not exist")}, + }, + wantErr: true, + }, + { + name: "contract not exist", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + reqs: []*protos.InvokeRequest{mockReq("contract not exist")}, + }, + wantErr: true, + }, + { + name: "no request", + args: args{ + ctx: &mockXContext{logger: new(mockLogger)}, + }, + want: &protos.InvokeResponse{}, + }, + { + name: "no logger", + args: args{ + ctx: &mockXContext{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &Chain{ + ctx: chain.Context(), + log: tt.fields.log, + miner: tt.fields.miner, + relyAgent: tt.fields.relyAgent, + txIdCache: tt.fields.txIdCache, + } + got, err := tr.PreExec(tt.args.ctx, tt.args.reqs, tt.args.initiator, tt.args.authRequires) + if (err != nil) != tt.wantErr { + t.Errorf("Chain.PreExec() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(got == tt.want || got.GasUsed == tt.want.GasUsed) { + t.Errorf("Chain.PreExec() = %v\n"+ + "\twant %v", + got, tt.want) + } + }) + } +} + +type mockXContext struct { + logger logs.Logger +} + +func (m mockXContext) Deadline() (deadline time.Time, ok bool) { + panic("implement me") +} + +func (m mockXContext) Done() <-chan struct{} { + panic("implement me") +} + +func (m mockXContext) Err() error { + panic("implement me") +} + +func (m mockXContext) Value(_ interface{}) interface{} { + panic("implement me") +} + +func (m mockXContext) GetLog() logs.Logger { + return m.logger +} + +func (m mockXContext) GetTimer() *timer.XTimer { + panic("implement me") +} + +type mockLogger struct { +} + +func (m mockLogger) GetLogId() string { + panic("implement me") +} + +func (m mockLogger) SetCommField(key string, value interface{}) { + panic("implement me") +} + +func (m mockLogger) SetInfoField(key string, value interface{}) { + panic("implement me") +} + +func (m mockLogger) Error(msg string, ctx ...interface{}) { + fmt.Println(msg, ctx) +} + +func (m mockLogger) Warn(msg string, ctx ...interface{}) { + panic("implement me") +} + +func (m mockLogger) Info(msg string, ctx ...interface{}) { + panic("implement me") +} + +func (m mockLogger) Trace(msg string, ctx ...interface{}) { + panic("implement me") +} + +func (m mockLogger) Debug(msg string, ctx ...interface{}) { + panic("implement me") +} + +func mockReq(req string) *protos.InvokeRequest { + switch req { + case "contract not exist": + return &protos.InvokeRequest{ + ContractName: "notExist", + } + case "method not exist": + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "notExist", + } + case "invalid args": + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "NewAccount", + Args: nil, + } + case "reserved contract": + return &protos.InvokeRequest{ + ModuleName: "wasm", + ContractName: "unified_check", + MethodName: "verify", + Args: map[string][]byte{ + "contract": []byte("$acl"), + }, + } + case "req with abnormal reserved contract": + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "NewAccount", + Args: mockKernelContractArgs("invalid args"), + } + case "req with normal reserved contract": + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "NewAccount", + Args: mockKernelContractArgs("normal contract"), + } + case "abnormal req with normal reserved contract": + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "notExist", // method not exist + Args: mockKernelContractArgs("normal contract"), + } + default: + // normal contract + return &protos.InvokeRequest{ + ModuleName: "xkernel", + ContractName: "$acl", + MethodName: "NewAccount", + Args: mockKernelContractArgs(""), + } + } +} + +func mockKernelContractArgs(reservedContractName string) map[string][]byte { + accountNumber := int64(rand.Uint32()) + 1234560000000000 + args := map[string][]byte{ + "account_name": []byte(strconv.FormatInt(accountNumber, 10)), + "acl": []byte(`{"pm": {"rule": 1,"acceptValue": 1.0},"aksWeight": {"TeyyPLpp9L7QAcxHangtcHTu7HUZ6iydY": 1}}`), + } + if reservedContractName != "" { + args[mockKeyReserved] = []byte(reservedContractName) + } + return args +} + +func Test_reqContext_IsReservedReq(t *testing.T) { + type args struct { + index int + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "reversed req", + args: args{ + index: 0, + }, + want: true, + }, + { + name: "not reversed req", + args: args{ + index: 1, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := mockReqCtx() + if got := c.IsReservedReq(tt.args.index); got != tt.want { + t.Errorf("reqContext.IsReservedReq() = %v, want %v", got, tt.want) + } + }) + } +} + +func mockReqCtx() *reqContext { + return &reqContext{ + requests: []*protos.InvokeRequest{ + { + ContractName: "reversed", + }, + { + ContractName: "transfer", + }, + }, + reservedReqCnt: 1, + } +} + +// mockKeyReserved key for test case arguments to implement mockGetReservedContractRequests +// which denote reserved contract test data to be used in its test case +const mockKeyReserved = "reserved" + +func mockGetReservedContractRequests(_ *state.State, req []*protos.InvokeRequest, + _ bool) ([]*protos.InvokeRequest, error) { + + if len(req) > 0 && req[0].Args != nil { + mockReservedContractName, ok := req[0].Args[mockKeyReserved] + if ok { + mockReq := mockReq(string(mockReservedContractName)) + return []*protos.InvokeRequest{mockReq}, nil + } + } + return nil, nil } diff --git a/kernel/engines/xuperos/engine.go b/kernel/engines/xuperos/engine.go index 63ef9b2b..b7d16b3b 100644 --- a/kernel/engines/xuperos/engine.go +++ b/kernel/engines/xuperos/engine.go @@ -2,7 +2,7 @@ package xuperos import ( "fmt" - "io/ioutil" + "os" "path/filepath" "sync" @@ -168,7 +168,7 @@ func (t *Engine) loadChains() error { dataDir := envCfg.GenDataAbsPath(envCfg.ChainDir) t.log.Trace("start load chain from blockchain data dir", "dir", dataDir) - dir, err := ioutil.ReadDir(dataDir) + dir, err := os.ReadDir(dataDir) if err != nil { t.log.Error("read blockchain data dir failed", "error", err, "dir", dataDir) return fmt.Errorf("read blockchain data dir failed") diff --git a/kernel/evm/eth_bench_test.go b/kernel/evm/eth_bench_test.go index 5cd15a9b..58e5e791 100644 --- a/kernel/evm/eth_bench_test.go +++ b/kernel/evm/eth_bench_test.go @@ -1,18 +1,18 @@ package evm import ( + "encoding/hex" + "os" + "testing" + _ "github.com/xuperchain/xupercore/bcs/contract/evm" _ "github.com/xuperchain/xupercore/bcs/contract/native" _ "github.com/xuperchain/xupercore/bcs/contract/xvm" - - "encoding/hex" "github.com/xuperchain/xupercore/kernel/contract" _ "github.com/xuperchain/xupercore/kernel/contract" _ "github.com/xuperchain/xupercore/kernel/contract/kernel" _ "github.com/xuperchain/xupercore/kernel/contract/manager" "github.com/xuperchain/xupercore/kernel/contract/mock" - "io/ioutil" - "testing" ) func BenchmarkEVM(b *testing.B) { @@ -35,12 +35,12 @@ func BenchmarkEVM(b *testing.B) { th := mock.NewTestHelper(contractConfig) defer th.Close() - bin, err := ioutil.ReadFile("testdata/counter.bin") + bin, err := os.ReadFile("testdata/counter.bin") if err != nil { b.Error(err) return } - abi, err := ioutil.ReadFile("testdata/counter.abi") + abi, err := os.ReadFile("testdata/counter.abi") if err != nil { b.Error(err) return