diff --git a/docs/rpc.md b/docs/rpc.md index 9b84e5719..c83d03be0 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -50,7 +50,7 @@ which would yield the response: | `getpeers` | Yes | | `getrawmempool` | No (#175) | | `getrawtransaction` | Yes | -| `getstorage` | No (#343) | +| `getstorage` | Yes | | `gettxout` | Yes | | `getunspents` | Yes | | `getversion` | Yes | diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 07e6ffe45..db6d97ab2 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -214,6 +214,18 @@ func _(t *testing.T) { t.Fatal(err) } + script = io.NewBufBinWriter() + emit.String(script.BinWriter, "testvalue") + emit.String(script.BinWriter, "testkey") + emit.Int(script.BinWriter, 2) + emit.Opcode(script.BinWriter, opcode.PACK) + emit.String(script.BinWriter, "Put") + emit.AppCall(script.BinWriter, hash.Hash160(avm), false) + + tx3 := transaction.NewInvocationTX(script.Bytes(), util.Fixed8FromFloat(100)) + b := newBlock(uint32(n+2), newMinerTX(), tx3) + require.NoError(t, bc.AddBlock(b)) + outStream, err := os.Create("../rpc/testdata/testblocks.acc") if err != nil { t.Fatal(err) diff --git a/pkg/rpc/prometheus.go b/pkg/rpc/prometheus.go index dd17124c4..2e5d5b4db 100644 --- a/pkg/rpc/prometheus.go +++ b/pkg/rpc/prometheus.go @@ -123,6 +123,14 @@ var ( Namespace: "neogo", }, ) + + getstorageCalled = prometheus.NewCounter( + prometheus.CounterOpts{ + Help: "Number of calls to getstorage rpc endpoint", + Name: "getstorage_called", + Namespace: "neogo", + }, + ) ) func init() { @@ -142,5 +150,6 @@ func init() { gettxoutCalled, getrawtransactionCalled, sendrawtransactionCalled, + getstorageCalled, ) } diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index cc0d5217f..0ed89cb6e 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -207,6 +207,10 @@ Methods: results = peers + case "getstorage": + getstorageCalled.Inc() + results, resultsErr = s.getStorage(reqParams) + case "validateaddress": validateaddressCalled.Inc() param, ok := reqParams.Value(0) @@ -282,6 +286,37 @@ Methods: s.WriteResponse(req, w, results) } +func (s *Server) getStorage(ps Params) (interface{}, error) { + param, ok := ps.Value(0) + if !ok { + return nil, errInvalidParams + } + + scriptHash, err := param.GetUint160FromHex() + if err != nil { + return nil, errInvalidParams + } + + scriptHash = scriptHash.Reverse() + + param, ok = ps.Value(1) + if !ok { + return nil, errInvalidParams + } + + key, err := param.GetBytesHex() + if err != nil { + return nil, errInvalidParams + } + + item := s.chain.GetStorageItem(scriptHash.Reverse(), key) + if item == nil { + return nil, nil + } + + return hex.EncodeToString(item.Value), nil +} + func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) { var resultsErr error var results interface{} diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go index a0c83480a..71826a559 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -2,6 +2,7 @@ package rpc import ( "bytes" + "encoding/hex" "encoding/json" "fmt" "io/ioutil" @@ -75,13 +76,13 @@ var rpcTestCases = map[string][]rpcTestCase{ "getcontractstate": { { name: "positive", - params: `["6d1eeca891ee93de2b7a77eb91c26f3b3c04d6cf"]`, + params: `["1a696b32e239dd5eace3f025cac0a193a5746a27"]`, 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, util.Uint160{0x1a, 0x69, 0x6b, 0x32, 0xe2, 0x39, 0xdd, 0x5e, 0xac, 0xe3, 0xf0, 0x25, 0xca, 0xc0, 0xa1, 0x93, 0xa5, 0x74, 0x6a, 0x27}, res.Result.ScriptHash) assert.Equal(t, "0.99", res.Result.CodeVersion) }, }, @@ -101,6 +102,48 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, + "getstorage": { + { + name: "positive", + params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "746573746b6579"]`, + result: func(e *executor) interface{} { return &StringResultResponse{} }, + check: func(t *testing.T, e *executor, result interface{}) { + res, ok := result.(*StringResultResponse) + require.True(t, ok) + assert.Equal(t, hex.EncodeToString([]byte("testvalue")), res.Result) + }, + }, + { + name: "missing key", + params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "7465"]`, + result: func(e *executor) interface{} { return &StringResultResponse{} }, + check: func(t *testing.T, e *executor, result interface{}) { + res, ok := result.(*StringResultResponse) + require.True(t, ok) + assert.Equal(t, "", res.Result) + }, + }, + { + name: "no params", + params: `[]`, + fail: true, + }, + { + name: "no second parameter", + params: `["1a696b32e239dd5eace3f025cac0a193a5746a27"]`, + fail: true, + }, + { + name: "invalid hash", + params: `["notahex"]`, + fail: true, + }, + { + name: "invalid key", + params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "notahex"]`, + fail: true, + }, + }, "getassetstate": { { name: "positive", diff --git a/pkg/rpc/testdata/test_contract.avm b/pkg/rpc/testdata/test_contract.avm index 10193d3de..15e36929d 100755 Binary files a/pkg/rpc/testdata/test_contract.avm and b/pkg/rpc/testdata/test_contract.avm differ diff --git a/pkg/rpc/testdata/test_contract.go b/pkg/rpc/testdata/test_contract.go new file mode 100644 index 000000000..f1ee83997 --- /dev/null +++ b/pkg/rpc/testdata/test_contract.go @@ -0,0 +1,9 @@ +package testdata + +import "github.com/CityOfZion/neo-go/pkg/interop/storage" + +func Main(operation string, args []interface{}) interface{} { + ctx := storage.GetContext() + storage.Put(ctx, args[0].([]byte), args[1].([]byte)) + return true +} diff --git a/pkg/rpc/testdata/testblocks.acc b/pkg/rpc/testdata/testblocks.acc index 3f63e2bdc..f0bcc1f75 100644 Binary files a/pkg/rpc/testdata/testblocks.acc and b/pkg/rpc/testdata/testblocks.acc differ