github.com/Tri-stone/burrow@v0.25.0/rpc/service.go (about)

     1  // Copyright 2017 Monax Industries Limited
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package rpc
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"math/big"
    21  	"time"
    22  
    23  	"github.com/hyperledger/burrow/acm"
    24  	"github.com/hyperledger/burrow/acm/acmstate"
    25  	"github.com/hyperledger/burrow/acm/validator"
    26  	"github.com/hyperledger/burrow/bcm"
    27  	"github.com/hyperledger/burrow/binary"
    28  	"github.com/hyperledger/burrow/consensus/tendermint"
    29  	"github.com/hyperledger/burrow/crypto"
    30  	"github.com/hyperledger/burrow/execution/names"
    31  	"github.com/hyperledger/burrow/logging"
    32  	"github.com/hyperledger/burrow/logging/structure"
    33  	"github.com/hyperledger/burrow/permission"
    34  	"github.com/hyperledger/burrow/project"
    35  	"github.com/hyperledger/burrow/txs"
    36  	"github.com/tendermint/tendermint/consensus"
    37  	"github.com/tendermint/tendermint/p2p"
    38  	core_types "github.com/tendermint/tendermint/rpc/core/types"
    39  	tmTypes "github.com/tendermint/tendermint/types"
    40  )
    41  
    42  // Magic! Should probably be configurable, but not shouldn't be so huge we
    43  // end up DoSing ourselves.
    44  const MaxBlockLookback = 1000
    45  
    46  // Base service that provides implementation for all underlying RPC methods
    47  type Service struct {
    48  	state      acmstate.IterableStatsReader
    49  	nameReg    names.IterableReader
    50  	blockchain bcm.BlockchainInfo
    51  	validators validator.History
    52  	nodeView   *tendermint.NodeView
    53  	logger     *logging.Logger
    54  }
    55  
    56  // Service provides an internal query and information service with serialisable return types on which can accomodate
    57  // a number of transport front ends
    58  func NewService(state acmstate.IterableStatsReader, nameReg names.IterableReader, blockchain bcm.BlockchainInfo,
    59  	validators validator.History, nodeView *tendermint.NodeView, logger *logging.Logger) *Service {
    60  
    61  	return &Service{
    62  		state:      state,
    63  		nameReg:    nameReg,
    64  		blockchain: blockchain,
    65  		validators: validators,
    66  		nodeView:   nodeView,
    67  		logger:     logger.With(structure.ComponentKey, "Service"),
    68  	}
    69  }
    70  
    71  func (s *Service) Stats() acmstate.AccountStatsGetter {
    72  	return s.state
    73  }
    74  
    75  func (s *Service) BlockchainInfo() bcm.BlockchainInfo {
    76  	return s.blockchain
    77  }
    78  
    79  func (s *Service) ChainID() string {
    80  	return s.blockchain.ChainID()
    81  }
    82  
    83  func (s *Service) UnconfirmedTxs(maxTxs int64) (*ResultUnconfirmedTxs, error) {
    84  	if s.nodeView == nil {
    85  		return nil, fmt.Errorf("cannot list unconfirmed transactions because NodeView not mounted")
    86  	}
    87  	// Get all transactions for now
    88  	transactions, err := s.nodeView.MempoolTransactions(int(maxTxs))
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	wrappedTxs := make([]*txs.Envelope, len(transactions))
    93  	for i, tx := range transactions {
    94  		wrappedTxs[i] = tx
    95  	}
    96  	return &ResultUnconfirmedTxs{
    97  		NumTxs: len(transactions),
    98  		Txs:    wrappedTxs,
    99  	}, nil
   100  }
   101  
   102  func (s *Service) Status() (*ResultStatus, error) {
   103  	return Status(s.BlockchainInfo(), s.validators, s.nodeView, "", "")
   104  }
   105  
   106  func (s *Service) StatusWithin(blockTimeWithin, blockSeenTimeWithin string) (*ResultStatus, error) {
   107  	return Status(s.BlockchainInfo(), s.validators, s.nodeView, blockTimeWithin, blockSeenTimeWithin)
   108  }
   109  
   110  func (s *Service) ChainIdentifiers() (*ResultChainId, error) {
   111  	return &ResultChainId{
   112  		ChainName:   s.blockchain.GenesisDoc().ChainName,
   113  		ChainId:     s.blockchain.ChainID(),
   114  		GenesisHash: s.blockchain.GenesisHash(),
   115  	}, nil
   116  }
   117  
   118  func (s *Service) Peers() []core_types.Peer {
   119  	if s.nodeView == nil {
   120  		return nil
   121  	}
   122  	p2pPeers := s.nodeView.Peers().List()
   123  	peers := make([]core_types.Peer, len(p2pPeers))
   124  	for i, peer := range p2pPeers {
   125  		ni, _ := peer.NodeInfo().(p2p.DefaultNodeInfo)
   126  		peers[i] = core_types.Peer{
   127  			NodeInfo:         ni,
   128  			IsOutbound:       peer.IsOutbound(),
   129  			ConnectionStatus: peer.Status(),
   130  		}
   131  	}
   132  	return peers
   133  }
   134  
   135  func (s *Service) Network() (*ResultNetwork, error) {
   136  	if s.nodeView == nil {
   137  		return nil, fmt.Errorf("cannot return network info because NodeView not mounted")
   138  	}
   139  	var listeners []string
   140  	peers := s.Peers()
   141  	return &ResultNetwork{
   142  		ThisNode: s.nodeView.NodeInfo(),
   143  		ResultNetInfo: &core_types.ResultNetInfo{
   144  			Listening: true,
   145  			Listeners: listeners,
   146  			NPeers:    len(peers),
   147  			Peers:     peers,
   148  		},
   149  	}, nil
   150  }
   151  
   152  func (s *Service) Genesis() (*ResultGenesis, error) {
   153  	return &ResultGenesis{
   154  		Genesis: s.blockchain.GenesisDoc(),
   155  	}, nil
   156  }
   157  
   158  // Accounts
   159  func (s *Service) Account(address crypto.Address) (*ResultAccount, error) {
   160  	acc, err := s.state.GetAccount(address)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	return &ResultAccount{Account: acc}, nil
   165  }
   166  
   167  func (s *Service) Accounts(predicate func(*acm.Account) bool) (*ResultAccounts, error) {
   168  	accounts := make([]*acm.Account, 0)
   169  	s.state.IterateAccounts(func(account *acm.Account) error {
   170  		if predicate(account) {
   171  			accounts = append(accounts, account)
   172  		}
   173  		return nil
   174  	})
   175  
   176  	return &ResultAccounts{
   177  		BlockHeight: s.blockchain.LastBlockHeight(),
   178  		Accounts:    accounts,
   179  	}, nil
   180  }
   181  
   182  func (s *Service) Storage(address crypto.Address, key []byte) (*ResultStorage, error) {
   183  	account, err := s.state.GetAccount(address)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	if account == nil {
   188  		return nil, fmt.Errorf("UnknownAddress: %s", address)
   189  	}
   190  
   191  	value, err := s.state.GetStorage(address, binary.LeftPadWord256(key))
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	if value == binary.Zero256 {
   196  		return &ResultStorage{Key: key, Value: nil}, nil
   197  	}
   198  	return &ResultStorage{Key: key, Value: value.UnpadLeft()}, nil
   199  }
   200  
   201  func (s *Service) DumpStorage(address crypto.Address) (*ResultDumpStorage, error) {
   202  	account, err := s.state.GetAccount(address)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	if account == nil {
   207  		return nil, fmt.Errorf("UnknownAddress: %X", address)
   208  	}
   209  	var storageItems []StorageItem
   210  	err = s.state.IterateStorage(address, func(key, value binary.Word256) error {
   211  		storageItems = append(storageItems, StorageItem{Key: key.UnpadLeft(), Value: value.UnpadLeft()})
   212  		return nil
   213  	})
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	return &ResultDumpStorage{
   218  		StorageItems: storageItems,
   219  	}, nil
   220  }
   221  
   222  func (s *Service) AccountHumanReadable(address crypto.Address) (*ResultAccountHumanReadable, error) {
   223  	acc, err := s.state.GetAccount(address)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	if acc == nil {
   228  		return &ResultAccountHumanReadable{}, nil
   229  	}
   230  	tokens, err := acc.Code.Tokens()
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	perms := permission.BasePermissionsToStringList(acc.Permissions.Base)
   235  
   236  	return &ResultAccountHumanReadable{
   237  		Account: &AccountHumanReadable{
   238  			Address:     acc.GetAddress(),
   239  			PublicKey:   acc.PublicKey,
   240  			Sequence:    acc.Sequence,
   241  			Balance:     acc.Balance,
   242  			Code:        tokens,
   243  			Permissions: perms,
   244  			Roles:       acc.Permissions.Roles,
   245  		},
   246  	}, nil
   247  }
   248  
   249  func (s *Service) AccountStats() (*ResultAccountStats, error) {
   250  	stats := s.state.GetAccountStats()
   251  	return &ResultAccountStats{
   252  		AccountsWithCode:    stats.AccountsWithCode,
   253  		AccountsWithoutCode: stats.AccountsWithoutCode,
   254  	}, nil
   255  }
   256  
   257  // Name registry
   258  func (s *Service) Name(name string) (*ResultName, error) {
   259  	entry, err := s.nameReg.GetName(name)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	if entry == nil {
   264  		return nil, fmt.Errorf("name %s not found", name)
   265  	}
   266  	return &ResultName{Entry: entry}, nil
   267  }
   268  
   269  func (s *Service) Names(predicate func(*names.Entry) bool) (*ResultNames, error) {
   270  	var nms []*names.Entry
   271  	s.nameReg.IterateNames(func(entry *names.Entry) error {
   272  		if predicate(entry) {
   273  			nms = append(nms, entry)
   274  		}
   275  		return nil
   276  	})
   277  	return &ResultNames{
   278  		BlockHeight: s.blockchain.LastBlockHeight(),
   279  		Names:       nms,
   280  	}, nil
   281  }
   282  
   283  func (s *Service) Block(height uint64) (*ResultBlock, error) {
   284  	if s.nodeView == nil {
   285  		return nil, fmt.Errorf("NodeView is not mounted so cannot pull Tendermint blocks")
   286  	}
   287  	return &ResultBlock{
   288  		Block:     &Block{s.nodeView.BlockStore().LoadBlock(int64(height))},
   289  		BlockMeta: &BlockMeta{s.nodeView.BlockStore().LoadBlockMeta(int64(height))},
   290  	}, nil
   291  }
   292  
   293  // Returns the current blockchain height and metadata for a range of blocks
   294  // between minHeight and maxHeight. Only returns maxBlockLookback block metadata
   295  // from the top of the range of blocks.
   296  // Passing 0 for maxHeight sets the upper height of the range to the current
   297  // blockchain height.
   298  func (s *Service) Blocks(minHeight, maxHeight int64) (*ResultBlocks, error) {
   299  	if s.nodeView == nil {
   300  		return nil, fmt.Errorf("NodeView is not mounted so cannot pull Tendermint blocks")
   301  	}
   302  	latestHeight := int64(s.blockchain.LastBlockHeight())
   303  
   304  	if minHeight < 1 {
   305  		minHeight = latestHeight
   306  	}
   307  	if maxHeight == 0 || latestHeight < maxHeight {
   308  		maxHeight = latestHeight
   309  	}
   310  	if maxHeight > minHeight && maxHeight-minHeight > MaxBlockLookback {
   311  		minHeight = maxHeight - MaxBlockLookback
   312  	}
   313  
   314  	var blockMetas []*tmTypes.BlockMeta
   315  	for height := minHeight; height <= maxHeight; height++ {
   316  		blockMeta := s.nodeView.BlockStore().LoadBlockMeta(height)
   317  		blockMetas = append(blockMetas, blockMeta)
   318  	}
   319  
   320  	return &ResultBlocks{
   321  		LastHeight: uint64(latestHeight),
   322  		BlockMetas: blockMetas,
   323  	}, nil
   324  }
   325  
   326  func (s *Service) Validators() (*ResultValidators, error) {
   327  	var validators []*validator.Validator
   328  	err := s.validators.Validators(0).IterateValidators(func(id crypto.Addressable, power *big.Int) error {
   329  		address := id.GetAddress()
   330  		validators = append(validators, &validator.Validator{
   331  			Address:   &address,
   332  			PublicKey: id.GetPublicKey(),
   333  			Power:     power.Uint64(),
   334  		})
   335  		return nil
   336  	})
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  	return &ResultValidators{
   341  		BlockHeight:         s.blockchain.LastBlockHeight(),
   342  		BondedValidators:    validators,
   343  		UnbondingValidators: nil,
   344  	}, nil
   345  }
   346  
   347  func (s *Service) ConsensusState() (*ResultConsensusState, error) {
   348  	if s.nodeView == nil {
   349  		return nil, fmt.Errorf("cannot pull ConsensusState because NodeView not mounted")
   350  	}
   351  	peers := s.nodeView.Peers().List()
   352  	peerStates := make([]core_types.PeerStateInfo, len(peers))
   353  	for i, peer := range peers {
   354  		peerState := peer.Get(tmTypes.PeerStateKey).(*consensus.PeerState)
   355  		peerStateJSON, err := peerState.ToJSON()
   356  		if err != nil {
   357  			return nil, err
   358  		}
   359  		peerStates[i] = core_types.PeerStateInfo{
   360  			// Peer basic info.
   361  			NodeAddress: p2p.IDAddressString(peer.ID(), peer.NodeInfo().NetAddress().String()),
   362  			// Peer consensus state.
   363  			PeerState: peerStateJSON,
   364  		}
   365  	}
   366  
   367  	roundStateJSON, err := s.nodeView.RoundStateJSON()
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	return &ResultConsensusState{
   372  		ResultDumpConsensusState: &core_types.ResultDumpConsensusState{
   373  			RoundState: roundStateJSON,
   374  			Peers:      peerStates,
   375  		},
   376  	}, nil
   377  }
   378  
   379  func (s *Service) GeneratePrivateAccount() (*ResultGeneratePrivateAccount, error) {
   380  	privateAccount, err := acm.GeneratePrivateAccount()
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	return &ResultGeneratePrivateAccount{
   385  		PrivateAccount: privateAccount.ConcretePrivateAccount(),
   386  	}, nil
   387  }
   388  
   389  func Status(blockchain bcm.BlockchainInfo, validators validator.History, nodeView *tendermint.NodeView, blockTimeWithin,
   390  	blockSeenTimeWithin string) (*ResultStatus, error) {
   391  	res := &ResultStatus{
   392  		ChainID:       blockchain.ChainID(),
   393  		RunID:         nodeView.RunID().String(),
   394  		BurrowVersion: project.FullVersion(),
   395  		GenesisHash:   blockchain.GenesisHash(),
   396  		NodeInfo:      nodeView.NodeInfo(),
   397  		SyncInfo:      bcm.GetSyncInfo(blockchain),
   398  		CatchingUp:    nodeView.IsFastSyncing(),
   399  	}
   400  	if nodeView != nil {
   401  		address := nodeView.ValidatorAddress()
   402  		power, err := validators.Validators(0).Power(address)
   403  		if err != nil {
   404  			return nil, err
   405  		}
   406  		res.ValidatorInfo = &validator.Validator{
   407  			Address:   &address,
   408  			PublicKey: nodeView.ValidatorPublicKey(),
   409  			Power:     power.Uint64(),
   410  		}
   411  	}
   412  
   413  	now := time.Now()
   414  
   415  	if blockTimeWithin != "" {
   416  		err := timeWithin(now, res.SyncInfo.LatestBlockTime, blockTimeWithin)
   417  		if err != nil {
   418  			return nil, fmt.Errorf("have not committed block with sufficiently recent timestamp: %v, current status: %s",
   419  				err, statusJSON(res))
   420  		}
   421  	}
   422  
   423  	if blockSeenTimeWithin != "" {
   424  		err := timeWithin(now, res.SyncInfo.LatestBlockSeenTime, blockSeenTimeWithin)
   425  		if err != nil {
   426  			return nil, fmt.Errorf("have not committed a block sufficiently recently: %v, current status: %s",
   427  				err, statusJSON(res))
   428  		}
   429  	}
   430  
   431  	return res, nil
   432  }
   433  
   434  func statusJSON(res *ResultStatus) string {
   435  	bs, err := json.Marshal(res)
   436  	if err != nil {
   437  		bs = []byte("<error: could not marshal status>")
   438  	}
   439  	return string(bs)
   440  }
   441  
   442  func timeWithin(now time.Time, testTime time.Time, within string) error {
   443  	duration, err := time.ParseDuration(within)
   444  	if err != nil {
   445  		return fmt.Errorf("could not parse duration '%s' to determine whether to throw error: %v", within, err)
   446  	}
   447  	// Take neg abs in case caller is counting backwards (note we later add the time since we normalise the duration to negative)
   448  	if duration > 0 {
   449  		duration = -duration
   450  	}
   451  	threshold := now.Add(duration)
   452  	if testTime.After(threshold) {
   453  		return nil
   454  	}
   455  	return fmt.Errorf("time %s does not fall within last %s (cutoff: %s)", testTime.Format(time.RFC3339), within,
   456  		threshold.Format(time.RFC3339))
   457  }