From ed188e49fd005dd94a7c2b4670e83358199eaa55 Mon Sep 17 00:00:00 2001 From: remoterami <142154971+remoterami@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:20:12 +0100 Subject: [PATCH] (BIDS-2550) fixed contract invocation detection by adding metadata column (#2618) * (BIDS-2550) saving contract updates to bigtable (BIDS-2550) use field for blob transactions (BIDS-2550) save contract updates to bigtable (BIDS-2550) removed metadataUpdates indirection; added destruction detection (BIDS-2550) support for internal transactions (BIDS-2550) handle contract destruction, show correct data (BIDS-2550) detect failed transactions (BIDS-2550) applied (most) suggestions (BIDS-2550) remove contract state updates on forks this approach might be a bit naive though, as bigtable probably can't filter by time efficiently (BIDS-2550) applied feedback (BIDS-2550) fixed invalid bt blockKey timestamps (BIDS-2550) added bigtable transform (BIDS-2550) correct variable naming (BIDS-2550) added new transform to list of all (BIDS-2550) fixed ts search (BIDS-2550) fixed failed itx detection, fixed update sorting, variable naming (BIDS-2550) fixed bigtable rowrange (BIDS-2550) using distinct contract update bt key (BIDS-2550) add column filter to ClearByPrefix misc command (BIDS-2550) delete keys output clarified (BIDS-2550) ui issue fixed, bit assumptions adjusted (BIDS-2550) fixed columns filter (BIDS-2550) disabled column filter * (BIDS-2550) fixed column filter * (BIDS-2550) added contract detection for parity traces * (BIDS-2550) fix parity contract detection * (BIDS-2550) using utils.LogFatal --- cmd/eth1indexer/main.go | 186 +------- cmd/misc/main.go | 12 +- db/bigtable_common.go | 61 ++- db/bigtable_eth1.go | 642 ++++++++++++++++++++----- db/ens.go | 8 +- handlers/eth1Block.go | 21 +- handlers/eth1Transactions.go | 30 +- rpc/erigon.go | 48 +- templates/execution/address.html | 2 +- templates/execution/transactions.html | 2 +- types/eth1.pb.go | 649 ++++++++++++++------------ types/eth1.proto | 10 +- types/templates.go | 9 + 13 files changed, 1026 insertions(+), 654 deletions(-) diff --git a/cmd/eth1indexer/main.go b/cmd/eth1indexer/main.go index 5d2a50b13d..0e801e2724 100644 --- a/cmd/eth1indexer/main.go +++ b/cmd/eth1indexer/main.go @@ -163,44 +163,10 @@ func main() { } }() } - // err = UpdateTokenPrices(bt, client, "tokenlists/tokens.uniswap.org.json") - // if err != nil { - // logrus.Fatal(err) - // } - // return + if *enableFullBalanceUpdater { ProcessMetadataUpdates(bt, client, balanceUpdaterPrefix, *balanceUpdaterBatchSize, -1) return - // currentKey := balanceUpdaterPrefix // "1:00028ebf7d36c5779c1deddf3ba72761fd46c8aa" - // for { - // keys, pairs, err := bt.GetMetadata(currentKey, *balanceUpdaterBatchSize) - // if err != nil { - // logrus.Fatal(err) - // } - - // if len(keys) == 0 { - // logrus.Infof("done") - // return - // } - // // for _, pair := range pairs { - // // logrus.Info(pair) - // // } - - // logrus.Infof("currently at %v, processing balances for %v pairs", currentKey, len(pairs)) - // balances, err := client.GetBalances(pairs, 1, 4) - // if err != nil { - // logrus.Fatal(err) - // } - // // for _, balance := range balances { - // // logrus.Infof("%x %x %s", balance.Address, balance.Token, new(big.Int).SetBytes(balance.Balance)) - // // } - - // err = bt.SaveBalances(balances, []string{}) - // if err != nil { - // logrus.Fatal(err) - // } - // currentKey = keys[len(keys)-1] - // } } transforms := make([]func(blk *types.Eth1Block, cache *freecache.Cache) (*types.BulkMutations, *types.BulkMutations, error), 0) @@ -214,7 +180,8 @@ func main() { bt.TransformERC1155, bt.TransformUncle, bt.TransformWithdrawals, - bt.TransformEnsNameRegistered) + bt.TransformEnsNameRegistered, + bt.TransformContract) cache := freecache.NewCache(100 * 1024 * 1024) // 100 MB limit @@ -297,9 +264,9 @@ func main() { continueAfterError := false if lastBlockFromNode > 0 { if lastBlockFromBlocksTable < int(lastBlockFromNode) { - logrus.Infof("missing blocks %v to %v in blocks table, indexing ...", lastBlockFromBlocksTable, lastBlockFromNode) + logrus.Infof("missing blocks %v to %v in blocks table, indexing ...", lastBlockFromBlocksTable+1, lastBlockFromNode) - startBlock := int64(lastBlockFromBlocksTable) - *offsetBlocks + startBlock := int64(lastBlockFromBlocksTable+1) - *offsetBlocks if startBlock < 0 { startBlock = 0 } @@ -340,9 +307,9 @@ func main() { } if lastBlockFromDataTable < int(lastBlockFromNode) { - logrus.Infof("missing blocks %v to %v in data table, indexing ...", lastBlockFromDataTable, lastBlockFromNode) + logrus.Infof("missing blocks %v to %v in data table, indexing ...", lastBlockFromDataTable+1, lastBlockFromNode) - startBlock := int64(lastBlockFromDataTable) - *offsetData + startBlock := int64(lastBlockFromDataTable+1) - *offsetData if startBlock < 0 { startBlock = 0 } @@ -498,6 +465,9 @@ func HandleChainReorgs(bt *db.Bigtable, client *rpc.ErigonClient, depth int) err latestNodeBlockNumber := latestNodeBlock.NumberU64() // for each block check if block node hash and block db hash match + if depth > int(latestNodeBlockNumber) { + depth = int(latestNodeBlockNumber) + } for i := latestNodeBlockNumber - uint64(depth); i <= latestNodeBlockNumber; i++ { nodeBlock, err := client.GetNativeClient().HeaderByNumber(ctx, big.NewInt(int64(i))) if err != nil { @@ -554,80 +524,6 @@ func HandleChainReorgs(bt *db.Bigtable, client *rpc.ErigonClient, depth int) err func ProcessMetadataUpdates(bt *db.Bigtable, client *rpc.ErigonClient, prefix string, batchSize int, iterations int) { lastKey := prefix - // for { - // updates, err := bt.GetMetadataUpdates(lastKey, batchSize) - // if err != nil { - // logrus.Fatal(err) - // } - - // currentAddress := "" - // tokens := make([]string, 0, 100) - // pairs := make([]string, 0, batchSize) - // for _, update := range updates { - // s := strings.Split(update, ":") - - // if len(s) != 3 { - // logrus.Fatalf("%v has an invalid format", update) - // } - - // if s[0] != "B" { - // logrus.Fatalf("%v has invalid balance update prefix", update) - // } - - // address := s[1] - // token := s[2] - // pairs = append(pairs, update) - - // if currentAddress == "" { - // currentAddress = address - // } else if address != currentAddress { - // logrus.Infof("retrieving %v token balances for address %v", len(tokens), currentAddress) - // start := time.Now() - // balances, err := client.GetBalancesForAddresse(currentAddress, tokens) - - // if err != nil { - // logrus.Errorf("error during balance checker contract call: %v", err) - // logrus.Infof("retrieving balances via batch rpc calls") - // balances, err = client.GetBalances(pairs) - // if err != nil { - // logrus.Fatal(err) - // } - // } - - // logrus.Infof("retrieved %v balances in %v", len(balances), time.Since(start)) - // // for i, t := range tokens { - // // if len(balances[i]) > 0 { - // // logrus.Infof("balance of address %v of token %v is %x", currentAddress, t, balances[i]) - // // } - // // } - // currentAddress = address - // tokens = make([]string, 0, 100) - // pairs = make([]string, 0, 1000) - // } - - // tokens = append(tokens, token) - // } - // logrus.Infof("retrieving %v token balances for address %v", len(tokens), currentAddress) - // start := time.Now() - // balances, err := client.GetBalancesForAddresse(currentAddress, tokens) - - // if err != nil { - // logrus.Errorf("error during balance checker contract call: %v", err) - // logrus.Infof("retrieving balances via batch rpc calls") - // balances, err = client.GetBalances(pairs) - // if err != nil { - // logrus.Fatal(err) - // } - // } - - // logrus.Infof("retrieved %v balances in %v", len(balances), time.Since(start)) - // // for i, t := range tokens { - // // if len(balances[i]) > 0 { - // // logrus.Infof("balance of address %v of token %v is %x", currentAddress, t, balances[i]) - // // } - // // } - // lastKey = updates[len(updates)-1] - // } its := 0 for { @@ -642,10 +538,6 @@ func ProcessMetadataUpdates(bt *db.Bigtable, client *rpc.ErigonClient, prefix st return } - // for _, b := range balances { - // logrus.Infof("retrieved balance %x for token %x of address %x", b.Balance, b.Token, b.Address) - // } - balances := make([]*types.Eth1AddressBalance, 0, len(pairs)) for b := 0; b < len(pairs); b += batchSize { start := b @@ -670,12 +562,6 @@ func ProcessMetadataUpdates(bt *db.Bigtable, client *rpc.ErigonClient, prefix st logrus.Errorf("error saving balances to bigtable: %v", err) return } - // for i, b := range balances { - - // if len(b) > 0 { - // logrus.Infof("balance for key %v is %x", updates[i], b) - // } - // } lastKey = keys[len(keys)-1] logrus.Infof("retrieved %v balances in %v, currently at %v", len(balances), time.Since(start), lastKey) @@ -686,58 +572,6 @@ func ProcessMetadataUpdates(bt *db.Bigtable, client *rpc.ErigonClient, prefix st return } } - // g := new(errgroup.Group) - // g.SetLimit(batchSize) - - // for _, update := range updates { - // update := update - - // g.Go(func() error { - // // logrus.Infof("updating balance of key %v", update) - // s := strings.Split(update, ":") - - // if len(s) != 3 { - // logrus.Fatalf("%v has an invalid format", update) - // } - - // if s[0] != "B" { - // logrus.Fatalf("%v has invalid balance update prefix", update) - // } - - // address := s[1] - // token := s[2] - - // if token == "00" { - // balance, err := client.GetNativeBalance(address) - // if err != nil { - // logrus.Fatal(err) - // } - - // balanceInt := new(big.Int).SetBytes(balance) - - // if balanceInt.Cmp(big.NewInt(0)) != 0 { - // logrus.Infof("native balance of %v is %x", address, balanceInt.String()) - // } - // } else { - // balance, err := client.GetERC20TokenBalance(address, token) - // if err != nil { - // logrus.Fatal(err) - // } - - // balanceInt := new(big.Int).SetBytes(balance) - // if balanceInt.Cmp(big.NewInt(0)) != 0 { - // logrus.Infof("token %v balance of %v is %v", token, address, balanceInt.String()) - // } - // } - // return nil - // }) - // } - - // err = g.Wait() - - // if err != nil { - // logrus.Fatal(err) - // } } func IndexFromNode(bt *db.Bigtable, client *rpc.ErigonClient, start, end, concurrency int64, traceMode string) error { diff --git a/cmd/misc/main.go b/cmd/misc/main.go index c9dfdea252..4a2f21c290 100644 --- a/cmd/misc/main.go +++ b/cmd/misc/main.go @@ -264,7 +264,7 @@ func main() { case "debug-blocks": err = debugBlocks() case "clear-bigtable": - clearBigtable(opts.Table, opts.Family, opts.Key, opts.DryRun, bt) + clearBigtable(opts.Table, opts.Family, opts.Columns, opts.Key, opts.DryRun, bt) case "index-old-eth1-blocks": indexOldEth1Blocks(opts.StartBlock, opts.EndBlock, opts.BatchSize, opts.DataConcurrency, opts.Transformers, bt, erigonClient) case "update-aggregation-bits": @@ -1318,10 +1318,10 @@ func compareRewards(dayStart uint64, dayEnd uint64, validator uint64, bt *db.Big } -func clearBigtable(table string, family string, key string, dryRun bool, bt *db.Bigtable) { +func clearBigtable(table string, family string, columns string, key string, dryRun bool, bt *db.Bigtable) { if !dryRun { - confirmation := utils.CmdPrompt(fmt.Sprintf("Are you sure you want to delete all big table entries starting with [%v] for family [%v]?", key, family)) + confirmation := utils.CmdPrompt(fmt.Sprintf("Are you sure you want to delete all big table entries starting with [%v] for family [%v] and columns [%v]?", key, family, columns)) if confirmation != "yes" { logrus.Infof("Abort!") return @@ -1341,7 +1341,7 @@ func clearBigtable(table string, family string, key string, dryRun bool, bt *db. // if err != nil { // logrus.Fatal(err) // } - err := bt.ClearByPrefix(table, family, key, dryRun) + err := bt.ClearByPrefix(table, family, columns, key, dryRun) if err != nil { logrus.Fatalf("error deleting from bigtable: %v", err) @@ -1445,7 +1445,7 @@ func indexOldEth1Blocks(startBlock uint64, endBlock uint64, batchSize uint64, co logrus.Infof("transformerFlag: %v", transformerFlag) transformerList := strings.Split(transformerFlag, ",") if transformerFlag == "all" { - transformerList = []string{"TransformBlock", "TransformTx", "TransformBlobTx", "TransformItx", "TransformERC20", "TransformERC721", "TransformERC1155", "TransformWithdrawals", "TransformUncle", "TransformEnsNameRegistered"} + transformerList = []string{"TransformBlock", "TransformTx", "TransformBlobTx", "TransformItx", "TransformERC20", "TransformERC721", "TransformERC1155", "TransformWithdrawals", "TransformUncle", "TransformEnsNameRegistered", "TransformContract"} } else if len(transformerList) == 0 { utils.LogError(nil, "no transformer functions provided", 0) return @@ -1478,6 +1478,8 @@ func indexOldEth1Blocks(startBlock uint64, endBlock uint64, batchSize uint64, co case "TransformEnsNameRegistered": transforms = append(transforms, bt.TransformEnsNameRegistered) importENSChanges = true + case "TransformContract": + transforms = append(transforms, bt.TransformContract) default: utils.LogError(nil, "Invalid transformer flag %v", 0) return diff --git a/db/bigtable_common.go b/db/bigtable_common.go index ff813f0aa4..129dc7e02a 100644 --- a/db/bigtable_common.go +++ b/db/bigtable_common.go @@ -6,6 +6,7 @@ import ( "eth2-exporter/utils" "fmt" "sort" + "strings" "time" gcp_bigtable "cloud.google.com/go/bigtable" @@ -74,9 +75,9 @@ func (bigtable *Bigtable) WriteBulk(mutations *types.BulkMutations, table *gcp_b return nil } -func (bigtable *Bigtable) ClearByPrefix(table string, family, prefix string, dryRun bool) error { - if family == "" || prefix == "" { - return fmt.Errorf("please provide family [%v] and prefix [%v]", family, prefix) +func (bigtable *Bigtable) ClearByPrefix(table string, family, columns, prefix string, dryRun bool) error { + if family == "" || prefix == "" || columns == "" { + return fmt.Errorf("please provide family [%v], columns [%v] and prefix [%v]", family, columns, prefix) } rowRange := gcp_bigtable.PrefixRange(prefix) @@ -106,32 +107,44 @@ func (bigtable *Bigtable) ClearByPrefix(table string, family, prefix string, dry mutsDelete := types.NewBulkMutations(MAX_BATCH_MUTATIONS) + var filter gcp_bigtable.Filter + columnsSlice := strings.Split(columns, ",") + if len(columnsSlice) > 1 { + columnNames := make([]gcp_bigtable.Filter, len(columnsSlice)) + for i, f := range columnsSlice { + columnNames[i] = gcp_bigtable.ColumnFilter(f) + } + filter = gcp_bigtable.InterleaveFilters(columnNames...) + } else { + filter = gcp_bigtable.ColumnFilter(columnsSlice[0]) + } + keysCount := 0 - err := btTable.ReadRows(context.Background(), rowRange, func(row gcp_bigtable.Row) bool { + deleteFunc := func(row gcp_bigtable.Row) bool { + var row_ string if family == "*" { - if dryRun { - logger.Infof("would delete key %v", row.Key()) - } + row_ = row.Key() + } else { + row_ = row[family][0].Row + } + if dryRun { + logger.Infof("would delete key %v", row_) + } - mutDelete := gcp_bigtable.NewMutation() + mutDelete := gcp_bigtable.NewMutation() + if columns == "*" { mutDelete.DeleteRow() - mutsDelete.Keys = append(mutsDelete.Keys, row.Key()) - mutsDelete.Muts = append(mutsDelete.Muts, mutDelete) - keysCount++ } else { - row_ := row[family][0] - if dryRun { - logger.Infof("would delete key %v", row_.Row) + for _, column := range columnsSlice { + mutDelete.DeleteCellsInColumn(family, column) } - - mutDelete := gcp_bigtable.NewMutation() - mutDelete.DeleteRow() - mutsDelete.Keys = append(mutsDelete.Keys, row_.Row) - mutsDelete.Muts = append(mutsDelete.Muts, mutDelete) - keysCount++ } + mutsDelete.Keys = append(mutsDelete.Keys, row_) + mutsDelete.Muts = append(mutsDelete.Muts, mutDelete) + keysCount++ + // we still need to commit in batches here (instead of just calling WriteBulk only once) as loading all keys to be deleted in memory first is not feasible as the delete function could be used to delete millions of rows if mutsDelete.Len() == MAX_BATCH_MUTATIONS { logrus.Infof("deleting %v keys (first key %v, last key %v)", len(mutsDelete.Keys), mutsDelete.Keys[0], mutsDelete.Keys[len(mutsDelete.Keys)-1]) @@ -146,7 +159,13 @@ func (bigtable *Bigtable) ClearByPrefix(table string, family, prefix string, dry mutsDelete = types.NewBulkMutations(MAX_BATCH_MUTATIONS) } return true - }) + } + var err error + if columns == "*" { + err = btTable.ReadRows(context.Background(), rowRange, deleteFunc) + } else { + err = btTable.ReadRows(context.Background(), rowRange, deleteFunc, gcp_bigtable.RowFilter(filter)) + } if err != nil { return err } diff --git a/db/bigtable_eth1.go b/db/bigtable_eth1.go index 57866303e5..0fada93f3a 100644 --- a/db/bigtable_eth1.go +++ b/db/bigtable_eth1.go @@ -69,7 +69,8 @@ const ( ERC20_METADATA_FAMILY = "erc20" ERC721_METADATA_FAMILY = "erc721" ERC1155_METADATA_FAMILY = "erc1155" - writeRowLimit = 10000 + TX_PER_BLOCK_LIMIT = 10_000 + ITX_PER_TX_LIMIT = 100_000 MAX_INT = 9223372036854775807 MIN_INT = -9223372036854775808 ) @@ -96,6 +97,18 @@ const ( ERC20_COLUMN_OGIMAGE_FORMAT = "OGIMAGEFORMAT" ) +const ( + // see https://cloud.google.com/bigtable/docs/using-filters#timestamp-range + TIMESTAMP_GBT_SCALE = 1000 + // tests showed it's possible to have 36900+ subcalls per tx, but very unlikely - save a bit + TIMESTAMP_TRACE_SCALE = 1 << 15 + // 30m gas / 21.000 gas per transfer = 1428 + TIMESTAMP_TX_SCALE = 1 << 11 + // 64 - (10 bits for TIMESTAMP_GBT_SCALE + TIMESTAMP_TRACE_SCALE + TIMESTAMP_TX_SCALE) + // = 28 bits left; with a block time of 12s, that's enough for 50+ years + TIMESTAMP_BLOCK_SCALE = 1 << (64 - (10 + 15 + 11)) +) + var ZERO_ADDRESS []byte = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} var ( @@ -936,10 +949,10 @@ func (bigtable *Bigtable) TransformTx(blk *types.Eth1Block, cache *freecache.Cac bulkMetadataUpdates = &types.BulkMutations{} for i, tx := range blk.Transactions { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } - iReverse := reversePaddedIndex(i, 10000) + iReverse := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) // logger.Infof("address to: %x address: contract: %x, len(to): %v, len(contract): %v, contranct zero: %v", tx.GetTo(), tx.GetContractAddress(), len(tx.GetTo()), len(tx.GetContractAddress()), bytes.Equal(tx.GetContractAddress(), ZERO_ADDRESS)) to := tx.GetTo() isContract := false @@ -948,10 +961,6 @@ func (bigtable *Bigtable) TransformTx(blk *types.Eth1Block, cache *freecache.Cac isContract = true } // logger.Infof("sending to: %x", to) - invokesContract := false - if len(tx.GetItx()) > 0 || tx.GetGasUsed() > 21000 || tx.GetErrorMsg() != "" { - invokesContract = true - } method := make([]byte, 0) if len(tx.GetData()) > 3 { method = tx.GetData()[:4] @@ -973,7 +982,6 @@ func (bigtable *Bigtable) TransformTx(blk *types.Eth1Block, cache *freecache.Cac BlobTxFee: blobFee, BlobGasPrice: tx.GetBlobGasPrice(), IsContractCreation: isContract, - InvokesContract: invokesContract, ErrorMsg: tx.GetErrorMsg(), } // Mark Sender and Recipient for balance update @@ -1035,22 +1043,18 @@ func (bigtable *Bigtable) TransformBlobTx(blk *types.Eth1Block, cache *freecache bulkMetadataUpdates = &types.BulkMutations{} for i, tx := range blk.Transactions { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } if tx.Type != 3 { // skip non blob-txs continue } - iReverse := reversePaddedIndex(i, 10000) + iReverse := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) // logger.Infof("address to: %x address: contract: %x, len(to): %v, len(contract): %v, contranct zero: %v", tx.GetTo(), tx.GetContractAddress(), len(tx.GetTo()), len(tx.GetContractAddress()), bytes.Equal(tx.GetContractAddress(), ZERO_ADDRESS)) to := tx.GetTo() // logger.Infof("sending to: %x", to) - invokesContract := false - if len(tx.GetItx()) > 0 || tx.GetGasUsed() > 21000 || tx.GetErrorMsg() != "" { - invokesContract = true - } key := fmt.Sprintf("%s:BTX:%x", bigtable.chainId, tx.GetHash()) fee := new(big.Int).Mul(new(big.Int).SetBytes(tx.GetGasPrice()), big.NewInt(int64(tx.GetGasUsed()))).Bytes() @@ -1066,7 +1070,6 @@ func (bigtable *Bigtable) TransformBlobTx(blk *types.Eth1Block, cache *freecache GasPrice: tx.GetGasPrice(), BlobTxFee: blobFee, BlobGasPrice: tx.GetBlobGasPrice(), - InvokesContract: invokesContract, ErrorMsg: tx.GetErrorMsg(), BlobVersionedHashes: tx.GetBlobVersionedHashes(), } @@ -1116,6 +1119,91 @@ func (bigtable *Bigtable) TransformBlobTx(blk *types.Eth1Block, cache *freecache return bulkData, bulkMetadataUpdates, nil } +// custom timestamp +func encodeIsContractUpdateTs(block_number, tx_idx, trace_idx uint64) (gcp_bigtable.Timestamp, error) { + var res uint64 + + if block_number >= TIMESTAMP_BLOCK_SCALE { + return 0, fmt.Errorf("error encoding IsContractTimestamp: block idx is >= %d (block %d, tx %d, trace %d)", TIMESTAMP_BLOCK_SCALE, block_number, tx_idx, trace_idx) + } + res += block_number + + if tx_idx >= TIMESTAMP_TX_SCALE { + return 0, fmt.Errorf("error encoding IsContractTimestamp: tx idx is >= %d (block %d, tx %d, trace %d)", TIMESTAMP_TX_SCALE, block_number, tx_idx, trace_idx) + } + res *= TIMESTAMP_TX_SCALE + res += tx_idx + + if trace_idx >= TIMESTAMP_TRACE_SCALE { + return 0, fmt.Errorf("error encoding IsContractTimestamp: trace idx is >= %d (block %d, tx %d, trace %d)", TIMESTAMP_TRACE_SCALE, block_number, tx_idx, trace_idx) + } + res *= TIMESTAMP_TRACE_SCALE + res += trace_idx + + return gcp_bigtable.Timestamp(res * TIMESTAMP_GBT_SCALE), nil +} + +func decodeIsContractUpdateTs(ts gcp_bigtable.Timestamp) (block_number, tx_idx, trace_idx uint64) { + n := uint64(ts) + n /= TIMESTAMP_GBT_SCALE + + trace_idx = n % TIMESTAMP_TRACE_SCALE + n /= TIMESTAMP_TRACE_SCALE + + tx_idx = n % TIMESTAMP_TX_SCALE + + block_number = n / TIMESTAMP_TX_SCALE + + return block_number, tx_idx, trace_idx +} + +func (bigtable *Bigtable) TransformContract(blk *types.Eth1Block, cache *freecache.Cache) (bulkData *types.BulkMutations, bulkMetadataUpdates *types.BulkMutations, err error) { + bulkData = &types.BulkMutations{} + bulkMetadataUpdates = &types.BulkMutations{} + contractUpdateWrites := &types.BulkMutations{} + + for i, tx := range blk.GetTransactions() { + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) + } + + for j, itx := range tx.GetItx() { + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of internal transactions in block expected at most %d but got: %v, tx: %x", ITX_PER_TX_LIMIT, j, tx.GetHash()) + } + + if itx.GetType() == "create" || itx.GetType() == "suicide" { + contractUpdate := &types.IsContractUpdate{ + IsContract: itx.GetType() == "create", + // also use success status of enclosing transaction, as even successful sub-calls can still be reverted later in the tx + Success: itx.GetErrorMsg() == "" && tx.GetErrorMsg() == "", + } + b, err := proto.Marshal(contractUpdate) + if err != nil { + return nil, nil, err + } + address := itx.GetTo() + if itx.GetType() == "suicide" { + address = itx.GetFrom() + } + + mutWrite := gcp_bigtable.NewMutation() + ts, err := encodeIsContractUpdateTs(blk.GetNumber(), uint64(i), uint64(j)) + if err != nil { + utils.LogError(err, "error generating bigtable isContract timestamp", 0) + } else { + mutWrite.Set(ACCOUNT_METADATA_FAMILY, ACCOUNT_IS_CONTRACT, ts, b) + contractUpdateWrites.Keys = append(contractUpdateWrites.Keys, fmt.Sprintf("%s:S:%x", bigtable.chainId, address)) + contractUpdateWrites.Muts = append(contractUpdateWrites.Muts, mutWrite) + } + } + } + } + + err = bigtable.WriteBulk(contractUpdateWrites, bigtable.tableMetadata, DEFAULT_BATCH_INSERTS) + return bulkData, bulkMetadataUpdates, err +} + // TransformItx extracts internal transactions from bigtable more specifically from the table blocks. // It transforms the internal transactions contained within a block and strips any information that is not necessary for our frontend views // It writes internal transactions to table data: @@ -1146,18 +1234,18 @@ func (bigtable *Bigtable) TransformItx(blk *types.Eth1Block, cache *freecache.Ca bulkMetadataUpdates = &types.BulkMutations{} for i, tx := range blk.GetTransactions() { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } - iReversed := reversePaddedIndex(i, 10000) + iReversed := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) - for j, idx := range tx.GetItx() { - if j > 999999 { - return nil, nil, fmt.Errorf("unexpected number of internal transactions in block expected at most 999999 but got: %v, tx: %x", j, tx.GetHash()) + for j, itx := range tx.GetItx() { + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of internal transactions in block expected at most %d but got: %v, tx: %x", ITX_PER_TX_LIMIT, j, tx.GetHash()) } - jReversed := reversePaddedIndex(j, 100000) + jReversed := reversePaddedIndex(j, ITX_PER_TX_LIMIT) - if idx.Path == "[]" || bytes.Equal(idx.Value, []byte{0x0}) { // skip top level and empty calls + if itx.Path == "[]" || bytes.Equal(itx.Value, []byte{0x0}) { // skip top level and empty calls continue } @@ -1166,10 +1254,10 @@ func (bigtable *Bigtable) TransformItx(blk *types.Eth1Block, cache *freecache.Ca ParentHash: tx.GetHash(), BlockNumber: blk.GetNumber(), Time: blk.GetTime(), - Type: idx.GetType(), - From: idx.GetFrom(), - To: idx.GetTo(), - Value: idx.GetValue(), + Type: itx.GetType(), + From: itx.GetFrom(), + To: itx.GetTo(), + Value: itx.GetValue(), } bigtable.markBalanceUpdate(indexedItx.To, []byte{0x0}, bulkMetadataUpdates, cache) @@ -1177,14 +1265,14 @@ func (bigtable *Bigtable) TransformItx(blk *types.Eth1Block, cache *freecache.Ca indexes := []string{ // fmt.Sprintf("%s:i:ITX::%s:%s:%s", bigtable.chainId, reversePaddedBigtableTimestamp(blk.GetTime()), fmt.Sprintf("%04d", i), fmt.Sprintf("%05d", j)), - fmt.Sprintf("%s:I:ITX:%x:TO:%x:%s:%s:%s", bigtable.chainId, idx.GetFrom(), idx.GetTo(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), - fmt.Sprintf("%s:I:ITX:%x:FROM:%x:%s:%s:%s", bigtable.chainId, idx.GetTo(), idx.GetFrom(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), - fmt.Sprintf("%s:I:ITX:%x:TIME:%s:%s:%s", bigtable.chainId, idx.GetFrom(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), - fmt.Sprintf("%s:I:ITX:%x:TIME:%s:%s:%s", bigtable.chainId, idx.GetTo(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), + fmt.Sprintf("%s:I:ITX:%x:TO:%x:%s:%s:%s", bigtable.chainId, itx.GetFrom(), itx.GetTo(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), + fmt.Sprintf("%s:I:ITX:%x:FROM:%x:%s:%s:%s", bigtable.chainId, itx.GetTo(), itx.GetFrom(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), + fmt.Sprintf("%s:I:ITX:%x:TIME:%s:%s:%s", bigtable.chainId, itx.GetFrom(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), + fmt.Sprintf("%s:I:ITX:%x:TIME:%s:%s:%s", bigtable.chainId, itx.GetTo(), reversePaddedBigtableTimestamp(blk.GetTime()), iReversed, jReversed), } // Delete existing delegatecall data or add/update other data - if idx.GetType() == "delegatecall" { + if itx.GetType() == "delegatecall" { mut := gcp_bigtable.NewMutation() mut.DeleteCellsInColumn(DEFAULT_FAMILY, DATA_COLUMN) @@ -1279,15 +1367,15 @@ func (bigtable *Bigtable) TransformERC20(blk *types.Eth1Block, cache *freecache. } for i, tx := range blk.GetTransactions() { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } - iReversed := reversePaddedIndex(i, 10000) + iReversed := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) for j, log := range tx.GetLogs() { - if j > 99999 { - return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most 99999 but got: %v tx: %x", j, tx.GetHash()) + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most %d but got: %v tx: %x", ITX_PER_TX_LIMIT-1, j, tx.GetHash()) } - jReversed := reversePaddedIndex(j, 100000) + jReversed := reversePaddedIndex(j, ITX_PER_TX_LIMIT) if len(log.GetTopics()) != 3 || !bytes.Equal(log.GetTopics()[0], erc20.TransferTopic) { continue } @@ -1430,18 +1518,18 @@ func (bigtable *Bigtable) TransformERC721(blk *types.Eth1Block, cache *freecache } for i, tx := range blk.GetTransactions() { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } - iReversed := reversePaddedIndex(i, 10000) + iReversed := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) for j, log := range tx.GetLogs() { - if j > 99999 { - return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most 99999 but got: %v tx: %x", j, tx.GetHash()) + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most %d but got: %v tx: %x", ITX_PER_TX_LIMIT-1, j, tx.GetHash()) } if len(log.GetTopics()) != 4 || !bytes.Equal(log.GetTopics()[0], erc721.TransferTopic) { continue } - jReversed := reversePaddedIndex(j, 100000) + jReversed := reversePaddedIndex(j, ITX_PER_TX_LIMIT) topics := make([]common.Hash, 0, len(log.GetTopics())) @@ -1580,15 +1668,15 @@ func (bigtable *Bigtable) TransformERC1155(blk *types.Eth1Block, cache *freecach } for i, tx := range blk.GetTransactions() { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } - iReversed := reversePaddedIndex(i, 10000) + iReversed := reversePaddedIndex(i, TX_PER_BLOCK_LIMIT) for j, log := range tx.GetLogs() { - if j > 99999 { - return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most 99999 but got: %v tx: %x", j, tx.GetHash()) + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most %d but got: %v tx: %x", ITX_PER_TX_LIMIT-1, j, tx.GetHash()) } - jReversed := reversePaddedIndex(j, 100000) + jReversed := reversePaddedIndex(j, ITX_PER_TX_LIMIT) key := fmt.Sprintf("%s:ERC1155:%x:%s", bigtable.chainId, tx.GetHash(), jReversed) @@ -1853,7 +1941,7 @@ func (bigtable *Bigtable) TransformWithdrawals(block *types.Eth1Block, cache *fr return bulkData, bulkMetadataUpdates, nil } -func (bigtable *Bigtable) GetEth1TxForAddress(prefix string, limit int64) ([]*types.Eth1TransactionIndexed, string, error) { +func (bigtable *Bigtable) GetEth1TxsForAddress(prefix string, limit int64) ([]*types.Eth1TransactionIndexed, []string, error) { tmr := time.AfterFunc(REPORT_TIMEOUT, func() { logger.WithFields(logrus.Fields{ @@ -1879,11 +1967,11 @@ func (bigtable *Bigtable) GetEth1TxForAddress(prefix string, limit int64) ([]*ty return true }, gcp_bigtable.LimitRows(limit)) if err != nil { - return nil, "", err + return nil, nil, err } if len(keys) == 0 { - return data, "", nil + return data, nil, nil } err = bigtable.tableData.ReadRows(ctx, gcp_bigtable.RowList(keys), func(row gcp_bigtable.Row) bool { @@ -1899,7 +1987,7 @@ func (bigtable *Bigtable) GetEth1TxForAddress(prefix string, limit int64) ([]*ty }) if err != nil { logger.WithError(err).WithField("prefix", prefix).WithField("limit", limit).Errorf("error reading rows in bigtable_eth1 / GetEth1TxForAddress") - return nil, "", err + return nil, nil, err } for _, key := range keys { @@ -1908,7 +1996,7 @@ func (bigtable *Bigtable) GetEth1TxForAddress(prefix string, limit int64) ([]*ty } } - return data, indexes[len(indexes)-1], nil + return data, indexes, nil } func (bigtable *Bigtable) GetAddressesNamesArMetadata(names *map[string]string, inputMetadata *map[string]*types.ERC20Metadata) (map[string]string, map[string]*types.ERC20Metadata, error) { @@ -2005,11 +2093,30 @@ func (bigtable *Bigtable) GetAddressTransactionsTableData(address []byte, pageTo pageToken = fmt.Sprintf("%s:I:TX:%x:%s:", bigtable.chainId, address, FILTER_TIME) } - transactions, lastKey, err := BigtableClient.GetEth1TxForAddress(pageToken, DefaultInfScrollRows) + transactions, keys, err := BigtableClient.GetEth1TxsForAddress(pageToken, DefaultInfScrollRows) if err != nil { return nil, err } + idxs := make([]int64, len(keys)) + for i, k := range keys { + tx_idx, err := strconv.Atoi(strings.Split(k, ":")[6]) + if err != nil { + return nil, fmt.Errorf("error parsing Eth1InternalTransactionIndexed tx index: %v", err) + } + tx_idx = TX_PER_BLOCK_LIMIT - tx_idx + if tx_idx < 0 { + return nil, fmt.Errorf("invalid Eth1InternalTransactionIndexed tx index: %d", tx_idx) + } + + idxs[i] = int64(tx_idx) + } + + contractInteractionTypes, err := BigtableClient.GetAddressContractInteractionsAtTransactions(transactions, idxs) + if err != nil { + utils.LogError(err, "error getting contract states", 0) + } + // retrieve metadata names := make(map[string]string) for _, t := range transactions { @@ -2024,9 +2131,11 @@ func (bigtable *Bigtable) GetAddressTransactionsTableData(address []byte, pageTo tableData := make([][]interface{}, len(transactions)) for i, t := range transactions { fromName := names[string(t.From)] - toName := names[string(t.To)] - - method := bigtable.GetMethodLabel(t.MethodId, t.InvokesContract) + var contractInteraction types.ContractInteractionType + if len(contractInteractionTypes) > i { + contractInteraction = contractInteractionTypes[i] + } + method := bigtable.GetMethodLabel(t.MethodId, contractInteraction != types.CONTRACT_NONE) tableData[i] = []interface{}{ utils.FormatTransactionHash(t.Hash, t.ErrorMsg == ""), @@ -2035,14 +2144,19 @@ func (bigtable *Bigtable) GetAddressTransactionsTableData(address []byte, pageTo utils.FormatTimestamp(t.Time.AsTime().Unix()), utils.FormatAddressWithLimitsInAddressPageTable(address, t.From, fromName, false, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), utils.FormatInOutSelf(address, t.From, t.To), - utils.FormatAddressWithLimitsInAddressPageTable(address, t.To, toName, false, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), + utils.FormatAddressWithLimitsInAddressPageTable(address, t.To, BigtableClient.GetAddressLabel(names[string(t.To)], contractInteraction), contractInteraction != types.CONTRACT_NONE, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), utils.FormatAmount(new(big.Int).SetBytes(t.Value), utils.Config.Frontend.ElCurrency, 6), } } + token := "" + if len(keys) > 0 { + token = keys[len(keys)-1] + } + data := &types.DataTableResponse{ Data: tableData, - PagingToken: lastKey, + PagingToken: token, } return data, nil @@ -2352,7 +2466,7 @@ func (bigtable *Bigtable) GetAddressBlobTableData(address []byte, pageToken stri return data, nil } -func (bigtable *Bigtable) GetEth1ItxForAddress(prefix string, limit int64) ([]*types.Eth1InternalTransactionIndexed, string, error) { +func (bigtable *Bigtable) GetEth1ItxsForAddress(prefix string, limit int64) ([]*types.Eth1InternalTransactionIndexed, []string, error) { tmr := time.AfterFunc(REPORT_TIMEOUT, func() { logger.WithFields(logrus.Fields{ @@ -2379,10 +2493,10 @@ func (bigtable *Bigtable) GetEth1ItxForAddress(prefix string, limit int64) ([]*t return true }, gcp_bigtable.LimitRows(limit)) if err != nil { - return nil, "", err + return nil, nil, err } if len(keys) == 0 { - return data, "", nil + return data, nil, nil } err = bigtable.tableData.ReadRows(ctx, gcp_bigtable.RowList(keys), func(row gcp_bigtable.Row) bool { @@ -2402,7 +2516,7 @@ func (bigtable *Bigtable) GetEth1ItxForAddress(prefix string, limit int64) ([]*t }) if err != nil { logger.WithError(err).WithField("prefix", prefix).WithField("limit", limit).Errorf("error reading rows in bigtable_eth1 / GetEth1ItxForAddress") - return nil, "", err + return nil, nil, err } for _, key := range keys { @@ -2411,7 +2525,7 @@ func (bigtable *Bigtable) GetEth1ItxForAddress(prefix string, limit int64) ([]*t } } - return data, indexes[len(indexes)-1], nil + return data, indexes, nil } func (bigtable *Bigtable) GetAddressInternalTableData(address []byte, pageToken string) (*types.DataTableResponse, error) { @@ -2429,13 +2543,13 @@ func (bigtable *Bigtable) GetAddressInternalTableData(address []byte, pageToken pageToken = fmt.Sprintf("%s:I:ITX:%x:%s:", bigtable.chainId, address, FILTER_TIME) } - transactions, lastKey, err := bigtable.GetEth1ItxForAddress(pageToken, DefaultInfScrollRows) + itransactions, keys, err := bigtable.GetEth1ItxsForAddress(pageToken, DefaultInfScrollRows) if err != nil { return nil, err } names := make(map[string]string) - for _, t := range transactions { + for _, t := range itransactions { names[string(t.From)] = "" names[string(t.To)] = "" } @@ -2444,12 +2558,44 @@ func (bigtable *Bigtable) GetAddressInternalTableData(address []byte, pageToken return nil, err } - tableData := make([][]interface{}, len(transactions)) - for i, t := range transactions { + idxs := make([][2]int64, len(keys)) + for i, k := range keys { + tx_idx, err := strconv.Atoi(strings.Split(k, ":")[6]) + if err != nil { + return nil, fmt.Errorf("error parsing Eth1InternalTransactionIndexed tx index: %v", err) + } + tx_idx = TX_PER_BLOCK_LIMIT - tx_idx + if tx_idx < 0 { + return nil, fmt.Errorf("invalid Eth1InternalTransactionIndexed tx index: %d", tx_idx) + } + + trace_idx, err := strconv.Atoi(strings.Split(k, ":")[7]) + if err != nil { + return nil, fmt.Errorf("error parsing Eth1InternalTransactionIndexed trace index: %v", err) + } + trace_idx = ITX_PER_TX_LIMIT - trace_idx + if tx_idx < 0 { + return nil, fmt.Errorf("invalid Eth1InternalTransactionIndexed trace index: %d", trace_idx) + } + idxs[i] = [2]int64{int64(tx_idx), int64(trace_idx)} + } + contractInteractionTypes, err := BigtableClient.GetAddressContractInteractionsAtITransactions(itransactions, idxs) + if err != nil { + utils.LogError(err, "error getting contract states", 0) + } + + tableData := make([][]interface{}, len(itransactions)) + for i, t := range itransactions { fromName := names[string(t.From)] toName := names[string(t.To)] + var from_contractInteraction, to_contractInteraction types.ContractInteractionType + if len(contractInteractionTypes) > i { + from_contractInteraction = contractInteractionTypes[i][0] + to_contractInteraction = contractInteractionTypes[i][1] + } + if t.Type == "suicide" { // erigon's "suicide" might be misleading for users t.Type = "selfdestruct" @@ -2459,49 +2605,32 @@ func (bigtable *Bigtable) GetAddressInternalTableData(address []byte, pageToken utils.FormatTransactionHash(t.ParentHash, true), utils.FormatBlockNumber(t.BlockNumber), utils.FormatTimestamp(t.Time.AsTime().Unix()), - utils.FormatAddressWithLimitsInAddressPageTable(address, t.From, fromName, false, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), + utils.FormatAddressWithLimitsInAddressPageTable(address, t.From, BigtableClient.GetAddressLabel(fromName, from_contractInteraction), from_contractInteraction != types.CONTRACT_NONE, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), utils.FormatInOutSelf(address, t.From, t.To), - utils.FormatAddressWithLimitsInAddressPageTable(address, t.To, toName, false, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), + utils.FormatAddressWithLimitsInAddressPageTable(address, t.To, BigtableClient.GetAddressLabel(toName, to_contractInteraction), to_contractInteraction != types.CONTRACT_NONE, digitLimitInAddressPagesTable, nameLimitInAddressPagesTable, true), utils.FormatAmount(new(big.Int).SetBytes(t.Value), utils.Config.Frontend.ElCurrency, 6), t.Type, } } + token := "" + if len(keys) > 0 { + token = keys[len(keys)-1] + } + data := &types.DataTableResponse{ Data: tableData, - PagingToken: lastKey, + PagingToken: token, } return data, nil } func (bigtable *Bigtable) GetInternalTransfersForTransaction(transaction []byte, from []byte, parityTrace []*rpc.ParityTraceResult, currency string) ([]types.ITransaction, error) { - getTraceInfo := func(trace *rpc.ParityTraceResult) ([]byte, []byte, []byte, string) { - var from, to, value []byte - tx_type := trace.Type - - switch trace.Type { - case "create": - from = common.FromHex(trace.Action.From) - to = common.FromHex(trace.Result.Address) - value = common.FromHex(trace.Action.Value) - case "suicide": - from = common.FromHex(trace.Action.Address) - to = common.FromHex(trace.Action.RefundAddress) - value = common.FromHex(trace.Action.Balance) - case "call": - from = common.FromHex(trace.Action.From) - to = common.FromHex(trace.Action.To) - value = common.FromHex(trace.Action.Value) - tx_type = trace.Action.CallType - default: - utils.LogError(nil, "unknown trace type", 0) - } - return from, to, value, tx_type - } + names := make(map[string]string) for _, trace := range parityTrace { - from, to, _, _ := getTraceInfo(trace) + from, to, _, _ := trace.ConvertFields() names[string(from)] = "" names[string(to)] = "" } @@ -2511,15 +2640,18 @@ func (bigtable *Bigtable) GetInternalTransfersForTransaction(transaction []byte, return nil, err } + contractInteractionTypes, err := BigtableClient.GetAddressContractInteractionsAtParityTraces(parityTrace) + if err != nil { + utils.LogError(err, "error getting contract states", 0) + } + data := make([]types.ITransaction, 0, len(parityTrace)-1) for i := 1; i < len(parityTrace); i++ { - from, to, value, tx_type := getTraceInfo(parityTrace[i]) + from, to, value, tx_type := parityTrace[i].ConvertFields() if tx_type == "suicide" { // erigon's "suicide" might be misleading for users tx_type = "selfdestruct" } - fromName := names[parityTrace[i].Action.From] - toName := names[parityTrace[i].Action.To] input := make([]byte, 0) if len(parityTrace[i].Action.Input) > 2 { input, err = hex.DecodeString(parityTrace[i].Action.Input[2:]) @@ -2527,9 +2659,19 @@ func (bigtable *Bigtable) GetInternalTransfersForTransaction(transaction []byte, utils.LogError(err, "can't convert hex string", 0) } } + + var from_contractInteraction, to_contractInteraction types.ContractInteractionType + if len(contractInteractionTypes) > i { + from_contractInteraction = contractInteractionTypes[i][0] + to_contractInteraction = contractInteractionTypes[i][1] + } + + fromName := BigtableClient.GetAddressLabel(names[string(from)], from_contractInteraction) + toName := BigtableClient.GetAddressLabel(names[string(to)], from_contractInteraction) + itx := types.ITransaction{ - From: utils.FormatAddress(from, nil, fromName, false, false, true), - To: utils.FormatAddress(to, nil, toName, false, false, true), + From: utils.FormatAddress(from, nil, fromName, false, from_contractInteraction != types.CONTRACT_NONE, true), + To: utils.FormatAddress(to, nil, toName, false, to_contractInteraction != types.CONTRACT_NONE, true), Amount: utils.FormatElCurrency(value, currency, 8, true, false, false, true), TracePath: utils.FormatTracePath(tx_type, parityTrace[i].TraceAddress, parityTrace[i].Error == "", bigtable.GetMethodLabel(input, true)), Advanced: tx_type == "delegatecall" || string(value) == "\x00", @@ -2581,7 +2723,7 @@ func (bigtable *Bigtable) GetArbitraryTokenTransfersForTransaction(transaction [ logrus.Fatalf("error parsing data for row %v: %v", row.Key(), err) return false } - rowN = 100000 - rowN + rowN = ITX_PER_TX_LIMIT - rowN mux.Lock() transfers[rowN] = b mux.Unlock() @@ -3469,6 +3611,228 @@ func (bigtable *Bigtable) GetAddressNames(addresses map[string]string) error { return err } +type isContractInfo struct { + update *types.IsContractUpdate + ts gcp_bigtable.Timestamp +} + +type contractInteractionAtRequest struct { + address string + block int64 + txIdx int64 + traceIdx int64 +} + +func (bigtable *Bigtable) getAddressIsContractHistories(histories map[string][]isContractInfo) error { + if len(histories) == 0 { + return nil + } + + tmr := time.AfterFunc(REPORT_TIMEOUT, func() { + logger.WithFields(logrus.Fields{}).Warnf("%s call took longer than %v", utils.GetCurrentFuncName(), REPORT_TIMEOUT) + }) + defer tmr.Stop() + + keys := make([]string, 0, len(histories)) + for address := range histories { + keys = append(keys, fmt.Sprintf("%s:S:%s", bigtable.chainId, address)) + } + + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30)) + defer cancel() + + filter := gcp_bigtable.ChainFilters(gcp_bigtable.FamilyFilter(ACCOUNT_METADATA_FAMILY), gcp_bigtable.ColumnFilter(ACCOUNT_IS_CONTRACT)) + + keyPrefix := fmt.Sprintf("%s:S:", bigtable.chainId) + err := bigtable.tableMetadata.ReadRows(ctx, gcp_bigtable.RowList(keys), func(row gcp_bigtable.Row) bool { + // results are returned in reverse order, so highest ts is first + address := strings.TrimPrefix(row.Key(), keyPrefix) + for _, v := range row[ACCOUNT_METADATA_FAMILY] { + b := &types.IsContractUpdate{} + err := proto.Unmarshal(v.Value, b) + if err != nil { + utils.LogError(err, "error parsing IsContractUpdate data", 0) + } + histories[address] = append(histories[address], isContractInfo{update: b, ts: v.Timestamp}) + } + + return true + }, gcp_bigtable.RowFilter(filter)) + + if err != nil { + return fmt.Errorf("error reading isContract histories from bigtable: %w", err) + } + + return nil +} + +// returns account state after the given execution state +// -1 is latest (e.g. "txIdx" = -1 returns the contract state after execution of "block", "block" = -1 returns the state at chain head) +func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractInteractionAtRequest) ([]types.ContractInteractionType, error) { + results := make([]types.ContractInteractionType, len(requests)) + if len(requests) == 0 { + return results, nil + } + + // get histories + histories := make(map[string][]isContractInfo, len(requests)) + for _, request := range requests { + histories[request.address] = nil + } + err := bigtable.getAddressIsContractHistories(histories) + if err != nil { + return nil, err + } + + // evaluate requests; CONTRACT_NONE is default + for i, request := range requests { + history, ok := histories[request.address] + if !ok || history == nil || len(history) == 0 { + continue + } + latestUpdateIdxBeforeReq := 0 + if request.block != -1 { + var block, tx, itx uint64 + if request.txIdx == -1 { + block = uint64(request.block + 1) + } else if request.traceIdx == -1 { + block = uint64(request.block) + tx = uint64(request.txIdx + 1) + } else { + block = uint64(request.block) + tx = uint64(request.txIdx) + itx = uint64(request.traceIdx + 1) + } + req_ts, err := encodeIsContractUpdateTs(block, tx, itx) + if err != nil { + return nil, err + } + latestUpdateIdxBeforeReq = sort.Search(len(history), func(j int) bool { + return history[j].ts < req_ts + }) + if len(history) == latestUpdateIdxBeforeReq { + // all updates happened after our request + continue + } + } + + b, tx, trace := decodeIsContractUpdateTs(history[latestUpdateIdxBeforeReq].ts) + exact_match := request.block == -1 || request.block == int64(b) && (request.txIdx == -1 || request.txIdx == int64(tx) && (request.traceIdx == -1 || request.traceIdx == int64(trace))) + + if exact_match { + results[i] = types.CONTRACT_DESTRUCTION + if history[latestUpdateIdxBeforeReq].update.IsContract { + results[i] = types.CONTRACT_CREATION + } + } else { + // find first successful prev update + for j := latestUpdateIdxBeforeReq; j < len(history); j++ { + if history[j].update.Success { + if history[j].update.IsContract { + results[i] = types.CONTRACT_PRESENT + } + break + } + } + } + } + return results, nil +} + +// convenience function to get contract interaction status per transaction of a block +func (bigtable *Bigtable) GetAddressContractInteractionsAtBlock(block *types.Eth1Block) ([]types.ContractInteractionType, error) { + requests := make([]contractInteractionAtRequest, len(block.GetTransactions())) + for i, tx := range block.GetTransactions() { + address := tx.GetTo() + if len(address) == 0 { + address = tx.GetContractAddress() + } + requests[i] = contractInteractionAtRequest{ + address: fmt.Sprintf("%x", address), + block: int64(block.GetNumber()), + txIdx: int64(i), + traceIdx: -1, + } + } + + return bigtable.GetAddressContractInteractionsAt(requests) +} + +// convenience function to get contract interaction status per subtransaction of a transaction +// 2nd parameter specifies [tx_idx, trace_idx] for each internal tx +func (bigtable *Bigtable) GetAddressContractInteractionsAtITransactions(itransactions []*types.Eth1InternalTransactionIndexed, idxs [][2]int64) ([][2]types.ContractInteractionType, error) { + requests := make([]contractInteractionAtRequest, 0, len(itransactions)*2) + for i, tx := range itransactions { + requests = append(requests, contractInteractionAtRequest{ + address: fmt.Sprintf("%x", tx.GetFrom()), + block: int64(tx.GetBlockNumber()), + txIdx: idxs[i][0], + traceIdx: idxs[i][1], + }) + requests = append(requests, contractInteractionAtRequest{ + address: fmt.Sprintf("%x", tx.GetTo()), + block: int64(tx.GetBlockNumber()), + txIdx: idxs[i][0], + traceIdx: idxs[i][1], + }) + } + results, err := bigtable.GetAddressContractInteractionsAt(requests) + if err != nil { + return nil, err + } + + resultPairs := make([][2]types.ContractInteractionType, len(itransactions)) + for i, v := range results { + resultPairs[i/2][i%2] = v + } + return resultPairs, nil +} + +// convenience function to get contract interaction status per parity trace +// 2nd parameter specifies [tx_idx, trace_idx] for each internal tx +func (bigtable *Bigtable) GetAddressContractInteractionsAtParityTraces(traces []*rpc.ParityTraceResult) ([][2]types.ContractInteractionType, error) { + requests := make([]contractInteractionAtRequest, 0, len(traces)*2) + for i, itx := range traces { + from, to, _, _ := itx.ConvertFields() + requests = append(requests, contractInteractionAtRequest{ + address: fmt.Sprintf("%x", from), + block: int64(itx.BlockNumber), + txIdx: int64(itx.TransactionPosition), + traceIdx: int64(i), + }) + requests = append(requests, contractInteractionAtRequest{ + address: fmt.Sprintf("%x", to), + block: int64(itx.BlockNumber), + txIdx: int64(itx.TransactionPosition), + traceIdx: int64(i), + }) + } + results, err := bigtable.GetAddressContractInteractionsAt(requests) + if err != nil { + return nil, err + } + + resultPairs := make([][2]types.ContractInteractionType, len(traces)) + for i, v := range results { + resultPairs[i/2][i%2] = v + } + return resultPairs, nil +} + +// convenience function to get contract interaction status per transaction +func (bigtable *Bigtable) GetAddressContractInteractionsAtTransactions(transactions []*types.Eth1TransactionIndexed, idxs []int64) ([]types.ContractInteractionType, error) { + requests := make([]contractInteractionAtRequest, len(transactions)) + for i, tx := range transactions { + requests[i] = contractInteractionAtRequest{ + address: fmt.Sprintf("%x", tx.GetTo()), + block: int64(tx.GetBlockNumber()), + txIdx: idxs[i], + traceIdx: -1, + } + } + return bigtable.GetAddressContractInteractionsAt(requests) +} + func (bigtable *Bigtable) SaveAddressName(address []byte, name string) error { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30)) defer cancel() @@ -3658,7 +4022,7 @@ func (bigtable *Bigtable) SaveBlockKeys(blockNumber uint64, blockHash []byte, ke defer cancel() mut := gcp_bigtable.NewMutation() - mut.Set(METADATA_UPDATES_FAMILY_BLOCKS, "keys", gcp_bigtable.Timestamp(0), []byte(keys)) + mut.Set(METADATA_UPDATES_FAMILY_BLOCKS, "keys", gcp_bigtable.Now(), []byte(keys)) key := fmt.Sprintf("%s:BLOCK:%s:%x", bigtable.chainId, reversedPaddedBlockNumber(blockNumber), blockHash) err := bigtable.tableMetadataUpdates.Apply(ctx, key, mut) @@ -3697,14 +4061,64 @@ func (bigtable *Bigtable) GetBlockKeys(blockNumber uint64, blockHash []byte) ([] // Deletes all block data from bigtable func (bigtable *Bigtable) DeleteBlock(blockNumber uint64, blockHash []byte) error { - // First receive all keys that were written by this block (entities & indices) + // handle contract state updates + starttime, err := encodeIsContractUpdateTs(blockNumber, 0, 0) + if err != nil { + return err + } + endtime, err := encodeIsContractUpdateTs(blockNumber+1, 0, 0) + if err != nil { + return err + } + + filter := gcp_bigtable.ChainFilters( + gcp_bigtable.FamilyFilter(ACCOUNT_METADATA_FAMILY), + gcp_bigtable.ColumnFilter(ACCOUNT_IS_CONTRACT), + gcp_bigtable.TimestampRangeFilterMicros(starttime, endtime-1), + ) + + mutsDelete := &types.BulkMutations{ + Keys: make([]string, 0), + Muts: make([]*gcp_bigtable.Mutation, 0), + } + + tmr := time.AfterFunc(REPORT_TIMEOUT, func() { + logger.WithFields(logrus.Fields{ + "blockNumber": blockNumber, + }).Warnf("%s call took longer than %v", utils.GetCurrentFuncName(), REPORT_TIMEOUT) + }) + defer tmr.Stop() + + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30)) + defer cancel() + + err = bigtable.tableMetadata.ReadRows(ctx, gcp_bigtable.PrefixRange(fmt.Sprintf("%s:S:", bigtable.chainId)), func(row gcp_bigtable.Row) bool { + mutDelete := gcp_bigtable.NewMutation() + mutDelete.DeleteTimestampRange(ACCOUNT_METADATA_FAMILY, ACCOUNT_IS_CONTRACT, starttime, endtime) + + mutsDelete.Keys = append(mutsDelete.Keys, row.Key()) + mutsDelete.Muts = append(mutsDelete.Muts, mutDelete) + return true + }, gcp_bigtable.RowFilter(filter)) + if err != nil { + return err + } + + if len(mutsDelete.Keys) > 0 { + err = bigtable.WriteBulk(mutsDelete, bigtable.tableMetadata, DEFAULT_BATCH_INSERTS) + if err != nil { + return err + } + } + + // receive all keys that were written by this block (entities & indices) keys, err := bigtable.GetBlockKeys(blockNumber, blockHash) if err != nil { return err } // Delete all of those keys - mutsDelete := &types.BulkMutations{ + mutsDelete = &types.BulkMutations{ Keys: make([]string, 0, len(keys)), Muts: make([]*gcp_bigtable.Mutation, 0, len(keys)), } @@ -4046,6 +4460,18 @@ func (bigtable *Bigtable) GetMethodLabel(id []byte, invokesContract bool) string return method } +// get a method label for its byte signature with defaults +func (bigtable *Bigtable) GetAddressLabel(id string, invoke_overwrite types.ContractInteractionType) string { + switch invoke_overwrite { + case types.CONTRACT_CREATION: + return "Contract Creation" + case types.CONTRACT_DESTRUCTION: + return "Contract Destruction" + default: + return id + } +} + // get an event label for its byte signature with defaults func (bigtable *Bigtable) GetEventLabel(id []byte) string { label := "" diff --git a/db/ens.go b/db/ens.go index 9932b18164..6629d8f5ff 100644 --- a/db/ens.go +++ b/db/ens.go @@ -85,8 +85,8 @@ func (bigtable *Bigtable) TransformEnsNameRegistered(blk *types.Eth1Block, cache keys := make(map[string]bool) for i, tx := range blk.GetTransactions() { - if i > 9999 { - return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most 9999 but got: %v, tx: %x", i, tx.GetHash()) + if i >= TX_PER_BLOCK_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of transactions in block expected at most %d but got: %v, tx: %x", TX_PER_BLOCK_LIMIT-1, i, tx.GetHash()) } // We look for the different ENS events, @@ -101,8 +101,8 @@ func (bigtable *Bigtable) TransformEnsNameRegistered(blk *types.Eth1Block, cache foundNewOwnerIndex := -1 logs := tx.GetLogs() for j, log := range logs { - if j > 99999 { - return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most 99999 but got: %v tx: %x", j, tx.GetHash()) + if j >= ITX_PER_TX_LIMIT { + return nil, nil, fmt.Errorf("unexpected number of logs in block expected at most %d but got: %v tx: %x", ITX_PER_TX_LIMIT-1, j, tx.GetHash()) } for _, lTopic := range log.GetTopics() { if isRegistarContract { diff --git a/handlers/eth1Block.go b/handlers/eth1Block.go index 5af55b6ae6..a261dfb22c 100644 --- a/handlers/eth1Block.go +++ b/handlers/eth1Block.go @@ -152,7 +152,13 @@ func GetExecutionBlockPageData(number uint64, limit int) (*types.Eth1BlockPageDa lowestGasPrice := big.NewInt(1 << 62) blobTxCount := 0 blobCount := 0 - for _, tx := range block.Transactions { + + contractInteractionTypes, err := db.BigtableClient.GetAddressContractInteractionsAtBlock(block) + if err != nil { + utils.LogError(err, "error getting contract states", 0) + } + + for i, tx := range block.Transactions { if tx.Type == 3 { blobTxCount++ blobCount += len(tx.BlobVersionedHashes) @@ -171,10 +177,10 @@ func GetExecutionBlockPageData(number uint64, limit int) (*types.Eth1BlockPageDa } } + contractCreation := tx.GetTo() == nil // set tx to if tx is contract creation - if tx.To == nil && len(tx.Itx) >= 1 { - tx.To = tx.Itx[0].To - names[string(tx.To)] = "Contract Creation" + if contractCreation { + tx.To = tx.ContractAddress } method := "Transfer" @@ -187,13 +193,18 @@ func GetExecutionBlockPageData(number uint64, limit int) (*types.Eth1BlockPageDa } } + var contractInteraction types.ContractInteractionType + if len(contractInteractionTypes) > i { + contractInteraction = contractInteractionTypes[i] + } + txs = append(txs, types.Eth1BlockPageTransaction{ Hash: fmt.Sprintf("%#x", tx.Hash), HashFormatted: utils.FormatTransactionHash(tx.Hash, tx.ErrorMsg == ""), From: fmt.Sprintf("%#x", tx.From), FromFormatted: utils.FormatAddressWithLimits(tx.From, names[string(tx.From)], false, "address", 15, 20, true), To: fmt.Sprintf("%#x", tx.To), - ToFormatted: utils.FormatAddressWithLimits(tx.To, names[string(tx.To)], names[string(tx.To)] == "Contract Creation" || len(method) > 0, "address", 15, 20, true), + ToFormatted: utils.FormatAddressWithLimits(tx.To, db.BigtableClient.GetAddressLabel(names[string(tx.To)], contractInteraction), contractInteraction != types.CONTRACT_NONE, "address", 15, 20, true), Value: new(big.Int).SetBytes(tx.Value), Fee: txFee, GasPrice: effectiveGasPrice, diff --git a/handlers/eth1Transactions.go b/handlers/eth1Transactions.go index 296e217668..bbef7dc9b8 100644 --- a/handlers/eth1Transactions.go +++ b/handlers/eth1Transactions.go @@ -67,6 +67,10 @@ func getTransactionDataStartingWithPageToken(pageToken string) *types.DataTableR return nil } t := b.GetTransactions() + contractInteractionTypes, err := db.BigtableClient.GetAddressContractInteractionsAtBlock(b) + if err != nil { + utils.LogError(err, "error getting contract states", 0) + } // retrieve metadata names := make(map[string]string) @@ -83,7 +87,7 @@ func getTransactionDataStartingWithPageToken(pageToken string) *types.DataTableR } var wg errgroup.Group - for _, v := range t { + for i, v := range t { wg.Go(func() error { method := "Transfer" { @@ -94,30 +98,20 @@ func getTransactionDataStartingWithPageToken(pageToken string) *types.DataTableR method = db.BigtableClient.GetMethodLabel(m, invokesContract) } } - - var toText template.HTML - { - to := v.GetTo() - if len(to) > 0 { - toText = utils.FormatAddressWithLimits(to, names[string(v.GetTo())], false, "address", visibleDigitsForHash+5, 18, true) - } else { - itx := v.GetItx() - if len(itx) > 0 && itx[0] != nil { - to = itx[0].GetTo() - if len(to) > 0 { - toText = utils.FormatAddressWithLimits(to, "Contract Creation", true, "address", visibleDigitsForHash+5, 18, true) - } - } - } + if v.GetTo() == nil { + v.To = v.ContractAddress + } + var contractInteraction types.ContractInteractionType + if len(contractInteractionTypes) > i { + contractInteraction = contractInteractionTypes[i] } - tableData = append(tableData, []interface{}{ utils.FormatAddressWithLimits(v.GetHash(), "", false, "tx", visibleDigitsForHash+5, 18, true), utils.FormatMethod(method), template.HTML(fmt.Sprintf(`%v`, b.GetNumber(), utils.FormatAddCommas(b.GetNumber()))), utils.FormatTimestamp(b.GetTime().AsTime().Unix()), utils.FormatAddressWithLimits(v.GetFrom(), names[string(v.GetFrom())], false, "address", visibleDigitsForHash+5, 18, true), - toText, + utils.FormatAddressWithLimits(v.GetTo(), db.BigtableClient.GetAddressLabel(names[string(v.GetTo())], contractInteraction), contractInteraction != types.CONTRACT_NONE, "address", 15, 20, true), utils.FormatAmountFormatted(new(big.Int).SetBytes(v.GetValue()), utils.Config.Frontend.ElCurrency, 8, 4, true, true, false), utils.FormatAmountFormatted(db.CalculateTxFeeFromTransaction(v, new(big.Int).SetBytes(b.GetBaseFee())), utils.Config.Frontend.ElCurrency, 8, 4, true, true, false), }) diff --git a/rpc/erigon.go b/rpc/erigon.go index 05899faef4..c00902e08c 100644 --- a/rpc/erigon.go +++ b/rpc/erigon.go @@ -6,6 +6,7 @@ import ( "eth2-exporter/contracts/oneinchoracle" "eth2-exporter/erc20" "eth2-exporter/types" + "eth2-exporter/utils" "fmt" "math/big" "strings" @@ -261,27 +262,7 @@ func (client *ErigonClient) GetBlock(number int64, traceMode string) (*types.Eth Path: fmt.Sprint(trace.TraceAddress), } - if tracePb.Type == "call" { - tracePb.Type = trace.Action.CallType - } - - if trace.Type == "create" { - tracePb.From = common.FromHex(trace.Action.From) - tracePb.To = common.FromHex(trace.Result.Address) - tracePb.Value = common.FromHex(trace.Action.Value) - } else if trace.Type == "suicide" { - tracePb.From = common.FromHex(trace.Action.Address) - tracePb.To = common.FromHex(trace.Action.RefundAddress) - tracePb.Value = common.FromHex(trace.Action.Balance) - } else if trace.Type == "call" { - tracePb.From = common.FromHex(trace.Action.From) - tracePb.To = common.FromHex(trace.Action.To) - tracePb.Value = common.FromHex(trace.Action.Value) - } else { - spew.Dump(trace) - logrus.Fatalf("unknown trace type %v in tx %v", trace.Type, trace.TransactionHash) - } - + tracePb.From, tracePb.To, tracePb.Value, tracePb.Type = trace.ConvertFields() c.Transactions[trace.TransactionPosition].Itx = append(c.Transactions[trace.TransactionPosition].Itx, tracePb) } } @@ -494,6 +475,31 @@ type ParityTraceResult struct { Type string `json:"type"` } +func (trace *ParityTraceResult) ConvertFields() ([]byte, []byte, []byte, string) { + var from, to, value []byte + tx_type := trace.Type + + switch trace.Type { + case "create": + from = common.FromHex(trace.Action.From) + to = common.FromHex(trace.Result.Address) + value = common.FromHex(trace.Action.Value) + case "suicide": + from = common.FromHex(trace.Action.Address) + to = common.FromHex(trace.Action.RefundAddress) + value = common.FromHex(trace.Action.Balance) + case "call": + from = common.FromHex(trace.Action.From) + to = common.FromHex(trace.Action.To) + value = common.FromHex(trace.Action.Value) + tx_type = trace.Action.CallType + default: + spew.Dump(trace) + utils.LogFatal(nil, "unknown trace type", 0, map[string]interface{}{"trace type": trace.Type, "tx hash": trace.TransactionHash}) + } + return from, to, value, tx_type +} + func (client *ErigonClient) TraceParity(blockNumber uint64) ([]*ParityTraceResult, error) { var res []*ParityTraceResult diff --git a/templates/execution/address.html b/templates/execution/address.html index c970afdeda..94181bff54 100644 --- a/templates/execution/address.html +++ b/templates/execution/address.html @@ -143,7 +143,7 @@ } .tbl-col-content { - max-width: 200px; + max-width: 210px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/templates/execution/transactions.html b/templates/execution/transactions.html index 7167315016..75f326828e 100644 --- a/templates/execution/transactions.html +++ b/templates/execution/transactions.html @@ -274,7 +274,7 @@ } .tbl-col-content { - max-width: 200px; + max-width: 210px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/types/eth1.pb.go b/types/eth1.pb.go index 8909f2ae17..8275684c19 100644 --- a/types/eth1.pb.go +++ b/types/eth1.pb.go @@ -544,6 +544,61 @@ func (x *Eth1Transaction) GetBlobGasUsed() uint64 { return 0 } +type IsContractUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IsContract bool `protobuf:"varint,1,opt,name=is_contract,json=isContract,proto3" json:"is_contract,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` +} + +func (x *IsContractUpdate) Reset() { + *x = IsContractUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_eth1_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IsContractUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsContractUpdate) ProtoMessage() {} + +func (x *IsContractUpdate) ProtoReflect() protoreflect.Message { + mi := &file_eth1_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsContractUpdate.ProtoReflect.Descriptor instead. +func (*IsContractUpdate) Descriptor() ([]byte, []int) { + return file_eth1_proto_rawDescGZIP(), []int{3} +} + +func (x *IsContractUpdate) GetIsContract() bool { + if x != nil { + return x.IsContract + } + return false +} + +func (x *IsContractUpdate) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + type AccessList struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -556,7 +611,7 @@ type AccessList struct { func (x *AccessList) Reset() { *x = AccessList{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[3] + mi := &file_eth1_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -569,7 +624,7 @@ func (x *AccessList) String() string { func (*AccessList) ProtoMessage() {} func (x *AccessList) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[3] + mi := &file_eth1_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -582,7 +637,7 @@ func (x *AccessList) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessList.ProtoReflect.Descriptor instead. func (*AccessList) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{3} + return file_eth1_proto_rawDescGZIP(), []int{4} } func (x *AccessList) GetAddress() []byte { @@ -613,7 +668,7 @@ type Eth1Log struct { func (x *Eth1Log) Reset() { *x = Eth1Log{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[4] + mi := &file_eth1_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -626,7 +681,7 @@ func (x *Eth1Log) String() string { func (*Eth1Log) ProtoMessage() {} func (x *Eth1Log) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[4] + mi := &file_eth1_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -639,7 +694,7 @@ func (x *Eth1Log) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1Log.ProtoReflect.Descriptor instead. func (*Eth1Log) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{4} + return file_eth1_proto_rawDescGZIP(), []int{5} } func (x *Eth1Log) GetAddress() []byte { @@ -686,7 +741,7 @@ type Eth1InternalTransaction struct { func (x *Eth1InternalTransaction) Reset() { *x = Eth1InternalTransaction{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[5] + mi := &file_eth1_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -699,7 +754,7 @@ func (x *Eth1InternalTransaction) String() string { func (*Eth1InternalTransaction) ProtoMessage() {} func (x *Eth1InternalTransaction) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[5] + mi := &file_eth1_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -712,7 +767,7 @@ func (x *Eth1InternalTransaction) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1InternalTransaction.ProtoReflect.Descriptor instead. func (*Eth1InternalTransaction) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{5} + return file_eth1_proto_rawDescGZIP(), []int{6} } func (x *Eth1InternalTransaction) GetType() string { @@ -792,7 +847,7 @@ type Eth1BlockIndexed struct { func (x *Eth1BlockIndexed) Reset() { *x = Eth1BlockIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[6] + mi := &file_eth1_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -805,7 +860,7 @@ func (x *Eth1BlockIndexed) String() string { func (*Eth1BlockIndexed) ProtoMessage() {} func (x *Eth1BlockIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[6] + mi := &file_eth1_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -818,7 +873,7 @@ func (x *Eth1BlockIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1BlockIndexed.ProtoReflect.Descriptor instead. func (*Eth1BlockIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{6} + return file_eth1_proto_rawDescGZIP(), []int{7} } func (x *Eth1BlockIndexed) GetHash() []byte { @@ -986,7 +1041,7 @@ type Eth1UncleIndexed struct { func (x *Eth1UncleIndexed) Reset() { *x = Eth1UncleIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[7] + mi := &file_eth1_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -999,7 +1054,7 @@ func (x *Eth1UncleIndexed) String() string { func (*Eth1UncleIndexed) ProtoMessage() {} func (x *Eth1UncleIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[7] + mi := &file_eth1_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1012,7 +1067,7 @@ func (x *Eth1UncleIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1UncleIndexed.ProtoReflect.Descriptor instead. func (*Eth1UncleIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{7} + return file_eth1_proto_rawDescGZIP(), []int{8} } func (x *Eth1UncleIndexed) GetBlockNumber() uint64 { @@ -1087,7 +1142,7 @@ type Eth1WithdrawalIndexed struct { func (x *Eth1WithdrawalIndexed) Reset() { *x = Eth1WithdrawalIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[8] + mi := &file_eth1_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1100,7 +1155,7 @@ func (x *Eth1WithdrawalIndexed) String() string { func (*Eth1WithdrawalIndexed) ProtoMessage() {} func (x *Eth1WithdrawalIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[8] + mi := &file_eth1_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1113,7 +1168,7 @@ func (x *Eth1WithdrawalIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1WithdrawalIndexed.ProtoReflect.Descriptor instead. func (*Eth1WithdrawalIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{8} + return file_eth1_proto_rawDescGZIP(), []int{9} } func (x *Eth1WithdrawalIndexed) GetBlockNumber() uint64 { @@ -1173,8 +1228,9 @@ type Eth1TransactionIndexed struct { TxFee []byte `protobuf:"bytes,8,opt,name=tx_fee,json=txFee,proto3" json:"tx_fee,omitempty"` GasPrice []byte `protobuf:"bytes,9,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` IsContractCreation bool `protobuf:"varint,10,opt,name=is_contract_creation,json=isContractCreation,proto3" json:"is_contract_creation,omitempty"` - InvokesContract bool `protobuf:"varint,11,opt,name=invokes_contract,json=invokesContract,proto3" json:"invokes_contract,omitempty"` - ErrorMsg string `protobuf:"bytes,12,opt,name=error_msg,json=errorMsg,proto3" json:"error_msg,omitempty"` + // invokes_contract is unused, should mark reserved! + InvokesContract bool `protobuf:"varint,11,opt,name=invokes_contract,json=invokesContract,proto3" json:"invokes_contract,omitempty"` + ErrorMsg string `protobuf:"bytes,12,opt,name=error_msg,json=errorMsg,proto3" json:"error_msg,omitempty"` // EIP 4844 BlobTxFee []byte `protobuf:"bytes,13,opt,name=blob_tx_fee,json=blobTxFee,proto3" json:"blob_tx_fee,omitempty"` BlobGasPrice []byte `protobuf:"bytes,14,opt,name=blob_gas_price,json=blobGasPrice,proto3" json:"blob_gas_price,omitempty"` @@ -1183,7 +1239,7 @@ type Eth1TransactionIndexed struct { func (x *Eth1TransactionIndexed) Reset() { *x = Eth1TransactionIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[9] + mi := &file_eth1_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1196,7 +1252,7 @@ func (x *Eth1TransactionIndexed) String() string { func (*Eth1TransactionIndexed) ProtoMessage() {} func (x *Eth1TransactionIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[9] + mi := &file_eth1_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1209,7 +1265,7 @@ func (x *Eth1TransactionIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1TransactionIndexed.ProtoReflect.Descriptor instead. func (*Eth1TransactionIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{9} + return file_eth1_proto_rawDescGZIP(), []int{10} } func (x *Eth1TransactionIndexed) GetHash() []byte { @@ -1327,7 +1383,7 @@ type Eth1InternalTransactionIndexed struct { func (x *Eth1InternalTransactionIndexed) Reset() { *x = Eth1InternalTransactionIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[10] + mi := &file_eth1_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1340,7 +1396,7 @@ func (x *Eth1InternalTransactionIndexed) String() string { func (*Eth1InternalTransactionIndexed) ProtoMessage() {} func (x *Eth1InternalTransactionIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[10] + mi := &file_eth1_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1353,7 +1409,7 @@ func (x *Eth1InternalTransactionIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1InternalTransactionIndexed.ProtoReflect.Descriptor instead. func (*Eth1InternalTransactionIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{10} + return file_eth1_proto_rawDescGZIP(), []int{11} } func (x *Eth1InternalTransactionIndexed) GetParentHash() []byte { @@ -1421,7 +1477,6 @@ type Eth1BlobTransactionIndexed struct { GasPrice []byte `protobuf:"bytes,8,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` BlobTxFee []byte `protobuf:"bytes,9,opt,name=blob_tx_fee,json=blobTxFee,proto3" json:"blob_tx_fee,omitempty"` BlobGasPrice []byte `protobuf:"bytes,10,opt,name=blob_gas_price,json=blobGasPrice,proto3" json:"blob_gas_price,omitempty"` - InvokesContract bool `protobuf:"varint,11,opt,name=invokes_contract,json=invokesContract,proto3" json:"invokes_contract,omitempty"` ErrorMsg string `protobuf:"bytes,12,opt,name=error_msg,json=errorMsg,proto3" json:"error_msg,omitempty"` BlobVersionedHashes [][]byte `protobuf:"bytes,13,rep,name=blob_versioned_hashes,json=blobVersionedHashes,proto3" json:"blob_versioned_hashes,omitempty"` } @@ -1429,7 +1484,7 @@ type Eth1BlobTransactionIndexed struct { func (x *Eth1BlobTransactionIndexed) Reset() { *x = Eth1BlobTransactionIndexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[11] + mi := &file_eth1_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1442,7 +1497,7 @@ func (x *Eth1BlobTransactionIndexed) String() string { func (*Eth1BlobTransactionIndexed) ProtoMessage() {} func (x *Eth1BlobTransactionIndexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[11] + mi := &file_eth1_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1455,7 +1510,7 @@ func (x *Eth1BlobTransactionIndexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1BlobTransactionIndexed.ProtoReflect.Descriptor instead. func (*Eth1BlobTransactionIndexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{11} + return file_eth1_proto_rawDescGZIP(), []int{12} } func (x *Eth1BlobTransactionIndexed) GetHash() []byte { @@ -1528,13 +1583,6 @@ func (x *Eth1BlobTransactionIndexed) GetBlobGasPrice() []byte { return nil } -func (x *Eth1BlobTransactionIndexed) GetInvokesContract() bool { - if x != nil { - return x.InvokesContract - } - return false -} - func (x *Eth1BlobTransactionIndexed) GetErrorMsg() string { if x != nil { return x.ErrorMsg @@ -1566,7 +1614,7 @@ type Eth1ERC20Indexed struct { func (x *Eth1ERC20Indexed) Reset() { *x = Eth1ERC20Indexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[12] + mi := &file_eth1_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1579,7 +1627,7 @@ func (x *Eth1ERC20Indexed) String() string { func (*Eth1ERC20Indexed) ProtoMessage() {} func (x *Eth1ERC20Indexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[12] + mi := &file_eth1_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1592,7 +1640,7 @@ func (x *Eth1ERC20Indexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1ERC20Indexed.ProtoReflect.Descriptor instead. func (*Eth1ERC20Indexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{12} + return file_eth1_proto_rawDescGZIP(), []int{13} } func (x *Eth1ERC20Indexed) GetParentHash() []byte { @@ -1661,7 +1709,7 @@ type Eth1ERC721Indexed struct { func (x *Eth1ERC721Indexed) Reset() { *x = Eth1ERC721Indexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[13] + mi := &file_eth1_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1674,7 +1722,7 @@ func (x *Eth1ERC721Indexed) String() string { func (*Eth1ERC721Indexed) ProtoMessage() {} func (x *Eth1ERC721Indexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[13] + mi := &file_eth1_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1687,7 +1735,7 @@ func (x *Eth1ERC721Indexed) ProtoReflect() protoreflect.Message { // Deprecated: Use Eth1ERC721Indexed.ProtoReflect.Descriptor instead. func (*Eth1ERC721Indexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{13} + return file_eth1_proto_rawDescGZIP(), []int{14} } func (x *Eth1ERC721Indexed) GetParentHash() []byte { @@ -1760,7 +1808,7 @@ type ETh1ERC1155Indexed struct { func (x *ETh1ERC1155Indexed) Reset() { *x = ETh1ERC1155Indexed{} if protoimpl.UnsafeEnabled { - mi := &file_eth1_proto_msgTypes[14] + mi := &file_eth1_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1773,7 +1821,7 @@ func (x *ETh1ERC1155Indexed) String() string { func (*ETh1ERC1155Indexed) ProtoMessage() {} func (x *ETh1ERC1155Indexed) ProtoReflect() protoreflect.Message { - mi := &file_eth1_proto_msgTypes[14] + mi := &file_eth1_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1786,7 +1834,7 @@ func (x *ETh1ERC1155Indexed) ProtoReflect() protoreflect.Message { // Deprecated: Use ETh1ERC1155Indexed.ProtoReflect.Descriptor instead. func (*ETh1ERC1155Indexed) Descriptor() ([]byte, []int) { - return file_eth1_proto_rawDescGZIP(), []int{14} + return file_eth1_proto_rawDescGZIP(), []int{15} } func (x *ETh1ERC1155Indexed) GetParentHash() []byte { @@ -1963,225 +2011,227 @@ var file_eth1_proto_rawDesc = []byte{ 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, - 0x55, 0x73, 0x65, 0x64, 0x22, 0x49, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x22, - 0x69, 0x0a, 0x07, 0x45, 0x74, 0x68, 0x31, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x17, 0x45, - 0x74, 0x68, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, - 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, - 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, - 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xf3, 0x05, 0x0a, 0x10, 0x45, 0x74, 0x68, 0x31, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, - 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x1d, 0x0a, 0x0a, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, - 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, - 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, - 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, - 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x75, 0x6e, 0x63, 0x6c, - 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x65, 0x76, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x6d, 0x65, 0x76, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x5f, - 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0e, 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, - 0x2a, 0x0a, 0x11, 0x68, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x68, 0x69, 0x67, 0x68, - 0x65, 0x73, 0x74, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, - 0x78, 0x5f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x74, 0x78, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x63, 0x6c, - 0x65, 0x5f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x18, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, - 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x26, 0x0a, - 0x0f, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, - 0x18, 0x1f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6c, - 0x6f, 0x62, 0x47, 0x61, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x20, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x88, 0x02, 0x0a, 0x10, - 0x45, 0x74, 0x68, 0x31, 0x55, 0x6e, 0x63, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x67, - 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, - 0x75, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, - 0x73, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1e, - 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x2e, - 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, - 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x22, 0xdb, 0x01, 0x0a, 0x15, 0x45, 0x74, 0x68, 0x31, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x55, 0x73, 0x65, 0x64, 0x22, 0x4d, 0x0a, 0x10, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, + 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x69, + 0x0a, 0x07, 0x45, 0x74, 0x68, 0x31, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x17, 0x45, 0x74, + 0x68, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, + 0x02, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x22, 0xf3, 0x05, 0x0a, 0x10, 0x45, 0x74, 0x68, 0x31, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, + 0x0a, 0x0a, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, + 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x64, + 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x6e, 0x63, 0x6c, 0x65, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x75, 0x6e, 0x63, 0x6c, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x65, 0x76, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x03, 0x6d, 0x65, 0x76, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x5f, 0x67, + 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, + 0x6c, 0x6f, 0x77, 0x65, 0x73, 0x74, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x2a, + 0x0a, 0x11, 0x68, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x68, 0x69, 0x67, 0x68, 0x65, + 0x73, 0x74, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x78, + 0x5f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x74, + 0x78, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x63, 0x6c, 0x65, + 0x5f, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x75, + 0x6e, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, + 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0f, + 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x18, + 0x1f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, + 0x62, 0x47, 0x61, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x20, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x88, 0x02, 0x0a, 0x10, 0x45, + 0x74, 0x68, 0x31, 0x55, 0x6e, 0x63, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, + 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, + 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, + 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, + 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x2e, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, + 0x65, 0x77, 0x61, 0x72, 0x64, 0x22, 0xdb, 0x01, 0x0a, 0x15, 0x45, 0x74, 0x68, 0x31, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x22, 0xca, 0x03, 0x0a, 0x16, 0x45, 0x74, 0x68, 0x31, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x15, 0x0a, 0x06, + 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x78, + 0x46, 0x65, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, + 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, + 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x1e, 0x0a, 0x0b, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x78, 0x46, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, + 0x22, 0xe2, 0x01, 0x0a, 0x1e, 0x45, 0x74, 0x68, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, + 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8e, 0x03, 0x0a, 0x1a, 0x45, 0x74, 0x68, 0x31, 0x42, 0x6c, + 0x6f, 0x62, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, + 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x78, 0x46, 0x65, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, + 0x62, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x78, 0x46, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, + 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x32, 0x0a, 0x15, + 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x13, 0x62, 0x6c, 0x6f, + 0x62, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x22, 0xe5, 0x01, 0x0a, 0x10, 0x45, 0x74, 0x68, 0x31, 0x45, + 0x52, 0x43, 0x32, 0x30, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x22, 0xca, 0x03, 0x0a, 0x16, 0x45, 0x74, 0x68, 0x31, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, - 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x15, 0x0a, - 0x06, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, - 0x78, 0x46, 0x65, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, - 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x12, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, - 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x1e, 0x0a, 0x0b, 0x62, - 0x6c, 0x6f, 0x62, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x78, 0x46, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x62, - 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x22, 0xe2, 0x01, 0x0a, 0x1e, 0x45, 0x74, 0x68, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xeb, + 0x01, 0x0a, 0x11, 0x45, 0x74, 0x68, 0x31, 0x45, 0x52, 0x43, 0x37, 0x32, 0x31, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x04, + 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, + 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, + 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x22, 0x9e, 0x02, 0x0a, + 0x12, 0x45, 0x54, 0x68, 0x31, 0x45, 0x52, 0x43, 0x31, 0x31, 0x35, 0x35, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb3, 0x03, 0x0a, 0x1a, 0x45, 0x74, 0x68, 0x31, 0x42, - 0x6c, 0x6f, 0x62, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, - 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x78, 0x46, 0x65, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x62, 0x6c, - 0x6f, 0x62, 0x5f, 0x74, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x54, 0x78, 0x46, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x6c, - 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x61, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x6f, - 0x6b, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x62, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, - 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, - 0x10, 0x45, 0x74, 0x68, 0x31, 0x45, 0x52, 0x43, 0x32, 0x30, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, - 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x11, 0x45, 0x74, 0x68, 0x31, 0x45, 0x52, 0x43, - 0x37, 0x32, 0x31, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, - 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, - 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x49, 0x64, 0x22, 0x9e, 0x02, 0x0a, 0x12, 0x45, 0x54, 0x68, 0x31, 0x45, 0x52, 0x43, 0x31, 0x31, - 0x35, 0x35, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, - 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, - 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x09, 0x5a, + 0x07, 0x2e, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2196,42 +2246,43 @@ func file_eth1_proto_rawDescGZIP() []byte { return file_eth1_proto_rawDescData } -var file_eth1_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_eth1_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_eth1_proto_goTypes = []interface{}{ (*Eth1Block)(nil), // 0: types.Eth1Block (*Eth1Withdrawal)(nil), // 1: types.Eth1Withdrawal (*Eth1Transaction)(nil), // 2: types.Eth1Transaction - (*AccessList)(nil), // 3: types.AccessList - (*Eth1Log)(nil), // 4: types.Eth1Log - (*Eth1InternalTransaction)(nil), // 5: types.Eth1InternalTransaction - (*Eth1BlockIndexed)(nil), // 6: types.Eth1BlockIndexed - (*Eth1UncleIndexed)(nil), // 7: types.Eth1UncleIndexed - (*Eth1WithdrawalIndexed)(nil), // 8: types.Eth1WithdrawalIndexed - (*Eth1TransactionIndexed)(nil), // 9: types.Eth1TransactionIndexed - (*Eth1InternalTransactionIndexed)(nil), // 10: types.Eth1InternalTransactionIndexed - (*Eth1BlobTransactionIndexed)(nil), // 11: types.Eth1BlobTransactionIndexed - (*Eth1ERC20Indexed)(nil), // 12: types.Eth1ERC20Indexed - (*Eth1ERC721Indexed)(nil), // 13: types.Eth1ERC721Indexed - (*ETh1ERC1155Indexed)(nil), // 14: types.ETh1ERC1155Indexed - (*timestamp.Timestamp)(nil), // 15: google.protobuf.Timestamp + (*IsContractUpdate)(nil), // 3: types.IsContractUpdate + (*AccessList)(nil), // 4: types.AccessList + (*Eth1Log)(nil), // 5: types.Eth1Log + (*Eth1InternalTransaction)(nil), // 6: types.Eth1InternalTransaction + (*Eth1BlockIndexed)(nil), // 7: types.Eth1BlockIndexed + (*Eth1UncleIndexed)(nil), // 8: types.Eth1UncleIndexed + (*Eth1WithdrawalIndexed)(nil), // 9: types.Eth1WithdrawalIndexed + (*Eth1TransactionIndexed)(nil), // 10: types.Eth1TransactionIndexed + (*Eth1InternalTransactionIndexed)(nil), // 11: types.Eth1InternalTransactionIndexed + (*Eth1BlobTransactionIndexed)(nil), // 12: types.Eth1BlobTransactionIndexed + (*Eth1ERC20Indexed)(nil), // 13: types.Eth1ERC20Indexed + (*Eth1ERC721Indexed)(nil), // 14: types.Eth1ERC721Indexed + (*ETh1ERC1155Indexed)(nil), // 15: types.ETh1ERC1155Indexed + (*timestamp.Timestamp)(nil), // 16: google.protobuf.Timestamp } var file_eth1_proto_depIdxs = []int32{ - 15, // 0: types.Eth1Block.time:type_name -> google.protobuf.Timestamp + 16, // 0: types.Eth1Block.time:type_name -> google.protobuf.Timestamp 0, // 1: types.Eth1Block.uncles:type_name -> types.Eth1Block 2, // 2: types.Eth1Block.transactions:type_name -> types.Eth1Transaction 1, // 3: types.Eth1Block.withdrawals:type_name -> types.Eth1Withdrawal - 3, // 4: types.Eth1Transaction.access_list:type_name -> types.AccessList - 4, // 5: types.Eth1Transaction.logs:type_name -> types.Eth1Log - 5, // 6: types.Eth1Transaction.itx:type_name -> types.Eth1InternalTransaction - 15, // 7: types.Eth1BlockIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 8: types.Eth1UncleIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 9: types.Eth1WithdrawalIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 10: types.Eth1TransactionIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 11: types.Eth1InternalTransactionIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 12: types.Eth1BlobTransactionIndexed.time:type_name -> google.protobuf.Timestamp - 15, // 13: types.Eth1ERC20Indexed.time:type_name -> google.protobuf.Timestamp - 15, // 14: types.Eth1ERC721Indexed.time:type_name -> google.protobuf.Timestamp - 15, // 15: types.ETh1ERC1155Indexed.time:type_name -> google.protobuf.Timestamp + 4, // 4: types.Eth1Transaction.access_list:type_name -> types.AccessList + 5, // 5: types.Eth1Transaction.logs:type_name -> types.Eth1Log + 6, // 6: types.Eth1Transaction.itx:type_name -> types.Eth1InternalTransaction + 16, // 7: types.Eth1BlockIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 8: types.Eth1UncleIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 9: types.Eth1WithdrawalIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 10: types.Eth1TransactionIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 11: types.Eth1InternalTransactionIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 12: types.Eth1BlobTransactionIndexed.time:type_name -> google.protobuf.Timestamp + 16, // 13: types.Eth1ERC20Indexed.time:type_name -> google.protobuf.Timestamp + 16, // 14: types.Eth1ERC721Indexed.time:type_name -> google.protobuf.Timestamp + 16, // 15: types.ETh1ERC1155Indexed.time:type_name -> google.protobuf.Timestamp 16, // [16:16] is the sub-list for method output_type 16, // [16:16] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name @@ -2282,7 +2333,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccessList); i { + switch v := v.(*IsContractUpdate); i { case 0: return &v.state case 1: @@ -2294,7 +2345,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1Log); i { + switch v := v.(*AccessList); i { case 0: return &v.state case 1: @@ -2306,7 +2357,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1InternalTransaction); i { + switch v := v.(*Eth1Log); i { case 0: return &v.state case 1: @@ -2318,7 +2369,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1BlockIndexed); i { + switch v := v.(*Eth1InternalTransaction); i { case 0: return &v.state case 1: @@ -2330,7 +2381,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1UncleIndexed); i { + switch v := v.(*Eth1BlockIndexed); i { case 0: return &v.state case 1: @@ -2342,7 +2393,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1WithdrawalIndexed); i { + switch v := v.(*Eth1UncleIndexed); i { case 0: return &v.state case 1: @@ -2354,7 +2405,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1TransactionIndexed); i { + switch v := v.(*Eth1WithdrawalIndexed); i { case 0: return &v.state case 1: @@ -2366,7 +2417,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1InternalTransactionIndexed); i { + switch v := v.(*Eth1TransactionIndexed); i { case 0: return &v.state case 1: @@ -2378,7 +2429,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1BlobTransactionIndexed); i { + switch v := v.(*Eth1InternalTransactionIndexed); i { case 0: return &v.state case 1: @@ -2390,7 +2441,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1ERC20Indexed); i { + switch v := v.(*Eth1BlobTransactionIndexed); i { case 0: return &v.state case 1: @@ -2402,7 +2453,7 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Eth1ERC721Indexed); i { + switch v := v.(*Eth1ERC20Indexed); i { case 0: return &v.state case 1: @@ -2414,6 +2465,18 @@ func file_eth1_proto_init() { } } file_eth1_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Eth1ERC721Indexed); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_eth1_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ETh1ERC1155Indexed); i { case 0: return &v.state @@ -2432,7 +2495,7 @@ func file_eth1_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_eth1_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, diff --git a/types/eth1.proto b/types/eth1.proto index c6782edfcd..db65049fdc 100644 --- a/types/eth1.proto +++ b/types/eth1.proto @@ -78,6 +78,11 @@ message Eth1Transaction { uint64 blob_gas_used = 28; } +message IsContractUpdate { + bool is_contract = 1; + bool success = 2; +} + message AccessList { bytes address = 1; repeated bytes storage_keys = 2; @@ -161,6 +166,7 @@ message Eth1TransactionIndexed { bytes tx_fee = 8; bytes gas_price = 9; bool is_contract_creation = 10; + // invokes_contract is unused, should mark reserved! bool invokes_contract = 11; string error_msg = 12; @@ -181,6 +187,8 @@ message Eth1InternalTransactionIndexed { // https://eips.ethereum.org/EIPS/eip-4844 message Eth1BlobTransactionIndexed { + reserved 11; // invokes_contract + bytes hash = 1; uint64 block_number = 2; google.protobuf.Timestamp time = 3; @@ -191,7 +199,7 @@ message Eth1BlobTransactionIndexed { bytes gas_price = 8; bytes blob_tx_fee = 9; bytes blob_gas_price = 10; - bool invokes_contract = 11; + string error_msg = 12; repeated bytes blob_versioned_hashes = 13; } diff --git a/types/templates.go b/types/templates.go index 606ea69679..8818b6106e 100644 --- a/types/templates.go +++ b/types/templates.go @@ -1594,6 +1594,15 @@ type Eth1AddressPageData struct { Tabs []Eth1AddressPageTabs } +type ContractInteractionType uint8 + +const ( + CONTRACT_NONE ContractInteractionType = 0 + CONTRACT_CREATION ContractInteractionType = 1 + CONTRACT_PRESENT ContractInteractionType = 2 + CONTRACT_DESTRUCTION ContractInteractionType = 3 +) + type Eth1AddressPageTabs struct { Id string Href string