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 }