github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/chain/burrow/burrow.go (about)

     1  package burrow
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strconv"
     8  
     9  	"github.com/hyperledger/burrow/event"
    10  	"github.com/hyperledger/burrow/event/query"
    11  	"github.com/hyperledger/burrow/vent/chain"
    12  
    13  	"github.com/hyperledger/burrow/binary"
    14  	"github.com/hyperledger/burrow/crypto"
    15  	"github.com/hyperledger/burrow/execution/errors"
    16  	"github.com/hyperledger/burrow/execution/exec"
    17  	"github.com/hyperledger/burrow/rpc/rpcevents"
    18  	"github.com/hyperledger/burrow/rpc/rpcquery"
    19  	"github.com/hyperledger/burrow/vent/types"
    20  	"google.golang.org/grpc"
    21  	"google.golang.org/grpc/connectivity"
    22  )
    23  
    24  type Chain struct {
    25  	conn       *grpc.ClientConn
    26  	filter     query.Query
    27  	query      rpcquery.QueryClient
    28  	exec       rpcevents.ExecutionEventsClient
    29  	chainID    string
    30  	version    string
    31  	continuity exec.ContinuityOpt
    32  }
    33  
    34  var _ chain.Chain = (*Chain)(nil)
    35  
    36  func New(conn *grpc.ClientConn, filter *chain.Filter) (*Chain, error) {
    37  	client := rpcquery.NewQueryClient(conn)
    38  	status, err := client.Status(context.Background(), &rpcquery.StatusParam{})
    39  	if err != nil {
    40  		return nil, fmt.Errorf("could not get initial status from Burrow: %w", err)
    41  	}
    42  	filterQuery, err := queryFromFilter(filter)
    43  	if err != nil {
    44  		return nil, fmt.Errorf("could not build Vent filter query: %w", err)
    45  	}
    46  	continuity := exec.Continuous
    47  	if !query.IsEmpty(filterQuery) {
    48  		// Since we may skip some events
    49  		continuity = exec.NonConsecutiveEvents
    50  	}
    51  	return &Chain{
    52  		conn:       conn,
    53  		query:      client,
    54  		filter:     filterQuery,
    55  		exec:       rpcevents.NewExecutionEventsClient(conn),
    56  		chainID:    status.ChainID,
    57  		version:    status.BurrowVersion,
    58  		continuity: continuity,
    59  	}, nil
    60  }
    61  
    62  func (b *Chain) GetChainID() string {
    63  	return b.chainID
    64  }
    65  
    66  func (b *Chain) GetVersion() string {
    67  	return b.version
    68  }
    69  
    70  func (b *Chain) StatusMessage(ctx context.Context, lastProcessedHeight uint64) []interface{} {
    71  	var catchUpRatio float64
    72  	status, err := b.query.Status(ctx, &rpcquery.StatusParam{})
    73  	if err != nil {
    74  		err = fmt.Errorf("could not get Burrow chain status: %w", err)
    75  		return []interface{}{
    76  			"msg", "status",
    77  			"error", err.Error(),
    78  		}
    79  	}
    80  	if status.SyncInfo.LatestBlockHeight > 0 {
    81  		catchUpRatio = float64(lastProcessedHeight) / float64(status.SyncInfo.LatestBlockHeight)
    82  	}
    83  	return []interface{}{
    84  		"msg", "status",
    85  		"chain_type", "Burrow",
    86  		"last_processed_height", lastProcessedHeight,
    87  		"fraction_caught_up", catchUpRatio,
    88  		"burrow_latest_block_height", status.SyncInfo.LatestBlockHeight,
    89  		"burrow_latest_block_duration", status.SyncInfo.LatestBlockDuration,
    90  		"burrow_latest_block_hash", status.SyncInfo.LatestBlockHash,
    91  		"burrow_latest_app_hash", status.SyncInfo.LatestAppHash,
    92  		"burrow_latest_block_time", status.SyncInfo.LatestBlockTime,
    93  		"burrow_latest_block_seen_time", status.SyncInfo.LatestBlockSeenTime,
    94  		"burrow_node_info", status.NodeInfo,
    95  		"burrow_catching_up", status.CatchingUp,
    96  	}
    97  }
    98  
    99  func (b *Chain) ConsumeBlocks(ctx context.Context, in *rpcevents.BlockRange, consumer func(chain.Block) error) error {
   100  	stream, err := b.exec.Stream(ctx, &rpcevents.BlocksRequest{
   101  		BlockRange: in,
   102  		Query:      b.filter.String(),
   103  	})
   104  	if err != nil {
   105  		return fmt.Errorf("could not connect to block stream: %w", err)
   106  	}
   107  
   108  	return rpcevents.ConsumeBlockExecutions(stream, func(blockExecution *exec.BlockExecution) error {
   109  		return consumer((*Block)(blockExecution))
   110  	}, exec.Continuous)
   111  }
   112  
   113  func (b *Chain) Connectivity() connectivity.State {
   114  	return b.conn.GetState()
   115  }
   116  
   117  func (b *Chain) GetABI(ctx context.Context, address crypto.Address) (string, error) {
   118  	result, err := b.query.GetMetadata(ctx, &rpcquery.GetMetadataParam{
   119  		Address: &address,
   120  	})
   121  	if err != nil {
   122  		return "", err
   123  	}
   124  	return result.Metadata, nil
   125  }
   126  
   127  func (b *Chain) Close() error {
   128  	return b.conn.Close()
   129  }
   130  
   131  type Block exec.BlockExecution
   132  
   133  func NewBurrowBlock(block *exec.BlockExecution) *Block {
   134  	return (*Block)(block)
   135  }
   136  
   137  func (b *Block) GetMetadata(columns types.SQLColumnNames) (map[string]interface{}, error) {
   138  	blockHeader, err := json.Marshal(b.Header)
   139  	if err != nil {
   140  		return nil, fmt.Errorf("could not marshal block header: %w", err)
   141  	}
   142  
   143  	return map[string]interface{}{
   144  		columns.Height:      strconv.FormatUint(b.Height, 10),
   145  		columns.TimeStamp:   b.Header.GetTime(),
   146  		columns.BlockHeader: string(blockHeader),
   147  	}, nil
   148  }
   149  
   150  var _ chain.Block = (*Block)(nil)
   151  
   152  func (b *Block) GetHeight() uint64 {
   153  	return b.Height
   154  }
   155  
   156  func (b *Block) GetTxs() []chain.Transaction {
   157  	txs := make([]chain.Transaction, len(b.TxExecutions))
   158  	for i, tx := range b.TxExecutions {
   159  		txs[i] = (*Transaction)(tx)
   160  	}
   161  	return txs
   162  }
   163  
   164  type Transaction exec.TxExecution
   165  
   166  var _ chain.Transaction = (*Transaction)(nil)
   167  
   168  func (tx *Transaction) GetOrigin() *chain.Origin {
   169  	origin := (*exec.TxExecution)(tx).GetOrigin()
   170  	if origin == nil {
   171  		return nil
   172  	}
   173  	return &chain.Origin{
   174  		ChainID: origin.ChainID,
   175  		Height:  origin.Height,
   176  		Index:   origin.Index,
   177  	}
   178  }
   179  
   180  func (tx *Transaction) GetException() *errors.Exception {
   181  	return tx.Exception
   182  }
   183  
   184  func (tx *Transaction) GetMetadata(columns types.SQLColumnNames) (map[string]interface{}, error) {
   185  	// transaction raw data
   186  	envelope, err := json.Marshal(tx.Envelope)
   187  	if err != nil {
   188  		return nil, fmt.Errorf("couldn't marshal envelope in tx %v: %v", tx, err)
   189  	}
   190  
   191  	events, err := json.Marshal(tx.Events)
   192  	if err != nil {
   193  		return nil, fmt.Errorf("couldn't marshal events in tx %v: %v", tx, err)
   194  	}
   195  
   196  	result, err := json.Marshal(tx.Result)
   197  	if err != nil {
   198  		return nil, fmt.Errorf("couldn't marshal result in tx %v: %v", tx, err)
   199  	}
   200  
   201  	receipt, err := json.Marshal(tx.Receipt)
   202  	if err != nil {
   203  		return nil, fmt.Errorf("couldn't marshal receipt in tx %v: %v", tx, err)
   204  	}
   205  
   206  	exception, err := json.Marshal(tx.Exception)
   207  	if err != nil {
   208  		return nil, fmt.Errorf("couldn't marshal exception in tx %v: %v", tx, err)
   209  	}
   210  
   211  	origin, err := json.Marshal(tx.Origin)
   212  	if err != nil {
   213  		return nil, fmt.Errorf("couldn't marshal origin in tx %v: %v", tx, err)
   214  	}
   215  
   216  	return map[string]interface{}{
   217  		columns.Height:    tx.Height,
   218  		columns.TxHash:    tx.TxHash.String(),
   219  		columns.TxIndex:   tx.Index,
   220  		columns.TxType:    tx.TxType.String(),
   221  		columns.Envelope:  string(envelope),
   222  		columns.Events:    string(events),
   223  		columns.Result:    string(result),
   224  		columns.Receipt:   string(receipt),
   225  		columns.Origin:    string(origin),
   226  		columns.Exception: string(exception),
   227  	}, nil
   228  }
   229  
   230  func (tx *Transaction) GetHash() binary.HexBytes {
   231  	return tx.TxHash
   232  }
   233  
   234  func (tx *Transaction) GetEvents() []chain.Event {
   235  	// All txs have events, but not all have LogEvents
   236  	var events []chain.Event
   237  	for _, ev := range tx.Events {
   238  		if ev.Log != nil {
   239  			events = append(events, (*Event)(ev))
   240  		}
   241  	}
   242  	return events
   243  }
   244  
   245  type Event exec.Event
   246  
   247  var _ chain.Event = (*Event)(nil)
   248  
   249  func (ev *Event) GetTransactionHash() binary.HexBytes {
   250  	return ev.Header.TxHash
   251  }
   252  
   253  func (ev *Event) GetIndex() uint64 {
   254  	return ev.Header.Index
   255  }
   256  
   257  func (ev *Event) GetTopics() []binary.Word256 {
   258  	return ev.Log.Topics
   259  }
   260  
   261  func (ev *Event) GetData() []byte {
   262  	return ev.Log.Data
   263  }
   264  
   265  func (ev *Event) GetAddress() crypto.Address {
   266  	return ev.Log.Address
   267  }
   268  
   269  // Tags
   270  func (ev *Event) Get(key string) (value interface{}, ok bool) {
   271  	return (*exec.Event)(ev).Get(key)
   272  }
   273  
   274  func queryFromFilter(filter *chain.Filter) (query.Query, error) {
   275  	if filter == nil || (len(filter.Topics) == 0 && len(filter.Addresses) == 0) {
   276  		return new(query.Empty), nil
   277  	}
   278  	matchesFilter := query.NewBuilder()
   279  	for _, address := range filter.Addresses {
   280  		matchesFilter = matchesFilter.AndEquals("Address", address)
   281  	}
   282  	for i, topic := range filter.Topics {
   283  		matchesFilter = matchesFilter.AndEquals(exec.LogNKey(i), topic)
   284  	}
   285  	// Note label vent's own EventTypeLabel has different casing!
   286  	notLog := query.NewBuilder().AndNotEquals(event.EventTypeKey, exec.TypeLog)
   287  	return matchesFilter.Or(notLog).Query()
   288  }