From 5dca765bd169376fe06ed7730b4f84b3372dd6f8 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 19:06:34 +0300 Subject: [PATCH 1/8] rpc: add custom error with code -100 Fixed getassetstate method: it should return error with code -100 when asset with specified id is not found (as in c# node). --- pkg/rpc/errors.go | 6 ++++++ pkg/rpc/server.go | 2 +- pkg/rpc/server_test.go | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/rpc/errors.go b/pkg/rpc/errors.go index f7c3441d1..966f5f678 100644 --- a/pkg/rpc/errors.go +++ b/pkg/rpc/errors.go @@ -62,6 +62,12 @@ func NewInternalServerError(data string, cause error) *Error { return newError(-32603, http.StatusInternalServerError, "Internal error", data, cause) } +// NewRPCError creates a new error with +// code -100 +func NewRPCError(message string, data string, cause error) *Error { + return newError(-100, http.StatusUnprocessableEntity, message, data, cause) +} + // Error implements the error interface. func (e Error) Error() string { return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause) diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index 3805da144..cec563a4a 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -234,7 +234,7 @@ Methods: if as != nil { results = wrappers.NewAssetState(as) } else { - results = "Invalid assetid" + resultsErr = NewRPCError("Unknown asset", "", nil) } case "getaccountstate": diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go index 0c03ffb4d..dac20d31e 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -87,7 +87,7 @@ var rpcTestCases = map[string][]rpcTestCase{ { name: "negative", params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de2"]`, - result: func(e *executor) interface{} { return "Invalid assetid" }, + fail: true, }, { name: "no params", From f310145612d4b9391b6d70543a5e70d8ab9d5e2e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 19:14:05 +0300 Subject: [PATCH 2/8] rpc: fix getrawtransaction error returncode Changed returncode of getrowtransaction method in case when transaction with specified hash does not exists. Now it returns error with code -100 instead of -32602 (as in c# node) --- pkg/rpc/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index cec563a4a..27616b5ce 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -288,7 +288,7 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) { resultsErr = errInvalidParams } else if tx, height, err := s.chain.GetTransaction(txHash); err != nil { err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash) - return nil, NewInvalidParamsError(err.Error(), err) + return nil, NewRPCError("Unknown transaction", err.Error(), err) } else if len(reqParams) >= 2 { _header := s.chain.GetHeaderHash(int(height)) header, err := s.chain.GetHeader(_header) From b8715ddf1124a1ba4bfdc2f458da3ebbb42e8959 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 19:23:11 +0300 Subject: [PATCH 3/8] rpc: refactor code of NewAccountState function Simplified reversing of scriptHash --- pkg/rpc/wrappers/account_state.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/rpc/wrappers/account_state.go b/pkg/rpc/wrappers/account_state.go index ddc960337..b56981e38 100644 --- a/pkg/rpc/wrappers/account_state.go +++ b/pkg/rpc/wrappers/account_state.go @@ -45,10 +45,7 @@ func NewAccountState(a *state.Account) AccountState { sort.Sort(balances) // reverse scriptHash to be consistent with other client - scriptHash, err := util.Uint160DecodeBytesBE(a.ScriptHash.BytesLE()) - if err != nil { - scriptHash = a.ScriptHash - } + scriptHash := a.ScriptHash.Reverse() return AccountState{ Version: a.Version, From 3ecc9c8bdbf3354088883ccdeb9e4017043a9839 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 19:26:47 +0300 Subject: [PATCH 4/8] docs: update link to NEO JSON-RPC 2.0 docs --- docs/rpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rpc.md b/docs/rpc.md index 117e957fa..a9a131ed5 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -76,4 +76,4 @@ Both methods also don't currently support arrays in function parameters. ## Reference * [JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification) -* [NEO JSON-RPC 2.0 docs](https://docs.neo.org/en-us/node/cli/apigen.html) +* [NEO JSON-RPC 2.0 docs](https://docs.neo.org/docs/en-us/reference/rpc/latest-version/api.html) From 3c63ef3dc36fc78b08cb1153cf52a3b5dabf1ff1 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 19:53:08 +0300 Subject: [PATCH 5/8] rpc: implement getcontractstate RPC --- pkg/rpc/prometheus.go | 9 +++++ pkg/rpc/server.go | 24 +++++++++++++ pkg/rpc/wrappers/contract_state.go | 56 ++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 pkg/rpc/wrappers/contract_state.go diff --git a/pkg/rpc/prometheus.go b/pkg/rpc/prometheus.go index 686d09853..dd17124c4 100644 --- a/pkg/rpc/prometheus.go +++ b/pkg/rpc/prometheus.go @@ -44,6 +44,14 @@ var ( }, ) + getcontractstateCalled = prometheus.NewCounter( + prometheus.CounterOpts{ + Help: "Number of calls to getcontractstate rpc endpoint", + Name: "getcontractstate_called", + Namespace: "neogo", + }, + ) + getversionCalled = prometheus.NewCounter( prometheus.CounterOpts{ Help: "Number of calls to getversion rpc endpoint", @@ -124,6 +132,7 @@ func init() { getblockcountCalled, getblockHashCalled, getconnectioncountCalled, + getcontractstateCalled, getversionCalled, getpeersCalled, validateaddressCalled, diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index 27616b5ce..cc0d5217f 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -241,6 +241,10 @@ Methods: getaccountstateCalled.Inc() results, resultsErr = s.getAccountState(reqParams, false) + case "getcontractstate": + getcontractstateCalled.Inc() + results, resultsErr = s.getContractState(reqParams) + case "getrawtransaction": getrawtransactionCalled.Inc() results, resultsErr = s.getrawtransaction(reqParams) @@ -349,6 +353,26 @@ func (s *Server) getTxOut(ps Params) (interface{}, error) { return wrappers.NewTxOutput(&out), nil } +// getContractState returns contract state (contract information, according to the contract script hash). +func (s *Server) getContractState(reqParams Params) (interface{}, error) { + var results interface{} + + param, ok := reqParams.ValueWithType(0, stringT) + if !ok { + return nil, errInvalidParams + } else if scriptHash, err := param.GetUint160FromHex(); err != nil { + return nil, errInvalidParams + } else { + cs := s.chain.GetContractState(scriptHash) + if cs != nil { + results = wrappers.NewContractState(cs) + } else { + return nil, NewRPCError("Unknown contract", "", nil) + } + } + return results, nil +} + // getAccountState returns account state either in short or full (unspents included) form. func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{}, error) { var resultsErr error diff --git a/pkg/rpc/wrappers/contract_state.go b/pkg/rpc/wrappers/contract_state.go new file mode 100644 index 000000000..ca1b11716 --- /dev/null +++ b/pkg/rpc/wrappers/contract_state.go @@ -0,0 +1,56 @@ +package wrappers + +import ( + "github.com/CityOfZion/neo-go/pkg/core/state" + "github.com/CityOfZion/neo-go/pkg/smartcontract" + "github.com/CityOfZion/neo-go/pkg/util" +) + +// ContractState wrapper used for the representation of +// state.Contract on the RPC Server. +type ContractState struct { + Version byte `json:"version"` + ScriptHash util.Uint160 `json:"hash"` + Script []byte `json:"script"` + ParamList []smartcontract.ParamType `json:"parameters"` + ReturnType smartcontract.ParamType `json:"returntype"` + Name string `json:"name"` + CodeVersion string `json:"code_version"` + Author string `json:"author"` + Email string `json:"email"` + Description string `json:"description"` + Properties Properties `json:"properties"` +} + +// Properties response wrapper. +type Properties struct { + HasStorage bool `json:"storage"` + HasDynamicInvoke bool `json:"dynamic_invoke"` + IsPayable bool `json:"is_payable"` +} + +// NewContractState creates a new Contract wrapper. +func NewContractState(c *state.Contract) ContractState { + // reverse scriptHash to be consistent with other client + scriptHash := c.ScriptHash().Reverse() + + properties := Properties{ + HasStorage: c.HasStorage(), + HasDynamicInvoke: c.HasDynamicInvoke(), + IsPayable: c.IsPayable(), + } + + return ContractState{ + Version: 0, + ScriptHash: scriptHash, + Script: c.Script, + ParamList: c.ParamList, + ReturnType: c.ReturnType, + Properties: properties, + Name: c.Name, + CodeVersion: c.CodeVersion, + Author: c.Author, + Email: c.Email, + Description: c.Description, + } +} From c99b42f738d3fa8c321e7979f877a612783f681d Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sat, 15 Feb 2020 20:00:38 +0300 Subject: [PATCH 6/8] rpc: add tests for getcontractstate RPC --- pkg/rpc/server_helper_test.go | 26 +++++++++++++++++++++++++- pkg/rpc/server_test.go | 29 +++++++++++++++++++++++++++++ pkg/rpc/testdata/50testblocks.acc | Bin 25954 -> 0 bytes pkg/rpc/testdata/testblocks.acc | Bin 0 -> 26597 bytes 4 files changed, 54 insertions(+), 1 deletion(-) delete mode 100644 pkg/rpc/testdata/50testblocks.acc create mode 100644 pkg/rpc/testdata/testblocks.acc diff --git a/pkg/rpc/server_helper_test.go b/pkg/rpc/server_helper_test.go index 369562930..b1fd3b491 100644 --- a/pkg/rpc/server_helper_test.go +++ b/pkg/rpc/server_helper_test.go @@ -13,6 +13,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/network" "github.com/CityOfZion/neo-go/pkg/rpc/result" "github.com/CityOfZion/neo-go/pkg/rpc/wrappers" + "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) @@ -150,6 +151,29 @@ type GetUnspents struct { ID int `json:"id"` } +// GetContractStateResponse struct for testing. +type GetContractStateResponce struct { + Jsonrpc string `json:"jsonrpc"` + Result struct { + Version byte `json:"version"` + ScriptHash util.Uint160 `json:"hash"` + Script []byte `json:"script"` + ParamList interface{} `json:"parameters"` + ReturnType interface{} `json:"returntype"` + Name string `json:"name"` + CodeVersion string `json:"code_version"` + Author string `json:"author"` + Email string `json:"email"` + Description string `json:"description"` + Properties struct { + HasStorage bool `json:"storage"` + HasDynamicInvoke bool `json:"dynamic_invoke"` + IsPayable bool `json:"is_payable"` + } `json:"properties"` + } `json:"result"` + ID int `json:"id"` +} + func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFunc) { var nBlocks uint32 @@ -165,7 +189,7 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFu go chain.Run() - f, err := os.Open("testdata/50testblocks.acc") + f, err := os.Open("testdata/testblocks.acc") require.Nil(t, err) br := io.NewBinReaderFromIO(f) nBlocks = br.ReadU32LE() diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go index dac20d31e..a0c83480a 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -72,6 +72,35 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, + "getcontractstate": { + { + name: "positive", + params: `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6cf"]`, + result: func(e *executor) interface{} { return &GetContractStateResponce{} }, + check: func(t *testing.T, e *executor, result interface{}) { + res, ok := result.(*GetContractStateResponce) + require.True(t, ok) + assert.Equal(t, byte(0), res.Result.Version) + assert.Equal(t, util.Uint160{0x6d, 0x1e, 0xec, 0xa8, 0x91, 0xee, 0x93, 0xde, 0x2b, 0x7a, 0x77, 0xeb, 0x91, 0xc2, 0x6f, 0x3b, 0x3c, 0x4, 0xd6, 0xcf}, res.Result.ScriptHash) + assert.Equal(t, "0.99", res.Result.CodeVersion) + }, + }, + { + name: "negative", + params: `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6c3"]`, + fail: true, + }, + { + name: "no params", + params: `[]`, + fail: true, + }, + { + name: "invalid hash", + params: `["notahex"]`, + fail: true, + }, + }, "getassetstate": { { name: "positive", diff --git a/pkg/rpc/testdata/50testblocks.acc b/pkg/rpc/testdata/50testblocks.acc deleted file mode 100644 index 91825648d4a58ecebe67c74cddd32342f6c6c091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25954 zcmd7aQ+rs!76#zhcGB3k(>6P{ZKJVmwLzmsZLG#g!^XC4+l`Hr^PHP=mt1Ckz`EJ* z^Ua=_HNy(>zwt9VWI=^4|3-|xX)#xeB7b7G>&Em}fH>w$$9E1co1aH@Fp!dMK=RhL zx6+sHRrctLD>G~J(Mm34e~s10l3Sk5TpiGtQfd(YtJf?(k$pJ^f?kyVv>9rr?`AJY zm<9R>0Sd%UP(_E4?F4_x3m#uW@;_|BE2_;F(5Zla9N-(OvT|8$3941jGi-2SNcFXq zhA;TZ2{>^a3N3jy-fGXqiZ}oQDcacbkd*p>Vm4erE;W(nx4Ngt->DKM*Ai=x7uGGy zPpPa(wB506>j{;oG%$^la(_*@nP>F#MEPyT{7J6>fsB;cyc3mc-Y9r(yF=F>xVc?d zgeQWi@C>|X2cUVw;Gb2V_MSdMO62J2bXx*6aMSoR^~YDdCzG3mGGK79fK9q}@mPh# zDdbp}m=Z_EhPcE4wn>hO6ic*u|htkRI~0@QP51G_8iNKe|CpV-p7HUYvmZ^o&i zv&Hv&I|=w40`!W}cJ#1o3?oXBLUEsPjk!LVr+$Q7R3PjQ;r37E(*J(5{~N|s#^r@Q z&fU=pS<5A#W#^%UO;ZTN9Ihj^T|pdgh0|YS!27}g?4{nT3<_H-6?k~e%;pJ9VLU%!AZ4evow@A~Rk`nzWC*=%bH$W-c9l5p0<}M~7bt$=fE}RPW{syaw2Z zqE^AZ@Mz~aeM(9Oh&-m1sF#om=dtkx)exE&J7=03Yq@*>etAGnGl%DI;m*!#nQYyh z`t{?o0JVbbE4kPAjcX}r` z_BDt344Ts3p=8`t?aT)QP6qit`e#ozKxLosz-W1pE(|hANp-3#B7Hm}S5Zr+PJoHX z(w!Ev`aQK-P5Zo+Bb%HJLMjFVvHmtK+~Y!fD1?hj_zyv?mcoFIu6* zBhEdl`Vo34YrG&dz{8;>!-lT(*~lVRI*)f(!f2%L{$R6SHKz24gjUG)C&*%t#mAnWt&yVwU@E!i0hK zfeL^*_%Dh=dEd;Ph1fL;T=!BatNKt(lAL&NouD)25;B$e8^UiQ)htL$Ozu-S6VFMU zU>cB2F~h<#l}FT!hqQ6vd+rFcPTI9S%dexW&747yu$Ek7Qx*BCtjW#2F6%G9kQXB1 zz_ko~KW;vJwh=W6?Gj1$`rYMO;%m)qy&A!6^^O^|J7HJ4jX0<{EesF^`s=Zc*s!Tw zrI$DaGvo)#{m7VdA) zkOOU49ow`#?*$}koQZ78v`!m5Qp{l7^v4uBA7%Fh#?_xQe%X#o_hBnCW}|pbfH_P7 z{J)j-nI8T)e(pcoq=BT&T@6d-&3xCTf5ZqiTU8Stw;a|qASqBhvK3N$|JAkU;69g8 z1-q4dCk1vB90B*9$DEz^l52TTeoQ_OjnpQsv~!VR1CB5$pghIfe@AT`jKpaym zWi>@dmPtnS=$m>-OO&xA`s?(hxr}>aA(~-)WM78FZrQVah1lB%m@x94e-eW5;1q7S z#82M2eIRQ_PhO^a$k9!b6>EcQxI->l{#3Khk&WtkkH3R)D4T2ZmmL=`B3@VPN04RH zHsDxe^SSi{ZoAnGfXOW-plxxO|l@XitkMjn^1_ zeqjLh76u}_$yVoi{Gk?=?sFV4Zcqj|B+IETzp)Bk{^CHW*y@pHO^ynkoUl|bI8>ZM zt?g{0`(-S3DN0oSPMD|NYefbSgYtU)K4ECx*E=C}VO3lA7^2V3;kR+b9J!20+y_S< z?Q^CwDPJcXURC+xuMg1RrEjdoYPpZ6hcmioEBvGfdP$#d(56g2R(G;`RBAt6Y}Z2! z^N}xc-F6D8`U(*ASgL4069O{kNf%XyFT84NY_yEu7P6qc{|jnx#oWtJES;Nw(H`L}-5 z(NT%R{iHg@e2J_Dl4r1))}LF1RFn8p%;0QK&F-=i9iG(e+fVps%&C`u^dDouP&4Ee zStK6WkdU_%8`g2n&Iyi`*E?ZQT-bqA-7?UmpZDAa6X%T-_RoMAyYd08t1y=0ObZsc zf^L}0N^o*Meu7_EJbbVa7Q2YuX3T;*-*6z*9s#Jxvauxq@qs9k-^C`amWw-zpdd;3 zi1J@6(UELPoPrA4@U#418yRp!$?T5vcGz4J=MA=NF{_V@{`kp+AyW~M-|R(e-!TIw zWU<=xCk}`|*(Efhv8eN93^^LSFs|<6V=lO&=6?TPV*tD`fO!jpL0j)R%32|1hYVdG z2hZOoTpJ_`Rjk5FT@+XfNQ@qRqv_~dBOLslq1#8W3q+|S=Vm%Vd(6!y!n)dw;eiSk zfT;LsrQhic^QntTs6kQSLMQ{1SbSR|!`T|g@3CnL%gf+n8CWx?8Kmud$_Pp zl)64&FYg~Uk{H^3%y_iDkmBpK?%UTG2)r zVW>~scE~&4Rnh_q222z_LTVqr5b(fW30DT?@mPRE6I!ey%_)akZf~0;d|rA znSk2$k=!#+UZi`|8AjW30~q;GEQ)?C^GTe1S6A8WH$rpbC*M1&00x5eVFuqIY4Z$$ zITf*adpSFh6YP&HefKA@!T?W@Ya!k}|4-DfYufLaLBFg7L=9?wFF+}tf@5`qp|fy~ zF4$5N9jWFB_1Hdp{u%?p7Y1-|VKDVXw}?kl-1EF;^`V4PwkFnhU$DI1Zx&x)nZ+KK z02F`b^Q2fs^%*QYhS0!wz02`0Zp*>}qvSbAX=D3C@ z^*Tq(H=O?>*MY|k;o>7&mOm;4iX4V2BcN&0}kDsP)f>_~KuDR9*`{#BQ~v($wBr-VU@^Uek>nh5`Y>74!8hzqyq zchYR7#j86MKz?LS@khTnq+KuFz50JKXQICJ)Ln2-o~p7S_rj3B;`V=ObN_OBdWf3f zsO;SjO@fSCF5-H;W4=B4I59ov+x3na;OR5pZHRmozcp&c*tjMpO|dEz-Xr_Wsrng@ zY0rsd_caDWFAU({!oar)ey!Fg=!j!}w?GbUn(gEYUDj}r-hX!gUzMEEU)>&2s83oD z`zt!v)}LHZ$$zvsaixnP)p=XkQ5IG- z6l)TjsQ3HMyEw#YmT32}IcVCDu~paL=vD8ggzPpKRQ^QzP&k;is)&L+QL zZMcZ`5xD%hF~Gg3>@l_Z$`vZJG|f-o4JtlQuSt#za~brWWRP6HqsQdLX!=5brqB}RFIPLE2an@{SgdD z@;dhQl~_o%|C09{XY`I4jMt(H%ba2ck;~<>;eB~Px?PQyVL8q%P8(SLBgSfi^BM!; z7X}D#VL%FvHINsZ=-%*Yx~TNF04B`0FH0{Eu_0Xq1XNe3w1VeI^uY*4j2(iK!QObA ziZB09uYpwHm=ZQ(Tgx~O)F9BtHK*c5^}qAU0?SC*<^mK#Y|1^L0dxL24& zGZwtBHd)cfIO{}8t-etxom$see05Mb@CXo9*A%Gu3Q1J386TaayP z&#G@5k?r8=dt!`b**;)ag3#O&IpUoS<@CjTM4p?(3dc=qaBU9#vQ zvTs>$CcR?@mQl;09dzi^RC5~+TOlHH#CNLK_GO1%6%_t* z<_aI;2-pL5K?q;)!EMJ#Ez`7?PkKPi5R@qh^f1_Y4DI%2EzqUa0VItz0YM3|cCy29e%%Tx=(sb;MU{#9)G8UxW621svVFl2LWwUG4{vmcSL zhhQNXrNjX1{6qxf??eW@Ka^pN(K(h;*g0EDAX0Zi5K&AyxKfRs-#)nI^3W6Hb9tkA zD}ef0xqVY`I^nFJziTDoa-_2dQZ~@&@aR;HU9GEa|KR@c(_MD7vwSk2?Yatr%3!Nb zhGn#^qw=KYNcacQj050dAGP!^r~92;gAO(2ceYW3H_4m9Tw z=vL!%QtghOZ=u!M?K)UsiuHUJ6JSz5FD!Qkk!!^4Gi*bl!ytcxzhef59yW#|;<2d$ zIoQExg%JXamtXnmj}(02XeU;>zx(XG#z5?a0rFcIw0UV!hj#8Kb)2c({yL5o$t1jz zpRW^z$&|O`VX}ObQ#UR=AzhzHleQkJiqMk%O$Lu-${pyP7P9UNy~k`X3IsX-RlSD- zJNp?ADQe|ufU$XoD7BM0UT9Mo)Eit+RPp;MWfU}MX2}`i!*2T$mjY&bKb(|j9K2ot z{(58w)i5x9VNZ$tD}+IekskUWuFViDlC3l-8I~|7kCN=$Cfi8#(Pqlpo+=5y=A_tE zv5($ie*El-{(eUV^4x3>O35$a`2!@0KMHQq&Dt;+V)tRV2x~Dl_Sya2Od@wpOdIsx zB%1Y?!r3k^rR;F|pghMg5zOwV%FS~taPshT*vVd`cg$c?s^0w?4zJXaZT+fWsC22R zRE~fdF}tf~&1j3#!dCP(2I4OaP~O6z$;j%PM|ih0X0z`84%tnAY4(x?ie29Q*2B&) z9YxngfzT{h-B-@WK-OHira5N03o(#026vTD!{(uDhoUg<0ILOGWjZTL?ls(wA9K6k zh{}R`F{O0~~^H~>}s zXlhil0$0JtUDv)ue#lnQr_^W6I=ofj6Do5gA2O_j)D3Iv=p#dW?Dcmr227zjj;@oe zr%rO|X;7yVEgi7bED`rmQ@Cb4bBfTYaYrLE$X+bg2GtpTG`Na$_d~Y3lfC%fjTnsM z#wFfyWXX*+!>N8UR-l4nbWD?gvY-8q86a&NwNIAgD3*={fkM!9)hmEwCZRFM#-vCq zrhgsh=Xi~Q#0vw|w=h^J*xpu@KtosaLWe8tP}Y}_j^l;a=htdlE@57hwODDTa$jwtle9wD&JeDeeIgkO5)b0$$n=C8a;9nwG?&rL zrXTH2nf~v@}dde37H9tB{#U5^bd7T_$ z&%T9uI@9W5kE;^Rabb(y5Su&*q}l;IQVDQGhDJza@FGT&9{g4K2A5#+M`v0VYO0;|1}1Z zFAUJ$!a&C}#B3u!xL?JVe8DzpAqXaJn%9b&P!iUnOgSryok*Rcn;0!PifPGB?X=zB z%T|08(D5^Ud*<>YyyXX#;*abK4%}(U*-7C=-r_a_@B=jyzy{|l+pt{xR7~so7;XNQ z5DfzSDT)PsbuxGV-y8y%&ojj^f2g%(j{KoIyU_Z7AH|nuGJodk{gQ|+FPJ#iZT^lK zoC{TEH(lCCusvn+@^h`Js}qtNu3J`B!?uVBg?6zMzQ#cMg#pG}7~CP?44bJW<7HVX zAf?b#o3p*443`^W>{8{_46rMw%MCX3_x`%jUZ=^ zMT<7P?E%1Q^U;K5CixV#Yg(2yed?K?&K3rmRXe##n{dekXXx2g{eR&=nfN}PIT8l1 ziLoouWC3Ldphkh3q^_8kS0hf=DLpJIw2&KKXCryK>3-U2!K;67FvSBoB5g9y+Bhpu zGHyhRXVTfYu+dkh)swb#h3tV}5|rov@=Ik!S34XGIzmrA(z_FnvJx8)*sVsNMo*-9 zU!%(w&G?aAu1WqVFC<}R{~4MjaeW@(|4=G$o&RFUkl7lChp`M{z$C`0 zkDOa5RjjX`&Gv% z9qZ4BXEtLekW7B95(=(gWkK$wNhT%nax8Iop90Q`ru?Xm!5vAxF|ISOtz;UAlYt%` zP)#8b$LtB-HrK{PDA=+f8!k=PD5#QCOs&!ERTKOjEF#68z0PvvYg^OY;P1Bdb;4^h7v ztL;o5K6_aC;J}ksYfqv1pD?HYefT*qLX?T9N@8_tzLGyfDCh3j=ud8r_Bzke|5;BiDZ2Q?7r2DWz>H1NE=|m?U-H(c321{!izi%&|hOhTB8(?$?2TqVAj8nfdld+nT zLU1-q@KiR#{6w>iEY|*TvL>Y`|9i>(X!j!6D%<-+j(Uc{47L8l;+kALud?UQs`z)z zz|b4d)0&pD1P1G(g2vlOAe#)NQCgu2+7VoKAG0K<>@^08FAVVB!k~`oknj2yUPO$} zj7`nbwlYC)Ut=q;JANWTQ?Khoz^>THb*R$xk7CDtKx1#NWM+gr85;)t1&hNKk{#MxJ`6aiY{bxx)aCt>CvWB+;(3N z1&Y|@^s=DY)4+Klk(=ze9* zfc*IT8SEq#ae0lF?_eQdEpcleUq{a(#J!ol{XHf;PRKeTsWV7yxj>k0h+HI~A+T9mkcX-X!^b3ZL*)GOu z%}F5BO10gE%)d|<1McjG*BB_jFu;EcgP2%fn^wwbogDE3H)mQKrzQFA-j=Z(pK@<^}EiSF~ zOLTX*;AYefcazne`Rk72p*@mYk#ot!u@d4N@gQUan5P?jW%raP=ae#@J@w}7|{Er z?JP+n$Q`Pb)JysnQV1iH9@;}spv30$J7&=G9dxKr5`~TU`Y%Dv4a71NaZ+`Pu5v6( za|u59EFafwa?o;_$W050I!=~Dd(Zbpqt5A;dq(-h(UhDd0xzs zG+^c}cHKEhj@?MB-T-)Knr;!^MSn@{J#+a=Z?y~^F#%CNTYaCt3)D~wIc-r(Tc5HU z<4hovMK7NOSM6@VF?+%nr;1X67xV-ym2jGRTqS=(D0*n{#_k-GB3SM~;}#~`9J`K{ zK?GV>KHjVQWL`tXQ*{_^=fVZ_3wrtO5{))7v1<{2du$q?UP_>>`NcV_t^eg50VVcg zq>HgC`$&~ud^*pgpEUFxGeG`FR{$M1P2bHmx&4<O3W~L`hqrlMGf1%@m@a0aE3OTBf#=4>`5FVY7Y0OcVW4X| zGH#3~rg1!+UmkL5jxShPeb9+K=xV&Q!FG;6g@>j}Ayk&51sbmV=)FF4h(xWAE!!7} z*&2=_6Mja*mI^4IsIxL8WKhd?kvyGgs)h6@Nsw&7$!F|}wakRnha1QJW=9c^MEcU% z%kV&jWA;RSOX){Ql~J|3^HBxVKO7ghinj|O9V-iEBiP#$+v?Iu5FZ(n^zInPAkYWf zO+xo(Y#sE(#|NEW7Q4mXXT73`5fv21%egsVbr_eeWg0;N!bd@3E=k<|tL;>J1^JOE zHy28#-3&=FoqTa|ubsX6Q=x{Cvi{6fJWNm#dB)C}fGfTsrZie1d+!Znl0dwK<9$CaR%&UihVa@RHH3sT042a*tpoBdc(WKjpOZyoG zPJ~$+y;87QK8!)%ui5VO7LBU(JF?PUb+t5F0m6P4f5p5AUyEBy5CN}}<+;LUl?l24 zJ21?cDuxg@WI!|XX=wzZO155~cwRuo*#WzUhAH)!&6Y;tdl$2&rGeyC*BLJ9eUIwN zT-KiHU5tw(%2_JZ6bGOdPZ`Sg)Fe};%V}Vb#Q~S-k)euW6?~yLS2-9KcibK}gpLg{ zmUfMvMXT`0uocu`Tg4!w?a>}6LsMrSId})~nxcDZ{&2rsfdRZnPOu@+ zGoT1IBs|hw=EHTUZ5;easMzq}^W?_7VCLEviJK3Iho_x=@x3(PF#~xwx)qEXAj|^kmdP?FiNpMW_ST?uRHX}Zi9@4*8VW^#o5?l%#+V9FCGFYT{b|=8 z^ntqca9VR6YGJ%Jk;a}VU>6muWFc4xXI6*?9JNPfB!zl-_tsYtq%Z{oN>R&s_nVn# z28xFWpo3gg{6ocz{A1p5sIhxf_jlVwS2)Aj?h83)Y~ooIn9{~QVHZ$!DU!Be9-r)Q z*<^>fD#pe-pMr=nc`KQPT7ijG6?~!qvgbUv<4(*hSg%yZ2hqzfT zh9EB4P>Oxm6TK7d@_LPd<_iPTw=kIEh4dy1eCAiU@VG}b*5&-|(PQrTG>FGYU&gKT zQ)k8T+VOI}CwM>G-Zn4RaJeeU2-LHuQrh95y=eK@ymK+IlTuSvSL>&Ogxz)~Z5k|(@NjP6 zJizxy^QNkm(*V1>@M-JRWu~g-1f*sRsS$y(j!m2^RjiT_oh?E4L)%ul7QTpJ1R`Rd zoUwIwQXEVv|H@T-CQuCV=@>8O&2g!=eh$s0(m1)JvM;uT>u?)-6o8Y2>4#me?^A>NYd zb3(~;ODiT%e5|~8@4nEu9PEe=N=OYP8hVNBg<4sc`r7B)jA9|*96q_!N2gdg2M#ywCuoe>y;VdbC{Dw> zC@g?R7FIQQdR--r7;o+sL#vLf21IbN#iVR=CzY!epLS&1DzKJ6mMamPj0b^ z>Om%_kFsLhkh*pEjdt9GrV{UdLLC)az@8|IC-NTm%5VehPfiSjr5kB2%jd7q`6ZXf z5XZ$Q2i)2ZfbMTT4DV+KUbgDya-DzN4U$R3LGxh#6%he)X>$~$|FS}BCHA;-rw)Wp zK*yH#IMa@hGHS^{wO61;H!(xPhXC3YGO0f~YCfG%&xgiL2*;CFd!NCtI>uY~O|ij` z9Q!d0{p`7!3{L;)IQNhp;)%!ngkmtev`$S)_oZj&m_P)+CXrSDpinl%1`ocs;N7>_ z5`>q_ht{Kj0BP}1C!*%NUo32&u|cSD`tw=Ay8wE&_L{Rt#0#0VIc0T!A>iRXA1!^T zAXtYCqW{@4w=hq9USc1fd^fH9h=b*2f)nZhU-{!T2D&c{DBr>WXTRx*GI;ViGd-|c zb#}72RZs~bGCZiBSYE3Z424W`G&e$N107>W&ZtLxr_-XQTa8{tagAhjf+Ksz?N6I2 zfS)RZD{tOvV#6$!=*s-xy;Pt)AhiQoHwLlXe;Tz_u!u>=LZ?nm4RNyoZWTdFt!FZ| zg;H1H$)#%vg(|Z`j(9R6YEwTkb123l~lJ5YY(uy}6>W$eKJTPDJe`tg5BsTp*${Z#U8PI5+{EWf% z>7Yl50hRxZ%kus_A$ULZ9M2!g$y6{Q&l)oJJ^=7 za|?yq4--+MZHlEtZ;ujcTW zuugssVzTA>ZUdnE1P!^KiaWRbR7F$v*^Myr{lwk6qa=0D#nff)N?_;lNwB_BYh;$@ ziCA;?IDs7pr&!_dOkXR9L@vDV(7}7&K60wV(Q+-}#4Ztxa-UE!;@ntTs5gAxW!TYj z88}eVz<7;;!3zVLw=l?8M*3=L(PWaoO*k_;m={0Jfg1V{R{5myx3A-q8x2}|fv?+8 zpfh2^ZAP@)$ojl1#sK7Jwa9|nV%0DTGrc%qHNt6yn}VP2N1jCQ2r}v!>uoL(dgG6^ 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 Date: Sat, 15 Feb 2020 20:02:41 +0300 Subject: [PATCH 7/8] docs: update rpc documentation Marked getcontractstate method in rpc docs as "implemented" --- docs/rpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rpc.md b/docs/rpc.md index a9a131ed5..9b84e5719 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -44,7 +44,7 @@ which would yield the response: | `getblockhash` | Yes | | `getblocksysfee` | No (#341) | | `getconnectioncount` | Yes | -| `getcontractstate` | No (#342) | +| `getcontractstate` | Yes | | `getnep5balances` | No (#498) | | `getnep5transfers` | No (#498) | | `getpeers` | Yes | From 2b9e63c5110cf68de3107c3c5b89a21ede467fd5 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 17 Feb 2020 15:04:25 +0300 Subject: [PATCH 8/8] core: add func to generate RPC test data newRPCTestChain function generates test chain "testblocks.acc" for RPC tests --- pkg/core/helper_test.go | 77 ++++++++++++++++++++++++++++++ pkg/rpc/server_helper_test.go | 6 +++ pkg/rpc/testdata/test_contract.avm | 1 + 3 files changed, 84 insertions(+) create mode 100755 pkg/rpc/testdata/test_contract.avm diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 355184706..07e6ffe45 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "testing" "time" @@ -17,6 +18,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/vm/emit" "github.com/CityOfZion/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" @@ -163,3 +165,78 @@ func newDumbBlock() *block.Block { }, } } + +// This function generates "../rpc/testdata/testblocks.acc" file which contains data +// for RPC unit tests. +// To generate new "../rpc/testdata/testblocks.acc", follow the steps: +// 1. Rename the function +// 2. Add specific test-case into "neo-go/pkg/core/blockchain_test.go" +// 3. Run tests with `$ make test` +func _(t *testing.T) { + bc := newTestChain(t) + n := 50 + blocks := makeBlocks(n) + + for i := 0; i < len(blocks); i++ { + if err := bc.AddBlock(blocks[i]); err != nil { + t.Fatal(err) + } + } + + tx1 := newMinerTX() + + avm, err := ioutil.ReadFile("../rpc/testdata/test_contract.avm") + if err != nil { + t.Fatal(err) + } + + var props smartcontract.PropertyState + script := io.NewBufBinWriter() + emit.Bytes(script.BinWriter, []byte("Da contract dat hallos u")) + emit.Bytes(script.BinWriter, []byte("joe@example.com")) + emit.Bytes(script.BinWriter, []byte("Random Guy")) + emit.Bytes(script.BinWriter, []byte("0.99")) + emit.Bytes(script.BinWriter, []byte("Helloer")) + props |= smartcontract.HasStorage + emit.Int(script.BinWriter, int64(props)) + emit.Int(script.BinWriter, int64(5)) + params := make([]byte, 1) + params[0] = byte(7) + emit.Bytes(script.BinWriter, params) + emit.Bytes(script.BinWriter, avm) + emit.Syscall(script.BinWriter, "Neo.Contract.Create") + txScript := script.Bytes() + + tx2 := transaction.NewInvocationTX(txScript, util.Fixed8FromFloat(100)) + + block := newBlock(uint32(n+1), tx1, tx2) + if err := bc.AddBlock(block); err != nil { + t.Fatal(err) + } + + outStream, err := os.Create("../rpc/testdata/testblocks.acc") + if err != nil { + t.Fatal(err) + } + defer outStream.Close() + + writer := io.NewBinWriterFromIO(outStream) + + count := bc.BlockHeight() + 1 + writer.WriteU32LE(count - 1) + + for i := 1; i < int(count); i++ { + bh := bc.GetHeaderHash(i) + b, err := bc.GetBlock(bh) + if err != nil { + t.Fatal(err) + } + buf := io.NewBufBinWriter() + b.EncodeBinary(buf.BinWriter) + bytes := buf.Bytes() + writer.WriteBytes(bytes) + if writer.Err != nil { + t.Fatal(err) + } + } +} diff --git a/pkg/rpc/server_helper_test.go b/pkg/rpc/server_helper_test.go index b1fd3b491..5451d4e1b 100644 --- a/pkg/rpc/server_helper_test.go +++ b/pkg/rpc/server_helper_test.go @@ -189,6 +189,12 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFu go chain.Run() + // File "./testdata/testblocks.acc" was generated by function core._ + // ("neo-go/pkg/core/helper_test.go"). + // To generate new "./testdata/testblocks.acc", follow the steps: + // 1. Rename the function + // 2. Add specific test-case into "neo-go/pkg/core/blockchain_test.go" + // 3. Run tests with `$ make test` f, err := os.Open("testdata/testblocks.acc") require.Nil(t, err) br := io.NewBinReaderFromIO(f) diff --git a/pkg/rpc/testdata/test_contract.avm b/pkg/rpc/testdata/test_contract.avm new file mode 100755 index 000000000..10193d3de --- /dev/null +++ b/pkg/rpc/testdata/test_contract.avm @@ -0,0 +1 @@ +QÅk Hello, world!hNeo.Runtime.Logaluf \ No newline at end of file