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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
@ -25,6 +27,12 @@ type GetProof struct {
|
|||
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.
|
||||
func (p *ProofWithKey) MarshalJSON() ([]byte, error) {
|
||||
w := io.NewBufBinWriter()
|
||||
|
@ -79,3 +87,36 @@ func (p *ProofWithKey) FromString(s string) error {
|
|||
p.DecodeBinary(r)
|
||||
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.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/block"
|
||||
"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/transaction"
|
||||
"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,
|
||||
"submitblock": (*Server).submitBlock,
|
||||
"validateaddress": (*Server).validateAddress,
|
||||
"verifyproof": (*Server).verifyProof,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
var height = s.chain.BlockHeight()
|
||||
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)
|
||||
})
|
||||
})
|
||||
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) {
|
||||
testRoot := func(t *testing.T, p string) {
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getstateroot", "params": [%s]}`, p)
|
||||
|
|
Loading…
Reference in a new issue