github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/rpc/rpcquery/query_server.go (about)

     1  package rpcquery
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  
     8  	"github.com/hyperledger/burrow/acm"
     9  	"github.com/hyperledger/burrow/acm/acmstate"
    10  	"github.com/hyperledger/burrow/acm/validator"
    11  	"github.com/hyperledger/burrow/bcm"
    12  	"github.com/hyperledger/burrow/consensus/tendermint"
    13  	"github.com/hyperledger/burrow/crypto"
    14  	"github.com/hyperledger/burrow/deploy/compile"
    15  	"github.com/hyperledger/burrow/event/query"
    16  	"github.com/hyperledger/burrow/execution/names"
    17  	"github.com/hyperledger/burrow/execution/proposal"
    18  	"github.com/hyperledger/burrow/execution/registry"
    19  	"github.com/hyperledger/burrow/execution/state"
    20  	"github.com/hyperledger/burrow/logging"
    21  	"github.com/hyperledger/burrow/rpc"
    22  	"github.com/hyperledger/burrow/txs/payload"
    23  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    24  	tmtypes "github.com/tendermint/tendermint/types"
    25  	"google.golang.org/grpc/codes"
    26  	"google.golang.org/grpc/status"
    27  )
    28  
    29  type queryServer struct {
    30  	UnimplementedQueryServer
    31  	state      QueryState
    32  	blockchain bcm.BlockchainInfo
    33  	nodeView   *tendermint.NodeView
    34  	logger     *logging.Logger
    35  }
    36  
    37  var _ QueryServer = &queryServer{}
    38  
    39  type QueryState interface {
    40  	acmstate.IterableStatsReader
    41  	acmstate.MetadataReader
    42  	names.IterableReader
    43  	registry.IterableReader
    44  	proposal.IterableReader
    45  	validator.History
    46  }
    47  
    48  func NewQueryServer(state QueryState, blockchain bcm.BlockchainInfo, nodeView *tendermint.NodeView, logger *logging.Logger) *queryServer {
    49  	return &queryServer{
    50  		state:      state,
    51  		blockchain: blockchain,
    52  		nodeView:   nodeView,
    53  		logger:     logger,
    54  	}
    55  }
    56  
    57  func (qs *queryServer) Status(ctx context.Context, param *StatusParam) (*rpc.ResultStatus, error) {
    58  	return rpc.Status(qs.blockchain, qs.state, qs.nodeView, param.BlockTimeWithin, param.BlockSeenTimeWithin)
    59  }
    60  
    61  // Account state
    62  
    63  func (qs *queryServer) GetAccount(ctx context.Context, param *GetAccountParam) (*acm.Account, error) {
    64  	acc, err := qs.state.GetAccount(param.Address)
    65  	if acc == nil {
    66  		acc = &acm.Account{}
    67  	}
    68  	return acc, err
    69  }
    70  
    71  // GetMetadata returns empty metadata string if not found. Metadata can be retrieved by account, or
    72  // by metadata hash
    73  func (qs *queryServer) GetMetadata(ctx context.Context, param *GetMetadataParam) (*MetadataResult, error) {
    74  	metadata := &MetadataResult{}
    75  	var contractMeta *acm.ContractMeta
    76  	var err error
    77  	if param.Address != nil {
    78  		acc, err := qs.state.GetAccount(*param.Address)
    79  		if err != nil {
    80  			return metadata, err
    81  		}
    82  		if acc != nil && acc.CodeHash != nil {
    83  			codehash := acc.CodeHash
    84  			if acc.Forebear != nil {
    85  				acc, err = qs.state.GetAccount(*acc.Forebear)
    86  				if err != nil {
    87  					return metadata, err
    88  				}
    89  			}
    90  
    91  			for _, m := range acc.ContractMeta {
    92  				if bytes.Equal(m.CodeHash, codehash) {
    93  					contractMeta = m
    94  					break
    95  				}
    96  			}
    97  
    98  			if contractMeta == nil {
    99  				deployCodehash := compile.GetDeployCodeHash(acc.EVMCode, *param.Address)
   100  				for _, m := range acc.ContractMeta {
   101  					if bytes.Equal(m.CodeHash, deployCodehash) {
   102  						contractMeta = m
   103  						break
   104  					}
   105  				}
   106  			}
   107  		}
   108  	} else if param.MetadataHash != nil {
   109  		contractMeta = &acm.ContractMeta{
   110  			MetadataHash: *param.MetadataHash,
   111  		}
   112  	}
   113  	if contractMeta == nil {
   114  		return metadata, nil
   115  	}
   116  	if contractMeta.Metadata != "" {
   117  		// Looks like the metadata is already memoised - (e.g. by native.State)
   118  		metadata.Metadata = contractMeta.Metadata
   119  	} else {
   120  		var metadataHash acmstate.MetadataHash
   121  		copy(metadataHash[:], contractMeta.MetadataHash)
   122  		metadata.Metadata, err = qs.state.GetMetadata(metadataHash)
   123  	}
   124  	return metadata, err
   125  }
   126  
   127  func (qs *queryServer) GetStorage(ctx context.Context, param *GetStorageParam) (*StorageValue, error) {
   128  	val, err := qs.state.GetStorage(param.Address, param.Key)
   129  	return &StorageValue{Value: val}, err
   130  }
   131  
   132  func (qs *queryServer) ListAccounts(param *ListAccountsParam, stream Query_ListAccountsServer) error {
   133  	qry, err := query.NewOrEmpty(param.Query)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	var streamErr error
   138  	err = qs.state.IterateAccounts(func(acc *acm.Account) error {
   139  		if qry.Matches(acc) {
   140  			return stream.Send(acc)
   141  		} else {
   142  			return nil
   143  		}
   144  	})
   145  	if err != nil {
   146  		return err
   147  	}
   148  	return streamErr
   149  }
   150  
   151  // Names
   152  
   153  func (qs *queryServer) GetName(ctx context.Context, param *GetNameParam) (entry *names.Entry, err error) {
   154  	entry, err = qs.state.GetName(param.Name)
   155  	if entry == nil && err == nil {
   156  		err = status.Error(codes.NotFound, fmt.Sprintf("name %s not found", param.Name))
   157  	}
   158  	return
   159  }
   160  
   161  func (qs *queryServer) ListNames(param *ListNamesParam, stream Query_ListNamesServer) error {
   162  	qry, err := query.NewOrEmpty(param.Query)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	var streamErr error
   167  	err = qs.state.IterateNames(func(entry *names.Entry) error {
   168  		if qry.Matches(entry) {
   169  			return stream.Send(entry)
   170  		} else {
   171  			return nil
   172  		}
   173  	})
   174  	if err != nil {
   175  		return err
   176  	}
   177  	return streamErr
   178  }
   179  
   180  // Validators
   181  
   182  func (qs *queryServer) GetValidatorSet(ctx context.Context, param *GetValidatorSetParam) (*ValidatorSet, error) {
   183  	set := validator.Copy(qs.state.Validators(0))
   184  	return &ValidatorSet{
   185  		Set: set.Validators(),
   186  	}, nil
   187  }
   188  
   189  func (qs *queryServer) GetValidatorSetHistory(ctx context.Context, param *GetValidatorSetHistoryParam) (*ValidatorSetHistory, error) {
   190  	lookback := int(param.IncludePrevious)
   191  	switch {
   192  	case lookback == 0:
   193  		lookback = 1
   194  	case lookback < 0 || lookback > state.DefaultValidatorsWindowSize:
   195  		lookback = state.DefaultValidatorsWindowSize
   196  	}
   197  	height := qs.blockchain.LastBlockHeight()
   198  	if height < uint64(lookback) {
   199  		lookback = int(height)
   200  	}
   201  	history := &ValidatorSetHistory{}
   202  	for i := 0; i < lookback; i++ {
   203  		set := validator.Copy(qs.state.Validators(i))
   204  		vs := &ValidatorSet{
   205  			Height: height - uint64(i),
   206  			Set:    set.Validators(),
   207  		}
   208  		history.History = append(history.History, vs)
   209  	}
   210  	return history, nil
   211  }
   212  
   213  func (qs *queryServer) GetNetworkRegistry(ctx context.Context, param *GetNetworkRegistryParam) (*NetworkRegistry, error) {
   214  	rv := make([]*RegisteredValidator, 0)
   215  	err := qs.state.IterateNodes(func(id crypto.Address, rn *registry.NodeIdentity) error {
   216  		rv = append(rv, &RegisteredValidator{
   217  			Address: rn.ValidatorPublicKey.GetAddress(),
   218  			Node:    rn,
   219  		})
   220  		return nil
   221  	})
   222  	return &NetworkRegistry{Set: rv}, err
   223  }
   224  
   225  // Proposals
   226  
   227  func (qs *queryServer) GetProposal(ctx context.Context, param *GetProposalParam) (proposal *payload.Ballot, err error) {
   228  	proposal, err = qs.state.GetProposal(param.Hash)
   229  	if proposal == nil && err == nil {
   230  		err = fmt.Errorf("proposal %x not found", param.Hash)
   231  	}
   232  	return
   233  }
   234  
   235  func (qs *queryServer) ListProposals(param *ListProposalsParam, stream Query_ListProposalsServer) error {
   236  	var streamErr error
   237  	err := qs.state.IterateProposals(func(hash []byte, ballot *payload.Ballot) error {
   238  		if !param.GetProposed() || ballot.ProposalState == payload.Ballot_PROPOSED {
   239  			return stream.Send(&ProposalResult{Hash: hash, Ballot: ballot})
   240  		} else {
   241  			return nil
   242  		}
   243  	})
   244  	if err != nil {
   245  		return err
   246  	}
   247  	return streamErr
   248  }
   249  
   250  func (qs *queryServer) GetStats(ctx context.Context, param *GetStatsParam) (*Stats, error) {
   251  	stats := qs.state.GetAccountStats()
   252  
   253  	return &Stats{
   254  		AccountsWithCode:    stats.AccountsWithCode,
   255  		AccountsWithoutCode: stats.AccountsWithoutCode,
   256  	}, nil
   257  }
   258  
   259  // Tendermint and blocks
   260  
   261  func (qs *queryServer) GetBlockHeader(ctx context.Context, param *GetBlockParam) (*tmproto.Header, error) {
   262  	header, err := qs.blockchain.GetBlockHeader(param.Height)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	abciHeader := tmtypes.TM2PB.Header(header)
   267  	return &abciHeader, nil
   268  }