mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-24 09:25:21 +00:00
rpc: implement verifyproof RPC
Test getproof and verifyproof together.
This commit is contained in:
parent
e38e8aa48a
commit
8e60a65b55
4 changed files with 108 additions and 0 deletions
|
@ -1,8 +1,10 @@
|
||||||
package result
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
)
|
)
|
||||||
|
@ -25,6 +27,12 @@ type GetProof struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyProof is a result of verifyproof RPC.
|
||||||
|
// nil Value is considered invalid.
|
||||||
|
type VerifyProof struct {
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
// MarshalJSON implements json.Marshaler.
|
||||||
func (p *ProofWithKey) MarshalJSON() ([]byte, error) {
|
func (p *ProofWithKey) MarshalJSON() ([]byte, error) {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
|
@ -79,3 +87,36 @@ func (p *ProofWithKey) FromString(s string) error {
|
||||||
p.DecodeBinary(r)
|
p.DecodeBinary(r)
|
||||||
return r.Err
|
return r.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (p *VerifyProof) MarshalJSON() ([]byte, error) {
|
||||||
|
if p.Value == nil {
|
||||||
|
return []byte(`"invalid"`), nil
|
||||||
|
}
|
||||||
|
return []byte(`{"value":"` + hex.EncodeToString(p.Value) + `"}`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (p *VerifyProof) UnmarshalJSON(data []byte) error {
|
||||||
|
if bytes.Equal(data, []byte(`"invalid"`)) {
|
||||||
|
p.Value = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var m map[string]string
|
||||||
|
if err := json.Unmarshal(data, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(m) != 1 {
|
||||||
|
return errors.New("must have single key")
|
||||||
|
}
|
||||||
|
v, ok := m["value"]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid json")
|
||||||
|
}
|
||||||
|
b, err := hex.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Value = b
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -55,3 +55,14 @@ func TestProofWithKey_EncodeString(t *testing.T) {
|
||||||
require.NoError(t, actual.FromString(expected.String()))
|
require.NoError(t, actual.FromString(expected.String()))
|
||||||
require.Equal(t, expected, &actual)
|
require.Equal(t, expected, &actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVerifyProof_MarshalJSON(t *testing.T) {
|
||||||
|
t.Run("Good", func(t *testing.T) {
|
||||||
|
vp := &VerifyProof{random.Bytes(100)}
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, vp, new(VerifyProof))
|
||||||
|
})
|
||||||
|
t.Run("NoValue", func(t *testing.T) {
|
||||||
|
vp := new(VerifyProof)
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, vp, &VerifyProof{[]byte{1, 2, 3}})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -114,6 +115,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon
|
||||||
"sendrawtransaction": (*Server).sendrawtransaction,
|
"sendrawtransaction": (*Server).sendrawtransaction,
|
||||||
"submitblock": (*Server).submitBlock,
|
"submitblock": (*Server).submitBlock,
|
||||||
"validateaddress": (*Server).validateAddress,
|
"validateaddress": (*Server).validateAddress,
|
||||||
|
"verifyproof": (*Server).verifyProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
var rpcWsHandlers = map[string]func(*Server, request.Params, *subscriber) (interface{}, *response.Error){
|
var rpcWsHandlers = map[string]func(*Server, request.Params, *subscriber) (interface{}, *response.Error){
|
||||||
|
@ -817,6 +819,36 @@ func (s *Server) getProof(ps request.Params) (interface{}, *response.Error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) verifyProof(ps request.Params) (interface{}, *response.Error) {
|
||||||
|
if s.chain.GetConfig().KeepOnlyLatestState {
|
||||||
|
return nil, response.NewInvalidRequestError("'verifyproof' is not supported", errKeepOnlyLatestState)
|
||||||
|
}
|
||||||
|
root, err := ps.Value(0).GetUint256()
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrInvalidParams
|
||||||
|
}
|
||||||
|
proofStr, err := ps.Value(1).GetString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, response.ErrInvalidParams
|
||||||
|
}
|
||||||
|
var p result.ProofWithKey
|
||||||
|
if err := p.FromString(proofStr); err != nil {
|
||||||
|
return nil, response.ErrInvalidParams
|
||||||
|
}
|
||||||
|
vp := new(result.VerifyProof)
|
||||||
|
val, ok := mpt.VerifyProof(root, p.Key, p.Proof)
|
||||||
|
if ok {
|
||||||
|
var si state.StorageItem
|
||||||
|
r := io.NewBinReaderFromBuf(val)
|
||||||
|
si.DecodeBinary(r)
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, response.NewInternalServerError("invalid item in trie", r.Err)
|
||||||
|
}
|
||||||
|
vp.Value = si.Value
|
||||||
|
}
|
||||||
|
return vp, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) getStateHeight(_ request.Params) (interface{}, *response.Error) {
|
func (s *Server) getStateHeight(_ request.Params) (interface{}, *response.Error) {
|
||||||
var height = s.chain.BlockHeight()
|
var height = s.chain.BlockHeight()
|
||||||
var stateHeight uint32
|
var stateHeight uint32
|
||||||
|
|
|
@ -1033,6 +1033,30 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
require.Equal(t, b.Hash(), res.Hash)
|
require.Equal(t, b.Hash(), res.Hash)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("getproof", func(t *testing.T) {
|
||||||
|
r, err := chain.GetStateRoot(3)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getproof", "params": ["%s", "%s", "%x"]}`,
|
||||||
|
r.Root.StringLE(), testContractHash, []byte("testkey"))
|
||||||
|
body := doRPCCall(rpc, httpSrv.URL, t)
|
||||||
|
rawRes := checkErrGetResult(t, body, false)
|
||||||
|
res := new(result.GetProof)
|
||||||
|
require.NoError(t, json.Unmarshal(rawRes, res))
|
||||||
|
require.True(t, res.Success)
|
||||||
|
h, _ := util.Uint160DecodeStringLE(testContractHash)
|
||||||
|
skey := makeStorageKey(chain.GetContractState(h).ID, []byte("testkey"))
|
||||||
|
require.Equal(t, skey, res.Result.Key)
|
||||||
|
require.True(t, len(res.Result.Proof) > 0)
|
||||||
|
|
||||||
|
rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "verifyproof", "params": ["%s", "%s"]}`,
|
||||||
|
r.Root.StringLE(), res.Result.String())
|
||||||
|
body = doRPCCall(rpc, httpSrv.URL, t)
|
||||||
|
rawRes = checkErrGetResult(t, body, false)
|
||||||
|
vp := new(result.VerifyProof)
|
||||||
|
require.NoError(t, json.Unmarshal(rawRes, vp))
|
||||||
|
require.Equal(t, []byte("testvalue"), vp.Value)
|
||||||
|
})
|
||||||
t.Run("getstateroot", func(t *testing.T) {
|
t.Run("getstateroot", func(t *testing.T) {
|
||||||
testRoot := func(t *testing.T, p string) {
|
testRoot := func(t *testing.T, p string) {
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p)
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p)
|
||||||
|
|
Loading…
Reference in a new issue