github.com/soomindae/tendermint@v0.0.5-0.20210528140126-84a0c70c8162/light/rpc/client_test.go (about)

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"testing"
     8  
     9  	ics23 "github.com/confio/ics23/go"
    10  	"github.com/soomindae/iavl"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  	"github.com/stretchr/testify/require"
    14  	dbm "github.com/soomindae/tm-db"
    15  
    16  	abci "github.com/soomindae/tendermint/abci/types"
    17  	"github.com/soomindae/tendermint/crypto/merkle"
    18  	"github.com/soomindae/tendermint/libs/bytes"
    19  	lcmock "github.com/soomindae/tendermint/light/rpc/mocks"
    20  	tmcrypto "github.com/soomindae/tendermint/proto/tendermint/crypto"
    21  	rpcmock "github.com/soomindae/tendermint/rpc/client/mocks"
    22  	ctypes "github.com/soomindae/tendermint/rpc/core/types"
    23  	"github.com/soomindae/tendermint/types"
    24  )
    25  
    26  // TestABCIQuery tests ABCIQuery requests and verifies proofs. HAPPY PATH 😀
    27  func TestABCIQuery(t *testing.T) {
    28  	tree, err := iavl.NewMutableTree(dbm.NewMemDB(), 100)
    29  	require.NoError(t, err)
    30  
    31  	var (
    32  		key   = []byte("foo")
    33  		value = []byte("bar")
    34  	)
    35  	tree.Set(key, value)
    36  
    37  	commitmentProof, err := tree.GetMembershipProof(key)
    38  	require.NoError(t, err)
    39  
    40  	op := &testOp{
    41  		Spec:  ics23.IavlSpec,
    42  		Key:   key,
    43  		Proof: commitmentProof,
    44  	}
    45  
    46  	next := &rpcmock.Client{}
    47  	next.On(
    48  		"ABCIQueryWithOptions",
    49  		context.Background(),
    50  		mock.AnythingOfType("string"),
    51  		bytes.HexBytes(key),
    52  		mock.AnythingOfType("client.ABCIQueryOptions"),
    53  	).Return(&ctypes.ResultABCIQuery{
    54  		Response: abci.ResponseQuery{
    55  			Code:   0,
    56  			Key:    key,
    57  			Value:  value,
    58  			Height: 1,
    59  			ProofOps: &tmcrypto.ProofOps{
    60  				Ops: []tmcrypto.ProofOp{op.ProofOp()},
    61  			},
    62  		},
    63  	}, nil)
    64  
    65  	lc := &lcmock.LightClient{}
    66  	appHash, _ := hex.DecodeString("5EFD44055350B5CC34DBD26085347A9DBBE44EA192B9286A9FC107F40EA1FAC5")
    67  	lc.On("VerifyLightBlockAtHeight", context.Background(), int64(2), mock.AnythingOfType("time.Time")).Return(
    68  		&types.LightBlock{
    69  			SignedHeader: &types.SignedHeader{
    70  				Header: &types.Header{AppHash: appHash},
    71  			},
    72  		},
    73  		nil,
    74  	)
    75  
    76  	c := NewClient(next, lc,
    77  		KeyPathFn(func(_ string, key []byte) (merkle.KeyPath, error) {
    78  			kp := merkle.KeyPath{}
    79  			kp = kp.AppendKey(key, merkle.KeyEncodingURL)
    80  			return kp, nil
    81  		}))
    82  	c.RegisterOpDecoder("ics23:iavl", testOpDecoder)
    83  	res, err := c.ABCIQuery(context.Background(), "/store/accounts/key", key)
    84  	require.NoError(t, err)
    85  
    86  	assert.NotNil(t, res)
    87  }
    88  
    89  type testOp struct {
    90  	Spec  *ics23.ProofSpec
    91  	Key   []byte
    92  	Proof *ics23.CommitmentProof
    93  }
    94  
    95  var _ merkle.ProofOperator = testOp{}
    96  
    97  func (op testOp) GetKey() []byte {
    98  	return op.Key
    99  }
   100  
   101  func (op testOp) ProofOp() tmcrypto.ProofOp {
   102  	bz, err := op.Proof.Marshal()
   103  	if err != nil {
   104  		panic(err.Error())
   105  	}
   106  	return tmcrypto.ProofOp{
   107  		Type: "ics23:iavl",
   108  		Key:  op.Key,
   109  		Data: bz,
   110  	}
   111  }
   112  
   113  func (op testOp) Run(args [][]byte) ([][]byte, error) {
   114  	// calculate root from proof
   115  	root, err := op.Proof.Calculate()
   116  	if err != nil {
   117  		return nil, fmt.Errorf("could not calculate root for proof: %v", err)
   118  	}
   119  	// Only support an existence proof or nonexistence proof (batch proofs currently unsupported)
   120  	switch len(args) {
   121  	case 0:
   122  		// Args are nil, so we verify the absence of the key.
   123  		absent := ics23.VerifyNonMembership(op.Spec, root, op.Proof, op.Key)
   124  		if !absent {
   125  			return nil, fmt.Errorf("proof did not verify absence of key: %s", string(op.Key))
   126  		}
   127  	case 1:
   128  		// Args is length 1, verify existence of key with value args[0]
   129  		if !ics23.VerifyMembership(op.Spec, root, op.Proof, op.Key, args[0]) {
   130  			return nil, fmt.Errorf("proof did not verify existence of key %s with given value %x", op.Key, args[0])
   131  		}
   132  	default:
   133  		return nil, fmt.Errorf("args must be length 0 or 1, got: %d", len(args))
   134  	}
   135  
   136  	return [][]byte{root}, nil
   137  }
   138  
   139  func testOpDecoder(pop tmcrypto.ProofOp) (merkle.ProofOperator, error) {
   140  	proof := &ics23.CommitmentProof{}
   141  	err := proof.Unmarshal(pop.Data)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	op := testOp{
   147  		Key:   pop.Key,
   148  		Spec:  ics23.IavlSpec,
   149  		Proof: proof,
   150  	}
   151  	return op, nil
   152  }