diff --git a/cmd/genesis-writer/Makefile b/cmd/genesis-writer/Makefile new file mode 100644 index 00000000..3a82c014 --- /dev/null +++ b/cmd/genesis-writer/Makefile @@ -0,0 +1,6 @@ +.PHONY: test +test: + @if [ -z "$(OPENAUDIO_CI)" ]; then \ + $(MAKE) -C ../.. docker-dev; \ + fi + go test -v -tags integration -run TestGenesisWriter -timeout 30m ./... diff --git a/cmd/genesis-writer/README.md b/cmd/genesis-writer/README.md new file mode 100644 index 00000000..b2237b29 --- /dev/null +++ b/cmd/genesis-writer/README.md @@ -0,0 +1,280 @@ +# genesis-writer + +Populates a new Core chain with full historical Audius state by writing +synthetic blocks **directly to PostgreSQL**, without going through consensus. +After writing, it primes CometBFT's `state.db` and `blockstore.db` so a +single node can start from the written height and immediately propose the +next live block. + +This is the offline alternative to `genesis-replay`. Where `genesis-replay` +requires a running network and submits every entity through `ForwardTransaction`, +`genesis-writer` works entirely against a database: no network is needed during +the main migration work. + +## How it works + +1. **Read** — streams every current, non-deleted entity from a source + Discovery Provider PostgreSQL database, ordered by entity type (users → + tracks → playlists → social → plays) to satisfy indexer dependencies. + +2. **Sign** — wraps each entity in a `ManageEntityLegacyMigration` proto and signs it + with the genesis migration keypair using EIP-712 (same structure as `ManageEntityLegacy`, + same keypair and domain as `genesis-replay`). The distinct proto type signals to indexers + that only the genesis migration authority needs to be verified — ownership, wallet-uniqueness, + and handle checks must not be applied. + +3. **Pack** — accumulates signed transactions into real CometBFT blocks + (via `MakeBlock`) up to `--max-txs-per-block`, signed with the + validator's ed25519 key. + +4. **Write** — inserts into `core_blocks`, `core_transactions`, and + `core_app_state` in a single PostgreSQL transaction per block. + +5. **Prime** — writes CometBFT `state.db` (via `Bootstrap`) and + `blockstore.db` (via `SaveBlock(block, parts, seenCommit)`, with the + `seenCommit` signed by the validator's ed25519 key) so the genesis node + can start from height N and propose N+1. Also updates `genesis.json` + with the migration address and end height. + +## Quick start + +The simplest invocation only needs a source database and a data directory. +Everything else is auto-generated: + +```bash +genesis-writer write \ + --src-dsn "postgres://user@host:5432/audius_dp?sslmode=disable" \ + --data-dir /data/genesis-output \ + --chain-id audius-mainnet-beta \ + --network prod +``` + +This will: +- Start a managed PostgreSQL instance at `/postgres/` +- Run Core chain schema migrations automatically +- Generate a migration Ethereum keypair (saved for resume, deleted on success) +- Generate `genesis.json` and `priv_validator_key.json` with production + consensus params (15MB max block size) +- Write all entities as synthetic blocks to `/core//` +- Write CometBFT `state.db` and `blockstore.db` +- Update `genesis.json` with the migration address and end height +- Print next-steps instructions for running a node + +## Usage + +``` +genesis-writer write \ + --src-dsn \ # required + --data-dir \ # required if --dst-dsn omitted + [--dst-dsn ] \ # optional: use external postgres + [--private-key ] \ # optional: auto-generated if omitted + [--genesis-file ] \ # optional: auto-generated if omitted + [--priv-validator-key-file ] \ # optional: auto-generated if omitted + [--network prod|stage|dev] \ # default: prod + [--chain-id audius-mainnet-beta] \ # default: audius-mainnet-beta + [--genesis-time 2025-01-01T00:00:00Z] \ # default: now + [--max-txs-per-block 10000] \ # default: 10000 + [--batch-size 1000] \ # default: 1000 + [--run-migrations] \ # auto-enabled for managed postgres + [--resume] \ # resume interrupted run + [--skip-users] [--skip-wallets] [--skip-tracks] [--skip-playlists] \ + [--skip-social] [--skip-plays] [--skip-apps] [--skip-comments] \ + [--skip-emails] [--skip-tip-reactions] +``` + +### Flags reference + +| Flag | Env var | Default | Description | +|------|---------|---------|-------------| +| `--src-dsn` | `GENESIS_SRC_DSN` | — | Source DP PostgreSQL DSN (**required**) | +| `--data-dir` | `GENESIS_DATA_DIR` | — | Root data directory. CometBFT state → `/core//`, postgres → `/postgres/` | +| `--dst-dsn` | `GENESIS_DST_DSN` | — | Target Core chain PostgreSQL DSN. If omitted, a local postgres is started at `/postgres/` | +| `--private-key` | `GENESIS_MIGRATION_PRIVATE_KEY` | auto-generated | Genesis migration Ethereum key (hex, with or without `0x`). If omitted, a key is generated and saved to `/genesis_migration_key.hex` for resume | +| `--network` | `NETWORK` | `prod` | EIP-712 signing domain (`prod`, `stage`, `dev`) | +| `--chain-id` | `CHAIN_ID` | `audius-mainnet-beta` | Core chain ID | +| `--genesis-time` | `GENESIS_TIME` | now | Chain genesis timestamp (RFC3339) | +| `--genesis-file` | `GENESIS_FILE` | `/core//config/genesis.json` | Path to CometBFT `genesis.json`. Auto-generated with production consensus params if it doesn't exist | +| `--priv-validator-key-file` | `PRIV_VALIDATOR_KEY_FILE` | `/core//config/priv_validator_key.json` | Path to `priv_validator_key.json`. Auto-generated if it doesn't exist | +| `--max-txs-per-block` | `GENESIS_MAX_TXS_PER_BLOCK` | `10000` | Transactions per synthetic block | +| `--batch-size` | `GENESIS_BATCH_SIZE` | `1000` | Rows fetched from source DB per query | +| `--run-migrations` | — | false | Apply the Core chain schema before writing (auto-enabled for managed postgres) | +| `--resume` | — | false | Resume from the last completed step of a previous run | +| `--skip-users` | `GENESIS_SKIP_USERS` | false | Skip user migration | +| `--skip-wallets` | `GENESIS_SKIP_WALLETS` | false | Skip associated wallets and dashboard wallet users | +| `--skip-tracks` | `GENESIS_SKIP_TRACKS` | false | Skip track migration | +| `--skip-playlists` | `GENESIS_SKIP_PLAYLISTS` | false | Skip playlist migration | +| `--skip-social` | `GENESIS_SKIP_SOCIAL` | false | Skip follows, saves, reposts, subscriptions, muted users | +| `--skip-plays` | `GENESIS_SKIP_PLAYS` | false | Skip play migration | +| `--skip-apps` | `GENESIS_SKIP_APPS` | false | Skip developer apps and grants | +| `--skip-comments` | `GENESIS_SKIP_COMMENTS` | false | Skip comments and comment reactions | +| `--skip-emails` | `GENESIS_SKIP_EMAILS` | false | Skip encrypted emails and email access grants | +| `--skip-tip-reactions` | `GENESIS_SKIP_TIP_REACTIONS` | false | Skip tip reactions | + +### Data directory layout + +When using `--data-dir`, the genesis-writer produces the same directory +layout as a production node: + +``` +/ +├── core/ +│ └── / +│ ├── config/ +│ │ ├── genesis.json +│ │ └── priv_validator_key.json +│ └── data/ +│ ├── blockstore.db/ +│ ├── state.db/ +│ └── priv_validator_state.json +└── postgres/ + └── +``` + +### Managed postgres + +When `--dst-dsn` is omitted, the genesis-writer starts its own PostgreSQL +instance at `/postgres/` using the system's `pg_ctl`. It: + +- Initializes a new cluster if none exists (`initdb`) +- Starts an existing cluster if stopped +- Reuses an already-running cluster +- Configures for bulk-load performance (fsync off, large WAL, trust auth) +- Runs Core chain schema migrations automatically +- Stops the cluster on exit + +The managed postgres runs on port 5440 to avoid conflicts with system postgres. + +### Resume + +Pass `--resume` to pick up from a previous interrupted run. The writer +tracks completed steps in a `genesis_writer_progress` table and recovers +chain state (height, app hash, block hash) from the database. The +auto-generated migration key is persisted to disk and reloaded on resume. + +**Note:** Resume granularity is per entity-type step (e.g., users, tracks). +If interrupted mid-step, the entire step is rerun on resume. This may +produce duplicate blocks for already-written entities, but the data is +idempotent — indexers processing these blocks will see the same final state. + +## Indexer integration + +Indexers that consume the Core chain must handle `ManageEntityLegacyMigration` +transactions. When this transaction type is encountered, the indexer should: + +1. Recover the signer address from the transaction `signature` and verify that + the recovered address matches the genesis migration authority configured in + `genesis.json` (`genesis_migration_address`). +2. Treat the transaction `signer` field as informational only for this + transaction type, because `genesis-writer` sets it to the entity wallet + address rather than the migration authority. +3. Apply entity data directly from `metadata` — the same JSON structure as + `ManageEntityLegacy`, with `action` values `Create`, `Follow`, `Save`, `Repost`. +4. **Skip** all standard checks that do not apply to historical migration data: + ownership validation, wallet uniqueness, handle filtering, character limits, + entity ID offset checks, and social action signer checks. + +For users, the `wallet` field in the metadata JSON is the user's real Ethereum +address and must be stored as-is (not derived from `signer`). + +## Integration test + +The integration test runs the full pipeline end-to-end: + +1. Runs `genesis-writer` against a seeded source DB (in-process) +2. Reads all `ManageEntityLegacyMigration` and `TrackPlays` transactions from + the core DB with a lightweight in-process indexer +3. Compares every entity (users, tracks, playlists, follows, saves, reposts, play count) + against the original source data +4. Starts an `openaudio-1` node pointed at the pre-populated Core DB +5. Verifies the node advances the chain beyond the genesis height (consensus works) + +No discovery-provider is required. + +### Prerequisites + +Build the core node image from the repo root: + +```bash +make docker-dev +``` + +### Running the test + +```bash +# 1. Create a directory for persistent test data +export EXT_DATA_DIR=$(mktemp -d) + +# 2. Start the infrastructure services (postgres DBs, ganache, nginx) +docker compose -f cmd/genesis-writer/docker-compose.yml up -d \ + src-db core-db eth-ganache ingress + +# 3. Wait for the DBs to be healthy, then run the test +go test -v -tags integration -run TestGenesisWriter -timeout 30m \ + ./cmd/genesis-writer/... + +# 4. Tear down all services and volumes when done +docker compose -f cmd/genesis-writer/docker-compose.yml down -v +``` + +The test starts `openaudio-1` (via `docker compose up --profile chain`) after +`genesis-writer` has finished writing, so there is no race between them. + +### Environment overrides + +| Variable | Default | Description | +|----------|---------|-------------| +| `GENESIS_SRC_DSN` | `postgres://postgres:postgres@localhost:5435/genesis_writer_source?sslmode=disable` | Source DP DB | +| `GENESIS_DST_DSN` | `postgres://postgres:postgres@localhost:5436/openaudio?sslmode=disable` | Core chain DB | +| `GENESIS_CHAIN_URL` | `https://node1.oap.devnet` | Chain gRPC/HTTP URL | +| `GENESIS_WRITER_DATA_DIR` | auto (temp dir) | Where CometBFT state files are written and mounted into openaudio-1 | + +### Ports + +| Service | Host port | +|---------|-----------| +| Source DB (postgres) | `5435` | +| Core DB (postgres) | `5436` | +| Core node (gRPC) | `50052` | +| Ingress (HTTPS) | `443` | + +### Test seed data + +The seed database (`testdata/seed.sql`) exercises every entity type and +nullable column combination: + +| Entity | Count | Variations | +|--------|-------|------------| +| Users | 6 | All fields filled; social handles only; all nullable fields NULL; fan accounts | +| Tracks | 9 | Public; unlisted; stream-gated; download-gated; remix; stem; with release date / preview / ISRC / BPM; NULL title+genre edge case | +| Playlists | 4 | Public album; private playlist; stream-gated; album with release date | +| Follows | 8 | Various follower/followee pairs including mutual follows and fan-to-fan | +| Saves | 5 | Track saves; album saves (`save_type='album'`, normalized to `'playlist'`); playlist saves | +| Reposts | 5 | Track and playlist reposts | +| Plays | 8 | With/without `user_id`; full geo (city/region/country); partial; none | + +## Known limitations + +### `is_verified` (artist badge) + +`is_verified` is included in the user metadata JSON as `"is_verified": true`. +Indexers that process `ManageEntityLegacyMigration` must read and apply this +field directly — it cannot be set via a standard `ManageEntityLegacy` Create +action (which treats it as immutable), so the migration transaction type is the +correct place to carry it. + +genesis-writer currently emits `is_verified` in the user Create metadata. An +indexer implementing `ManageEntityLegacyMigration` support must apply it during +the initial Create pass. + +### Timestamps and `created_at` + +Block timestamps are synthetic (sequential seconds from genesis time), not +the original Audius timestamps. To preserve real creation dates, the +genesis-writer includes a `"created_at"` field in each entity's metadata +JSON. Indexers should use this field — not the block or transaction +timestamp — when displaying entity creation dates. + +### Current state only + +Only the final current state of each entity is written; no intermediate update +history is preserved. The indexed state matches production final state. diff --git a/cmd/genesis-writer/batch.go b/cmd/genesis-writer/batch.go new file mode 100644 index 00000000..b0de48cb --- /dev/null +++ b/cmd/genesis-writer/batch.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "runtime" + "sync" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + "go.uber.org/zap" +) + +// processBatched streams rows from srcDB using selectQuery and calls emit for +// each row. Emit is called concurrently from a worker pool of NumCPU goroutines, +// so emit must be safe for concurrent use (addManageEntity is, via blockMu). +// +// It first runs countQuery to log total count for progress. The scan function +// receives pgx.Rows and must call rows.Scan to populate a value, then return it. +// pgx v5 streams results by default, so no LIMIT/OFFSET is needed. +func processBatched[T any]( + ctx context.Context, + w *Writer, + name string, + countQuery string, + selectQuery string, + scan func(pgx.Rows) (T, error), + emit func(context.Context, T) error, +) error { + var total int64 + if err := w.srcDB.QueryRow(ctx, countQuery).Scan(&total); err != nil { + return fmt.Errorf("count %s: %w", name, err) + } + if total == 0 { + w.logger.Info("no rows", zap.String("entity", name)) + return nil + } + w.logger.Info("processing", zap.String("entity", name), zap.Int64("total", total)) + + rows, err := w.srcDB.Query(ctx, selectQuery) + if err != nil { + return fmt.Errorf("query %s: %w", name, err) + } + defer rows.Close() + + workers := runtime.NumCPU() + batchSize := w.cfg.BatchSize + if batchSize <= 0 { + batchSize = workers * 2 + } + + var processed int64 + batch := make([]T, 0, batchSize) + + // processBatch signs and emits a batch of items concurrently. + processBatch := func(items []T) error { + var wg sync.WaitGroup + errs := make([]error, len(items)) + + // Use a semaphore to limit concurrency to NumCPU. + sem := make(chan struct{}, workers) + for i, item := range items { + wg.Add(1) + sem <- struct{}{} + go func(idx int, it T) { + defer wg.Done() + defer func() { <-sem }() + errs[idx] = emit(ctx, it) + }(i, item) + } + wg.Wait() + + for i, err := range errs { + if err != nil { + return fmt.Errorf("emit %s row %d: %w", name, processed-int64(len(items))+int64(i), err) + } + } + return nil + } + + for rows.Next() { + item, err := scan(rows) + if err != nil { + return fmt.Errorf("scan %s row %d: %w", name, processed, err) + } + batch = append(batch, item) + processed++ + + if len(batch) >= batchSize { + if err := processBatch(batch); err != nil { + return err + } + batch = batch[:0] + } + + if processed%100000 == 0 { + w.logger.Info("progress", zap.String("entity", name), zap.Int64("processed", processed), zap.Int64("total", total)) + } + } + if err := rows.Err(); err != nil { + return fmt.Errorf("rows %s: %w", name, err) + } + + // Process remaining items. + if len(batch) > 0 { + if err := processBatch(batch); err != nil { + return err + } + } + + w.logger.Info("done", zap.String("entity", name), zap.Int64("processed", processed)) + return nil +} + +// preloadMap runs a two-column query and returns a map from the first column to +// a slice of values from the second column. Useful for pre-loading related data +// (e.g. comment threads, email access grants). +func preloadMap[K comparable, V any](ctx context.Context, db *pgxpool.Pool, query string) (map[K][]V, error) { + rows, err := db.Query(ctx, query) + if err != nil { + return nil, err + } + defer rows.Close() + + m := make(map[K][]V) + for rows.Next() { + var k K + var v V + if err := rows.Scan(&k, &v); err != nil { + return nil, err + } + m[k] = append(m[k], v) + } + return m, rows.Err() +} + +// deref safely dereferences a string pointer, returning "" if nil. +func deref(s *string) string { + if s == nil { + return "" + } + return *s +} + +// derefInt safely dereferences an int pointer, returning 0 if nil. +func derefInt(i *int) int { + if i == nil { + return 0 + } + return *i +} + +// unmarshalJSONB unmarshals a JSONB byte slice into an interface{} value. +// Returns nil if the input is empty or invalid JSON. +func unmarshalJSONB(b []byte) interface{} { + if len(b) == 0 { + return nil + } + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return nil + } + return v +} diff --git a/cmd/genesis-writer/cmt_state.go b/cmd/genesis-writer/cmt_state.go new file mode 100644 index 00000000..cbc53e9b --- /dev/null +++ b/cmd/genesis-writer/cmt_state.go @@ -0,0 +1,277 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/privval" + cmtstate "github.com/cometbft/cometbft/state" + cmtstore "github.com/cometbft/cometbft/store" + cmttypes "github.com/cometbft/cometbft/types" + "go.uber.org/zap" +) + +// initBlockSigning loads the genesis validator key and consensus parameters +// so that flushBlock can build and sign real CometBFT blocks. +// If CMTHome is set, it also opens blockstore.db for writing. +func (w *Writer) initBlockSigning() error { + if w.cfg.GenesisFile == "" { + return fmt.Errorf("--genesis-file is required") + } + if w.cfg.PrivValidatorKeyFile == "" { + return fmt.Errorf("--priv-validator-key-file is required") + } + + genDoc, err := cmttypes.GenesisDocFromFile(w.cfg.GenesisFile) + if err != nil { + return fmt.Errorf("load genesis file: %w", err) + } + + genesisState, err := cmtstate.MakeGenesisState(genDoc) + if err != nil { + return fmt.Errorf("make genesis state: %w", err) + } + + w.validatorsHash = genesisState.Validators.Hash() + w.nextValHash = genesisState.Validators.Hash() + w.consensusHash = genesisState.ConsensusParams.Hash() + + pv := privval.LoadFilePVEmptyState(w.cfg.PrivValidatorKeyFile, "") + w.cmtPrivKey = pv.Key.PrivKey + w.proposerAddr = pv.Key.Address + + if w.cfg.CMTHome != "" { + dataDir := filepath.Join(w.cfg.CMTHome, "data") + if err := os.MkdirAll(dataDir, 0o755); err != nil { + return fmt.Errorf("mkdir data: %w", err) + } + bsDB, err := dbm.NewDB("blockstore", dbm.PebbleDBBackend, dataDir) + if err != nil { + return fmt.Errorf("open blockstore.db: %w", err) + } + w.bsDB = bsDB + w.blockStore = cmtstore.NewBlockStore(bsDB) + } + + return nil +} + +// writeCMTState writes state.db so the genesis node can start from finalHeight +// and immediately propose block finalHeight+1. blockstore.db is already fully +// populated by flushBlock (SaveBlock called per block), so no extra work is +// needed here for blockstore. +func (w *Writer) writeCMTState(_ context.Context) error { + genDoc, err := cmttypes.GenesisDocFromFile(w.cfg.GenesisFile) + if err != nil { + return fmt.Errorf("load genesis file: %w", err) + } + + genesisState, err := cmtstate.MakeGenesisState(genDoc) + if err != nil { + return fmt.Errorf("make genesis state: %w", err) + } + + // prevBlockID is the real BlockID of the last written block (set by flushBlock). + // + // LastHeightValidatorsChanged must equal finalHeight+1 — the height at which + // Bootstrap stores the actual validator set bytes. If it were set to + // InitialHeight (e.g. 1) instead, every subsequent stateStore.Save() would + // write validator references pointing to height 1, which has no data, causing + // a CONSENSUS FAILURE when CometBFT tries to load the validator set. + validatorsStoredAt := w.finalHeight + 1 + state := cmtstate.State{ + Version: genesisState.Version, + ChainID: genDoc.ChainID, + InitialHeight: genDoc.InitialHeight, + + LastBlockHeight: w.finalHeight, + LastBlockID: w.prevBlockID, + LastBlockTime: w.finalTime, + + NextValidators: genesisState.Validators.CopyIncrementProposerPriority(1), + Validators: genesisState.Validators.Copy(), + LastValidators: genesisState.Validators.Copy(), + LastHeightValidatorsChanged: validatorsStoredAt, + + ConsensusParams: genesisState.ConsensusParams, + LastHeightConsensusParamsChanged: validatorsStoredAt, + + AppHash: w.finalAppHash, + } + + dataDir := filepath.Join(w.cfg.CMTHome, "data") + if err := os.MkdirAll(dataDir, 0o755); err != nil { + return fmt.Errorf("mkdir data: %w", err) + } + + stateDB, err := dbm.NewDB("state", dbm.PebbleDBBackend, dataDir) + if err != nil { + return fmt.Errorf("open state.db: %w", err) + } + stateStore := cmtstate.NewStore(stateDB, cmtstate.StoreOptions{}) + if err := stateStore.Bootstrap(state); err != nil { + stateDB.Close() + return fmt.Errorf("bootstrap state.db: %w", err) + } + stateDB.Close() + + w.logger.Info("wrote state.db", + zap.Int64("height", w.finalHeight), + zap.String("block_hash", fmt.Sprintf("%x", w.prevBlockID.Hash)), + zap.String("app_hash", fmt.Sprintf("%x", w.finalAppHash)), + ) + + return nil +} + +// writeGenesisFile reads the input genesis.json, sets genesis_migration_address +// and genesis_migration_end_height to lock down the migration key after the +// genesis write, then writes the updated file to CMTHome/config/genesis.json. +func (w *Writer) writeGenesisFile() error { + raw, err := os.ReadFile(w.cfg.GenesisFile) + if err != nil { + return fmt.Errorf("read genesis file: %w", err) + } + + // Decode into a generic map to preserve all fields. + var doc map[string]json.RawMessage + if err := json.Unmarshal(raw, &doc); err != nil { + return fmt.Errorf("parse genesis file: %w", err) + } + + // Merge migration fields into existing app_state (if any). + appState := make(map[string]interface{}) + if existing, ok := doc["app_state"]; ok { + if err := json.Unmarshal(existing, &appState); err != nil { + return fmt.Errorf("unmarshal existing app_state: %w", err) + } + } + appState["genesis_migration_address"] = w.signerAddr + appState["genesis_migration_end_height"] = w.finalHeight + appStateJSON, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("marshal app_state: %w", err) + } + doc["app_state"] = appStateJSON + + out, err := json.MarshalIndent(doc, "", " ") + if err != nil { + return fmt.Errorf("marshal genesis: %w", err) + } + + outPath := filepath.Join(w.cfg.CMTHome, "config", "genesis.json") + if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil { + return fmt.Errorf("mkdir config: %w", err) + } + if err := os.WriteFile(outPath, append(out, '\n'), 0o644); err != nil { + return fmt.Errorf("write genesis file: %w", err) + } + + w.logger.Info("wrote genesis.json", + zap.String("path", outPath), + zap.String("migration_address", w.signerAddr), + zap.Int64("migration_end_height", w.finalHeight), + ) + + return nil +} + +// ensureGenesisFiles creates genesis.json and priv_validator_key.json if they +// don't exist at the expected paths. The genesis doc matches prod.json consensus +// params (15MB max_bytes, 10MB evidence max_bytes) with a single bootstrap +// validator and the provided chain ID and genesis time. +func ensureGenesisFiles(genesisFile, privValKeyFile, chainID string, genesisTime time.Time, logger *zap.Logger) error { + genesisExists := fileExists(genesisFile) + keyExists := fileExists(privValKeyFile) + + if genesisExists && keyExists { + return nil + } + + // Generate a new ed25519 validator key if needed. + var pubKey ed25519.PubKey + if !keyExists { + privKey := ed25519.GenPrivKey() + pubKey = privKey.PubKey().(ed25519.PubKey) + + if err := os.MkdirAll(filepath.Dir(privValKeyFile), 0o755); err != nil { + return fmt.Errorf("mkdir for priv_validator_key: %w", err) + } + // priv_validator_state.json goes alongside the key in the data dir. + stateFile := filepath.Join(filepath.Dir(filepath.Dir(privValKeyFile)), "data", "priv_validator_state.json") + if err := os.MkdirAll(filepath.Dir(stateFile), 0o755); err != nil { + return fmt.Errorf("mkdir for priv_validator_state: %w", err) + } + pv := privval.NewFilePV(privKey, privValKeyFile, stateFile) + pv.Save() + + logger.Info("generated validator key", + zap.String("path", privValKeyFile), + zap.String("address", pv.GetAddress().String()), + ) + } else { + // Load existing key to get the pub key for the genesis doc. + pv := privval.LoadFilePVEmptyState(privValKeyFile, "") + pubKey = pv.Key.PubKey.(ed25519.PubKey) + } + + if !genesisExists { + // Consensus params matching prod.json. + genDoc := &cmttypes.GenesisDoc{ + GenesisTime: genesisTime, + ChainID: chainID, + InitialHeight: 0, + ConsensusParams: &cmttypes.ConsensusParams{ + Block: cmttypes.BlockParams{ + MaxBytes: 15728640, // 15MB + MaxGas: 10000000, + }, + Evidence: cmttypes.EvidenceParams{ + MaxAgeNumBlocks: 100000, + MaxAgeDuration: 172800000000000, // 48h in ns + MaxBytes: 1572864, // ~1.5MB + }, + Validator: cmttypes.ValidatorParams{ + PubKeyTypes: []string{"ed25519"}, + }, + }, + Validators: []cmttypes.GenesisValidator{ + { + Address: pubKey.Address(), + PubKey: pubKey, + Power: 100, + Name: "bootstrap", + }, + }, + } + + if err := genDoc.ValidateAndComplete(); err != nil { + return fmt.Errorf("validate genesis doc: %w", err) + } + + if err := os.MkdirAll(filepath.Dir(genesisFile), 0o755); err != nil { + return fmt.Errorf("mkdir for genesis.json: %w", err) + } + if err := genDoc.SaveAs(genesisFile); err != nil { + return fmt.Errorf("save genesis.json: %w", err) + } + + logger.Info("generated genesis.json", + zap.String("path", genesisFile), + zap.String("chain_id", chainID), + ) + } + + return nil +} + +func fileExists(path string) bool { + _, err := os.Stat(path) + return err == nil +} diff --git a/cmd/genesis-writer/docker-compose.yml b/cmd/genesis-writer/docker-compose.yml new file mode 100644 index 00000000..5509abc6 --- /dev/null +++ b/cmd/genesis-writer/docker-compose.yml @@ -0,0 +1,214 @@ +name: genesis-writer + +# Integration-test stack for genesis-writer end-to-end verification. +# All paths are relative to the repo root. +# +# Usage (from repo root): +# +# export EXT_DATA_DIR=$(mktemp -d) +# docker compose -f cmd/genesis-writer/docker-compose.yml up -d src-db core-db eth-ganache ingress +# go test -v -tags integration -run TestGenesisWriter -timeout 30m ./cmd/genesis-writer/... +# docker compose -f cmd/genesis-writer/docker-compose.yml down -v +# +# The integration test runs genesis-writer in-process, verifies the core DB +# content with a lightweight indexer, then starts openaudio-1 to confirm +# consensus advances from the genesis height. + +services: + + # ---------------------------------------------------------------- + # Source database – holds the Audius Discovery Provider schema and + # is pre-seeded with test entities via genesis_writer_source DB. + # ---------------------------------------------------------------- + src-db: + image: postgres:17 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: discovery_provider_1 + volumes: + - ../../cmd/genesis-replay/testdata/dp_schema.sql:/docker-entrypoint-initdb.d/01_schema.sql:ro + - ../../cmd/genesis-writer/testdata/dp_seed.sql:/docker-entrypoint-initdb.d/02_seed.sql:ro + - ../../cmd/genesis-writer/testdata/source_init.sh:/docker-entrypoint-initdb.d/03_source_init.sh:ro + - ../../cmd/genesis-writer/testdata/seed.sql:/tmp/source_seed.sql:ro + - ${EXT_DATA_DIR}/gw-src-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "psql -h 127.0.0.1 -U postgres -d genesis_writer_source -c 'SELECT 1' > /dev/null 2>&1"] + interval: 3s + timeout: 5s + retries: 30 + start_period: 10s + ports: + - "5435:5432" + + # ---------------------------------------------------------------- + # Core chain database – starts empty; genesis-writer applies the + # Core chain schema via RunMigrations and then populates it. + # openaudio-1 reads from this DB once genesis-writer is done. + # ---------------------------------------------------------------- + core-db: + image: postgres:15 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: openaudio + volumes: + - ${EXT_DATA_DIR}/gw-core-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 3s + retries: 10 + ports: + - "5436:5432" + + # ---------------------------------------------------------------- + # Ethereum test network required by openaudio-1 for contract calls. + # ---------------------------------------------------------------- + eth-ganache: + image: audius/eth-ganache:latest + pull_policy: always + stop_grace_period: 0s + command: > + npx ganache + --server.host 0.0.0.0 + --wallet.deterministic + --wallet.totalAccounts 50 + --database.dbPath /usr/db + --chain.networkId 12345 + healthcheck: + test: + - CMD-SHELL + - > + node -e "require('http').request({port:8545,method:'POST',headers:{'Content-Type':'application/json'}}, + r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>process.exit(JSON.parse(d).result?0:1))}).end(JSON.stringify({jsonrpc:'2.0',method:'eth_blockNumber',params:[],id:1}))" + interval: 5s + timeout: 5s + retries: 12 + + # ---------------------------------------------------------------- + # TLS-terminating ingress for the devnet node. + # ---------------------------------------------------------------- + ingress: + image: nginx:latest + volumes: + - ../../dev/nginx.conf:/etc/nginx/conf.d/vhost.conf:ro + - ../../dev/tls/cert.pem:/etc/nginx/ssl/cert.pem:ro + - ../../dev/tls/key.pem:/etc/nginx/ssl/key.pem:ro + extra_hosts: + - "node1.oap.devnet:host-gateway" + ports: + - "80:80" + - "443:443" + + # ---------------------------------------------------------------- + # Second core DB – used by openaudio-2 during state sync tests. + # Starts empty; state sync restores a pg_dump from openaudio-1. + # ---------------------------------------------------------------- + core-db-2: + image: postgres:15 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: openaudio + volumes: + - ${EXT_DATA_DIR}/gw-core-pg-2-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 3s + retries: 10 + ports: + - "5437:5432" + + # ---------------------------------------------------------------- + # OpenAudio consensus node – started by the integration test AFTER + # genesis-writer has populated core-db and written CometBFT state. + # + # Key settings: + # OPENAUDIO_CORE_ONLY=true – skip embedded postgres; use core-db + # dbUrl – external core-db DSN + # audius_core_root_dir – mounts the volume written by genesis-writer + # ---------------------------------------------------------------- + openaudio-1: + image: ${OPENAUDIO_IMAGE:-openaudio/go-openaudio:dev} + profiles: [chain] + healthcheck: + test: + - CMD-SHELL + - "curl -fsk https://localhost/health-check | grep -v 'core service not ready'" + interval: 10s + timeout: 5s + retries: 30 + start_period: 30s + environment: + NETWORK: dev + OPENAUDIO_ENV: dev + OPENAUDIO_GENESIS: dev-v2 + OPENAUDIO_TLS_SELF_SIGNED: "true" + OPENAUDIO_GENESIS_MIGRATION: "true" + OPENAUDIO_CORE_ONLY: "true" + nodeEndpoint: https://node1.oap.devnet + delegatePrivateKey: d09ba371c359f10f22ccda12fd26c598c7921bda3220c9942174562bc6a36fe8 + dbUrl: postgresql://postgres:postgres@core-db:5432/openaudio?sslmode=disable + audius_core_root_dir: /data/core + uptimeDataDir: /data/bolt + stateSyncServeSnapshots: "true" + stateSyncBlockInterval: "20" + stateSyncEnable: "false" + externalAddress: "openaudio-1:26656" + extra_hosts: + - "node1.oap.devnet:host-gateway" + volumes: + - ${GENESIS_WRITER_DATA_DIR:-/tmp/genesis-writer-cmt-data}:/data/core + ports: + - "50052:50051" + depends_on: + core-db: + condition: service_healthy + eth-ganache: + condition: service_healthy + + # ---------------------------------------------------------------- + # Second OpenAudio node – state syncs from openaudio-1. + # Started by the integration test AFTER openaudio-1 has produced + # at least one snapshot. + # ---------------------------------------------------------------- + openaudio-2: + image: ${OPENAUDIO_IMAGE:-openaudio/go-openaudio:dev} + profiles: [statesync] + healthcheck: + test: + - CMD-SHELL + - "curl -fsk https://localhost/health-check | grep -v 'core service not ready'" + interval: 10s + timeout: 5s + retries: 60 + start_period: 30s + environment: + NETWORK: dev + OPENAUDIO_ENV: dev + OPENAUDIO_GENESIS: dev-v2 + OPENAUDIO_TLS_SELF_SIGNED: "true" + OPENAUDIO_CORE_ONLY: "true" + nodeEndpoint: https://node2.oap.devnet + delegatePrivateKey: e19a18cfeb4ea5c0fef65cd1cb4c20f9c52a1bb9c54e3a58ecd0e1bcbea1cc04 + dbUrl: postgresql://postgres:postgres@core-db-2:5432/openaudio?sslmode=disable + audius_core_root_dir: /data/core + uptimeDataDir: /data/bolt + skipEthRegistration: "true" + stateSyncEnable: "true" + stateSyncServeSnapshots: "false" + stateSyncRPCServers: "https://node1.oap.devnet,https://node1.oap.devnet" + persistentPeers: "${PERSISTENT_PEERS:-}" + extra_hosts: + - "node1.oap.devnet:host-gateway" + - "node2.oap.devnet:host-gateway" + volumes: + - ${EXT_DATA_DIR}/oap2-core:/data/core + depends_on: + core-db-2: + condition: service_healthy + eth-ganache: + condition: service_healthy + diff --git a/cmd/genesis-writer/entities_comment.go b/cmd/genesis-writer/entities_comment.go new file mode 100644 index 00000000..567de0e7 --- /dev/null +++ b/cmd/genesis-writer/entities_comment.go @@ -0,0 +1,128 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +// --- Comments --- + +type commentMetadata struct { + Body string `json:"body"` + EntityID int64 `json:"entity_id"` + EntityType string `json:"entity_type"` + ParentCommentID *int64 `json:"parent_comment_id,omitempty"` + TrackTimestampS *int `json:"track_timestamp_s,omitempty"` + Mentions []int64 `json:"mentions,omitempty"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceComment struct { + CommentID int64 + Text *string + UserID int64 + EntityID int64 + EntityType string + TrackTimestampS *int + CreatedAt time.Time +} + +func (w *Writer) writeComments(ctx context.Context) error { + // Pre-load comment threads (parent_comment_id for each comment). + threads, err := preloadMap[int64, int64](ctx, w.srcDB, + `SELECT comment_id, parent_comment_id FROM comment_threads`) + if err != nil { + return fmt.Errorf("preload comment threads: %w", err) + } + + // Pre-load comment mentions (user_ids mentioned in each comment). + mentions, err := preloadMap[int64, int64](ctx, w.srcDB, + `SELECT comment_id, user_id FROM comment_mentions WHERE is_delete = false`) + if err != nil { + return fmt.Errorf("preload comment mentions: %w", err) + } + + return processBatched(ctx, w, "comments", + `SELECT count(*) FROM comments WHERE is_delete = false`, + `SELECT comment_id, text, user_id, entity_id, entity_type, track_timestamp_s, created_at + FROM comments + WHERE is_delete = false + ORDER BY comment_id`, + func(rows pgx.Rows) (sourceComment, error) { + var c sourceComment + err := rows.Scan(&c.CommentID, &c.Text, &c.UserID, &c.EntityID, &c.EntityType, &c.TrackTimestampS, &c.CreatedAt) + return c, err + }, + func(ctx context.Context, c sourceComment) error { + meta := commentMetadata{ + Body: deref(c.Text), + EntityID: c.EntityID, + EntityType: c.EntityType, + TrackTimestampS: c.TrackTimestampS, + CreatedAt: c.CreatedAt.Format(time.RFC3339), + } + + // Attach parent comment if this is a reply. + if parents := threads[c.CommentID]; len(parents) > 0 { + meta.ParentCommentID = &parents[0] + } + + // Attach mentioned user IDs. + if m := mentions[c.CommentID]; len(m) > 0 { + meta.Mentions = m + } + + metaJSON, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("marshal comment %d metadata: %w", c.CommentID, err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: c.UserID, + EntityType: "Comment", + EntityId: c.CommentID, + Action: "Create", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Comment Reactions --- + +func (w *Writer) writeCommentReactions(ctx context.Context) error { + type commentReaction struct { + commentID int64 + userID int64 + createdAt time.Time + } + return processBatched(ctx, w, "comment_reactions", + `SELECT count(*) FROM comment_reactions WHERE is_delete = false`, + `SELECT comment_id, user_id, created_at + FROM comment_reactions + WHERE is_delete = false + ORDER BY comment_id, user_id`, + func(rows pgx.Rows) (commentReaction, error) { + var cr commentReaction + err := rows.Scan(&cr.commentID, &cr.userID, &cr.createdAt) + return cr, err + }, + func(ctx context.Context, cr commentReaction) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: cr.createdAt.Format(time.RFC3339)}) + if err != nil { + return fmt.Errorf("marshal comment reaction metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: cr.userID, + EntityType: "CommentReaction", + EntityId: cr.commentID, + Action: "React", + Metadata: string(metaJSON), + }) + }, + ) +} diff --git a/cmd/genesis-writer/entities_dashboard_wallet.go b/cmd/genesis-writer/entities_dashboard_wallet.go new file mode 100644 index 00000000..215925dc --- /dev/null +++ b/cmd/genesis-writer/entities_dashboard_wallet.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +type dashboardWalletMetadata struct { + Wallet string `json:"wallet"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceDashboardWalletUser struct { + Wallet string + UserID int64 + CreatedAt time.Time +} + +func (w *Writer) writeDashboardWalletUsers(ctx context.Context) error { + return processBatched(ctx, w, "dashboard_wallet_users", + `SELECT count(*) FROM dashboard_wallet_users WHERE is_delete = false`, + `SELECT wallet, user_id, created_at + FROM dashboard_wallet_users + WHERE is_delete = false + ORDER BY user_id, wallet`, + func(rows pgx.Rows) (sourceDashboardWalletUser, error) { + var d sourceDashboardWalletUser + err := rows.Scan(&d.Wallet, &d.UserID, &d.CreatedAt) + return d, err + }, + func(ctx context.Context, d sourceDashboardWalletUser) error { + metaJSON, err := json.Marshal(dashboardWalletMetadata{ + Wallet: d.Wallet, + CreatedAt: d.CreatedAt.Format(time.RFC3339), + }) + if err != nil { + return fmt.Errorf("marshal dashboard wallet user metadata: %w", err) + } + // DP uses params.signer (the wallet) as identity for DashboardWalletUser. + return w.addManageEntityWithSigner(ctx, &corev1.ManageEntityLegacy{ + UserId: d.UserID, + EntityType: "DashboardWalletUser", + EntityId: 0, + Action: "Create", + Metadata: string(metaJSON), + }, d.Wallet) + }, + ) +} diff --git a/cmd/genesis-writer/entities_developer_app.go b/cmd/genesis-writer/entities_developer_app.go new file mode 100644 index 00000000..8109745c --- /dev/null +++ b/cmd/genesis-writer/entities_developer_app.go @@ -0,0 +1,112 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +// --- Developer Apps --- + +type developerAppMetadata struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + ImageURL string `json:"image_url,omitempty"` + IsPersonalAccess bool `json:"is_personal_access,omitempty"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceDeveloperApp struct { + Address string + UserID int64 + Name *string + Description *string + ImageURL *string + IsPersonalAccess bool + CreatedAt time.Time +} + +func (w *Writer) writeDeveloperApps(ctx context.Context) error { + return processBatched(ctx, w, "developer_apps", + `SELECT count(*) FROM developer_apps WHERE is_current = true AND is_delete = false`, + `SELECT address, user_id, name, description, image_url, is_personal_access, created_at + FROM developer_apps + WHERE is_current = true AND is_delete = false + ORDER BY user_id, address`, + func(rows pgx.Rows) (sourceDeveloperApp, error) { + var d sourceDeveloperApp + err := rows.Scan(&d.Address, &d.UserID, &d.Name, &d.Description, &d.ImageURL, &d.IsPersonalAccess, &d.CreatedAt) + return d, err + }, + func(ctx context.Context, d sourceDeveloperApp) error { + meta := developerAppMetadata{ + Name: deref(d.Name), + Description: deref(d.Description), + ImageURL: deref(d.ImageURL), + IsPersonalAccess: d.IsPersonalAccess, + CreatedAt: d.CreatedAt.Format(time.RFC3339), + } + metaJSON, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("marshal developer app %s metadata: %w", d.Address, err) + } + // DP uses params.signer (the app address) as the DeveloperApp identity. + return w.addManageEntityWithSigner(ctx, &corev1.ManageEntityLegacy{ + UserId: d.UserID, + EntityType: "DeveloperApp", + EntityId: 0, + Action: "Create", + Metadata: string(metaJSON), + }, d.Address) + }, + ) +} + +// --- Grants --- + +type grantMetadata struct { + GranteeAddress string `json:"grantee_address"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceGrant struct { + GranteeAddress string + UserID int64 + CreatedAt time.Time +} + +func (w *Writer) writeGrants(ctx context.Context) error { + return processBatched(ctx, w, "grants", + `SELECT count(*) FROM grants WHERE is_current = true AND is_revoked = false AND is_approved = true`, + `SELECT grantee_address, user_id, created_at + FROM grants + WHERE is_current = true AND is_revoked = false AND is_approved = true + ORDER BY user_id, grantee_address`, + func(rows pgx.Rows) (sourceGrant, error) { + var g sourceGrant + err := rows.Scan(&g.GranteeAddress, &g.UserID, &g.CreatedAt) + return g, err + }, + func(ctx context.Context, g sourceGrant) error { + metaJSON, err := json.Marshal(grantMetadata{ + GranteeAddress: g.GranteeAddress, + CreatedAt: g.CreatedAt.Format(time.RFC3339), + }) + if err != nil { + return fmt.Errorf("marshal grant metadata: %w", err) + } + // DP uses the grantee address as signer for Grant creation. + return w.addManageEntityWithSigner(ctx, &corev1.ManageEntityLegacy{ + UserId: g.UserID, + EntityType: "Grant", + EntityId: 0, + Action: "Create", + Metadata: string(metaJSON), + }, g.GranteeAddress) + }, + ) +} diff --git a/cmd/genesis-writer/entities_email.go b/cmd/genesis-writer/entities_email.go new file mode 100644 index 00000000..a6aaf185 --- /dev/null +++ b/cmd/genesis-writer/entities_email.go @@ -0,0 +1,136 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +// --- Encrypted Emails --- + +type encryptedEmailMetadata struct { + EncryptedEmail string `json:"encrypted_email"` + AccessGrants []emailAccessGrantInline `json:"access_grants,omitempty"` + CreatedAt string `json:"created_at,omitempty"` +} + +type emailAccessGrantInline struct { + ReceivingUserID int64 `json:"receiving_user_id"` + GrantorUserID int64 `json:"grantor_user_id"` + EncryptedKey string `json:"encrypted_key"` +} + +type sourceEncryptedEmail struct { + EmailOwnerUserID int64 + EncryptedEmail string + CreatedAt time.Time +} + +func (w *Writer) writeEncryptedEmails(ctx context.Context) error { + // Pre-load email access grants keyed by email_owner_user_id. + accessRows, err := w.srcDB.Query(ctx, + `SELECT email_owner_user_id, receiving_user_id, grantor_user_id, encrypted_key + FROM email_access + ORDER BY email_owner_user_id`) + if err != nil { + return fmt.Errorf("query email_access: %w", err) + } + defer accessRows.Close() + + accessByOwner := make(map[int64][]emailAccessGrantInline) + for accessRows.Next() { + var ownerID, receivingID, grantorID int64 + var encKey string + if err := accessRows.Scan(&ownerID, &receivingID, &grantorID, &encKey); err != nil { + return fmt.Errorf("scan email_access: %w", err) + } + accessByOwner[ownerID] = append(accessByOwner[ownerID], emailAccessGrantInline{ + ReceivingUserID: receivingID, + GrantorUserID: grantorID, + EncryptedKey: encKey, + }) + } + if err := accessRows.Err(); err != nil { + return fmt.Errorf("email_access rows: %w", err) + } + accessRows.Close() + + return processBatched(ctx, w, "encrypted_emails", + `SELECT count(*) FROM encrypted_emails`, + `SELECT email_owner_user_id, encrypted_email, created_at + FROM encrypted_emails + ORDER BY email_owner_user_id`, + func(rows pgx.Rows) (sourceEncryptedEmail, error) { + var e sourceEncryptedEmail + err := rows.Scan(&e.EmailOwnerUserID, &e.EncryptedEmail, &e.CreatedAt) + return e, err + }, + func(ctx context.Context, e sourceEncryptedEmail) error { + meta := encryptedEmailMetadata{ + EncryptedEmail: e.EncryptedEmail, + AccessGrants: accessByOwner[e.EmailOwnerUserID], + CreatedAt: e.CreatedAt.Format(time.RFC3339), + } + metaJSON, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("marshal encrypted email metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: e.EmailOwnerUserID, + EntityType: "EncryptedEmail", + EntityId: 0, + Action: "AddEmail", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Email Access --- +// Email access grants that are not tied to an encrypted email (standalone grants). +// Most are pre-loaded with their parent email above. This handles any orphans. + +func (w *Writer) writeEmailAccess(ctx context.Context) error { + type emailAccess struct { + emailOwnerUserID int64 + receivingUserID int64 + grantorUserID int64 + encryptedKey string + createdAt time.Time + } + return processBatched(ctx, w, "email_access", + `SELECT count(*) FROM email_access ea + WHERE NOT EXISTS (SELECT 1 FROM encrypted_emails ee WHERE ee.email_owner_user_id = ea.email_owner_user_id)`, + `SELECT ea.email_owner_user_id, ea.receiving_user_id, ea.grantor_user_id, ea.encrypted_key, ea.created_at + FROM email_access ea + WHERE NOT EXISTS (SELECT 1 FROM encrypted_emails ee WHERE ee.email_owner_user_id = ea.email_owner_user_id) + ORDER BY ea.email_owner_user_id, ea.receiving_user_id`, + func(rows pgx.Rows) (emailAccess, error) { + var ea emailAccess + err := rows.Scan(&ea.emailOwnerUserID, &ea.receivingUserID, &ea.grantorUserID, &ea.encryptedKey, &ea.createdAt) + return ea, err + }, + func(ctx context.Context, ea emailAccess) error { + metaJSON, err := json.Marshal(map[string]interface{}{ + "receiving_user_id": ea.receivingUserID, + "grantor_user_id": ea.grantorUserID, + "encrypted_key": ea.encryptedKey, + "created_at": ea.createdAt.Format(time.RFC3339), + }) + if err != nil { + return fmt.Errorf("marshal email access metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: ea.emailOwnerUserID, + EntityType: "EmailAccess", + EntityId: 0, + Action: "Create", + Metadata: string(metaJSON), + }) + }, + ) +} diff --git a/cmd/genesis-writer/entities_play.go b/cmd/genesis-writer/entities_play.go new file mode 100644 index 00000000..2cd55f07 --- /dev/null +++ b/cmd/genesis-writer/entities_play.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const playsPerTransaction = 20 + +type sourcePlay struct { + UserID *string + TrackID string + CreatedAt time.Time + City *string + Region *string + Country *string +} + +func (w *Writer) writePlays(ctx context.Context) error { + var total int64 + if err := w.srcDB.QueryRow(ctx, `SELECT count(*) FROM plays`).Scan(&total); err != nil { + return fmt.Errorf("count plays: %w", err) + } + if total == 0 { + w.logger.Info("no rows", zap.String("entity", "plays")) + return nil + } + w.logger.Info("processing", zap.String("entity", "plays"), zap.Int64("total", total)) + + rows, err := w.srcDB.Query(ctx, + `SELECT user_id::text, play_item_id::text, created_at, city, region, country + FROM plays + ORDER BY created_at, play_item_id`) + if err != nil { + return fmt.Errorf("query plays: %w", err) + } + defer rows.Close() + + var batch []*corev1.TrackPlay + var processed int64 + + flush := func() error { + if len(batch) == 0 { + return nil + } + if err := w.addTrackPlays(ctx, &corev1.TrackPlays{Plays: batch}); err != nil { + return err + } + batch = batch[:0] + return nil + } + + for rows.Next() { + var p sourcePlay + if err := rows.Scan(&p.UserID, &p.TrackID, &p.CreatedAt, &p.City, &p.Region, &p.Country); err != nil { + return fmt.Errorf("scan play row %d: %w", processed, err) + } + + tp := &corev1.TrackPlay{ + TrackId: p.TrackID, + Timestamp: timestamppb.New(p.CreatedAt), + } + if p.UserID != nil { + tp.UserId = *p.UserID + } + if p.City != nil { + tp.City = *p.City + } + if p.Region != nil { + tp.Region = *p.Region + } + if p.Country != nil { + tp.Country = *p.Country + } + + batch = append(batch, tp) + if len(batch) >= playsPerTransaction { + if err := flush(); err != nil { + return fmt.Errorf("flush plays at row %d: %w", processed, err) + } + } + + processed++ + if processed%1000000 == 0 { + w.logger.Info("progress", zap.String("entity", "plays"), zap.Int64("processed", processed), zap.Int64("total", total)) + } + } + if err := rows.Err(); err != nil { + return fmt.Errorf("plays rows: %w", err) + } + + if err := flush(); err != nil { + return fmt.Errorf("flush remaining plays: %w", err) + } + + w.logger.Info("done", zap.String("entity", "plays"), zap.Int64("processed", processed)) + return nil +} diff --git a/cmd/genesis-writer/entities_playlist.go b/cmd/genesis-writer/entities_playlist.go new file mode 100644 index 00000000..bc8b0445 --- /dev/null +++ b/cmd/genesis-writer/entities_playlist.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +type playlistMetadataWrapper struct { + CID string `json:"cid"` + Data playlistMetadataInner `json:"data"` +} + +type playlistMetadataInner struct { + CreatedAt string `json:"created_at,omitempty"` + PlaylistName string `json:"playlist_name"` + Description string `json:"description,omitempty"` + IsAlbum bool `json:"is_album,omitempty"` + IsPrivate bool `json:"is_private,omitempty"` + PlaylistImageSizesHash string `json:"playlist_image_sizes_multihash,omitempty"` + PlaylistContents interface{} `json:"playlist_contents,omitempty"` + ReleaseDate string `json:"release_date,omitempty"` + IsStreamGated bool `json:"is_stream_gated,omitempty"` + StreamConditions interface{} `json:"stream_conditions,omitempty"` + UPC string `json:"upc,omitempty"` +} + +type sourcePlaylist struct { + PlaylistID int64 + PlaylistOwnerID int64 + PlaylistName *string + Description *string + IsAlbum bool + IsPrivate bool + MetadataMultihash *string + ImageSizesMultihash *string + PlaylistContents []byte // JSONB + ReleaseDate *string + IsStreamGated bool + StreamConditions []byte // JSONB + CreatedAt time.Time +} + +func (w *Writer) writePlaylists(ctx context.Context) error { + return processBatched(ctx, w, "playlists", + `SELECT count(*) FROM playlists WHERE is_current = true AND is_delete = false`, + `SELECT + playlist_id, playlist_owner_id, playlist_name, description, + is_album, is_private, + metadata_multihash, playlist_image_sizes_multihash, playlist_contents, + release_date::text, is_stream_gated, stream_conditions, + created_at + FROM playlists + WHERE is_current = true AND is_delete = false + ORDER BY playlist_id`, + func(rows pgx.Rows) (sourcePlaylist, error) { + var p sourcePlaylist + err := rows.Scan( + &p.PlaylistID, &p.PlaylistOwnerID, &p.PlaylistName, &p.Description, + &p.IsAlbum, &p.IsPrivate, + &p.MetadataMultihash, &p.ImageSizesMultihash, &p.PlaylistContents, + &p.ReleaseDate, &p.IsStreamGated, &p.StreamConditions, + &p.CreatedAt, + ) + return p, err + }, + func(ctx context.Context, p sourcePlaylist) error { + inner := playlistMetadataInner{ + CreatedAt: p.CreatedAt.Format(time.RFC3339), + PlaylistName: deref(p.PlaylistName), + Description: deref(p.Description), + IsAlbum: p.IsAlbum, + IsPrivate: p.IsPrivate, + PlaylistImageSizesHash: deref(p.ImageSizesMultihash), + ReleaseDate: deref(p.ReleaseDate), + IsStreamGated: p.IsStreamGated, + } + + inner.PlaylistContents = unmarshalJSONB(p.PlaylistContents) + inner.StreamConditions = unmarshalJSONB(p.StreamConditions) + + cid := "genesis-import" + if p.MetadataMultihash != nil && *p.MetadataMultihash != "" { + cid = *p.MetadataMultihash + } + + metaJSON, err := json.Marshal(playlistMetadataWrapper{ + CID: cid, + Data: inner, + }) + if err != nil { + return fmt.Errorf("marshal playlist %d metadata: %w", p.PlaylistID, err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: p.PlaylistOwnerID, + EntityType: "Playlist", + EntityId: p.PlaylistID, + Action: "Create", + Metadata: string(metaJSON), + }) + }, + ) +} diff --git a/cmd/genesis-writer/entities_social.go b/cmd/genesis-writer/entities_social.go new file mode 100644 index 00000000..2fb686c1 --- /dev/null +++ b/cmd/genesis-writer/entities_social.go @@ -0,0 +1,239 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +// createdAtMeta is a minimal metadata payload carrying only created_at. +type createdAtMeta struct { + CreatedAt string `json:"created_at"` +} + +func fmtCreatedAt(t time.Time) string { + return t.Format(time.RFC3339) +} + +// --- Follows --- + +func (w *Writer) writeFollows(ctx context.Context) error { + type follow struct { + follower, followee int64 + createdAt time.Time + } + return processBatched(ctx, w, "follows", + `SELECT count(*) FROM follows WHERE is_current = true AND is_delete = false`, + `SELECT follower_user_id, followee_user_id, created_at + FROM follows + WHERE is_current = true AND is_delete = false + ORDER BY follower_user_id, followee_user_id`, + func(rows pgx.Rows) (follow, error) { + var f follow + err := rows.Scan(&f.follower, &f.followee, &f.createdAt) + return f, err + }, + func(ctx context.Context, f follow) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(f.createdAt)}) + if err != nil { + return fmt.Errorf("marshal follow metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: f.follower, + EntityType: "User", + EntityId: f.followee, + Action: "Follow", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Saves --- + +func (w *Writer) writeSaves(ctx context.Context) error { + type save struct { + userID, itemID int64 + saveType string + createdAt time.Time + } + return processBatched(ctx, w, "saves", + `SELECT count(*) FROM saves WHERE is_current = true AND is_delete = false`, + `SELECT user_id, save_item_id, save_type, created_at + FROM saves + WHERE is_current = true AND is_delete = false + ORDER BY user_id, save_item_id`, + func(rows pgx.Rows) (save, error) { + var s save + err := rows.Scan(&s.userID, &s.itemID, &s.saveType, &s.createdAt) + return s, err + }, + func(ctx context.Context, s save) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + if err != nil { + return fmt.Errorf("marshal save metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: s.userID, + EntityType: saveRepostEntityType(s.saveType), + EntityId: s.itemID, + Action: "Save", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Reposts --- + +func (w *Writer) writeReposts(ctx context.Context) error { + type repost struct { + userID, itemID int64 + repostType string + createdAt time.Time + } + return processBatched(ctx, w, "reposts", + `SELECT count(*) FROM reposts WHERE is_current = true AND is_delete = false`, + `SELECT user_id, repost_item_id, repost_type, created_at + FROM reposts + WHERE is_current = true AND is_delete = false + ORDER BY user_id, repost_item_id`, + func(rows pgx.Rows) (repost, error) { + var r repost + err := rows.Scan(&r.userID, &r.itemID, &r.repostType, &r.createdAt) + return r, err + }, + func(ctx context.Context, r repost) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(r.createdAt)}) + if err != nil { + return fmt.Errorf("marshal repost metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: r.userID, + EntityType: saveRepostEntityType(r.repostType), + EntityId: r.itemID, + Action: "Repost", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Shares --- + +func (w *Writer) writeShares(ctx context.Context) error { + type share struct { + userID int64 + itemID int64 + shareType string + createdAt time.Time + } + return processBatched(ctx, w, "shares", + `SELECT count(*) FROM shares`, + `SELECT user_id, share_item_id, share_type, created_at + FROM shares + ORDER BY user_id, share_item_id`, + func(rows pgx.Rows) (share, error) { + var s share + err := rows.Scan(&s.userID, &s.itemID, &s.shareType, &s.createdAt) + return s, err + }, + func(ctx context.Context, s share) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + if err != nil { + return fmt.Errorf("marshal share metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: s.userID, + EntityType: saveRepostEntityType(s.shareType), + EntityId: s.itemID, + Action: "Share", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Subscriptions --- + +func (w *Writer) writeSubscriptions(ctx context.Context) error { + type subscription struct { + subscriberID, userID int64 + createdAt time.Time + } + return processBatched(ctx, w, "subscriptions", + `SELECT count(*) FROM subscriptions WHERE is_current = true AND is_delete = false`, + `SELECT subscriber_id, user_id, created_at + FROM subscriptions + WHERE is_current = true AND is_delete = false + ORDER BY subscriber_id, user_id`, + func(rows pgx.Rows) (subscription, error) { + var s subscription + err := rows.Scan(&s.subscriberID, &s.userID, &s.createdAt) + return s, err + }, + func(ctx context.Context, s subscription) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + if err != nil { + return fmt.Errorf("marshal subscription metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: s.subscriberID, + EntityType: "User", + EntityId: s.userID, + Action: "Subscribe", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Muted Users --- + +func (w *Writer) writeMutedUsers(ctx context.Context) error { + type mutedUser struct { + userID, mutedUserID int64 + createdAt time.Time + } + return processBatched(ctx, w, "muted_users", + `SELECT count(*) FROM muted_users WHERE is_delete = false`, + `SELECT user_id, muted_user_id, created_at + FROM muted_users + WHERE is_delete = false + ORDER BY user_id, muted_user_id`, + func(rows pgx.Rows) (mutedUser, error) { + var m mutedUser + err := rows.Scan(&m.userID, &m.mutedUserID, &m.createdAt) + return m, err + }, + func(ctx context.Context, m mutedUser) error { + metaJSON, err := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(m.createdAt)}) + if err != nil { + return fmt.Errorf("marshal muted user metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: m.userID, + EntityType: "MutedUser", + EntityId: m.mutedUserID, + Action: "Mute", + Metadata: string(metaJSON), + }) + }, + ) +} + +// saveRepostEntityType maps save_type / repost_type DB strings to DP entity type names. +func saveRepostEntityType(t string) string { + switch t { + case "track": + return "Track" + case "playlist", "album": + return "Playlist" + default: + return "Track" + } +} diff --git a/cmd/genesis-writer/entities_tip.go b/cmd/genesis-writer/entities_tip.go new file mode 100644 index 00000000..5c9d06d5 --- /dev/null +++ b/cmd/genesis-writer/entities_tip.go @@ -0,0 +1,74 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" + "go.uber.org/zap" +) + +type tipReactionMetadata struct { + ReactedTo string `json:"reacted_to"` + ReactionValue string `json:"reaction_value"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceTipReaction struct { + SenderWallet string + ReactedTo string + ReactionValue string + Timestamp time.Time +} + +func (w *Writer) writeTipReactions(ctx context.Context) error { + // Pre-load wallet → user_id mapping so we can set UserId on the ManageEntity. + walletToUser, err := preloadMap[string, int64](ctx, w.srcDB, + `SELECT LOWER(wallet), user_id FROM users WHERE is_current = true AND wallet IS NOT NULL ORDER BY wallet, user_id`) + if err != nil { + return fmt.Errorf("preload wallet→user map: %w", err) + } + + return processBatched(ctx, w, "tip_reactions", + `SELECT count(*) FROM reactions WHERE reaction_type = 'tip'`, + `SELECT sender_wallet, reacted_to, reaction_value, timestamp + FROM reactions + WHERE reaction_type = 'tip' + ORDER BY id`, + func(rows pgx.Rows) (sourceTipReaction, error) { + var tr sourceTipReaction + err := rows.Scan(&tr.SenderWallet, &tr.ReactedTo, &tr.ReactionValue, &tr.Timestamp) + return tr, err + }, + func(ctx context.Context, tr sourceTipReaction) error { + // Look up user ID from sender wallet. + users := walletToUser[strings.ToLower(tr.SenderWallet)] + if len(users) == 0 { + w.logger.Warn("tip reaction sender wallet not found, skipping", + zap.String("wallet", tr.SenderWallet)) + return nil + } + userID := users[0] + + metaJSON, err := json.Marshal(tipReactionMetadata{ + ReactedTo: tr.ReactedTo, + ReactionValue: tr.ReactionValue, + CreatedAt: tr.Timestamp.Format(time.RFC3339), + }) + if err != nil { + return fmt.Errorf("marshal tip reaction metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: userID, + EntityType: "Tip", + EntityId: 0, + Action: "React", + Metadata: string(metaJSON), + }) + }, + ) +} diff --git a/cmd/genesis-writer/entities_track.go b/cmd/genesis-writer/entities_track.go new file mode 100644 index 00000000..3143c24e --- /dev/null +++ b/cmd/genesis-writer/entities_track.go @@ -0,0 +1,215 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +type trackMetadataWrapper struct { + CID string `json:"cid"` + Data trackMetadataInner `json:"data"` +} + +type trackMetadataInner struct { + CreatedAt string `json:"created_at,omitempty"` + Title string `json:"title,omitempty"` + OwnerID int64 `json:"owner_id"` + Duration int `json:"duration,omitempty"` + Description string `json:"description,omitempty"` + Genre string `json:"genre,omitempty"` + Mood string `json:"mood,omitempty"` + Tags string `json:"tags,omitempty"` + TrackCID string `json:"track_cid,omitempty"` + PreviewCID string `json:"preview_cid,omitempty"` + CoverArt string `json:"cover_art,omitempty"` + CoverArtSizes string `json:"cover_art_sizes,omitempty"` + IsUnlisted bool `json:"is_unlisted,omitempty"` + IsDownloadable bool `json:"is_downloadable,omitempty"` + IsOriginalAvail bool `json:"is_original_available,omitempty"` + ReleaseDate string `json:"release_date,omitempty"` + License string `json:"license,omitempty"` + ISRC string `json:"isrc,omitempty"` + ISWC string `json:"iswc,omitempty"` + BPM *float64 `json:"bpm,omitempty"` + MusicalKey string `json:"musical_key,omitempty"` + RemixOf interface{} `json:"remix_of,omitempty"` + StemOf interface{} `json:"stem_of,omitempty"` + IsStreamGated bool `json:"is_stream_gated,omitempty"` + StreamConditions interface{} `json:"stream_conditions,omitempty"` + IsDownloadGated bool `json:"is_download_gated,omitempty"` + DownloadConditions interface{} `json:"download_conditions,omitempty"` +} + +type sourceTrack struct { + TrackID int64 + OwnerID int64 + Title *string + Description *string + Duration *int + Genre *string + Mood *string + Tags *string + TrackCID *string + CoverArt *string + CoverArtSizes *string + PreviewCID *string + IsUnlisted bool + IsDownloadable bool + IsOriginalAvailable bool + ReleaseDate *string + License *string + ISRC *string + ISWC *string + BPM *float64 + MusicalKey *string + RemixOf []byte // JSONB + StemOf []byte // JSONB + IsStreamGated bool + StreamConditions []byte // JSONB + IsDownloadGated bool + DownloadConditions []byte // JSONB + CreatedAt time.Time +} + +func (w *Writer) writeTracks(ctx context.Context) error { + return processBatched(ctx, w, "tracks", + `SELECT count(*) FROM tracks WHERE is_current = true AND is_delete = false AND is_available = true`, + `SELECT + track_id, owner_id, title, description, duration, genre, mood, tags, + track_cid, + cover_art, cover_art_sizes, preview_cid, + is_unlisted, is_downloadable, is_original_available, + release_date::text, license, isrc, iswc, bpm, musical_key, + remix_of, stem_of, + is_stream_gated, stream_conditions, + is_download_gated, download_conditions, + created_at + FROM tracks + WHERE is_current = true AND is_delete = false AND is_available = true + ORDER BY track_id`, + func(rows pgx.Rows) (sourceTrack, error) { + var t sourceTrack + err := rows.Scan( + &t.TrackID, &t.OwnerID, &t.Title, &t.Description, &t.Duration, &t.Genre, &t.Mood, &t.Tags, + &t.TrackCID, + &t.CoverArt, &t.CoverArtSizes, &t.PreviewCID, + &t.IsUnlisted, &t.IsDownloadable, &t.IsOriginalAvailable, + &t.ReleaseDate, &t.License, &t.ISRC, &t.ISWC, &t.BPM, &t.MusicalKey, + &t.RemixOf, &t.StemOf, + &t.IsStreamGated, &t.StreamConditions, + &t.IsDownloadGated, &t.DownloadConditions, + &t.CreatedAt, + ) + return t, err + }, + func(ctx context.Context, t sourceTrack) error { + inner := trackMetadataInner{ + CreatedAt: t.CreatedAt.Format(time.RFC3339), + OwnerID: t.OwnerID, + Title: deref(t.Title), + Description: deref(t.Description), + Duration: derefInt(t.Duration), + Genre: deref(t.Genre), + Mood: deref(t.Mood), + Tags: deref(t.Tags), + CoverArt: deref(t.CoverArt), + CoverArtSizes: deref(t.CoverArtSizes), + PreviewCID: deref(t.PreviewCID), + IsUnlisted: t.IsUnlisted, + IsDownloadable: t.IsDownloadable, + IsOriginalAvail: t.IsOriginalAvailable, + ReleaseDate: deref(t.ReleaseDate), + License: deref(t.License), + ISRC: deref(t.ISRC), + ISWC: deref(t.ISWC), + BPM: t.BPM, + MusicalKey: deref(t.MusicalKey), + IsStreamGated: t.IsStreamGated, + IsDownloadGated: t.IsDownloadGated, + } + + inner.RemixOf = unmarshalJSONB(t.RemixOf) + inner.StemOf = unmarshalJSONB(t.StemOf) + inner.StreamConditions = unmarshalJSONB(t.StreamConditions) + inner.DownloadConditions = unmarshalJSONB(t.DownloadConditions) + + inner.TrackCID = deref(t.TrackCID) + + metaJSON, err := json.Marshal(trackMetadataWrapper{ + CID: deref(t.TrackCID), + Data: inner, + }) + if err != nil { + return fmt.Errorf("marshal track %d metadata: %w", t.TrackID, err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: t.OwnerID, + EntityType: "Track", + EntityId: t.TrackID, + Action: "Create", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Track Downloads --- + +type trackDownloadMetadata struct { + City string `json:"city,omitempty"` + Region string `json:"region,omitempty"` + Country string `json:"country,omitempty"` + CreatedAt string `json:"created_at,omitempty"` +} + +type sourceTrackDownload struct { + ParentTrackID int64 + TrackID int64 + UserID *int64 + City *string + Region *string + Country *string + CreatedAt time.Time +} + +func (w *Writer) writeTrackDownloads(ctx context.Context) error { + return processBatched(ctx, w, "track_downloads", + `SELECT count(*) FROM track_downloads`, + `SELECT parent_track_id, track_id, user_id, city, region, country, created_at + FROM track_downloads + ORDER BY parent_track_id, track_id`, + func(rows pgx.Rows) (sourceTrackDownload, error) { + var d sourceTrackDownload + err := rows.Scan(&d.ParentTrackID, &d.TrackID, &d.UserID, &d.City, &d.Region, &d.Country, &d.CreatedAt) + return d, err + }, + func(ctx context.Context, d sourceTrackDownload) error { + var userID int64 + if d.UserID != nil { + userID = *d.UserID + } + meta := trackDownloadMetadata{ + City: deref(d.City), + Region: deref(d.Region), + Country: deref(d.Country), + CreatedAt: d.CreatedAt.Format(time.RFC3339), + } + metaJSON, err := json.Marshal(meta) + if err != nil { + return fmt.Errorf("marshal track download metadata: %w", err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: userID, + EntityType: "Track", + EntityId: d.TrackID, + Action: "Download", + Metadata: string(metaJSON), + }) + }, + ) +} diff --git a/cmd/genesis-writer/entities_user.go b/cmd/genesis-writer/entities_user.go new file mode 100644 index 00000000..bf509a25 --- /dev/null +++ b/cmd/genesis-writer/entities_user.go @@ -0,0 +1,151 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/jackc/pgx/v5" +) + +// --- Users --- + +type userMetadataWrapper struct { + CID string `json:"cid"` + Data userMetadata `json:"data"` +} + +type userMetadata struct { + CreatedAt string `json:"created_at,omitempty"` + Name string `json:"name,omitempty"` + Handle string `json:"handle,omitempty"` + Bio string `json:"bio,omitempty"` + Location string `json:"location,omitempty"` + Wallet string `json:"wallet,omitempty"` + ProfilePicture string `json:"profile_picture,omitempty"` + ProfilePictureSizes string `json:"profile_picture_sizes,omitempty"` + CoverPhoto string `json:"cover_photo,omitempty"` + CoverPhotoSizes string `json:"cover_photo_sizes,omitempty"` + TwitterHandle string `json:"twitter_handle,omitempty"` + InstagramHandle string `json:"instagram_handle,omitempty"` + Website string `json:"website,omitempty"` + IsVerified bool `json:"is_verified,omitempty"` +} + +type sourceUser struct { + UserID int64 + Wallet *string + Handle *string + Name *string + Bio *string + Location *string + ProfilePicture *string + ProfilePictureSizes *string + CoverPhoto *string + CoverPhotoSizes *string + TwitterHandle *string + InstagramHandle *string + Website *string + IsVerified bool + CreatedAt time.Time +} + +func (w *Writer) writeUsers(ctx context.Context) error { + return processBatched(ctx, w, "users", + `SELECT count(*) FROM users WHERE is_current = true AND is_deactivated = false AND is_available = true`, + `SELECT + user_id, wallet, handle, name, bio, location, + profile_picture, profile_picture_sizes, + cover_photo, cover_photo_sizes, + twitter_handle, instagram_handle, website, + is_verified, created_at + FROM users + WHERE is_current = true AND is_deactivated = false AND is_available = true + ORDER BY user_id`, + func(rows pgx.Rows) (sourceUser, error) { + var u sourceUser + err := rows.Scan( + &u.UserID, &u.Wallet, &u.Handle, &u.Name, &u.Bio, &u.Location, + &u.ProfilePicture, &u.ProfilePictureSizes, + &u.CoverPhoto, &u.CoverPhotoSizes, + &u.TwitterHandle, &u.InstagramHandle, &u.Website, + &u.IsVerified, &u.CreatedAt, + ) + return u, err + }, + func(ctx context.Context, u sourceUser) error { + meta := userMetadata{ + CreatedAt: u.CreatedAt.Format(time.RFC3339), + Name: deref(u.Name), + Handle: deref(u.Handle), + Bio: deref(u.Bio), + Location: deref(u.Location), + Wallet: deref(u.Wallet), + ProfilePicture: deref(u.ProfilePicture), + ProfilePictureSizes: deref(u.ProfilePictureSizes), + CoverPhoto: deref(u.CoverPhoto), + CoverPhotoSizes: deref(u.CoverPhotoSizes), + TwitterHandle: deref(u.TwitterHandle), + InstagramHandle: deref(u.InstagramHandle), + Website: deref(u.Website), + IsVerified: u.IsVerified, + } + metaJSON, err := json.Marshal(userMetadataWrapper{ + CID: "genesis-import", + Data: meta, + }) + if err != nil { + return fmt.Errorf("marshal user %d metadata: %w", u.UserID, err) + } + return w.addManageEntity(ctx, &corev1.ManageEntityLegacy{ + UserId: u.UserID, + EntityType: "User", + EntityId: u.UserID, + Action: "Create", + Metadata: string(metaJSON), + }) + }, + ) +} + +// --- Associated Wallets --- + +type sourceAssociatedWallet struct { + UserID int64 + Wallet string + Chain string +} + +func (w *Writer) writeAssociatedWallets(ctx context.Context) error { + return processBatched(ctx, w, "associated_wallets", + `SELECT count(*) FROM associated_wallets WHERE is_current = true AND is_delete = false`, + `SELECT user_id, wallet, chain + FROM associated_wallets + WHERE is_current = true AND is_delete = false + ORDER BY user_id, wallet`, + func(rows pgx.Rows) (sourceAssociatedWallet, error) { + var aw sourceAssociatedWallet + err := rows.Scan(&aw.UserID, &aw.Wallet, &aw.Chain) + return aw, err + }, + func(ctx context.Context, aw sourceAssociatedWallet) error { + metaJSON, err := json.Marshal(map[string]string{ + "wallet": aw.Wallet, + "chain": aw.Chain, + }) + if err != nil { + return fmt.Errorf("marshal associated wallet: %w", err) + } + // DP uses params.signer (the wallet address) as identity for AssociatedWallet. + return w.addManageEntityWithSigner(ctx, &corev1.ManageEntityLegacy{ + UserId: aw.UserID, + EntityType: "AssociatedWallet", + EntityId: 0, + Action: "Create", + Metadata: string(metaJSON), + }, aw.Wallet) + }, + ) +} diff --git a/cmd/genesis-writer/integration_test.go b/cmd/genesis-writer/integration_test.go new file mode 100644 index 00000000..d7658148 --- /dev/null +++ b/cmd/genesis-writer/integration_test.go @@ -0,0 +1,798 @@ +//go:build integration + +package main + +import ( + "context" + "crypto/ecdsa" + "crypto/tls" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + "time" + + "connectrpc.com/connect" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + corev1connect "github.com/OpenAudio/go-openaudio/pkg/api/core/v1/v1connect" + "github.com/OpenAudio/go-openaudio/pkg/common" + genesisPkg "github.com/OpenAudio/go-openaudio/pkg/core/config/genesis" + "github.com/cometbft/cometbft/p2p" + "github.com/cometbft/cometbft/privval" + "github.com/ethereum/go-ethereum/crypto" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +// testDelegatePrivKey is the Ethereum key for the devnet validator. +// Matches delegatePrivateKey in docker-compose.yml. +// Address: 0x73EB6d82CFB20bA669e9c178b718d770C49BB52f +// FOR LOCAL TESTING ONLY. +const testDelegatePrivKey = "d09ba371c359f10f22ccda12fd26c598c7921bda3220c9942174562bc6a36fe8" + +// testMigrationPrivKey is the genesis_migration_address key (same as genesis-replay). +// Address: 0xF7Bd9D733fAD4e2f0594bb88180cef8D8D03dEbB +const testMigrationPrivKey = "fc972c220946cde07bf3c6be380cc956c3d5680a2d7b225c660866e450cf1299" + +// TestGenesisWriter is a full end-to-end integration test. +// +// The test is self-contained: it starts the required Docker services +// (src-db, core-db, eth-ganache, ingress, openaudio-1), runs genesis-writer +// in-process, verifies entity counts with a lightweight indexer, confirms +// consensus is advancing, then tears everything down. +// +// Optional env overrides: +// +// GENESIS_SRC_DSN source DB DSN (default: postgres://postgres:postgres@localhost:5435/genesis_writer_source?sslmode=disable) +// GENESIS_DST_DSN core DB DSN (default: postgres://postgres:postgres@localhost:5436/openaudio?sslmode=disable) +// GENESIS_CHAIN_URL chain URL (default: https://node1.oap.devnet) +// OPENAUDIO_IMAGE docker image (default: openaudio/go-openaudio:dev) +// +// Run with: +// +// go test -v -tags integration -run TestGenesisWriter -timeout 30m ./cmd/genesis-writer/... +func TestGenesisWriter(t *testing.T) { + srcDSN := envOrDefault("GENESIS_SRC_DSN", "postgres://postgres:postgres@localhost:5435/genesis_writer_source?sslmode=disable") + dstDSN := envOrDefault("GENESIS_DST_DSN", "postgres://postgres:postgres@localhost:5436/openaudio?sslmode=disable") + chainURL := envOrDefault("GENESIS_CHAIN_URL", "https://node1.oap.devnet") + + migrationKey, err := parsePrivKey(testMigrationPrivKey) + require.NoError(t, err, "parse migration private key") + + logger, _ := zap.NewDevelopment() + defer logger.Sync() //nolint:errcheck + + ctx, cancel := context.WithTimeout(context.Background(), 25*time.Minute) + defer cancel() + + // ---- 0. Start infrastructure --------------------------------------------- + // workDir is the unified work directory for this test run: + // gw-src-pg-data/ – postgres bind-mount for src-db + // gw-core-pg-data/ – postgres bind-mount for core-db + // audius-mainnet-v2/ – CometBFT home written by genesis-writer + // + // t.TempDir registers its cleanup before our teardownAll cleanup, so + // teardownAll (stopping containers) runs first and the directory is removed + // only after Docker has released all file handles. + workDir := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(workDir, "gw-src-pg-data"), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(workDir, "gw-core-pg-data"), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(workDir, "gw-core-pg-2-data"), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(workDir, "oap2-core"), 0o755)) + + composeFile := composeFilePath(t) + composeEnv := append(os.Environ(), + "EXT_DATA_DIR="+workDir, + "GENESIS_WRITER_DATA_DIR="+workDir, + "OPENAUDIO_IMAGE="+envOrDefault("OPENAUDIO_IMAGE", "openaudio/go-openaudio:dev"), + ) + + t.Log("starting infrastructure (src-db, core-db, eth-ganache, ingress)...") + startBaseServices(t, composeFile, composeEnv) + t.Cleanup(func() { teardownAll(composeFile, composeEnv) }) + + // ---- 1. Snapshot expected state from source DB --------------------------- + srcPool, err := pgxpool.New(ctx, srcDSN) + require.NoError(t, err) + defer srcPool.Close() + require.NoError(t, srcPool.Ping(ctx), "ping source DB") + + expected := snapshotSource(t, ctx, srcPool) + t.Logf("source snapshot: %d users, %d tracks, %d playlists, %d follows, %d saves, %d reposts, %d plays", + len(expected.users), len(expected.tracks), len(expected.playlists), + len(expected.follows), len(expected.saves), len(expected.reposts), expected.playCount) + + // ---- 2. Set up CometBFT home directory ----------------------------------- + // Genesis-writer writes state.db + blockstore.db into cmtHome. + // openaudio-1 mounts workDir as /data/core so that + // /data/core/audius-mainnet-v2/ is the CometBFT root dir. + cmtHome := filepath.Join(workDir, "audius-mainnet-v2") + require.NoError(t, os.MkdirAll(filepath.Join(cmtHome, "config"), 0o755)) + require.NoError(t, os.MkdirAll(filepath.Join(cmtHome, "data"), 0o755)) + + // Derive the validator's ed25519 key from the Ethereum delegate key. + ethKey, err := common.EthToEthKey(testDelegatePrivKey) + require.NoError(t, err, "parse delegate private key") + cometKey, err := common.EthToCometKey(ethKey) + require.NoError(t, err, "derive comet key") + + privValKeyFile := filepath.Join(cmtHome, "config", "priv_validator_key.json") + privValStateFile := filepath.Join(cmtHome, "data", "priv_validator_state.json") + pv := privval.NewFilePV(*cometKey, privValKeyFile, privValStateFile) + pv.Save() + t.Logf("validator address: %s", pv.GetAddress()) + + // Write the genesis.json that genesis-writer needs for state.db construction. + genDoc, err := genesisPkg.Read("dev") + require.NoError(t, err, "read dev genesis") + genesisFile := filepath.Join(cmtHome, "config", "genesis.json") + require.NoError(t, genDoc.SaveAs(genesisFile), "save genesis.json") + + // ---- 3. Reset core DB schema and run genesis-writer ---------------------- + dstPool, err := pgxpool.New(ctx, dstDSN) + require.NoError(t, err) + defer dstPool.Close() + require.NoError(t, dstPool.Ping(ctx), "ping core DB") + _, err = dstPool.Exec(ctx, "DROP SCHEMA public CASCADE; CREATE SCHEMA public;") + require.NoError(t, err, "reset core DB schema") + + t.Log("running genesis-writer...") + cfg := &WriterConfig{ + SrcDSN: srcDSN, + DstDSN: dstDSN, + PrivKey: migrationKey, + Network: "dev", + ChainID: genDoc.ChainID, + GenesisTime: genDoc.GenesisTime, + GenesisFile: genesisFile, + PrivValidatorKeyFile: privValKeyFile, + CMTHome: cmtHome, + MaxTxsPerBlock: 500, + BatchSize: 100, + RunMigrations: true, + } + + w, err := NewWriter(cfg, logger) + require.NoError(t, err, "init writer") + defer w.Close() + require.NoError(t, w.Run(ctx), "run genesis-writer") + + genesisHeight := w.finalHeight + t.Logf("genesis-writer done: height=%d, txs=%d, blocks=%d", + genesisHeight, w.totalTxs, w.totalBlocks) + + // ---- 4. Verify core DB has synthetic blocks ------------------------------ + var blockCount, txCount int64 + require.NoError(t, dstPool.QueryRow(ctx, "SELECT count(*) FROM core_blocks").Scan(&blockCount)) + require.NoError(t, dstPool.QueryRow(ctx, "SELECT count(*) FROM core_transactions").Scan(&txCount)) + assert.Greater(t, blockCount, int64(0), "core_blocks should be populated") + assert.Greater(t, txCount, int64(0), "core_transactions should be populated") + assert.GreaterOrEqual(t, blockCount, genesisHeight, "core_blocks count should be at least the genesis height") + t.Logf("core DB: %d blocks, %d transactions", blockCount, txCount) + + // state.db and blockstore.db are directories (PebbleDB), not plain files. + require.DirExists(t, filepath.Join(cmtHome, "data", "state.db")) + require.DirExists(t, filepath.Join(cmtHome, "data", "blockstore.db")) + + // ---- 5. Index the core DB and compare ------------------------------------ + // The lightweight indexer reads ManageEntityLegacyMigration transactions + // directly from the core DB — no discovery-provider needed. + t.Log("indexing chain transactions...") + indexed := indexChain(t, ctx, dstPool, genesisHeight) + t.Logf("indexed: %d users, %d tracks, %d playlists, %d follows, %d saves, %d reposts, %d plays", + len(indexed.users), len(indexed.tracks), len(indexed.playlists), + len(indexed.follows), len(indexed.saves), len(indexed.reposts), indexed.playCount) + + assert.Equal(t, expected.users, indexed.users, "users mismatch") + assert.Equal(t, expected.tracks, indexed.tracks, "tracks mismatch") + assert.Equal(t, expected.playlists, indexed.playlists, "playlists mismatch") + assert.Equal(t, expected.follows, indexed.follows, "follows mismatch") + assert.Equal(t, expected.saves, indexed.saves, "saves mismatch") + assert.Equal(t, expected.reposts, indexed.reposts, "reposts mismatch") + assert.Equal(t, expected.playCount, indexed.playCount, "plays count mismatch") + + // ---- 6. Start openaudio-1 and verify consensus --------------------------- + t.Log("starting openaudio-1...") + startService(t, composeFile, composeEnv, "openaudio-1") + + t.Logf("waiting for openaudio-1 to produce blocks beyond genesis height %d...", genesisHeight) + chainClient := newChainClient(chainURL) + waitForHeight(t, ctx, chainClient, genesisHeight+2) + t.Log("consensus confirmed: chain is advancing") + + // ---- 7. Wait for openaudio-1 to produce a snapshot ----------------------- + t.Log("waiting for openaudio-1 to create a snapshot...") + waitForSnapshot(t, ctx, chainClient) + + // ---- 8. State sync openaudio-2 from openaudio-1 -------------------------- + // Read openaudio-1's node key so we can configure persistent peers. + nodeKeyFile := filepath.Join(workDir, "audius-mainnet-v2", "config", "node_key.json") + nodeKey, err := p2p.LoadNodeKey(nodeKeyFile) + require.NoError(t, err, "load openaudio-1 node key") + persistentPeers := fmt.Sprintf("%s@openaudio-1:26656", nodeKey.ID()) + t.Logf("openaudio-1 node ID: %s", nodeKey.ID()) + + ssEnv := make([]string, len(composeEnv), len(composeEnv)+1) + copy(ssEnv, composeEnv) + ssEnv = append(ssEnv, "PERSISTENT_PEERS="+persistentPeers) + + t.Log("starting openaudio-2 (state sync from openaudio-1)...") + startServiceWithProfile(t, composeFile, ssEnv, []string{"statesync"}, "openaudio-2") + + // Verify that openaudio-2's health check reports state sync is in progress. + chain2URL := envOrDefault("GENESIS_CHAIN2_URL", "https://node2.oap.devnet") + chain2Client := newChainClient(chain2URL) + waitForStateSyncProgress(t, ctx, chain2Client) + + // ---- 9. Verify state-synced node has identical genesis data --------------- + dst2DSN := envOrDefault("GENESIS_DST2_DSN", "postgres://postgres:postgres@localhost:5437/openaudio?sslmode=disable") + db2Pool, err := pgxpool.New(ctx, dst2DSN) + require.NoError(t, err) + defer db2Pool.Close() + require.NoError(t, db2Pool.Ping(ctx), "ping core-db-2") + + // Poll core-db-2 until state sync has restored the genesis transactions. + // The docker healthcheck passes before the pg_restore completes. + waitForStateSyncData(t, ctx, db2Pool, genesisHeight) + + indexed2 := indexChain(t, ctx, db2Pool, genesisHeight) + t.Logf("state-synced node: %d users, %d tracks, %d playlists, %d follows, %d saves, %d reposts, %d plays", + len(indexed2.users), len(indexed2.tracks), len(indexed2.playlists), + len(indexed2.follows), len(indexed2.saves), len(indexed2.reposts), indexed2.playCount) + + assert.Equal(t, expected.users, indexed2.users, "state-synced users mismatch") + assert.Equal(t, expected.tracks, indexed2.tracks, "state-synced tracks mismatch") + assert.Equal(t, expected.playlists, indexed2.playlists, "state-synced playlists mismatch") + assert.Equal(t, expected.follows, indexed2.follows, "state-synced follows mismatch") + assert.Equal(t, expected.saves, indexed2.saves, "state-synced saves mismatch") + assert.Equal(t, expected.reposts, indexed2.reposts, "state-synced reposts mismatch") + assert.Equal(t, expected.playCount, indexed2.playCount, "state-synced plays count mismatch") + t.Log("state sync verification passed: all entities match") +} + +// ---- helpers ---------------------------------------------------------------- + +func parsePrivKey(hexKey string) (*ecdsa.PrivateKey, error) { + b, err := hex.DecodeString(strings.TrimPrefix(hexKey, "0x")) + if err != nil { + return nil, err + } + return crypto.ToECDSA(b) +} + +func envOrDefault(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} + +// composeFilePath returns the absolute path to cmd/genesis-writer/docker-compose.yml, +// derived from the location of this test file. +func composeFilePath(t *testing.T) string { + t.Helper() + _, thisFile, _, _ := runtime.Caller(0) + return filepath.Join(filepath.Dir(thisFile), "docker-compose.yml") +} + +// testComposeProject is the docker compose project name used by the integration +// test. It is distinct from "genesis-writer" (used by run-local.sh) so the two +// stacks can coexist on the same host without port or project conflicts. +const testComposeProject = "genesis-writer-test" + +// startBaseServices tears down any previous test-project stack, then starts +// src-db, core-db, core-db-2, eth-ganache, and ingress and waits until healthy. +func startBaseServices(t *testing.T, composeFile string, env []string) { + t.Helper() + // Remove any remnants of a previous test run with the same project name. + teardownAll(composeFile, env) + + cmd := exec.Command("docker", "compose", "-p", testComposeProject, + "-f", composeFile, "up", "-d", "--wait", + "src-db", "core-db", "core-db-2", "eth-ganache", "ingress") + cmd.Env = env + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("docker compose up output:\n%s", string(out)) + require.NoError(t, err, "start base services") + } +} + +// startServiceWithProfile runs docker compose up -d --wait for the named +// services, activating the given profiles. +func startServiceWithProfile(t *testing.T, composeFile string, env []string, profiles []string, services ...string) { + t.Helper() + args := []string{"compose", "-p", testComposeProject, "-f", composeFile} + for _, p := range profiles { + args = append(args, "--profile", p) + } + args = append(args, "up", "-d", "--wait") + args = append(args, services...) + cmd := exec.Command("docker", args...) + cmd.Env = env + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("docker compose up output:\n%s", string(out)) + require.NoError(t, err, "docker compose up -d %s", strings.Join(services, " ")) + } +} + +// startService runs docker compose up -d --wait for the named services (chain profile). +func startService(t *testing.T, composeFile string, env []string, services ...string) { + t.Helper() + startServiceWithProfile(t, composeFile, env, []string{"chain"}, services...) +} + +// teardownAll stops all services (all profiles) and removes volumes. +func teardownAll(composeFile string, env []string) { + _ = exec.Command("docker", "compose", "-p", testComposeProject, + "-f", composeFile, + "--profile", "chain", "--profile", "statesync", + "down", "-v").Run() +} + +// newChainClient creates a CoreService gRPC client for the chain. +func newChainClient(chainURL string) corev1connect.CoreServiceClient { + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec + }, + Timeout: 10 * time.Second, + } + url := chainURL + if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { + url = "https://" + url + } + return corev1connect.NewCoreServiceClient(httpClient, url) +} + +// waitForHeight polls the chain until it has produced a block at or beyond +// the target height, confirming that consensus is making progress. +func waitForHeight(t *testing.T, ctx context.Context, client corev1connect.CoreServiceClient, targetHeight int64) { + t.Helper() + deadline := time.Now().Add(5 * time.Minute) + for { + if ctx.Err() != nil { + t.Fatal("context cancelled waiting for chain height") + } + if time.Now().After(deadline) { + t.Fatalf("timed out waiting for chain height >= %d", targetHeight) + } + resp, err := client.GetBlock(ctx, connect.NewRequest(&corev1.GetBlockRequest{Height: targetHeight})) + if err == nil && resp.Msg.Block != nil { + t.Logf("chain reached height %d (hash=%s)", targetHeight, resp.Msg.Block.Hash) + return + } + time.Sleep(3 * time.Second) + } +} + +// waitForStateSyncData polls core-db-2 until state sync has restored genesis +// data AND the node has produced at least one block beyond the genesis height, +// confirming that the state-synced node is fully operational. +func waitForStateSyncData(t *testing.T, ctx context.Context, pool *pgxpool.Pool, genesisHeight int64) { + t.Helper() + deadline := time.Now().Add(5 * time.Minute) + for { + if time.Now().After(deadline) { + t.Fatal("timed out waiting for state sync to restore data in core-db-2") + } + var maxBlock int64 + err := pool.QueryRow(ctx, + "SELECT COALESCE(MAX(height), 0) FROM core_blocks").Scan(&maxBlock) + if err == nil && maxBlock > genesisHeight { + t.Logf("state sync restored: max block height %d (genesis was %d)", maxBlock, genesisHeight) + return + } + time.Sleep(3 * time.Second) + } +} + +// waitForSnapshot polls openaudio-1 until at least one snapshot is available. +func waitForSnapshot(t *testing.T, ctx context.Context, client corev1connect.CoreServiceClient) { + t.Helper() + deadline := time.Now().Add(3 * time.Minute) + for { + if time.Now().After(deadline) { + t.Fatal("timed out waiting for snapshot to be created") + } + resp, err := client.GetStoredSnapshots(ctx, connect.NewRequest(&corev1.GetStoredSnapshotsRequest{})) + if err == nil && len(resp.Msg.Snapshots) > 0 { + snap := resp.Msg.Snapshots[0] + t.Logf("snapshot available: height=%d, chunks=%d", snap.Height, snap.ChunkCount) + return + } + time.Sleep(3 * time.Second) + } +} + +// waitForStateSyncProgress polls openaudio-2's GetStatus endpoint until state +// sync has completed. State sync may finish so quickly that intermediate phases +// (DOWNLOADING_CHUNKS, RESTORING_PG_DUMP) are never observed; in that case +// syncInfo.synced=true indicates completion. +func waitForStateSyncProgress(t *testing.T, ctx context.Context, client corev1connect.CoreServiceClient) { + t.Helper() + deadline := time.Now().Add(90 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("timed out waiting for state sync to complete") + } + resp, err := client.GetStatus(ctx, connect.NewRequest(&corev1.GetStatusRequest{})) + if err == nil && resp.Msg.SyncInfo != nil { + if ss := resp.Msg.SyncInfo.GetStateSync(); ss != nil { + t.Logf("openaudio-2 state sync phase: %s", ss.Phase.String()) + } + if resp.Msg.SyncInfo.Synced { + t.Log("openaudio-2 state sync complete (synced=true)") + return + } + } + time.Sleep(2 * time.Second) + } +} + +// ---- source snapshot -------------------------------------------------------- + +type sourceSnapshot struct { + users []snapshotUser + tracks []snapshotTrack + playlists []snapshotPlaylist + follows []followPair + saves []saveTuple + reposts []repostTuple + playCount int +} + +type snapshotUser struct { + UserID int64 + Handle string + Name string + Bio string + Location string +} + +type snapshotTrack struct { + TrackID int64 + OwnerID int64 + Title string + Genre string +} + +type snapshotPlaylist struct { + PlaylistID int64 + PlaylistOwnerID int64 + PlaylistName string + IsAlbum bool +} + +type followPair struct{ Follower, Followee int64 } + +type saveTuple struct { + UserID int64 + ItemID int64 + SaveType string +} + +type repostTuple struct { + UserID int64 + ItemID int64 + RepostType string +} + +func snapshotSource(t *testing.T, ctx context.Context, db *pgxpool.Pool) sourceSnapshot { + t.Helper() + var s sourceSnapshot + s.users = queryUsers(t, ctx, db) + s.tracks = queryTracks(t, ctx, db) + s.playlists = queryPlaylists(t, ctx, db) + s.follows = queryFollows(t, ctx, db) + s.saves = querySaves(t, ctx, db) + s.reposts = queryReposts(t, ctx, db) + s.playCount = queryPlayCount(t, ctx, db) + return s +} + +func queryUsers(t *testing.T, ctx context.Context, db *pgxpool.Pool) []snapshotUser { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT user_id, + COALESCE(handle, ''), COALESCE(name, ''), + COALESCE(bio, ''), COALESCE(location, '') + FROM users + WHERE is_current = true AND is_deactivated = false AND is_available = true + ORDER BY user_id`) + require.NoError(t, err) + defer rows.Close() + var out []snapshotUser + for rows.Next() { + var u snapshotUser + require.NoError(t, rows.Scan(&u.UserID, &u.Handle, &u.Name, &u.Bio, &u.Location)) + out = append(out, u) + } + require.NoError(t, rows.Err()) + return out +} + +func queryTracks(t *testing.T, ctx context.Context, db *pgxpool.Pool) []snapshotTrack { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT track_id, owner_id, COALESCE(title, ''), COALESCE(genre, '') + FROM tracks + WHERE is_current = true AND is_delete = false AND is_available = true + ORDER BY track_id`) + require.NoError(t, err) + defer rows.Close() + var out []snapshotTrack + for rows.Next() { + var tr snapshotTrack + require.NoError(t, rows.Scan(&tr.TrackID, &tr.OwnerID, &tr.Title, &tr.Genre)) + out = append(out, tr) + } + require.NoError(t, rows.Err()) + return out +} + +func queryPlaylists(t *testing.T, ctx context.Context, db *pgxpool.Pool) []snapshotPlaylist { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT playlist_id, playlist_owner_id, COALESCE(playlist_name, ''), is_album + FROM playlists + WHERE is_current = true AND is_delete = false + ORDER BY playlist_id`) + require.NoError(t, err) + defer rows.Close() + var out []snapshotPlaylist + for rows.Next() { + var p snapshotPlaylist + require.NoError(t, rows.Scan(&p.PlaylistID, &p.PlaylistOwnerID, &p.PlaylistName, &p.IsAlbum)) + out = append(out, p) + } + require.NoError(t, rows.Err()) + return out +} + +func queryFollows(t *testing.T, ctx context.Context, db *pgxpool.Pool) []followPair { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT follower_user_id, followee_user_id + FROM follows + WHERE is_current = true AND is_delete = false + ORDER BY follower_user_id, followee_user_id`) + require.NoError(t, err) + defer rows.Close() + var out []followPair + for rows.Next() { + var f followPair + require.NoError(t, rows.Scan(&f.Follower, &f.Followee)) + out = append(out, f) + } + require.NoError(t, rows.Err()) + return out +} + +func querySaves(t *testing.T, ctx context.Context, db *pgxpool.Pool) []saveTuple { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT user_id, save_item_id, save_type + FROM saves + WHERE is_current = true AND is_delete = false + ORDER BY user_id, save_item_id`) + require.NoError(t, err) + defer rows.Close() + var out []saveTuple + for rows.Next() { + var s saveTuple + require.NoError(t, rows.Scan(&s.UserID, &s.ItemID, &s.SaveType)) + out = append(out, s) + } + require.NoError(t, rows.Err()) + return out +} + +func queryReposts(t *testing.T, ctx context.Context, db *pgxpool.Pool) []repostTuple { + t.Helper() + rows, err := db.Query(ctx, ` + SELECT user_id, repost_item_id, repost_type + FROM reposts + WHERE is_current = true AND is_delete = false + ORDER BY user_id, repost_item_id`) + require.NoError(t, err) + defer rows.Close() + var out []repostTuple + for rows.Next() { + var rp repostTuple + require.NoError(t, rows.Scan(&rp.UserID, &rp.ItemID, &rp.RepostType)) + out = append(out, rp) + } + require.NoError(t, rows.Err()) + return out +} + +func queryPlayCount(t *testing.T, ctx context.Context, db *pgxpool.Pool) int { + t.Helper() + var n int + require.NoError(t, db.QueryRow(ctx, `SELECT count(*) FROM plays`).Scan(&n)) + return n +} + +// ---- lightweight chain indexer ---------------------------------------------- + +// indexChain reads all ManageEntityLegacyMigration and TrackPlays transactions +// from core_transactions and reconstructs entity state for comparison with the +// source snapshot. No discovery-provider is needed. +func indexChain(t *testing.T, ctx context.Context, pool *pgxpool.Pool, maxHeight int64) sourceSnapshot { + t.Helper() + + rows, err := pool.Query(ctx, ` + SELECT transaction FROM core_transactions + WHERE block_id <= $1 + ORDER BY block_id, index`, maxHeight) + require.NoError(t, err) + defer rows.Close() + + userMap := map[int64]snapshotUser{} + trackMap := map[int64]snapshotTrack{} + playlistMap := map[int64]snapshotPlaylist{} + + type followKey struct{ Follower, Followee int64 } + followSet := map[followKey]bool{} + + type saveKey struct { + UserID, ItemID int64 + SaveType string + } + saveSet := map[saveKey]bool{} + + type repostKey struct { + UserID, ItemID int64 + RepostType string + } + repostSet := map[repostKey]bool{} + + playCount := 0 + + for rows.Next() { + var txBytes []byte + require.NoError(t, rows.Scan(&txBytes)) + + var stx corev1.SignedTransaction + require.NoError(t, proto.Unmarshal(txBytes, &stx), "unmarshal transaction") + + switch tx := stx.Transaction.(type) { + case *corev1.SignedTransaction_ManageEntityMigration: + me := tx.ManageEntityMigration + // Action is the primary discriminator; entity_type refines within each action. + switch me.Action { + case "Create": + switch me.EntityType { + case "User": + var w struct { + Data struct { + Handle string `json:"handle"` + Name string `json:"name"` + Bio string `json:"bio"` + Location string `json:"location"` + } `json:"data"` + } + _ = json.Unmarshal([]byte(me.Metadata), &w) + userMap[me.EntityId] = snapshotUser{ + UserID: me.EntityId, + Handle: w.Data.Handle, + Name: w.Data.Name, + Bio: w.Data.Bio, + Location: w.Data.Location, + } + case "Track": + var w struct { + Data struct { + OwnerID int64 `json:"owner_id"` + Title string `json:"title"` + Genre string `json:"genre"` + } `json:"data"` + } + _ = json.Unmarshal([]byte(me.Metadata), &w) + trackMap[me.EntityId] = snapshotTrack{ + TrackID: me.EntityId, + OwnerID: w.Data.OwnerID, + Title: w.Data.Title, + Genre: w.Data.Genre, + } + case "Playlist": + var w struct { + Data struct { + PlaylistName string `json:"playlist_name"` + IsAlbum bool `json:"is_album"` + } `json:"data"` + } + _ = json.Unmarshal([]byte(me.Metadata), &w) + playlistMap[me.EntityId] = snapshotPlaylist{ + PlaylistID: me.EntityId, + PlaylistOwnerID: me.UserId, + PlaylistName: w.Data.PlaylistName, + IsAlbum: w.Data.IsAlbum, + } + } + case "Follow": + // UserId=follower, EntityId=followee + followSet[followKey{me.UserId, me.EntityId}] = true + case "Save": + saveType := "track" + if me.EntityType == "Playlist" { + saveType = "playlist" + } + saveSet[saveKey{me.UserId, me.EntityId, saveType}] = true + case "Repost": + repostType := "track" + if me.EntityType == "Playlist" { + repostType = "playlist" + } + repostSet[repostKey{me.UserId, me.EntityId, repostType}] = true + } + + case *corev1.SignedTransaction_Plays: + playCount += len(tx.Plays.Plays) + } + } + require.NoError(t, rows.Err()) + + // Convert maps to sorted slices matching sourceSnapshot ordering. + var s sourceSnapshot + s.playCount = playCount + + for _, u := range userMap { + s.users = append(s.users, u) + } + sort.Slice(s.users, func(i, j int) bool { return s.users[i].UserID < s.users[j].UserID }) + + for _, tr := range trackMap { + s.tracks = append(s.tracks, tr) + } + sort.Slice(s.tracks, func(i, j int) bool { return s.tracks[i].TrackID < s.tracks[j].TrackID }) + + for _, p := range playlistMap { + s.playlists = append(s.playlists, p) + } + sort.Slice(s.playlists, func(i, j int) bool { return s.playlists[i].PlaylistID < s.playlists[j].PlaylistID }) + + for k := range followSet { + s.follows = append(s.follows, followPair{k.Follower, k.Followee}) + } + sort.Slice(s.follows, func(i, j int) bool { + if s.follows[i].Follower != s.follows[j].Follower { + return s.follows[i].Follower < s.follows[j].Follower + } + return s.follows[i].Followee < s.follows[j].Followee + }) + + for k := range saveSet { + s.saves = append(s.saves, saveTuple{k.UserID, k.ItemID, k.SaveType}) + } + sort.Slice(s.saves, func(i, j int) bool { + if s.saves[i].UserID != s.saves[j].UserID { + return s.saves[i].UserID < s.saves[j].UserID + } + return s.saves[i].ItemID < s.saves[j].ItemID + }) + + for k := range repostSet { + s.reposts = append(s.reposts, repostTuple{k.UserID, k.ItemID, k.RepostType}) + } + sort.Slice(s.reposts, func(i, j int) bool { + if s.reposts[i].UserID != s.reposts[j].UserID { + return s.reposts[i].UserID < s.reposts[j].UserID + } + return s.reposts[i].ItemID < s.reposts[j].ItemID + }) + + return s +} diff --git a/cmd/genesis-writer/main.go b/cmd/genesis-writer/main.go new file mode 100644 index 00000000..276e6df0 --- /dev/null +++ b/cmd/genesis-writer/main.go @@ -0,0 +1,320 @@ +// cmd/genesis-writer: writes historical Audius state directly into Core chain +// PostgreSQL tables without going through consensus. On completion it also +// primes the CometBFT state.db and blockstore.db so the genesis node can start +// from the written height and immediately propose the next live block. +// +// Usage: +// +// genesis-writer write \ +// --src-dsn \ +// --data-dir /data \ +// --genesis-file \ +// --priv-validator-key-file +package main + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "fmt" + "os" + "os/signal" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/urfave/cli/v2" + "go.uber.org/zap" +) + +func main() { + app := &cli.App{ + Name: "genesis-writer", + Usage: "Write historical Audius state directly into Core chain PostgreSQL tables", + Commands: []*cli.Command{ + writeCmd(), + }, + } + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} + +// cmtHome returns the CometBFT home directory derived from data-dir and chain-id. +// Matches production layout: /core/ +func cmtHome(dataDir, chainID string) string { + return filepath.Join(dataDir, "core", chainID) +} + +func writeCmd() *cli.Command { + return &cli.Command{ + Name: "write", + Usage: "Read entities from source DP DB and write them as synthetic blocks into the Core DB", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "src-dsn", + Usage: "Source Audius Discovery Provider PostgreSQL DSN", + EnvVars: []string{"GENESIS_SRC_DSN"}, + Required: true, + }, + &cli.StringFlag{ + Name: "dst-dsn", + Usage: "Target Core chain PostgreSQL DSN. If omitted, a local postgres is started at /postgres/.", + EnvVars: []string{"GENESIS_DST_DSN"}, + }, + &cli.StringFlag{ + Name: "private-key", + Usage: "Genesis migration Ethereum private key (hex, with or without 0x). If omitted, a key is generated and saved for resume.", + EnvVars: []string{"GENESIS_MIGRATION_PRIVATE_KEY"}, + }, + &cli.StringFlag{ + Name: "data-dir", + Usage: "Root data directory. CometBFT state goes to /core//, postgres to /postgres/. Mirrors production node layout.", + EnvVars: []string{"GENESIS_DATA_DIR"}, + }, + &cli.StringFlag{ + Name: "network", + Usage: "Network environment: prod, stage, dev", + EnvVars: []string{"NETWORK"}, + Value: "prod", + }, + &cli.StringFlag{ + Name: "chain-id", + Usage: "Core chain ID (e.g. audius-mainnet-beta)", + EnvVars: []string{"CHAIN_ID"}, + Value: "audius-mainnet-beta", + }, + &cli.StringFlag{ + Name: "genesis-time", + Usage: "Chain genesis time in RFC3339 (default: now)", + EnvVars: []string{"GENESIS_TIME"}, + }, + &cli.StringFlag{ + Name: "genesis-file", + Usage: "Path to CometBFT genesis.json (defaults to /core//config/genesis.json)", + EnvVars: []string{"GENESIS_FILE"}, + }, + &cli.StringFlag{ + Name: "priv-validator-key-file", + Usage: "Path to CometBFT priv_validator_key.json (defaults to /core//config/priv_validator_key.json)", + EnvVars: []string{"PRIV_VALIDATOR_KEY_FILE"}, + }, + &cli.IntFlag{ + Name: "max-txs-per-block", + Usage: "Maximum number of transactions per synthetic block", + EnvVars: []string{"GENESIS_MAX_TXS_PER_BLOCK"}, + Value: 10000, + }, + &cli.IntFlag{ + Name: "batch-size", + Usage: "Rows fetched from source DB per batch", + EnvVars: []string{"GENESIS_BATCH_SIZE"}, + Value: 1000, + }, + &cli.BoolFlag{ + Name: "run-migrations", + Usage: "Apply the Core chain schema to dst-dsn before writing (for fresh databases)", + }, + &cli.BoolFlag{ + Name: "resume", + Usage: "Resume from the last completed step of a previous run", + }, + &cli.BoolFlag{Name: "skip-users", EnvVars: []string{"GENESIS_SKIP_USERS"}, Usage: "Skip users"}, + &cli.BoolFlag{Name: "skip-wallets", EnvVars: []string{"GENESIS_SKIP_WALLETS"}, Usage: "Skip associated wallets and dashboard wallet users"}, + &cli.BoolFlag{Name: "skip-tracks", EnvVars: []string{"GENESIS_SKIP_TRACKS"}}, + &cli.BoolFlag{Name: "skip-playlists", EnvVars: []string{"GENESIS_SKIP_PLAYLISTS"}}, + &cli.BoolFlag{Name: "skip-social", EnvVars: []string{"GENESIS_SKIP_SOCIAL"}, Usage: "Skip follows, saves, reposts, subscriptions, muted users"}, + &cli.BoolFlag{Name: "skip-plays", EnvVars: []string{"GENESIS_SKIP_PLAYS"}}, + &cli.BoolFlag{Name: "skip-apps", EnvVars: []string{"GENESIS_SKIP_APPS"}, Usage: "Skip developer apps and grants"}, + &cli.BoolFlag{Name: "skip-comments", EnvVars: []string{"GENESIS_SKIP_COMMENTS"}, Usage: "Skip comments and comment reactions"}, + &cli.BoolFlag{Name: "skip-emails", EnvVars: []string{"GENESIS_SKIP_EMAILS"}, Usage: "Skip encrypted emails and email access grants"}, + &cli.BoolFlag{Name: "skip-tip-reactions", EnvVars: []string{"GENESIS_SKIP_TIP_REACTIONS"}}, + }, + Action: func(c *cli.Context) error { + logger, _ := zap.NewProduction() + defer logger.Sync() //nolint:errcheck + + dataDir := c.String("data-dir") + chainID := c.String("chain-id") + + // Derive CMTHome from data-dir. + var cHome string + if dataDir != "" { + cHome = cmtHome(dataDir, chainID) + } + + privKey, keyFile, err := resolvePrivKey(c.String("private-key"), cHome, logger) + if err != nil { + return err + } + + genesisTime := time.Now().UTC() + if gt := c.String("genesis-time"); gt != "" { + genesisTime, err = time.Parse(time.RFC3339, gt) + if err != nil { + return fmt.Errorf("parse genesis-time: %w", err) + } + } + + // Resolve config file paths — explicit flags take precedence, + // otherwise default to conventional paths under CMTHome. + genesisFile := c.String("genesis-file") + if genesisFile == "" && cHome != "" { + genesisFile = filepath.Join(cHome, "config", "genesis.json") + } + privValKeyFile := c.String("priv-validator-key-file") + if privValKeyFile == "" && cHome != "" { + privValKeyFile = filepath.Join(cHome, "config", "priv_validator_key.json") + } + + // Auto-generate genesis.json and priv_validator_key.json if they + // don't exist. Uses prod.json consensus params with a single + // bootstrap validator. + if genesisFile != "" && privValKeyFile != "" { + if err := ensureGenesisFiles(genesisFile, privValKeyFile, chainID, genesisTime, logger); err != nil { + return fmt.Errorf("ensure genesis files: %w", err) + } + } + + // Resolve destination DSN — start a managed postgres if needed. + dstDSN := c.String("dst-dsn") + runMigrations := c.Bool("run-migrations") + var pg *managedPostgres + if dstDSN == "" { + if dataDir == "" { + return fmt.Errorf("either --dst-dsn or --data-dir must be set") + } + pg, dstDSN, err = startManagedPostgres(dataDir, logger) + if err != nil { + return fmt.Errorf("managed postgres: %w", err) + } + defer pg.Stop() + // Always run migrations for managed postgres — they're idempotent + // and fast, so safe on both fresh runs and resume. + runMigrations = true + } + + cfg := &WriterConfig{ + SrcDSN: c.String("src-dsn"), + DstDSN: dstDSN, + PrivKey: privKey, + Network: c.String("network"), + ChainID: chainID, + GenesisTime: genesisTime, + GenesisFile: genesisFile, + PrivValidatorKeyFile: privValKeyFile, + CMTHome: cHome, + MaxTxsPerBlock: c.Int("max-txs-per-block"), + BatchSize: c.Int("batch-size"), + RunMigrations: runMigrations, + Resume: c.Bool("resume"), + SkipUsers: c.Bool("skip-users"), + SkipWallets: c.Bool("skip-wallets"), + SkipTracks: c.Bool("skip-tracks"), + SkipPlaylists: c.Bool("skip-playlists"), + SkipSocial: c.Bool("skip-social"), + SkipPlays: c.Bool("skip-plays"), + SkipApps: c.Bool("skip-apps"), + SkipComments: c.Bool("skip-comments"), + SkipEmails: c.Bool("skip-emails"), + SkipTipReactions: c.Bool("skip-tip-reactions"), + } + + w, err := NewWriter(cfg, logger) + if err != nil { + return fmt.Errorf("init writer: %w", err) + } + defer w.Close() + + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + if err := w.Run(ctx); err != nil { + return err + } + + // Clean up the generated key file on successful completion. + if keyFile != "" { + if err := os.Remove(keyFile); err != nil { + logger.Warn("could not remove migration key file", zap.String("path", keyFile), zap.Error(err)) + } else { + logger.Info("removed migration key file", zap.String("path", keyFile)) + } + } + + return nil + }, + } +} + +// migrationKeyFileName is the name of the ephemeral key file saved to CMTHome +// when a key is auto-generated. Deleted on successful completion. +const migrationKeyFileName = "genesis_migration_key.hex" + +// resolvePrivKey returns the migration private key and, if a key file was +// created or loaded, the path to that file (so it can be cleaned up later). +// +// Priority: +// 1. Explicit --private-key flag / env var +// 2. Existing key file at CMTHome/genesis_migration_key.hex (for resume) +// 3. Generate a new key and save it to that file +func resolvePrivKey(flagValue, cmtHome string, logger *zap.Logger) (*ecdsa.PrivateKey, string, error) { + // Explicit key provided — use it directly, no key file. + if flagValue != "" { + s := strings.TrimPrefix(flagValue, "0x") + b, err := hex.DecodeString(s) + if err != nil { + return nil, "", fmt.Errorf("parse private key: %w", err) + } + key, err := crypto.ToECDSA(b) + if err != nil { + return nil, "", fmt.Errorf("parse private key: %w", err) + } + return key, "", nil + } + + // Determine key file path. + keyDir := cmtHome + if keyDir == "" { + keyDir = "." + } + keyFile := filepath.Join(keyDir, migrationKeyFileName) + + // Try loading an existing key file (resume case). + if data, err := os.ReadFile(keyFile); err == nil { + s := strings.TrimSpace(strings.TrimPrefix(string(data), "0x")) + b, err := hex.DecodeString(s) + if err != nil { + return nil, "", fmt.Errorf("parse key file %s: %w", keyFile, err) + } + key, err := crypto.ToECDSA(b) + if err != nil { + return nil, "", fmt.Errorf("parse key file %s: %w", keyFile, err) + } + addr := crypto.PubkeyToAddress(key.PublicKey) + logger.Info("loaded migration key from file", zap.String("path", keyFile), zap.String("address", addr.Hex())) + return key, keyFile, nil + } + + // Generate a new key. + key, err := crypto.GenerateKey() + if err != nil { + return nil, "", fmt.Errorf("generate migration key: %w", err) + } + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Save to file for resume. + if err := os.MkdirAll(keyDir, 0o755); err != nil { + return nil, "", fmt.Errorf("mkdir for key file: %w", err) + } + keyHex := hex.EncodeToString(crypto.FromECDSA(key)) + if err := os.WriteFile(keyFile, []byte(keyHex+"\n"), 0o600); err != nil { + return nil, "", fmt.Errorf("write key file: %w", err) + } + + logger.Info("generated migration key", zap.String("path", keyFile), zap.String("address", addr.Hex())) + return key, keyFile, nil +} diff --git a/cmd/genesis-writer/postgres.go b/cmd/genesis-writer/postgres.go new file mode 100644 index 00000000..400ca11e --- /dev/null +++ b/cmd/genesis-writer/postgres.go @@ -0,0 +1,253 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" + "time" + + "go.uber.org/zap" +) + +const ( + pgDatabase = "openaudio" + pgPort = "5440" +) + +// managedPostgres manages a local PostgreSQL instance for the genesis writer. +// It initializes, starts, and stops a postgres cluster whose data directory +// mirrors the production layout: /postgres alongside +// /core//. +type managedPostgres struct { + dataDir string + port string + binDir string + logger *zap.Logger + started bool +} + +// findPgBinDir searches common locations for the pg_ctl binary directory. +func findPgBinDir() (string, error) { + candidates := []string{ + // macOS Homebrew + "/opt/homebrew/opt/postgresql@17/bin", + "/opt/homebrew/opt/postgresql@16/bin", + "/opt/homebrew/opt/postgresql@15/bin", + "/usr/local/opt/postgresql@17/bin", + "/usr/local/opt/postgresql@16/bin", + "/usr/local/opt/postgresql@15/bin", + // Linux + "/usr/lib/postgresql/17/bin", + "/usr/lib/postgresql/16/bin", + "/usr/lib/postgresql/15/bin", + "/usr/bin", + } + for _, dir := range candidates { + if _, err := os.Stat(filepath.Join(dir, "pg_ctl")); err == nil { + return dir, nil + } + } + // Fall back to PATH + if path, err := exec.LookPath("pg_ctl"); err == nil { + return filepath.Dir(path), nil + } + return "", fmt.Errorf("could not find pg_ctl; install PostgreSQL or set --dst-dsn") +} + +// startManagedPostgres ensures a local PostgreSQL instance is running with its +// data directory at /postgres/. It handles all combinations: +// +// - Fresh run: initdb → start → create database +// - Resume (stopped): start existing cluster → verify database exists +// - Resume (already running): verify database exists +func startManagedPostgres(dataDir string, logger *zap.Logger) (*managedPostgres, string, error) { + binDir, err := findPgBinDir() + if err != nil { + return nil, "", err + } + + pgDataDir := filepath.Join(dataDir, "postgres") + pg := &managedPostgres{ + dataDir: pgDataDir, + port: pgPort, + binDir: binDir, + logger: logger, + } + + if pg.isRunning() { + logger.Info("postgres already running", zap.String("dataDir", pgDataDir)) + } else if pg.isInitialized() { + logger.Info("postgres cluster exists, starting", zap.String("dataDir", pgDataDir)) + if err := pg.start(); err != nil { + return nil, "", fmt.Errorf("start postgres: %w", err) + } + } else { + logger.Info("no postgres cluster found, initializing", zap.String("dataDir", pgDataDir)) + if err := pg.initDB(); err != nil { + return nil, "", fmt.Errorf("initdb: %w", err) + } + if err := pg.start(); err != nil { + return nil, "", fmt.Errorf("start postgres: %w", err) + } + } + + if err := pg.ensureDatabase(); err != nil { + return nil, "", fmt.Errorf("ensure database: %w", err) + } + + username := os.Getenv("USER") + if username == "" { + if u, err := user.Current(); err == nil { + username = u.Username + } else { + username = "postgres" + } + } + dsn := fmt.Sprintf("postgres://%s@localhost:%s/%s?sslmode=disable", username, pg.port, pgDatabase) + logger.Info("managed postgres ready", + zap.String("dataDir", pgDataDir), + zap.String("dsn", dsn), + ) + + return pg, dsn, nil +} + +func (pg *managedPostgres) pgCtl(args ...string) *exec.Cmd { + cmd := exec.Command(filepath.Join(pg.binDir, "pg_ctl"), args...) + cmd.Env = append(os.Environ(), "PGDATA="+pg.dataDir) + return cmd +} + +func (pg *managedPostgres) isInitialized() bool { + _, err := os.Stat(filepath.Join(pg.dataDir, "PG_VERSION")) + return err == nil +} + +func (pg *managedPostgres) isRunning() bool { + cmd := pg.pgCtl("status", "-D", pg.dataDir) + return cmd.Run() == nil +} + +func (pg *managedPostgres) initDB() error { + pg.logger.Info("initializing postgres", zap.String("dataDir", pg.dataDir)) + if err := os.MkdirAll(pg.dataDir, 0o700); err != nil { + return err + } + initdb := exec.Command(filepath.Join(pg.binDir, "initdb"), "-D", pg.dataDir, "--encoding=UTF8") + initdb.Stdout = os.Stdout + initdb.Stderr = os.Stderr + if err := initdb.Run(); err != nil { + return fmt.Errorf("initdb failed: %w", err) + } + + // Configure for trust auth (local connections only) and performance. + confPath := filepath.Join(pg.dataDir, "postgresql.conf") + conf, err := os.ReadFile(confPath) + if err != nil { + return fmt.Errorf("read postgresql.conf: %w", err) + } + extras := fmt.Sprintf(` +# genesis-writer overrides +port = %s +shared_buffers = '256MB' +maintenance_work_mem = '1GB' +wal_level = minimal +max_wal_senders = 0 +fsync = off +synchronous_commit = off +full_page_writes = off +checkpoint_completion_target = 0.9 +max_wal_size = '4GB' +`, pg.port) + if err := os.WriteFile(confPath, append(conf, []byte(extras)...), 0o600); err != nil { + return fmt.Errorf("write postgresql.conf: %w", err) + } + + // Trust all local connections. + hbaPath := filepath.Join(pg.dataDir, "pg_hba.conf") + hba, err := os.ReadFile(hbaPath) + if err != nil { + return fmt.Errorf("read pg_hba.conf: %w", err) + } + hbaStr := strings.ReplaceAll(string(hba), "scram-sha-256", "trust") + hbaStr = strings.ReplaceAll(hbaStr, "md5", "trust") + hbaStr = strings.ReplaceAll(hbaStr, "peer", "trust") + if err := os.WriteFile(hbaPath, []byte(hbaStr), 0o600); err != nil { + return fmt.Errorf("write pg_hba.conf: %w", err) + } + + // Pin listen_addresses to localhost since we're using trust auth. + confPath := filepath.Join(pg.dataDir, "postgresql.conf") + conf, err := os.ReadFile(confPath) + if err != nil { + return fmt.Errorf("read postgresql.conf: %w", err) + } + confStr := string(conf) + if !strings.Contains(confStr, "listen_addresses = 'localhost'") { + confStr += "\nlisten_addresses = 'localhost'\n" + if err := os.WriteFile(confPath, []byte(confStr), 0o600); err != nil { + return fmt.Errorf("write postgresql.conf: %w", err) + } + } + + return nil +} + +func (pg *managedPostgres) start() error { + pg.logger.Info("starting postgres", zap.String("dataDir", pg.dataDir), zap.String("port", pg.port)) + logFile := filepath.Join(pg.dataDir, "logfile") + cmd := pg.pgCtl("start", "-D", pg.dataDir, "-l", logFile, "-o", fmt.Sprintf("-p %s", pg.port)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("pg_ctl start: %w", err) + } + pg.started = true + + // Wait for ready. + pgIsReady := filepath.Join(pg.binDir, "pg_isready") + for i := 0; i < 30; i++ { + cmd := exec.Command(pgIsReady, "-p", pg.port, "-q") + if cmd.Run() == nil { + return nil + } + time.Sleep(time.Second) + } + return fmt.Errorf("postgres did not become ready within 30s") +} + +// ensureDatabase creates the openaudio database if it doesn't exist. +func (pg *managedPostgres) ensureDatabase() error { + psql := filepath.Join(pg.binDir, "psql") + out, err := exec.Command(psql, "-p", pg.port, "-d", "postgres", "-tAc", + fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", pgDatabase)).Output() + if err != nil { + return fmt.Errorf("check database: %w", err) + } + if strings.TrimSpace(string(out)) == "1" { + return nil + } + pg.logger.Info("creating database", zap.String("name", pgDatabase)) + cmd := exec.Command(psql, "-p", pg.port, "-d", "postgres", "-c", + fmt.Sprintf("CREATE DATABASE %s", pgDatabase)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +// Stop shuts down the managed postgres instance if we started it. +func (pg *managedPostgres) Stop() { + if !pg.started { + return + } + pg.logger.Info("stopping managed postgres") + cmd := pg.pgCtl("stop", "-D", pg.dataDir, "-m", "fast") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + pg.logger.Warn("failed to stop postgres", zap.Error(err)) + } +} diff --git a/cmd/genesis-writer/testdata/dp_seed.sql b/cmd/genesis-writer/testdata/dp_seed.sql new file mode 100644 index 00000000..530e97bb --- /dev/null +++ b/cmd/genesis-writer/testdata/dp_seed.sql @@ -0,0 +1,7 @@ +-- Seed data required for the discovery provider indexer to start. +-- The indexer expects a current block record to exist before it can process core events. +INSERT INTO public.blocks (blockhash, parenthash, number, is_current) +VALUES ('0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + 0, + true); diff --git a/cmd/genesis-writer/testdata/seed.sql b/cmd/genesis-writer/testdata/seed.sql new file mode 100644 index 00000000..f9c05ec4 --- /dev/null +++ b/cmd/genesis-writer/testdata/seed.sql @@ -0,0 +1,547 @@ +-- Comprehensive seed data for genesis-writer integration tests. +-- Covers all entity types (users, tracks, playlists, social, plays) with +-- the full range of nullable column values so that genesis-writer's entity +-- serialization is exercised for every code path. +-- +-- ID ranges mirror production offsets: +-- users: >= 3,000,000 +-- tracks: >= 2,000,000 +-- playlists: >= 400,000 + +-- ============================================================ +-- BLOCK (anchor for all FK references) +-- ============================================================ + +INSERT INTO blocks (blockhash, number, parenthash, is_current) VALUES + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + '0x0000000000000000000000000000000000000000000000000000000000000000', true); + +-- ============================================================ +-- USERS +-- 3000001 – full artist profile: all optional text fields set, verified +-- 3000002 – artist with social handles but no bio/location +-- 3000003 – minimal artist: only wallet + handle, all nullable fields NULL +-- 3000004 – fan account: no artist data, website only +-- 3000005 – fan account: pure fan, all optional fields NULL +-- 3000006 – artist with cover_photo_sizes but no profile_picture fields +-- ============================================================ + +INSERT INTO users ( + blockhash, blocknumber, user_id, is_current, txhash, + handle, handle_lc, wallet, name, bio, location, + profile_picture, profile_picture_sizes, + cover_photo, cover_photo_sizes, + twitter_handle, instagram_handle, website, + is_verified, is_deactivated, is_available, is_storage_v2, + allow_ai_attribution, created_at, updated_at +) VALUES + -- 1: full artist – every optional column filled + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000001, true, '0xaaa0000000000000000000000000000000000000000000000000000000000001', + 'full_artist', 'full_artist', '0xAAAA000000000000000000000000000000000001', + 'Full Artist Name', + 'Electronic producer based in LA with 10 years of experience crafting immersive soundscapes.', + 'Los Angeles, CA', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001pic', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001psz', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001csz', + 'full_artist_tw', 'full_artist_ig', 'https://fullartist.example.com', + true, false, true, true, false, + '2021-01-01 00:00:00', '2021-01-01 00:00:00' + ), + -- 2: artist with social handles, no bio or location + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000002, true, '0xaaa0000000000000000000000000000000000000000000000000000000000002', + 'social_artist', 'social_artist', '0xAAAA000000000000000000000000000000000002', + 'Social Artist', + NULL, NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0002pic', + NULL, + NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0002csz', + 'social_artist_tw', 'social_artist_ig', NULL, + false, false, true, true, false, + '2021-02-01 00:00:00', '2021-02-01 00:00:00' + ), + -- 3: minimal artist – every nullable field NULL + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000003, true, '0xaaa0000000000000000000000000000000000000000000000000000000000003', + 'minimal_artist', 'minimal_artist', '0xAAAA000000000000000000000000000000000003', + NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + false, false, true, false, false, + '2021-03-01 00:00:00', '2021-03-01 00:00:00' + ), + -- 4: fan with website only + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, true, '0xaaa0000000000000000000000000000000000000000000000000000000000004', + 'fan_with_web', 'fan_with_web', '0xAAAA000000000000000000000000000000000004', + 'Fan With Website', + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, 'https://fanwebsite.example.com', + false, false, true, false, false, + '2021-04-01 00:00:00', '2021-04-01 00:00:00' + ), + -- 5: pure fan – all optional text NULL + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, true, '0xaaa0000000000000000000000000000000000000000000000000000000000005', + 'pure_fan', 'pure_fan', '0xAAAA000000000000000000000000000000000005', + 'Pure Fan', + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + false, false, true, false, false, + '2021-05-01 00:00:00', '2021-05-01 00:00:00' + ), + -- 6: artist with cover_photo_sizes but no profile_picture + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000006, true, '0xaaa0000000000000000000000000000000000000000000000000000000000006', + 'cover_only_artist', 'cover_only_artist', '0xAAAA000000000000000000000000000000000006', + 'Cover Only Artist', + 'Has a cover photo but no profile picture.', + 'Nashville, TN', + NULL, NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0006cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0006csz', + NULL, NULL, NULL, + false, false, true, true, false, + '2021-06-01 00:00:00', '2021-06-01 00:00:00' + ); + +-- ============================================================ +-- TRACKS +-- 2000001 – full public track: bpm, musical_key, isrc, iswc, downloadable +-- 2000002 – unlisted track (is_unlisted=true) +-- 2000003 – stream gated (follow gate) +-- 2000004 – download gated +-- 2000005 – remix of 2000001 +-- 2000006 – stem of 2000001 +-- 2000007 – with release_date, preview_cid, is_original_available +-- 2000008 – jazz, all text fields filled +-- 2000009 – NULL title, genre, mood, tags (edge case for nullable strings) +-- ============================================================ + +INSERT INTO tracks ( + blockhash, blocknumber, track_id, is_current, is_delete, txhash, + owner_id, title, description, duration, genre, mood, tags, + track_cid, cover_art, cover_art_sizes, preview_cid, + track_segments, is_downloadable, is_original_available, + is_unlisted, is_scheduled_release, is_stream_gated, stream_conditions, + is_download_gated, download_conditions, + is_available, is_playlist_upload, is_owned_by_user, + remix_of, stem_of, + release_date, + license, isrc, iswc, bpm, musical_key, + comments_disabled, no_ai_use, + created_at, updated_at +) VALUES + -- 1: full public track with all metadata + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000001, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000001', + 3000001, + 'Sunrise Drive', + 'An energizing electronic track perfect for morning runs.', + 210, 'Electronic', 'Energizing', 'electronic,synth,morning', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0001csz', + NULL, + '[]', true, false, + false, false, false, NULL, + false, NULL, + true, false, true, + NULL, NULL, + NULL, + 'CC BY 4.0', 'US-AB1-21-00001', 'T-123.456.789-Z', + 128.5, 'C major', + false, false, + '2021-06-01 00:00:00', '2021-06-01 00:00:00' + ), + -- 2: unlisted track + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000002, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000002', + 3000001, + 'Hidden Gem (Unlisted)', + NULL, + 180, 'Electronic', 'Melancholic', NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0002cid', + NULL, NULL, NULL, + '[]', false, false, + true, false, false, NULL, + false, NULL, + true, false, true, + NULL, NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, + false, false, + '2021-06-15 00:00:00', '2021-06-15 00:00:00' + ), + -- 3: stream gated (follow gate condition) + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000003, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000003', + 3000002, + 'Exclusive Track', + 'Only for followers.', + 240, 'Pop', 'Happy', 'pop,exclusive', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0003cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0003cov', + NULL, NULL, + '[]', false, false, + false, false, true, + '{"follow_user_id": 3000002}', + false, NULL, + true, false, true, + NULL, NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, + false, false, + '2021-07-01 00:00:00', '2021-07-01 00:00:00' + ), + -- 4: download gated + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000004, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000004', + 3000002, + 'Premium Download', + 'Download requires NFT ownership.', + 300, 'Hip-Hop/Rap', 'Excited', 'hiphop,premium', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0004cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0004cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0004csz', + NULL, + '[]', true, false, + false, false, false, NULL, + true, + '{"nft_collection": {"chain": "eth", "address": "0xabcdef1234567890abcdef1234567890abcdef12", "standard": "ERC721", "name": "Test NFT"}}', + true, false, true, + NULL, NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, + false, false, + '2021-07-15 00:00:00', '2021-07-15 00:00:00' + ), + -- 5: remix of track 2000001 + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000005, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000005', + 3000006, + 'Sunrise Drive (Remix)', + 'A darker take on the original.', + 195, 'Electronic', 'Fiery', 'remix,electronic,dark', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0005cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0005cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0005csz', + NULL, + '[]', false, false, + false, false, false, NULL, + false, NULL, + true, false, true, + '{"tracks": [{"parent_track_id": 2000001}]}', + NULL, + NULL, + NULL, NULL, NULL, + 140.0, 'A minor', + false, false, + '2021-08-01 00:00:00', '2021-08-01 00:00:00' + ), + -- 6: stem of track 2000001 + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000006, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000006', + 3000001, + 'Sunrise Drive – Vocal Stem', + NULL, + 210, NULL, NULL, NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0006cid', + NULL, NULL, NULL, + '[]', true, false, + false, false, false, NULL, + false, NULL, + true, true, true, + NULL, + '{"parent_track_id": 2000001}', + NULL, + NULL, NULL, NULL, + NULL, NULL, + false, false, + '2021-08-15 00:00:00', '2021-08-15 00:00:00' + ), + -- 7: with release_date, preview_cid, is_original_available + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000007, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000007', + 3000003, + 'Scheduled Release', + 'Coming soon.', + 270, 'Rock', 'Energizing', 'rock,upcoming', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0007cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0007cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0007csz', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0007prv', + '[]', true, true, + false, false, false, NULL, + false, NULL, + true, false, true, + NULL, NULL, + '2022-01-01 00:00:00', + 'All Rights Reserved', + 'GB-AB1-22-00007', NULL, + 95.0, 'D major', + false, false, + '2021-09-01 00:00:00', '2021-09-01 00:00:00' + ), + -- 8: jazz track, fully filled, original available + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000008, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000008', + 3000001, + 'Blue Note Sessions', + 'Recorded live in one take.', + 320, 'Jazz', 'Peaceful', 'jazz,live,acoustic', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0008cid', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0008cov', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0008csz', + NULL, + '[]', true, true, + false, false, false, NULL, + false, NULL, + true, false, true, + NULL, NULL, + NULL, + 'CC BY-NC 4.0', NULL, NULL, + NULL, 'Bb major', + false, false, + '2021-10-01 00:00:00', '2021-10-01 00:00:00' + ), + -- 9: null title, genre, mood, tags – edge case + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 2000009, true, false, + '0xbbb0000000000000000000000000000000000000000000000000000000000009', + 3000003, + NULL, + NULL, + 120, NULL, NULL, NULL, + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0009cid', + NULL, NULL, NULL, + '[]', false, false, + false, false, false, NULL, + false, NULL, + true, false, true, + NULL, NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, + false, false, + '2021-11-01 00:00:00', '2021-11-01 00:00:00' + ); + +-- ============================================================ +-- PLAYLISTS +-- 400001 – public album (is_album=true, is_private=false) +-- 400002 – private user playlist +-- 400003 – stream gated playlist +-- 400004 – album with release_date and description +-- ============================================================ + +INSERT INTO playlists ( + blockhash, blocknumber, playlist_id, is_current, is_delete, txhash, + playlist_owner_id, playlist_name, is_album, is_private, + is_scheduled_release, is_stream_gated, stream_conditions, + is_image_autogenerated, + playlist_contents, description, + playlist_image_sizes_multihash, + release_date, + created_at, updated_at +) VALUES + -- 1: public album + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 400001, true, false, + '0xccc0000000000000000000000000000000000000000000000000000000000001', + 3000001, 'Sunrise EP', true, false, + false, false, NULL, + false, + '{"track_ids": [{"track": 2000001, "timestamp": 1622505600}, {"track": 2000002, "timestamp": 1623715200}]}', + 'My debut EP featuring two electronic tracks.', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0401img', + NULL, + '2021-09-01 00:00:00', '2021-09-01 00:00:00' + ), + -- 2: private user playlist (no description, no image) + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 400002, true, false, + '0xccc0000000000000000000000000000000000000000000000000000000000002', + 3000004, 'My Private Stash', false, true, + false, false, NULL, + false, + '{"track_ids": [{"track": 2000001, "timestamp": 1622505600}, {"track": 2000008, "timestamp": 1633046400}]}', + NULL, + NULL, + NULL, + '2021-10-01 00:00:00', '2021-10-01 00:00:00' + ), + -- 3: stream gated playlist + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 400003, true, false, + '0xccc0000000000000000000000000000000000000000000000000000000000003', + 3000002, 'Exclusive Playlist', false, false, + false, true, + '{"follow_user_id": 3000002}', + false, + '{"track_ids": [{"track": 2000003, "timestamp": 1625097600}, {"track": 2000004, "timestamp": 1626912000}]}', + 'Only for my followers.', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0403img', + NULL, + '2021-11-01 00:00:00', '2021-11-01 00:00:00' + ), + -- 4: album with release_date and description + ( + '0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 400004, true, false, + '0xccc0000000000000000000000000000000000000000000000000000000000004', + 3000006, 'Future Sounds', true, false, + false, false, NULL, + false, + '{"track_ids": [{"track": 2000005, "timestamp": 1628467200}, {"track": 2000007, "timestamp": 1630368000}]}', + 'A forward-looking album.', + 'baeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0404img', + '2022-03-01 00:00:00', + '2021-12-01 00:00:00', '2021-12-01 00:00:00' + ); + +-- ============================================================ +-- FOLLOWS +-- Various follower→followee pairs covering all 6 users +-- ============================================================ + +INSERT INTO follows ( + blockhash, blocknumber, follower_user_id, followee_user_id, + is_current, is_delete, txhash, created_at +) VALUES + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 3000001, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000001', '2021-10-02 00:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 3000002, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000002', '2021-10-02 01:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 3000006, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000003', '2021-10-02 02:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 3000001, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000004', '2021-10-03 00:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 3000002, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000005', '2021-10-03 01:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000006, 3000001, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000006', '2021-10-04 00:00:00'), + -- mutual follow + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000001, 3000006, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000007', '2021-10-04 01:00:00'), + -- fan follows fan + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 3000005, true, false, '0xddd0000000000000000000000000000000000000000000000000000000000008', '2021-10-05 00:00:00'); + +-- ============================================================ +-- REPOSTS +-- track reposts and playlist reposts +-- ============================================================ + +INSERT INTO reposts ( + blockhash, blocknumber, user_id, repost_item_id, repost_type, + is_current, is_delete, is_repost_of_repost, txhash, created_at +) VALUES + -- track reposts + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 2000001, 'track', true, false, false, + '0xeee0000000000000000000000000000000000000000000000000000000000001', '2021-10-06 00:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 2000001, 'track', true, false, false, + '0xeee0000000000000000000000000000000000000000000000000000000000002', '2021-10-06 01:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000006, 2000008, 'track', true, false, false, + '0xeee0000000000000000000000000000000000000000000000000000000000003', '2021-10-06 02:00:00'), + -- playlist repost + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 400002, 'playlist', true, false, false, + '0xeee0000000000000000000000000000000000000000000000000000000000004', '2021-10-07 00:00:00'), + -- album repost (stored as 'playlist' — 'album' was a historical type cleaned up by migration 0021) + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 400001, 'playlist', true, false, false, + '0xeee0000000000000000000000000000000000000000000000000000000000005', '2021-10-07 01:00:00'); + +-- ============================================================ +-- SAVES +-- track saves and playlist saves +-- ============================================================ + +INSERT INTO saves ( + blockhash, blocknumber, user_id, save_item_id, save_type, + is_current, is_delete, is_save_of_repost, txhash, created_at +) VALUES + -- track saves + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000004, 2000001, 'track', true, false, false, + '0xfff0000000000000000000000000000000000000000000000000000000000001', '2021-10-08 00:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 2000008, 'track', true, false, false, + '0xfff0000000000000000000000000000000000000000000000000000000000002', '2021-10-08 01:00:00'), + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000006, 2000001, 'track', true, false, false, + '0xfff0000000000000000000000000000000000000000000000000000000000003', '2021-10-08 02:00:00'), + -- playlist save + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000005, 400001, 'playlist', true, false, false, + '0xfff0000000000000000000000000000000000000000000000000000000000004', '2021-10-09 00:00:00'), + -- playlist save + ('0x0000000000000000000000000000000000000000000000000000000000000001', 1, + 3000006, 400002, 'playlist', true, false, false, + '0xfff0000000000000000000000000000000000000000000000000000000000005', '2021-10-09 01:00:00'); + +-- ============================================================ +-- PLAYS +-- Mix of identified (user_id set) and anonymous (user_id=NULL) +-- Mix of full geo, partial geo, no geo +-- ============================================================ + +INSERT INTO plays (user_id, play_item_id, source, created_at, updated_at, signature, city, region, country) VALUES + -- with user_id, full geo + (3000004, 2000001, 'feed', '2021-10-10 10:00:00', '2021-10-10 10:00:00', 'sig-0001', 'Los Angeles', 'CA', 'US'), + -- with user_id, no geo + (3000005, 2000001, 'search', '2021-10-10 11:00:00', '2021-10-10 11:00:00', 'sig-0002', NULL, NULL, NULL), + -- with user_id, partial geo (region + country only) + (3000004, 2000008, 'profile', '2021-10-10 12:00:00', '2021-10-10 12:00:00', 'sig-0003', NULL, 'NY', 'US'), + -- anonymous, city + country + (NULL, 2000003, 'embed', '2021-10-11 09:00:00', '2021-10-11 09:00:00', 'sig-0004', 'London', NULL, 'GB'), + -- anonymous, no geo + (NULL, 2000005, 'widget', '2021-10-11 10:00:00', '2021-10-11 10:00:00', 'sig-0005', NULL, NULL, NULL), + -- with user_id, country only + (3000006, 2000008, 'trending','2021-10-12 14:00:00', '2021-10-12 14:00:00', 'sig-0006', NULL, NULL, 'DE'), + -- same track played by multiple users (tests play aggregation) + (3000003, 2000001, 'profile', '2021-10-13 08:00:00', '2021-10-13 08:00:00', 'sig-0007', NULL, NULL, NULL), + (3000001, 2000001, 'trending','2021-10-13 09:00:00', '2021-10-13 09:00:00', 'sig-0008', 'New York', 'NY', 'US'); diff --git a/cmd/genesis-writer/testdata/source_init.sh b/cmd/genesis-writer/testdata/source_init.sh new file mode 100755 index 00000000..e7cd84df --- /dev/null +++ b/cmd/genesis-writer/testdata/source_init.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Creates the genesis_writer_source database and seeds it with test data. +# Runs as part of postgres docker-entrypoint-initdb.d (after 01_schema.sql and 02_seed.sql). +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE DATABASE genesis_writer_source; +EOSQL + +psql -v ON_ERROR_STOP=1 \ + --username "$POSTGRES_USER" \ + --dbname genesis_writer_source \ + -f /docker-entrypoint-initdb.d/01_schema.sql + +psql -v ON_ERROR_STOP=1 \ + --username "$POSTGRES_USER" \ + --dbname genesis_writer_source \ + -f /tmp/source_seed.sql diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go new file mode 100644 index 00000000..e77c935b --- /dev/null +++ b/cmd/genesis-writer/writer.go @@ -0,0 +1,884 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "crypto/sha256" + "encoding/hex" + "fmt" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "time" + + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + corecfg "github.com/OpenAudio/go-openaudio/pkg/core/config" + coredb "github.com/OpenAudio/go-openaudio/pkg/core/db" + "github.com/OpenAudio/go-openaudio/pkg/core/server" + cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1" + cmtapiversion "github.com/cometbft/cometbft/api/cometbft/version/v1" + dbm "github.com/cometbft/cometbft-db" + cmtcrypto "github.com/cometbft/cometbft/crypto" + cmtstore "github.com/cometbft/cometbft/store" + cmttypes "github.com/cometbft/cometbft/types" + cmtversion "github.com/cometbft/cometbft/version" + "github.com/ethereum/go-ethereum/crypto" + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + _ "github.com/lib/pq" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +// WriterConfig holds all configuration for the genesis writer. +type WriterConfig struct { + SrcDSN string + DstDSN string + PrivKey *ecdsa.PrivateKey + Network string + ChainID string + GenesisTime time.Time + GenesisFile string + PrivValidatorKeyFile string + CMTHome string + MaxTxsPerBlock int + BatchSize int + SkipUsers bool + SkipWallets bool + SkipTracks bool + SkipPlaylists bool + SkipSocial bool + SkipPlays bool + SkipApps bool + SkipComments bool + SkipEmails bool + SkipTipReactions bool + // RunMigrations applies the Core chain schema to DstDSN before writing. + // Useful when starting from a fresh database (e.g., in integration tests). + RunMigrations bool + // Resume picks up from the last completed step if a previous run was interrupted. + Resume bool +} + +// Writer reads Audius DP entities, signs them, and writes real CometBFT blocks +// directly to the Core chain PostgreSQL tables and blockstore.db. +type Writer struct { + cfg *WriterConfig + srcDB *pgxpool.Pool + dstDB *pgxpool.Pool + sigCfg *corecfg.Config + privKey *ecdsa.PrivateKey + signerAddr string + nonce atomic.Uint64 + logger *zap.Logger + + // current block being assembled + height int64 + blockTxs [][]byte + blockTime time.Time + + // block signing state — constant across all blocks + cmtPrivKey cmtcrypto.PrivKey // validator ed25519 key + proposerAddr cmttypes.Address + validatorsHash []byte + nextValHash []byte + consensusHash []byte + + // per-block chain state — updated after each flush + prevBlockID cmttypes.BlockID // BlockID of the last written block + lastCommit *cmttypes.Commit // commit over the last written block + prevAppHash []byte // appHash of last block (→ current block's Header.AppHash) + + // blockstore — open throughout the write phase when CMTHome is set + blockStore *cmtstore.BlockStore + bsDB dbm.DB + + // running totals + totalTxs int64 + totalBlocks int64 + + // final block state — set after the last flushBlock + finalHeight int64 + finalAppHash []byte + finalTime time.Time + + // async block writer pipeline + blockWriteCh chan pendingBlock + blockWriteErr atomic.Pointer[error] + blockWriteDone chan struct{} + + // proto marshal buffer pool (reduces GC pressure) + marshalPool sync.Pool + + // blockMu serializes addTx/flushBlock access so emit can be called concurrently. + blockMu sync.Mutex +} + +// pendingBlock holds a fully-built block ready for async DB+blockstore write. +type pendingBlock struct { + height int64 + blockTime time.Time + appHash []byte + hashHex string + txData []txRow // pre-computed tx hashes and bytes + block *cmttypes.Block + blockParts *cmttypes.PartSet + seenCommit *cmttypes.Commit +} + +// txRow holds pre-computed data for a single transaction insert. +type txRow struct { + hash string + txBytes []byte +} + +// NewWriter connects to both databases and returns a ready Writer. +func NewWriter(cfg *WriterConfig, logger *zap.Logger) (*Writer, error) { + if cfg.RunMigrations { + if err := coredb.RunMigrations(logger, cfg.DstDSN, false); err != nil { + return nil, fmt.Errorf("run migrations: %w", err) + } + } + + srcDB, err := pgxpool.New(context.Background(), cfg.SrcDSN) + if err != nil { + return nil, fmt.Errorf("connect src db: %w", err) + } + if err := srcDB.Ping(context.Background()); err != nil { + return nil, fmt.Errorf("ping src db: %w", err) + } + + dstDB, err := pgxpool.New(context.Background(), cfg.DstDSN) + if err != nil { + return nil, fmt.Errorf("connect dst db: %w", err) + } + if err := dstDB.Ping(context.Background()); err != nil { + return nil, fmt.Errorf("ping dst db: %w", err) + } + + sigCfg := signingConfig(cfg.Network) + addr := crypto.PubkeyToAddress(cfg.PrivKey.PublicKey) + + w := &Writer{ + cfg: cfg, + srcDB: srcDB, + dstDB: dstDB, + sigCfg: sigCfg, + privKey: cfg.PrivKey, + signerAddr: addr.Hex(), + logger: logger, + height: 1, + blockTime: cfg.GenesisTime, + marshalPool: sync.Pool{ + New: func() interface{} { + return proto.MarshalOptions{} + }, + }, + } + + if err := w.initBlockSigning(); err != nil { + srcDB.Close() + dstDB.Close() + return nil, fmt.Errorf("init block signing: %w", err) + } + + logger.Info("genesis writer initialized", + zap.String("genesis_keypair", addr.Hex()), + zap.String("network", cfg.Network), + zap.String("chain_id", cfg.ChainID), + zap.Int("max_txs_per_block", cfg.MaxTxsPerBlock), + ) + + return w, nil +} + +// Close releases database connections and the blockstore. +func (w *Writer) Close() { + w.srcDB.Close() + w.dstDB.Close() + if w.bsDB != nil { + w.bsDB.Close() + } +} + +// startBlockWriter launches the async block writer goroutine. +// Blocks are sent to blockWriteCh and written to postgres+blockstore in the background. +func (w *Writer) startBlockWriter(ctx context.Context) { + w.blockWriteCh = make(chan pendingBlock, 4) + w.blockWriteDone = make(chan struct{}) + go func() { + defer close(w.blockWriteDone) + for pb := range w.blockWriteCh { + if err := w.writeBlockToDB(ctx, pb); err != nil { + w.blockWriteErr.Store(&err) + // Drain remaining blocks to unblock senders. + for range w.blockWriteCh { + } + return + } + } + }() +} + +// stopBlockWriter closes the write channel and waits for the writer to finish. +// Returns any error from the background writer. +func (w *Writer) stopBlockWriter() error { + if w.blockWriteCh != nil { + close(w.blockWriteCh) + <-w.blockWriteDone + w.blockWriteCh = nil + } + if ep := w.blockWriteErr.Load(); ep != nil { + return *ep + } + return nil +} + +// writeBlockToDB writes a single block to postgres (using COPY for transactions) +// and blockstore.db. Postgres is committed first so that on failure the blockstore +// doesn't contain blocks that aren't in the SQL tables. +func (w *Writer) writeBlockToDB(ctx context.Context, pb pendingBlock) error { + pgTx, err := w.dstDB.Begin(ctx) + if err != nil { + return fmt.Errorf("begin tx height=%d: %w", pb.height, err) + } + defer pgTx.Rollback(ctx) //nolint:errcheck + + // Insert block header. + _, err = pgTx.Exec(ctx, + `INSERT INTO core_blocks (height, chain_id, hash, proposer, created_at) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (height, chain_id) DO NOTHING`, + pb.height, w.cfg.ChainID, pb.hashHex, w.proposerAddr.String(), pb.blockTime, + ) + if err != nil { + return fmt.Errorf("insert core_blocks height=%d: %w", pb.height, err) + } + + // COPY transactions in bulk — much faster than individual INSERTs. + _, err = pgTx.CopyFrom(ctx, + pgx.Identifier{"core_transactions"}, + []string{"block_id", "index", "tx_hash", "transaction", "created_at"}, + &txCopySource{height: pb.height, blockTime: pb.blockTime, rows: pb.txData}, + ) + if err != nil { + return fmt.Errorf("copy core_transactions height=%d: %w", pb.height, err) + } + + // Insert app state. + _, err = pgTx.Exec(ctx, + `INSERT INTO core_app_state (block_height, app_hash) + VALUES ($1, $2) + ON CONFLICT (block_height, app_hash) DO NOTHING`, + pb.height, pb.appHash, + ) + if err != nil { + return fmt.Errorf("insert core_app_state height=%d: %w", pb.height, err) + } + + if err := pgTx.Commit(ctx); err != nil { + return fmt.Errorf("commit tx height=%d: %w", pb.height, err) + } + + // Write to blockstore after postgres commit so they stay in sync. + if w.blockStore != nil { + w.blockStore.SaveBlock(pb.block, pb.blockParts, pb.seenCommit) + } + + return nil +} + +// txCopySource implements pgx.CopyFromSource for bulk-inserting transactions. +type txCopySource struct { + height int64 + blockTime time.Time + rows []txRow + idx int +} + +func (s *txCopySource) Next() bool { + return s.idx < len(s.rows) +} + +func (s *txCopySource) Values() ([]interface{}, error) { + row := s.rows[s.idx] + vals := []interface{}{s.height, s.idx, row.hash, row.txBytes, s.blockTime} + s.idx++ + return vals, nil +} + +func (s *txCopySource) Err() error { return nil } + +// Run processes all entity types in dependency order, writes them as real +// CometBFT blocks, then writes CometBFT state files. +func (w *Writer) Run(ctx context.Context) error { + start := time.Now() + w.logger.Info("starting genesis write") + + // Create progress table for resume support. + if _, err := w.dstDB.Exec(ctx, + `CREATE TABLE IF NOT EXISTS genesis_writer_progress ( + step_name TEXT PRIMARY KEY, + completed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + )`); err != nil { + return fmt.Errorf("create progress table: %w", err) + } + + // If resuming, load the last known chain state from the database. + if w.cfg.Resume { + var maxHeight int64 + err := w.dstDB.QueryRow(ctx, `SELECT COALESCE(MAX(height), 0) FROM core_blocks WHERE chain_id = $1`, w.cfg.ChainID).Scan(&maxHeight) + if err != nil { + return fmt.Errorf("query max height for resume: %w", err) + } + if maxHeight > 0 { + w.height = maxHeight + 1 + w.blockTime = w.cfg.GenesisTime.Add(time.Duration(maxHeight) * time.Second) + + // Restore prevAppHash from core_app_state (the app hash AFTER executing maxHeight). + // Header.AppHash is the app hash after the *previous* block, so loading from + // the block header would be off-by-one. + var prevAppHash []byte + err = w.dstDB.QueryRow(ctx, + `SELECT app_hash FROM core_app_state WHERE block_height = $1`, maxHeight). + Scan(&prevAppHash) + if err != nil { + return fmt.Errorf("query app hash for resume height %d: %w", maxHeight, err) + } + w.prevAppHash = prevAppHash + + // Restore block linkage from blockstore so new blocks chain correctly. + if w.blockStore == nil { + return fmt.Errorf("cannot resume without blockstore (--cmt-home required)") + } + lastBlock, _ := w.blockStore.LoadBlock(maxHeight) + if lastBlock == nil { + return fmt.Errorf("load resume block %d from blockstore: not found", maxHeight) + } + blockParts, err := lastBlock.MakePartSet(cmttypes.BlockPartSizeBytes) + if err != nil { + return fmt.Errorf("make part set for resume block %d: %w", maxHeight, err) + } + w.prevBlockID = cmttypes.BlockID{ + Hash: lastBlock.Hash(), + PartSetHeader: blockParts.Header(), + } + lastCommit := w.blockStore.LoadBlockCommit(maxHeight) + if lastCommit == nil { + return fmt.Errorf("load resume commit %d from blockstore: not found", maxHeight) + } + w.lastCommit = lastCommit + + w.logger.Info("resuming from height", zap.Int64("height", w.height)) + } + } + + // Drop non-PK indexes on core_transactions for faster bulk loading + // (only on fresh runs — on resume, indexes may already be rebuilt). + if !w.cfg.Resume { + if err := w.dropTransactionIndexes(ctx); err != nil { + return fmt.Errorf("drop indexes: %w", err) + } + } + + // Start the async block writer pipeline. + w.startBlockWriter(ctx) + defer w.stopBlockWriter() //nolint:errcheck // explicit stop below captures the error + + steps := []struct { + name string + skip bool + fn func(context.Context) error + }{ + // Phase 1: Identity — users and their linked wallets + {"users", w.cfg.SkipUsers, w.writeUsers}, + {"associated wallets", w.cfg.SkipWallets, w.writeAssociatedWallets}, + {"dashboard wallet users", w.cfg.SkipWallets, w.writeDashboardWalletUsers}, + + // Phase 2: Content — tracks and playlists + {"tracks", w.cfg.SkipTracks, w.writeTracks}, + {"track downloads", w.cfg.SkipTracks, w.writeTrackDownloads}, + {"playlists", w.cfg.SkipPlaylists, w.writePlaylists}, + + // Phase 3: Social — relationships between users and content + {"follows", w.cfg.SkipSocial, w.writeFollows}, + {"saves", w.cfg.SkipSocial, w.writeSaves}, + {"reposts", w.cfg.SkipSocial, w.writeReposts}, + {"shares", w.cfg.SkipSocial, w.writeShares}, + {"subscriptions", w.cfg.SkipSocial, w.writeSubscriptions}, + {"muted users", w.cfg.SkipSocial, w.writeMutedUsers}, + + // Phase 4: Apps — developer apps and API grants + {"developer apps", w.cfg.SkipApps, w.writeDeveloperApps}, + {"grants", w.cfg.SkipApps, w.writeGrants}, + + // Phase 5: Comments — comments and reactions on content + {"comments", w.cfg.SkipComments, w.writeComments}, + {"comment reactions", w.cfg.SkipComments, w.writeCommentReactions}, + + // Phase 6: Emails — encrypted emails and access grants + {"encrypted emails", w.cfg.SkipEmails, w.writeEncryptedEmails}, + {"email access", w.cfg.SkipEmails, w.writeEmailAccess}, + + // Phase 7: Activity — plays and tip reactions + {"plays", w.cfg.SkipPlays, w.writePlays}, + {"tip reactions", w.cfg.SkipTipReactions, w.writeTipReactions}, + } + + // Load completed steps for resume. + completedSteps := make(map[string]bool) + if w.cfg.Resume { + rows, err := w.dstDB.Query(ctx, `SELECT step_name FROM genesis_writer_progress`) + if err != nil { + return fmt.Errorf("query progress: %w", err) + } + defer rows.Close() + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + return fmt.Errorf("scan progress: %w", err) + } + completedSteps[name] = true + } + if err := rows.Err(); err != nil { + return fmt.Errorf("iterate progress: %w", err) + } + } + + for _, step := range steps { + if step.skip { + w.logger.Info("skipping", zap.String("step", step.name)) + continue + } + if completedSteps[step.name] { + w.logger.Info("already completed (resume)", zap.String("step", step.name)) + continue + } + // Check for async writer errors before starting next step. + if ep := w.blockWriteErr.Load(); ep != nil { + return fmt.Errorf("block writer: %w", *ep) + } + w.logger.Info("writing", zap.String("step", step.name)) + if err := step.fn(ctx); err != nil { + if ctx.Err() != nil { + w.logger.Info("write interrupted", zap.String("step", step.name)) + return ctx.Err() + } + return fmt.Errorf("write %s: %w", step.name, err) + } + // Record step completion for resume. + if _, err := w.dstDB.Exec(ctx, + `INSERT INTO genesis_writer_progress (step_name) VALUES ($1) ON CONFLICT DO NOTHING`, + step.name); err != nil { + return fmt.Errorf("save progress for %s: %w", step.name, err) + } + } + + // Flush any partial block. + if len(w.blockTxs) > 0 { + if err := w.flushBlock(ctx); err != nil { + return fmt.Errorf("final flush: %w", err) + } + } + + // Wait for all blocks to be written. + if err := w.stopBlockWriter(); err != nil { + return fmt.Errorf("block writer: %w", err) + } + + w.logger.Info("genesis write complete", + zap.Duration("elapsed", time.Since(start)), + zap.Int64("total_blocks", w.totalBlocks), + zap.Int64("total_txs", w.totalTxs), + zap.Int64("final_height", w.finalHeight), + zap.String("final_app_hash", hex.EncodeToString(w.finalAppHash)), + zap.String("final_block_hash", hex.EncodeToString(w.prevBlockID.Hash)), + ) + + // If resuming and no new blocks were written, recover final state from DB + // so that writeCMTState can still run. + if w.finalHeight == 0 && w.cfg.Resume { + var maxHeight int64 + var appHash []byte + err := w.dstDB.QueryRow(ctx, + `SELECT block_height, app_hash FROM core_app_state WHERE block_height = (SELECT MAX(block_height) FROM core_app_state)`). + Scan(&maxHeight, &appHash) + if err == nil && maxHeight > 0 { + w.finalHeight = maxHeight + w.finalAppHash = appHash + // Recover block hash from core_blocks. + var blockHash string + if err := w.dstDB.QueryRow(ctx, + `SELECT hash FROM core_blocks WHERE height = $1`, maxHeight). + Scan(&blockHash); err == nil { + hashBytes, _ := hex.DecodeString(blockHash) + w.prevBlockID = cmttypes.BlockID{ + Hash: hashBytes, + } + } + w.logger.Info("recovered final state for CMT write", + zap.Int64("height", w.finalHeight), + zap.String("app_hash", hex.EncodeToString(appHash)), + ) + } + } + + // Write CometBFT state and genesis file before index rebuild — fast and critical. + if w.cfg.CMTHome != "" && w.finalHeight > 0 { + if err := w.writeCMTState(ctx); err != nil { + return fmt.Errorf("write cmt state: %w", err) + } + if err := w.writeGenesisFile(); err != nil { + return fmt.Errorf("write genesis file: %w", err) + } + } + + // Rebuild indexes last — this is the slowest step and can be re-run independently. + if err := w.createTransactionIndexes(ctx); err != nil { + return fmt.Errorf("create indexes: %w", err) + } + + // Print next-steps instructions. + if w.cfg.CMTHome != "" && w.finalHeight > 0 { + genesisPath := filepath.Join(w.cfg.CMTHome, "config", "genesis.json") + w.logger.Info("genesis write finished — next steps:\n\n" + + " 1. Copy the genesis file into the source tree and rebuild:\n" + + " cp " + genesisPath + " pkg/core/config/genesis/prod.json\n" + + " Then add it to pkg/core/config/genesis/genesis.go and rebuild the binary.\n\n" + + " 2. Start the bootstrap node with the genesis-writer output as the data dir.\n" + + " In docker-compose.yml, mount it to /data:\n" + + " volumes:\n" + + " - " + filepath.Dir(w.cfg.CMTHome) + ":/data\n" + + " The node will pick up at height " + fmt.Sprintf("%d", w.finalHeight+1) + " and begin live consensus.\n\n" + + " 3. Once the bootstrap node is running, other nodes can state-sync from it:\n" + + " [statesync]\n" + + " enable = true\n" + + " rpc_servers = \":26657,:26657\"\n" + + " trust_height = " + fmt.Sprintf("%d", w.finalHeight+1) + "\n" + + " trust_hash = \"\"\n") + } + + return nil +} + +// signAndMarshal signs a ManageEntityLegacy, wraps it as a migration transaction, +// and marshals to bytes. Safe for concurrent use — all inputs are per-call. +func (w *Writer) signAndMarshal(me *corev1.ManageEntityLegacy, signer string) ([]byte, error) { + me.Nonce = w.nextNonce() + if err := server.SignManageEntity(w.sigCfg, me, w.privKey); err != nil { + return nil, fmt.Errorf("sign manage entity: %w", err) + } + // Override Signer with the entity's real wallet address. The signature was + // computed with the migration key, so it will NOT recover to this Signer value. + // Indexers must verify authority by recovering from the signature and checking + // against the genesis migration authority — Signer is an identity hint only. + me.Signer = signer + + migration := &corev1.ManageEntityLegacyMigration{ + UserId: me.UserId, + EntityType: me.EntityType, + EntityId: me.EntityId, + Action: me.Action, + Metadata: me.Metadata, + Signature: me.Signature, + Signer: me.Signer, + Nonce: me.Nonce, + } + + stx := &corev1.SignedTransaction{ + RequestId: uuid.NewString(), + Transaction: &corev1.SignedTransaction_ManageEntityMigration{ + ManageEntityMigration: migration, + }, + } + + // Reuse marshal options from pool to reduce allocations. + opts := w.marshalPool.Get().(proto.MarshalOptions) + buf, err := opts.Marshal(stx) + w.marshalPool.Put(opts) + if err != nil { + return nil, fmt.Errorf("marshal manage entity migration: %w", err) + } + return buf, nil +} + +// addManageEntity signs and wraps a ManageEntityLegacy via the signing pool, +// then appends it to the current block. +func (w *Writer) addManageEntity(ctx context.Context, me *corev1.ManageEntityLegacy) error { + return w.addManageEntityWithSigner(ctx, me, w.signerAddr) +} + +// addManageEntityWithSigner is like addManageEntity but overrides the signer address. +// Used for entity types where the DP uses params.signer as an identity +// (e.g. DeveloperApp address, user wallet for AssociatedWallet). +func (w *Writer) addManageEntityWithSigner(ctx context.Context, me *corev1.ManageEntityLegacy, signer string) error { + txBytes, err := w.signAndMarshal(me, signer) + if err != nil { + return err + } + return w.addTx(ctx, txBytes) +} + +// addTrackPlays appends a TrackPlays transaction to the current block. +// Plays do not require EIP712 signing. +func (w *Writer) addTrackPlays(ctx context.Context, plays *corev1.TrackPlays) error { + stx := &corev1.SignedTransaction{ + RequestId: uuid.NewString(), + Transaction: &corev1.SignedTransaction_Plays{ + Plays: plays, + }, + } + opts := w.marshalPool.Get().(proto.MarshalOptions) + txBytes, err := opts.Marshal(stx) + w.marshalPool.Put(opts) + if err != nil { + return fmt.Errorf("marshal plays: %w", err) + } + return w.addTx(ctx, txBytes) +} + +// addTx appends raw tx bytes to the current block, flushing when full. +// Safe for concurrent use — serialized by blockMu. +func (w *Writer) addTx(ctx context.Context, txBytes []byte) error { + w.blockMu.Lock() + defer w.blockMu.Unlock() + // Check for async writer errors. + if ep := w.blockWriteErr.Load(); ep != nil { + return fmt.Errorf("block writer: %w", *ep) + } + w.blockTxs = append(w.blockTxs, txBytes) + if len(w.blockTxs) >= w.cfg.MaxTxsPerBlock { + return w.flushBlock(ctx) + } + return nil +} + +// flushBlock builds a real CometBFT block from the accumulated transactions +// and sends it to the async writer pipeline. Chain state is advanced immediately. +func (w *Writer) flushBlock(ctx context.Context) error { + if len(w.blockTxs) == 0 { + return nil + } + + height := w.height + blockTime := w.blockTime + + // app_hash = SHA256(all tx bytes concatenated) — convention shared with the + // core server's FinalizeBlock implementation for genesis-range blocks. + h := sha256.New() + for _, tx := range w.blockTxs { + h.Write(tx) + } + appHash := h.Sum(nil) + + // Pre-compute tx hashes for the COPY insert. + // Use uppercase hex to match CometBFT's HexBytes.String() / common.ToTxHashFromBytes. + txData := make([]txRow, len(w.blockTxs)) + for i, tx := range w.blockTxs { + txData[i] = txRow{ + hash: strings.ToUpper(hex.EncodeToString(sha256Bytes(tx))), + txBytes: tx, + } + } + + // Build the CometBFT block. MakeBlock fills DataHash, LastCommitHash, EvidenceHash. + txList := make(cmttypes.Txs, len(w.blockTxs)) + for i, tx := range w.blockTxs { + txList[i] = cmttypes.Tx(tx) + } + lastCommit := w.lastCommit + if lastCommit == nil { + lastCommit = &cmttypes.Commit{} // empty commit for block 1 + } + block := cmttypes.MakeBlock(height, txList, lastCommit, nil) + + // Populate state-derived header fields. + // Header.AppHash is the app state AFTER executing the previous block. + block.Header.Populate( + cmtapiversion.Consensus{Block: cmtversion.BlockProtocol, App: 0}, + w.cfg.ChainID, + blockTime, + w.prevBlockID, + w.validatorsHash, + w.nextValHash, + w.consensusHash, + w.prevAppHash, + cmttypes.ABCIResults(nil).Hash(), // LastResultsHash: empty for all genesis blocks + w.proposerAddr, + ) + + blockHash := block.Hash() + + blockParts, err := block.MakePartSet(cmttypes.BlockPartSizeBytes) + if err != nil { + return fmt.Errorf("make part set height=%d: %w", height, err) + } + blockID := cmttypes.BlockID{ + Hash: blockHash, + PartSetHeader: blockParts.Header(), + } + + // Sign a precommit vote over this block. The resulting commit becomes the + // LastCommit included in the next block's header. + commitTime := blockTime.Add(time.Second) + voteProto := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: height, + Round: 0, + BlockID: blockID.ToProto(), + Timestamp: commitTime, + ValidatorAddress: w.proposerAddr, + ValidatorIndex: 0, + } + signBytes := cmttypes.VoteSignBytes(w.cfg.ChainID, voteProto) + sig, err := w.cmtPrivKey.Sign(signBytes) + if err != nil { + return fmt.Errorf("sign block %d: %w", height, err) + } + seenCommit := &cmttypes.Commit{ + Height: height, + Round: 0, + BlockID: blockID, + Signatures: []cmttypes.CommitSig{{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: w.proposerAddr, + Timestamp: commitTime, + Signature: sig, + }}, + } + + blockHashHex := hex.EncodeToString(blockHash) + + // Send to async writer pipeline. + pb := pendingBlock{ + height: height, + blockTime: blockTime, + appHash: appHash, + hashHex: blockHashHex, + txData: txData, + block: block, + blockParts: blockParts, + seenCommit: seenCommit, + } + select { + case w.blockWriteCh <- pb: + case <-ctx.Done(): + return ctx.Err() + } + + // Advance chain state for the next block. + w.prevBlockID = blockID + w.lastCommit = seenCommit + w.prevAppHash = appHash + + w.finalHeight = height + w.finalAppHash = appHash + w.finalTime = blockTime + + w.totalTxs += int64(len(w.blockTxs)) + w.totalBlocks++ + + w.height++ + w.blockTime = blockTime.Add(time.Second) + w.blockTxs = w.blockTxs[:0] + + if w.totalBlocks%1000 == 0 { + w.logger.Info("flush progress", + zap.Int64("blocks", w.totalBlocks), + zap.Int64("txs", w.totalTxs), + zap.Int64("height", w.finalHeight), + ) + } + + return nil +} + +// dropTransactionIndexes drops non-PK indexes on core_transactions for faster bulk loading. +func (w *Writer) dropTransactionIndexes(ctx context.Context) error { + indexes := []string{ + "idx_core_transactions_tx_hash_lower", + "idx_core_transactions_block_id", + "idx_core_transactions_tx_hash", + "idx_core_transactions_created_at", + } + for _, idx := range indexes { + if _, err := w.dstDB.Exec(ctx, fmt.Sprintf("DROP INDEX IF EXISTS %s", idx)); err != nil { + return fmt.Errorf("drop index %s: %w", idx, err) + } + } + w.logger.Info("dropped transaction indexes for bulk load") + return nil +} + +// createTransactionIndexes rebuilds indexes on core_transactions after bulk loading. +func (w *Writer) createTransactionIndexes(ctx context.Context) error { + w.logger.Info("rebuilding transaction indexes (this may take a while)...") + + // Use a single connection so SET persists across all CREATE INDEX calls. + conn, err := w.dstDB.Acquire(ctx) + if err != nil { + return fmt.Errorf("acquire conn for index rebuild: %w", err) + } + defer conn.Release() + + // Increase maintenance_work_mem for faster index builds on large tables. + if _, err := conn.Exec(ctx, "SET maintenance_work_mem = '1GB'"); err != nil { + w.logger.Warn("could not increase maintenance_work_mem", zap.Error(err)) + } + + indexes := []string{ + "CREATE INDEX IF NOT EXISTS idx_core_transactions_tx_hash_lower ON core_transactions (LOWER(tx_hash))", + "CREATE INDEX IF NOT EXISTS idx_core_transactions_block_id ON core_transactions(block_id)", + "CREATE INDEX IF NOT EXISTS idx_core_transactions_tx_hash ON core_transactions(tx_hash)", + "CREATE INDEX IF NOT EXISTS idx_core_transactions_created_at ON core_transactions(created_at)", + } + for _, ddl := range indexes { + w.logger.Info("creating index", zap.String("ddl", ddl)) + if _, err := conn.Exec(ctx, ddl); err != nil { + return fmt.Errorf("create index: %w", err) + } + } + w.logger.Info("transaction indexes rebuilt") + return nil +} + +func (w *Writer) nextNonce() string { + n := w.nonce.Add(1) + b := make([]byte, 32) + b[24] = byte(n >> 56) + b[25] = byte(n >> 48) + b[26] = byte(n >> 40) + b[27] = byte(n >> 32) + b[28] = byte(n >> 24) + b[29] = byte(n >> 16) + b[30] = byte(n >> 8) + b[31] = byte(n) + return "0x" + hex.EncodeToString(b) +} + +func sha256Bytes(data []byte) []byte { + h := sha256.Sum256(data) + return h[:] +} + +func signingConfig(network string) *corecfg.Config { + switch network { + case "prod", "production", "mainnet": + return &corecfg.Config{ + AcdcEntityManagerAddress: corecfg.ProdAcdcAddress, + AcdcChainID: corecfg.ProdAcdcChainID, + } + case "stage", "staging": + return &corecfg.Config{ + AcdcEntityManagerAddress: corecfg.StageAcdcAddress, + AcdcChainID: corecfg.StageAcdcChainID, + } + default: + return &corecfg.Config{ + AcdcEntityManagerAddress: corecfg.DevAcdcAddress, + AcdcChainID: corecfg.DevAcdcChainID, + } + } +} diff --git a/go.mod b/go.mod index f14352a1..332caf11 100644 --- a/go.mod +++ b/go.mod @@ -49,12 +49,14 @@ require ( github.com/golang-migrate/migrate/v4 v4.18.3 github.com/google/uuid v1.6.0 github.com/iancoleman/strcase v0.3.0 + github.com/lib/pq v1.10.9 github.com/maypok86/otter v1.2.4 github.com/oschwald/maxminddb-golang v1.13.1 github.com/rubenv/sql-migrate v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/shirou/gopsutil/v4 v4.25.1 github.com/tus/tusd/v2 v2.8.0 + github.com/urfave/cli/v2 v2.27.6 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.44.0 golang.org/x/mod v0.29.0 @@ -119,6 +121,7 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/continuity v0.4.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -177,7 +180,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/labstack/gommon v0.4.2 // indirect - github.com/lib/pq v1.10.9 // indirect github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -207,14 +209,15 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/supranational/blst v0.3.13 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/urfave/cli/v2 v2.27.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect diff --git a/go.sum b/go.sum index ab2b5947..b9db9d9b 100644 --- a/go.sum +++ b/go.sum @@ -571,7 +571,6 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= diff --git a/pkg/api/core/v1/types.pb.go b/pkg/api/core/v1/types.pb.go index b0bb28e5..559fedcc 100644 --- a/pkg/api/core/v1/types.pb.go +++ b/pkg/api/core/v1/types.pb.go @@ -1500,6 +1500,7 @@ type SignedTransaction struct { // *SignedTransaction_Release // *SignedTransaction_Reward // *SignedTransaction_FileUpload + // *SignedTransaction_ManageEntityMigration Transaction isSignedTransaction_Transaction `protobuf_oneof:"transaction"` } @@ -1633,6 +1634,13 @@ func (x *SignedTransaction) GetFileUpload() *FileUpload { return nil } +func (x *SignedTransaction) GetManageEntityMigration() *ManageEntityLegacyMigration { + if x, ok := x.GetTransaction().(*SignedTransaction_ManageEntityMigration); ok { + return x.ManageEntityMigration + } + return nil +} + type isSignedTransaction_Transaction interface { isSignedTransaction_Transaction() } @@ -1681,6 +1689,10 @@ type SignedTransaction_FileUpload struct { FileUpload *FileUpload `protobuf:"bytes,1010,opt,name=file_upload,json=fileUpload,proto3,oneof"` } +type SignedTransaction_ManageEntityMigration struct { + ManageEntityMigration *ManageEntityLegacyMigration `protobuf:"bytes,1011,opt,name=manage_entity_migration,json=manageEntityMigration,proto3,oneof"` +} + func (*SignedTransaction_Plays) isSignedTransaction_Transaction() {} func (*SignedTransaction_ValidatorRegistration) isSignedTransaction_Transaction() {} @@ -1703,6 +1715,8 @@ func (*SignedTransaction_Reward) isSignedTransaction_Transaction() {} func (*SignedTransaction_FileUpload) isSignedTransaction_Transaction() {} +func (*SignedTransaction_ManageEntityMigration) isSignedTransaction_Transaction() {} + type TrackPlays struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2169,6 +2183,114 @@ func (x *ManageEntityLegacy) GetNonce() string { return "" } +// ManageEntityLegacyMigration is structurally identical to ManageEntityLegacy +// but signals to indexers that the transaction was produced by genesis-writer +// during the initial chain migration. Indexers must verify only that the signer +// matches the genesis migration authority; standard ownership, wallet-uniqueness, +// and signer checks must not be applied. +type ManageEntityLegacyMigration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + EntityType string `protobuf:"bytes,2,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"` + EntityId int64 `protobuf:"varint,3,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + Action string `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"` + Metadata string `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"` + Signature string `protobuf:"bytes,6,opt,name=signature,proto3" json:"signature,omitempty"` + Signer string `protobuf:"bytes,7,opt,name=signer,proto3" json:"signer,omitempty"` + Nonce string `protobuf:"bytes,8,opt,name=nonce,proto3" json:"nonce,omitempty"` +} + +func (x *ManageEntityLegacyMigration) Reset() { + *x = ManageEntityLegacyMigration{} + if protoimpl.UnsafeEnabled { + mi := &file_core_v1_types_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ManageEntityLegacyMigration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ManageEntityLegacyMigration) ProtoMessage() {} + +func (x *ManageEntityLegacyMigration) ProtoReflect() protoreflect.Message { + mi := &file_core_v1_types_proto_msgTypes[31] + 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 ManageEntityLegacyMigration.ProtoReflect.Descriptor instead. +func (*ManageEntityLegacyMigration) Descriptor() ([]byte, []int) { + return file_core_v1_types_proto_rawDescGZIP(), []int{31} +} + +func (x *ManageEntityLegacyMigration) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *ManageEntityLegacyMigration) GetEntityType() string { + if x != nil { + return x.EntityType + } + return "" +} + +func (x *ManageEntityLegacyMigration) GetEntityId() int64 { + if x != nil { + return x.EntityId + } + return 0 +} + +func (x *ManageEntityLegacyMigration) GetAction() string { + if x != nil { + return x.Action + } + return "" +} + +func (x *ManageEntityLegacyMigration) GetMetadata() string { + if x != nil { + return x.Metadata + } + return "" +} + +func (x *ManageEntityLegacyMigration) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +func (x *ManageEntityLegacyMigration) GetSigner() string { + if x != nil { + return x.Signer + } + return "" +} + +func (x *ManageEntityLegacyMigration) GetNonce() string { + if x != nil { + return x.Nonce + } + return "" +} + type ValidatorMisbehaviorDeregistration struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2181,7 +2303,7 @@ type ValidatorMisbehaviorDeregistration struct { func (x *ValidatorMisbehaviorDeregistration) Reset() { *x = ValidatorMisbehaviorDeregistration{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[31] + mi := &file_core_v1_types_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2194,7 +2316,7 @@ func (x *ValidatorMisbehaviorDeregistration) String() string { func (*ValidatorMisbehaviorDeregistration) ProtoMessage() {} func (x *ValidatorMisbehaviorDeregistration) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[31] + mi := &file_core_v1_types_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2207,7 +2329,7 @@ func (x *ValidatorMisbehaviorDeregistration) ProtoReflect() protoreflect.Message // Deprecated: Use ValidatorMisbehaviorDeregistration.ProtoReflect.Descriptor instead. func (*ValidatorMisbehaviorDeregistration) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{31} + return file_core_v1_types_proto_rawDescGZIP(), []int{32} } func (x *ValidatorMisbehaviorDeregistration) GetCometAddress() string { @@ -2239,7 +2361,7 @@ type StorageProof struct { func (x *StorageProof) Reset() { *x = StorageProof{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[32] + mi := &file_core_v1_types_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2252,7 +2374,7 @@ func (x *StorageProof) String() string { func (*StorageProof) ProtoMessage() {} func (x *StorageProof) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[32] + mi := &file_core_v1_types_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2265,7 +2387,7 @@ func (x *StorageProof) ProtoReflect() protoreflect.Message { // Deprecated: Use StorageProof.ProtoReflect.Descriptor instead. func (*StorageProof) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{32} + return file_core_v1_types_proto_rawDescGZIP(), []int{33} } func (x *StorageProof) GetHeight() int64 { @@ -2315,7 +2437,7 @@ type StorageProofVerification struct { func (x *StorageProofVerification) Reset() { *x = StorageProofVerification{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[33] + mi := &file_core_v1_types_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2328,7 +2450,7 @@ func (x *StorageProofVerification) String() string { func (*StorageProofVerification) ProtoMessage() {} func (x *StorageProofVerification) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[33] + mi := &file_core_v1_types_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2341,7 +2463,7 @@ func (x *StorageProofVerification) ProtoReflect() protoreflect.Message { // Deprecated: Use StorageProofVerification.ProtoReflect.Descriptor instead. func (*StorageProofVerification) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{33} + return file_core_v1_types_proto_rawDescGZIP(), []int{34} } func (x *StorageProofVerification) GetHeight() int64 { @@ -2374,7 +2496,7 @@ type Attestation struct { func (x *Attestation) Reset() { *x = Attestation{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[34] + mi := &file_core_v1_types_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2387,7 +2509,7 @@ func (x *Attestation) String() string { func (*Attestation) ProtoMessage() {} func (x *Attestation) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[34] + mi := &file_core_v1_types_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2400,7 +2522,7 @@ func (x *Attestation) ProtoReflect() protoreflect.Message { // Deprecated: Use Attestation.ProtoReflect.Descriptor instead. func (*Attestation) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{34} + return file_core_v1_types_proto_rawDescGZIP(), []int{35} } func (x *Attestation) GetSignatures() []string { @@ -2466,7 +2588,7 @@ type ValidatorRegistration struct { func (x *ValidatorRegistration) Reset() { *x = ValidatorRegistration{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[35] + mi := &file_core_v1_types_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2479,7 +2601,7 @@ func (x *ValidatorRegistration) String() string { func (*ValidatorRegistration) ProtoMessage() {} func (x *ValidatorRegistration) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[35] + mi := &file_core_v1_types_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2492,7 +2614,7 @@ func (x *ValidatorRegistration) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidatorRegistration.ProtoReflect.Descriptor instead. func (*ValidatorRegistration) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{35} + return file_core_v1_types_proto_rawDescGZIP(), []int{36} } func (x *ValidatorRegistration) GetDelegateWallet() string { @@ -2572,7 +2694,7 @@ type ValidatorDeregistration struct { func (x *ValidatorDeregistration) Reset() { *x = ValidatorDeregistration{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[36] + mi := &file_core_v1_types_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2585,7 +2707,7 @@ func (x *ValidatorDeregistration) String() string { func (*ValidatorDeregistration) ProtoMessage() {} func (x *ValidatorDeregistration) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[36] + mi := &file_core_v1_types_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2598,7 +2720,7 @@ func (x *ValidatorDeregistration) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidatorDeregistration.ProtoReflect.Descriptor instead. func (*ValidatorDeregistration) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{36} + return file_core_v1_types_proto_rawDescGZIP(), []int{37} } func (x *ValidatorDeregistration) GetCometAddress() string { @@ -2638,7 +2760,7 @@ type GetStoredSnapshotsRequest struct { func (x *GetStoredSnapshotsRequest) Reset() { *x = GetStoredSnapshotsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[37] + mi := &file_core_v1_types_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2651,7 +2773,7 @@ func (x *GetStoredSnapshotsRequest) String() string { func (*GetStoredSnapshotsRequest) ProtoMessage() {} func (x *GetStoredSnapshotsRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[37] + mi := &file_core_v1_types_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2664,7 +2786,7 @@ func (x *GetStoredSnapshotsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStoredSnapshotsRequest.ProtoReflect.Descriptor instead. func (*GetStoredSnapshotsRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{37} + return file_core_v1_types_proto_rawDescGZIP(), []int{38} } type GetStoredSnapshotsResponse struct { @@ -2678,7 +2800,7 @@ type GetStoredSnapshotsResponse struct { func (x *GetStoredSnapshotsResponse) Reset() { *x = GetStoredSnapshotsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[38] + mi := &file_core_v1_types_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2691,7 +2813,7 @@ func (x *GetStoredSnapshotsResponse) String() string { func (*GetStoredSnapshotsResponse) ProtoMessage() {} func (x *GetStoredSnapshotsResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[38] + mi := &file_core_v1_types_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2704,7 +2826,7 @@ func (x *GetStoredSnapshotsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStoredSnapshotsResponse.ProtoReflect.Descriptor instead. func (*GetStoredSnapshotsResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{38} + return file_core_v1_types_proto_rawDescGZIP(), []int{39} } func (x *GetStoredSnapshotsResponse) GetSnapshots() []*SnapshotMetadata { @@ -2728,7 +2850,7 @@ type SnapshotMetadata struct { func (x *SnapshotMetadata) Reset() { *x = SnapshotMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[39] + mi := &file_core_v1_types_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2741,7 +2863,7 @@ func (x *SnapshotMetadata) String() string { func (*SnapshotMetadata) ProtoMessage() {} func (x *SnapshotMetadata) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[39] + mi := &file_core_v1_types_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2754,7 +2876,7 @@ func (x *SnapshotMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotMetadata.ProtoReflect.Descriptor instead. func (*SnapshotMetadata) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{39} + return file_core_v1_types_proto_rawDescGZIP(), []int{40} } func (x *SnapshotMetadata) GetHeight() int64 { @@ -2797,7 +2919,7 @@ type ClaimAuthority struct { func (x *ClaimAuthority) Reset() { *x = ClaimAuthority{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[40] + mi := &file_core_v1_types_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2810,7 +2932,7 @@ func (x *ClaimAuthority) String() string { func (*ClaimAuthority) ProtoMessage() {} func (x *ClaimAuthority) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[40] + mi := &file_core_v1_types_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2823,7 +2945,7 @@ func (x *ClaimAuthority) ProtoReflect() protoreflect.Message { // Deprecated: Use ClaimAuthority.ProtoReflect.Descriptor instead. func (*ClaimAuthority) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{40} + return file_core_v1_types_proto_rawDescGZIP(), []int{41} } func (x *ClaimAuthority) GetAddress() string { @@ -2854,7 +2976,7 @@ type Reward struct { func (x *Reward) Reset() { *x = Reward{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[41] + mi := &file_core_v1_types_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2867,7 +2989,7 @@ func (x *Reward) String() string { func (*Reward) ProtoMessage() {} func (x *Reward) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[41] + mi := &file_core_v1_types_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2880,7 +3002,7 @@ func (x *Reward) ProtoReflect() protoreflect.Message { // Deprecated: Use Reward.ProtoReflect.Descriptor instead. func (*Reward) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{41} + return file_core_v1_types_proto_rawDescGZIP(), []int{42} } func (x *Reward) GetRewardId() string { @@ -2922,7 +3044,7 @@ type GetRewardsRequest struct { func (x *GetRewardsRequest) Reset() { *x = GetRewardsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[42] + mi := &file_core_v1_types_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2935,7 +3057,7 @@ func (x *GetRewardsRequest) String() string { func (*GetRewardsRequest) ProtoMessage() {} func (x *GetRewardsRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[42] + mi := &file_core_v1_types_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2948,7 +3070,7 @@ func (x *GetRewardsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardsRequest.ProtoReflect.Descriptor instead. func (*GetRewardsRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{42} + return file_core_v1_types_proto_rawDescGZIP(), []int{43} } func (x *GetRewardsRequest) GetClaimAuthority() string { @@ -2969,7 +3091,7 @@ type GetRewardsResponse struct { func (x *GetRewardsResponse) Reset() { *x = GetRewardsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[43] + mi := &file_core_v1_types_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2982,7 +3104,7 @@ func (x *GetRewardsResponse) String() string { func (*GetRewardsResponse) ProtoMessage() {} func (x *GetRewardsResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[43] + mi := &file_core_v1_types_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2995,7 +3117,7 @@ func (x *GetRewardsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardsResponse.ProtoReflect.Descriptor instead. func (*GetRewardsResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{43} + return file_core_v1_types_proto_rawDescGZIP(), []int{44} } func (x *GetRewardsResponse) GetRewards() []*GetRewardResponse { @@ -3023,7 +3145,7 @@ type GetRewardAttestationRequest struct { func (x *GetRewardAttestationRequest) Reset() { *x = GetRewardAttestationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[44] + mi := &file_core_v1_types_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3036,7 +3158,7 @@ func (x *GetRewardAttestationRequest) String() string { func (*GetRewardAttestationRequest) ProtoMessage() {} func (x *GetRewardAttestationRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[44] + mi := &file_core_v1_types_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3049,7 +3171,7 @@ func (x *GetRewardAttestationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardAttestationRequest.ProtoReflect.Descriptor instead. func (*GetRewardAttestationRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{44} + return file_core_v1_types_proto_rawDescGZIP(), []int{45} } func (x *GetRewardAttestationRequest) GetEthRecipientAddress() string { @@ -3120,7 +3242,7 @@ type GetRewardAttestationResponse struct { func (x *GetRewardAttestationResponse) Reset() { *x = GetRewardAttestationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[45] + mi := &file_core_v1_types_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3133,7 +3255,7 @@ func (x *GetRewardAttestationResponse) String() string { func (*GetRewardAttestationResponse) ProtoMessage() {} func (x *GetRewardAttestationResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[45] + mi := &file_core_v1_types_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3146,7 +3268,7 @@ func (x *GetRewardAttestationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardAttestationResponse.ProtoReflect.Descriptor instead. func (*GetRewardAttestationResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{45} + return file_core_v1_types_proto_rawDescGZIP(), []int{46} } func (x *GetRewardAttestationResponse) GetOwner() string { @@ -3178,7 +3300,7 @@ type SlashRecommendation struct { func (x *SlashRecommendation) Reset() { *x = SlashRecommendation{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[46] + mi := &file_core_v1_types_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3191,7 +3313,7 @@ func (x *SlashRecommendation) String() string { func (*SlashRecommendation) ProtoMessage() {} func (x *SlashRecommendation) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[46] + mi := &file_core_v1_types_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3204,7 +3326,7 @@ func (x *SlashRecommendation) ProtoReflect() protoreflect.Message { // Deprecated: Use SlashRecommendation.ProtoReflect.Descriptor instead. func (*SlashRecommendation) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{46} + return file_core_v1_types_proto_rawDescGZIP(), []int{47} } func (x *SlashRecommendation) GetAddress() string { @@ -3253,7 +3375,7 @@ type GetSlashAttestationRequest struct { func (x *GetSlashAttestationRequest) Reset() { *x = GetSlashAttestationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[47] + mi := &file_core_v1_types_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3266,7 +3388,7 @@ func (x *GetSlashAttestationRequest) String() string { func (*GetSlashAttestationRequest) ProtoMessage() {} func (x *GetSlashAttestationRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[47] + mi := &file_core_v1_types_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3279,7 +3401,7 @@ func (x *GetSlashAttestationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlashAttestationRequest.ProtoReflect.Descriptor instead. func (*GetSlashAttestationRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{47} + return file_core_v1_types_proto_rawDescGZIP(), []int{48} } func (x *GetSlashAttestationRequest) GetData() *SlashRecommendation { @@ -3301,7 +3423,7 @@ type GetSlashAttestationResponse struct { func (x *GetSlashAttestationResponse) Reset() { *x = GetSlashAttestationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[48] + mi := &file_core_v1_types_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3314,7 +3436,7 @@ func (x *GetSlashAttestationResponse) String() string { func (*GetSlashAttestationResponse) ProtoMessage() {} func (x *GetSlashAttestationResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[48] + mi := &file_core_v1_types_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3327,7 +3449,7 @@ func (x *GetSlashAttestationResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlashAttestationResponse.ProtoReflect.Descriptor instead. func (*GetSlashAttestationResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{48} + return file_core_v1_types_proto_rawDescGZIP(), []int{49} } func (x *GetSlashAttestationResponse) GetSignature() string { @@ -3355,7 +3477,7 @@ type GetSlashAttestationsRequest struct { func (x *GetSlashAttestationsRequest) Reset() { *x = GetSlashAttestationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[49] + mi := &file_core_v1_types_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3368,7 +3490,7 @@ func (x *GetSlashAttestationsRequest) String() string { func (*GetSlashAttestationsRequest) ProtoMessage() {} func (x *GetSlashAttestationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[49] + mi := &file_core_v1_types_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3381,7 +3503,7 @@ func (x *GetSlashAttestationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlashAttestationsRequest.ProtoReflect.Descriptor instead. func (*GetSlashAttestationsRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{49} + return file_core_v1_types_proto_rawDescGZIP(), []int{50} } func (x *GetSlashAttestationsRequest) GetRequest() *GetSlashAttestationRequest { @@ -3402,7 +3524,7 @@ type GetSlashAttestationsResponse struct { func (x *GetSlashAttestationsResponse) Reset() { *x = GetSlashAttestationsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[50] + mi := &file_core_v1_types_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3415,7 +3537,7 @@ func (x *GetSlashAttestationsResponse) String() string { func (*GetSlashAttestationsResponse) ProtoMessage() {} func (x *GetSlashAttestationsResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[50] + mi := &file_core_v1_types_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3428,7 +3550,7 @@ func (x *GetSlashAttestationsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlashAttestationsResponse.ProtoReflect.Descriptor instead. func (*GetSlashAttestationsResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{50} + return file_core_v1_types_proto_rawDescGZIP(), []int{51} } func (x *GetSlashAttestationsResponse) GetAttestations() []*GetSlashAttestationResponse { @@ -3449,7 +3571,7 @@ type GetERNRequest struct { func (x *GetERNRequest) Reset() { *x = GetERNRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[51] + mi := &file_core_v1_types_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3462,7 +3584,7 @@ func (x *GetERNRequest) String() string { func (*GetERNRequest) ProtoMessage() {} func (x *GetERNRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[51] + mi := &file_core_v1_types_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3475,7 +3597,7 @@ func (x *GetERNRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetERNRequest.ProtoReflect.Descriptor instead. func (*GetERNRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{51} + return file_core_v1_types_proto_rawDescGZIP(), []int{52} } func (x *GetERNRequest) GetAddress() string { @@ -3496,7 +3618,7 @@ type GetERNResponse struct { func (x *GetERNResponse) Reset() { *x = GetERNResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[52] + mi := &file_core_v1_types_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3509,7 +3631,7 @@ func (x *GetERNResponse) String() string { func (*GetERNResponse) ProtoMessage() {} func (x *GetERNResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[52] + mi := &file_core_v1_types_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3522,7 +3644,7 @@ func (x *GetERNResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetERNResponse.ProtoReflect.Descriptor instead. func (*GetERNResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{52} + return file_core_v1_types_proto_rawDescGZIP(), []int{53} } func (x *GetERNResponse) GetErn() *v1beta11.NewReleaseMessage { @@ -3543,7 +3665,7 @@ type GetPartyRequest struct { func (x *GetPartyRequest) Reset() { *x = GetPartyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[53] + mi := &file_core_v1_types_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3556,7 +3678,7 @@ func (x *GetPartyRequest) String() string { func (*GetPartyRequest) ProtoMessage() {} func (x *GetPartyRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[53] + mi := &file_core_v1_types_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3569,7 +3691,7 @@ func (x *GetPartyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPartyRequest.ProtoReflect.Descriptor instead. func (*GetPartyRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{53} + return file_core_v1_types_proto_rawDescGZIP(), []int{54} } func (x *GetPartyRequest) GetAddress() string { @@ -3590,7 +3712,7 @@ type GetPartyResponse struct { func (x *GetPartyResponse) Reset() { *x = GetPartyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[54] + mi := &file_core_v1_types_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3603,7 +3725,7 @@ func (x *GetPartyResponse) String() string { func (*GetPartyResponse) ProtoMessage() {} func (x *GetPartyResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[54] + mi := &file_core_v1_types_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3616,7 +3738,7 @@ func (x *GetPartyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPartyResponse.ProtoReflect.Descriptor instead. func (*GetPartyResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{54} + return file_core_v1_types_proto_rawDescGZIP(), []int{55} } func (x *GetPartyResponse) GetParty() *v1beta11.Party { @@ -3637,7 +3759,7 @@ type GetResourceRequest struct { func (x *GetResourceRequest) Reset() { *x = GetResourceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[55] + mi := &file_core_v1_types_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3650,7 +3772,7 @@ func (x *GetResourceRequest) String() string { func (*GetResourceRequest) ProtoMessage() {} func (x *GetResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[55] + mi := &file_core_v1_types_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3663,7 +3785,7 @@ func (x *GetResourceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetResourceRequest.ProtoReflect.Descriptor instead. func (*GetResourceRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{55} + return file_core_v1_types_proto_rawDescGZIP(), []int{56} } func (x *GetResourceRequest) GetAddress() string { @@ -3684,7 +3806,7 @@ type GetResourceResponse struct { func (x *GetResourceResponse) Reset() { *x = GetResourceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[56] + mi := &file_core_v1_types_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3697,7 +3819,7 @@ func (x *GetResourceResponse) String() string { func (*GetResourceResponse) ProtoMessage() {} func (x *GetResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[56] + mi := &file_core_v1_types_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3710,7 +3832,7 @@ func (x *GetResourceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetResourceResponse.ProtoReflect.Descriptor instead. func (*GetResourceResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{56} + return file_core_v1_types_proto_rawDescGZIP(), []int{57} } func (x *GetResourceResponse) GetResource() *v1beta11.Resource { @@ -3731,7 +3853,7 @@ type GetReleaseRequest struct { func (x *GetReleaseRequest) Reset() { *x = GetReleaseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[57] + mi := &file_core_v1_types_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3744,7 +3866,7 @@ func (x *GetReleaseRequest) String() string { func (*GetReleaseRequest) ProtoMessage() {} func (x *GetReleaseRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[57] + mi := &file_core_v1_types_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3757,7 +3879,7 @@ func (x *GetReleaseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReleaseRequest.ProtoReflect.Descriptor instead. func (*GetReleaseRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{57} + return file_core_v1_types_proto_rawDescGZIP(), []int{58} } func (x *GetReleaseRequest) GetAddress() string { @@ -3778,7 +3900,7 @@ type GetReleaseResponse struct { func (x *GetReleaseResponse) Reset() { *x = GetReleaseResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[58] + mi := &file_core_v1_types_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3791,7 +3913,7 @@ func (x *GetReleaseResponse) String() string { func (*GetReleaseResponse) ProtoMessage() {} func (x *GetReleaseResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[58] + mi := &file_core_v1_types_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3804,7 +3926,7 @@ func (x *GetReleaseResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReleaseResponse.ProtoReflect.Descriptor instead. func (*GetReleaseResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{58} + return file_core_v1_types_proto_rawDescGZIP(), []int{59} } func (x *GetReleaseResponse) GetRelease() *v1beta11.Release { @@ -3825,7 +3947,7 @@ type GetDealRequest struct { func (x *GetDealRequest) Reset() { *x = GetDealRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[59] + mi := &file_core_v1_types_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3838,7 +3960,7 @@ func (x *GetDealRequest) String() string { func (*GetDealRequest) ProtoMessage() {} func (x *GetDealRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[59] + mi := &file_core_v1_types_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3851,7 +3973,7 @@ func (x *GetDealRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDealRequest.ProtoReflect.Descriptor instead. func (*GetDealRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{59} + return file_core_v1_types_proto_rawDescGZIP(), []int{60} } func (x *GetDealRequest) GetAddress() string { @@ -3872,7 +3994,7 @@ type GetDealResponse struct { func (x *GetDealResponse) Reset() { *x = GetDealResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[60] + mi := &file_core_v1_types_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3885,7 +4007,7 @@ func (x *GetDealResponse) String() string { func (*GetDealResponse) ProtoMessage() {} func (x *GetDealResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[60] + mi := &file_core_v1_types_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3898,7 +4020,7 @@ func (x *GetDealResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetDealResponse.ProtoReflect.Descriptor instead. func (*GetDealResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{60} + return file_core_v1_types_proto_rawDescGZIP(), []int{61} } func (x *GetDealResponse) GetDeal() *v1beta11.Deal { @@ -3919,7 +4041,7 @@ type GetMEADRequest struct { func (x *GetMEADRequest) Reset() { *x = GetMEADRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[61] + mi := &file_core_v1_types_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3932,7 +4054,7 @@ func (x *GetMEADRequest) String() string { func (*GetMEADRequest) ProtoMessage() {} func (x *GetMEADRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[61] + mi := &file_core_v1_types_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3945,7 +4067,7 @@ func (x *GetMEADRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMEADRequest.ProtoReflect.Descriptor instead. func (*GetMEADRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{61} + return file_core_v1_types_proto_rawDescGZIP(), []int{62} } func (x *GetMEADRequest) GetAddress() string { @@ -3966,7 +4088,7 @@ type GetMEADResponse struct { func (x *GetMEADResponse) Reset() { *x = GetMEADResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[62] + mi := &file_core_v1_types_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3979,7 +4101,7 @@ func (x *GetMEADResponse) String() string { func (*GetMEADResponse) ProtoMessage() {} func (x *GetMEADResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[62] + mi := &file_core_v1_types_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3992,7 +4114,7 @@ func (x *GetMEADResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMEADResponse.ProtoReflect.Descriptor instead. func (*GetMEADResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{62} + return file_core_v1_types_proto_rawDescGZIP(), []int{63} } func (x *GetMEADResponse) GetMead() *v1beta11.MeadMessage { @@ -4013,7 +4135,7 @@ type GetPIERequest struct { func (x *GetPIERequest) Reset() { *x = GetPIERequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[63] + mi := &file_core_v1_types_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4026,7 +4148,7 @@ func (x *GetPIERequest) String() string { func (*GetPIERequest) ProtoMessage() {} func (x *GetPIERequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[63] + mi := &file_core_v1_types_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4039,7 +4161,7 @@ func (x *GetPIERequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPIERequest.ProtoReflect.Descriptor instead. func (*GetPIERequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{63} + return file_core_v1_types_proto_rawDescGZIP(), []int{64} } func (x *GetPIERequest) GetAddress() string { @@ -4060,7 +4182,7 @@ type GetPIEResponse struct { func (x *GetPIEResponse) Reset() { *x = GetPIEResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[64] + mi := &file_core_v1_types_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4073,7 +4195,7 @@ func (x *GetPIEResponse) String() string { func (*GetPIEResponse) ProtoMessage() {} func (x *GetPIEResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[64] + mi := &file_core_v1_types_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4086,7 +4208,7 @@ func (x *GetPIEResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPIEResponse.ProtoReflect.Descriptor instead. func (*GetPIEResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{64} + return file_core_v1_types_proto_rawDescGZIP(), []int{65} } func (x *GetPIEResponse) GetPie() *v1beta11.PieMessage { @@ -4111,7 +4233,7 @@ type RewardMessage struct { func (x *RewardMessage) Reset() { *x = RewardMessage{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[65] + mi := &file_core_v1_types_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4124,7 +4246,7 @@ func (x *RewardMessage) String() string { func (*RewardMessage) ProtoMessage() {} func (x *RewardMessage) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[65] + mi := &file_core_v1_types_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4137,7 +4259,7 @@ func (x *RewardMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use RewardMessage.ProtoReflect.Descriptor instead. func (*RewardMessage) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{65} + return file_core_v1_types_proto_rawDescGZIP(), []int{66} } func (m *RewardMessage) GetAction() isRewardMessage_Action { @@ -4193,7 +4315,7 @@ type CreateReward struct { func (x *CreateReward) Reset() { *x = CreateReward{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[66] + mi := &file_core_v1_types_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4206,7 +4328,7 @@ func (x *CreateReward) String() string { func (*CreateReward) ProtoMessage() {} func (x *CreateReward) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[66] + mi := &file_core_v1_types_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4219,7 +4341,7 @@ func (x *CreateReward) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateReward.ProtoReflect.Descriptor instead. func (*CreateReward) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{66} + return file_core_v1_types_proto_rawDescGZIP(), []int{67} } func (x *CreateReward) GetRewardId() string { @@ -4277,7 +4399,7 @@ type DeleteReward struct { func (x *DeleteReward) Reset() { *x = DeleteReward{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[67] + mi := &file_core_v1_types_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4290,7 +4412,7 @@ func (x *DeleteReward) String() string { func (*DeleteReward) ProtoMessage() {} func (x *DeleteReward) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[67] + mi := &file_core_v1_types_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4303,7 +4425,7 @@ func (x *DeleteReward) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteReward.ProtoReflect.Descriptor instead. func (*DeleteReward) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{67} + return file_core_v1_types_proto_rawDescGZIP(), []int{68} } func (x *DeleteReward) GetAddress() string { @@ -4339,7 +4461,7 @@ type GetRewardRequest struct { func (x *GetRewardRequest) Reset() { *x = GetRewardRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[68] + mi := &file_core_v1_types_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4352,7 +4474,7 @@ func (x *GetRewardRequest) String() string { func (*GetRewardRequest) ProtoMessage() {} func (x *GetRewardRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[68] + mi := &file_core_v1_types_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4365,7 +4487,7 @@ func (x *GetRewardRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardRequest.ProtoReflect.Descriptor instead. func (*GetRewardRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{68} + return file_core_v1_types_proto_rawDescGZIP(), []int{69} } func (x *GetRewardRequest) GetAddress() string { @@ -4399,7 +4521,7 @@ type GetRewardResponse struct { func (x *GetRewardResponse) Reset() { *x = GetRewardResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[69] + mi := &file_core_v1_types_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4412,7 +4534,7 @@ func (x *GetRewardResponse) String() string { func (*GetRewardResponse) ProtoMessage() {} func (x *GetRewardResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[69] + mi := &file_core_v1_types_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4425,7 +4547,7 @@ func (x *GetRewardResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRewardResponse.ProtoReflect.Descriptor instead. func (*GetRewardResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{69} + return file_core_v1_types_proto_rawDescGZIP(), []int{70} } func (x *GetRewardResponse) GetAddress() string { @@ -4493,7 +4615,7 @@ type RewardAttestationSignature struct { func (x *RewardAttestationSignature) Reset() { *x = RewardAttestationSignature{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[70] + mi := &file_core_v1_types_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4506,7 +4628,7 @@ func (x *RewardAttestationSignature) String() string { func (*RewardAttestationSignature) ProtoMessage() {} func (x *RewardAttestationSignature) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[70] + mi := &file_core_v1_types_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4519,7 +4641,7 @@ func (x *RewardAttestationSignature) ProtoReflect() protoreflect.Message { // Deprecated: Use RewardAttestationSignature.ProtoReflect.Descriptor instead. func (*RewardAttestationSignature) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{70} + return file_core_v1_types_proto_rawDescGZIP(), []int{71} } func (x *RewardAttestationSignature) GetEthRecipientAddress() string { @@ -4575,7 +4697,7 @@ type UploadSignature struct { func (x *UploadSignature) Reset() { *x = UploadSignature{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[71] + mi := &file_core_v1_types_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4588,7 +4710,7 @@ func (x *UploadSignature) String() string { func (*UploadSignature) ProtoMessage() {} func (x *UploadSignature) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[71] + mi := &file_core_v1_types_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4601,7 +4723,7 @@ func (x *UploadSignature) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadSignature.ProtoReflect.Descriptor instead. func (*UploadSignature) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{71} + return file_core_v1_types_proto_rawDescGZIP(), []int{72} } func (x *UploadSignature) GetCid() string { @@ -4637,7 +4759,7 @@ type FileUpload struct { func (x *FileUpload) Reset() { *x = FileUpload{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[72] + mi := &file_core_v1_types_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4650,7 +4772,7 @@ func (x *FileUpload) String() string { func (*FileUpload) ProtoMessage() {} func (x *FileUpload) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[72] + mi := &file_core_v1_types_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4663,7 +4785,7 @@ func (x *FileUpload) ProtoReflect() protoreflect.Message { // Deprecated: Use FileUpload.ProtoReflect.Descriptor instead. func (*FileUpload) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{72} + return file_core_v1_types_proto_rawDescGZIP(), []int{73} } func (x *FileUpload) GetUploaderAddress() string { @@ -4728,7 +4850,7 @@ type GetStreamURLsSignature struct { func (x *GetStreamURLsSignature) Reset() { *x = GetStreamURLsSignature{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[73] + mi := &file_core_v1_types_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4741,7 +4863,7 @@ func (x *GetStreamURLsSignature) String() string { func (*GetStreamURLsSignature) ProtoMessage() {} func (x *GetStreamURLsSignature) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[73] + mi := &file_core_v1_types_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4754,7 +4876,7 @@ func (x *GetStreamURLsSignature) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStreamURLsSignature.ProtoReflect.Descriptor instead. func (*GetStreamURLsSignature) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{73} + return file_core_v1_types_proto_rawDescGZIP(), []int{74} } func (x *GetStreamURLsSignature) GetAddresses() []string { @@ -4791,7 +4913,7 @@ type GetStreamURLsRequest struct { func (x *GetStreamURLsRequest) Reset() { *x = GetStreamURLsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[74] + mi := &file_core_v1_types_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4804,7 +4926,7 @@ func (x *GetStreamURLsRequest) String() string { func (*GetStreamURLsRequest) ProtoMessage() {} func (x *GetStreamURLsRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[74] + mi := &file_core_v1_types_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4817,7 +4939,7 @@ func (x *GetStreamURLsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStreamURLsRequest.ProtoReflect.Descriptor instead. func (*GetStreamURLsRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{74} + return file_core_v1_types_proto_rawDescGZIP(), []int{75} } func (x *GetStreamURLsRequest) GetSignature() string { @@ -4854,7 +4976,7 @@ type GetStreamURLsResponse struct { func (x *GetStreamURLsResponse) Reset() { *x = GetStreamURLsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[75] + mi := &file_core_v1_types_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4867,7 +4989,7 @@ func (x *GetStreamURLsResponse) String() string { func (*GetStreamURLsResponse) ProtoMessage() {} func (x *GetStreamURLsResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[75] + mi := &file_core_v1_types_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4880,7 +5002,7 @@ func (x *GetStreamURLsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetStreamURLsResponse.ProtoReflect.Descriptor instead. func (*GetStreamURLsResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{75} + return file_core_v1_types_proto_rawDescGZIP(), []int{76} } func (x *GetStreamURLsResponse) GetEntityStreamUrls() map[string]*GetStreamURLsResponse_EntityStreamURLs { @@ -4901,7 +5023,7 @@ type GetUploadByCIDRequest struct { func (x *GetUploadByCIDRequest) Reset() { *x = GetUploadByCIDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[76] + mi := &file_core_v1_types_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4914,7 +5036,7 @@ func (x *GetUploadByCIDRequest) String() string { func (*GetUploadByCIDRequest) ProtoMessage() {} func (x *GetUploadByCIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[76] + mi := &file_core_v1_types_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4927,7 +5049,7 @@ func (x *GetUploadByCIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUploadByCIDRequest.ProtoReflect.Descriptor instead. func (*GetUploadByCIDRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{76} + return file_core_v1_types_proto_rawDescGZIP(), []int{77} } func (x *GetUploadByCIDRequest) GetCid() string { @@ -4951,7 +5073,7 @@ type GetUploadByCIDResponse struct { func (x *GetUploadByCIDResponse) Reset() { *x = GetUploadByCIDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[77] + mi := &file_core_v1_types_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4964,7 +5086,7 @@ func (x *GetUploadByCIDResponse) String() string { func (*GetUploadByCIDResponse) ProtoMessage() {} func (x *GetUploadByCIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[77] + mi := &file_core_v1_types_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4977,7 +5099,7 @@ func (x *GetUploadByCIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUploadByCIDResponse.ProtoReflect.Descriptor instead. func (*GetUploadByCIDResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{77} + return file_core_v1_types_proto_rawDescGZIP(), []int{78} } func (x *GetUploadByCIDResponse) GetExists() bool { @@ -5020,7 +5142,7 @@ type GetRewardSenderAttestationRequest struct { func (x *GetRewardSenderAttestationRequest) Reset() { *x = GetRewardSenderAttestationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[78] + mi := &file_core_v1_types_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5033,7 +5155,7 @@ func (x *GetRewardSenderAttestationRequest) String() string { func (*GetRewardSenderAttestationRequest) ProtoMessage() {} func (x *GetRewardSenderAttestationRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[78] + mi := &file_core_v1_types_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5046,7 +5168,7 @@ func (x *GetRewardSenderAttestationRequest) ProtoReflect() protoreflect.Message // Deprecated: Use GetRewardSenderAttestationRequest.ProtoReflect.Descriptor instead. func (*GetRewardSenderAttestationRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{78} + return file_core_v1_types_proto_rawDescGZIP(), []int{79} } func (x *GetRewardSenderAttestationRequest) GetAddress() string { @@ -5075,7 +5197,7 @@ type GetRewardSenderAttestationResponse struct { func (x *GetRewardSenderAttestationResponse) Reset() { *x = GetRewardSenderAttestationResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[79] + mi := &file_core_v1_types_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5088,7 +5210,7 @@ func (x *GetRewardSenderAttestationResponse) String() string { func (*GetRewardSenderAttestationResponse) ProtoMessage() {} func (x *GetRewardSenderAttestationResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[79] + mi := &file_core_v1_types_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5101,7 +5223,7 @@ func (x *GetRewardSenderAttestationResponse) ProtoReflect() protoreflect.Message // Deprecated: Use GetRewardSenderAttestationResponse.ProtoReflect.Descriptor instead. func (*GetRewardSenderAttestationResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{79} + return file_core_v1_types_proto_rawDescGZIP(), []int{80} } func (x *GetRewardSenderAttestationResponse) GetOwner() string { @@ -5129,7 +5251,7 @@ type StreamBlocksRequest struct { func (x *StreamBlocksRequest) Reset() { *x = StreamBlocksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[80] + mi := &file_core_v1_types_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5142,7 +5264,7 @@ func (x *StreamBlocksRequest) String() string { func (*StreamBlocksRequest) ProtoMessage() {} func (x *StreamBlocksRequest) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[80] + mi := &file_core_v1_types_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5155,7 +5277,7 @@ func (x *StreamBlocksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StreamBlocksRequest.ProtoReflect.Descriptor instead. func (*StreamBlocksRequest) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{80} + return file_core_v1_types_proto_rawDescGZIP(), []int{81} } func (x *StreamBlocksRequest) GetCanon() bool { @@ -5176,7 +5298,7 @@ type StreamBlocksResponse struct { func (x *StreamBlocksResponse) Reset() { *x = StreamBlocksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[81] + mi := &file_core_v1_types_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5189,7 +5311,7 @@ func (x *StreamBlocksResponse) String() string { func (*StreamBlocksResponse) ProtoMessage() {} func (x *StreamBlocksResponse) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[81] + mi := &file_core_v1_types_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5202,7 +5324,7 @@ func (x *StreamBlocksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StreamBlocksResponse.ProtoReflect.Descriptor instead. func (*StreamBlocksResponse) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{81} + return file_core_v1_types_proto_rawDescGZIP(), []int{82} } func (x *StreamBlocksResponse) GetBlock() *Block { @@ -5232,7 +5354,7 @@ type GetStatusResponse_ProcessInfo struct { func (x *GetStatusResponse_ProcessInfo) Reset() { *x = GetStatusResponse_ProcessInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[82] + mi := &file_core_v1_types_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5245,7 +5367,7 @@ func (x *GetStatusResponse_ProcessInfo) String() string { func (*GetStatusResponse_ProcessInfo) ProtoMessage() {} func (x *GetStatusResponse_ProcessInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[82] + mi := &file_core_v1_types_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5345,7 +5467,7 @@ type GetStatusResponse_NodeInfo struct { func (x *GetStatusResponse_NodeInfo) Reset() { *x = GetStatusResponse_NodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[83] + mi := &file_core_v1_types_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5358,7 +5480,7 @@ func (x *GetStatusResponse_NodeInfo) String() string { func (*GetStatusResponse_NodeInfo) ProtoMessage() {} func (x *GetStatusResponse_NodeInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[83] + mi := &file_core_v1_types_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5416,7 +5538,7 @@ type GetStatusResponse_ChainInfo struct { func (x *GetStatusResponse_ChainInfo) Reset() { *x = GetStatusResponse_ChainInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[84] + mi := &file_core_v1_types_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5429,7 +5551,7 @@ func (x *GetStatusResponse_ChainInfo) String() string { func (*GetStatusResponse_ChainInfo) ProtoMessage() {} func (x *GetStatusResponse_ChainInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[84] + mi := &file_core_v1_types_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5489,7 +5611,7 @@ type GetStatusResponse_SyncInfo struct { func (x *GetStatusResponse_SyncInfo) Reset() { *x = GetStatusResponse_SyncInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[85] + mi := &file_core_v1_types_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5502,7 +5624,7 @@ func (x *GetStatusResponse_SyncInfo) String() string { func (*GetStatusResponse_SyncInfo) ProtoMessage() {} func (x *GetStatusResponse_SyncInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[85] + mi := &file_core_v1_types_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5580,7 +5702,7 @@ type GetStatusResponse_PruningInfo struct { func (x *GetStatusResponse_PruningInfo) Reset() { *x = GetStatusResponse_PruningInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[86] + mi := &file_core_v1_types_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5593,7 +5715,7 @@ func (x *GetStatusResponse_PruningInfo) String() string { func (*GetStatusResponse_PruningInfo) ProtoMessage() {} func (x *GetStatusResponse_PruningInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[86] + mi := &file_core_v1_types_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5682,7 +5804,7 @@ type GetStatusResponse_ResourceInfo struct { func (x *GetStatusResponse_ResourceInfo) Reset() { *x = GetStatusResponse_ResourceInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[87] + mi := &file_core_v1_types_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5695,7 +5817,7 @@ func (x *GetStatusResponse_ResourceInfo) String() string { func (*GetStatusResponse_ResourceInfo) ProtoMessage() {} func (x *GetStatusResponse_ResourceInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[87] + mi := &file_core_v1_types_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5774,7 +5896,7 @@ type GetStatusResponse_MempoolInfo struct { func (x *GetStatusResponse_MempoolInfo) Reset() { *x = GetStatusResponse_MempoolInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[88] + mi := &file_core_v1_types_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5787,7 +5909,7 @@ func (x *GetStatusResponse_MempoolInfo) String() string { func (*GetStatusResponse_MempoolInfo) ProtoMessage() {} func (x *GetStatusResponse_MempoolInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[88] + mi := &file_core_v1_types_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5843,7 +5965,7 @@ type GetStatusResponse_SnapshotInfo struct { func (x *GetStatusResponse_SnapshotInfo) Reset() { *x = GetStatusResponse_SnapshotInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[89] + mi := &file_core_v1_types_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5856,7 +5978,7 @@ func (x *GetStatusResponse_SnapshotInfo) String() string { func (*GetStatusResponse_SnapshotInfo) ProtoMessage() {} func (x *GetStatusResponse_SnapshotInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[89] + mi := &file_core_v1_types_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5897,7 +6019,7 @@ type GetStatusResponse_PeerInfo struct { func (x *GetStatusResponse_PeerInfo) Reset() { *x = GetStatusResponse_PeerInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[90] + mi := &file_core_v1_types_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5910,7 +6032,7 @@ func (x *GetStatusResponse_PeerInfo) String() string { func (*GetStatusResponse_PeerInfo) ProtoMessage() {} func (x *GetStatusResponse_PeerInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[90] + mi := &file_core_v1_types_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5949,7 +6071,7 @@ type GetStatusResponse_StorageInfo struct { func (x *GetStatusResponse_StorageInfo) Reset() { *x = GetStatusResponse_StorageInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[91] + mi := &file_core_v1_types_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5962,7 +6084,7 @@ func (x *GetStatusResponse_StorageInfo) String() string { func (*GetStatusResponse_StorageInfo) ProtoMessage() {} func (x *GetStatusResponse_StorageInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[91] + mi := &file_core_v1_types_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6035,7 +6157,7 @@ type GetStatusResponse_ProcessInfo_ProcessStateInfo struct { func (x *GetStatusResponse_ProcessInfo_ProcessStateInfo) Reset() { *x = GetStatusResponse_ProcessInfo_ProcessStateInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[92] + mi := &file_core_v1_types_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6048,7 +6170,7 @@ func (x *GetStatusResponse_ProcessInfo_ProcessStateInfo) String() string { func (*GetStatusResponse_ProcessInfo_ProcessStateInfo) ProtoMessage() {} func (x *GetStatusResponse_ProcessInfo_ProcessStateInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[92] + mi := &file_core_v1_types_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6115,7 +6237,7 @@ type GetStatusResponse_SyncInfo_StateSyncInfo struct { func (x *GetStatusResponse_SyncInfo_StateSyncInfo) Reset() { *x = GetStatusResponse_SyncInfo_StateSyncInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[93] + mi := &file_core_v1_types_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6128,7 +6250,7 @@ func (x *GetStatusResponse_SyncInfo_StateSyncInfo) String() string { func (*GetStatusResponse_SyncInfo_StateSyncInfo) ProtoMessage() {} func (x *GetStatusResponse_SyncInfo_StateSyncInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[93] + mi := &file_core_v1_types_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6202,7 +6324,7 @@ type GetStatusResponse_SyncInfo_BlockSyncInfo struct { func (x *GetStatusResponse_SyncInfo_BlockSyncInfo) Reset() { *x = GetStatusResponse_SyncInfo_BlockSyncInfo{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[94] + mi := &file_core_v1_types_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6215,7 +6337,7 @@ func (x *GetStatusResponse_SyncInfo_BlockSyncInfo) String() string { func (*GetStatusResponse_SyncInfo_BlockSyncInfo) ProtoMessage() {} func (x *GetStatusResponse_SyncInfo_BlockSyncInfo) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[94] + mi := &file_core_v1_types_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6294,7 +6416,7 @@ type GetStatusResponse_PeerInfo_Peer struct { func (x *GetStatusResponse_PeerInfo_Peer) Reset() { *x = GetStatusResponse_PeerInfo_Peer{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[95] + mi := &file_core_v1_types_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6307,7 +6429,7 @@ func (x *GetStatusResponse_PeerInfo_Peer) String() string { func (*GetStatusResponse_PeerInfo_Peer) ProtoMessage() {} func (x *GetStatusResponse_PeerInfo_Peer) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[95] + mi := &file_core_v1_types_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6418,7 +6540,7 @@ type GetStreamURLsResponse_EntityStreamURLs struct { func (x *GetStreamURLsResponse_EntityStreamURLs) Reset() { *x = GetStreamURLsResponse_EntityStreamURLs{} if protoimpl.UnsafeEnabled { - mi := &file_core_v1_types_proto_msgTypes[97] + mi := &file_core_v1_types_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6431,7 +6553,7 @@ func (x *GetStreamURLsResponse_EntityStreamURLs) String() string { func (*GetStreamURLsResponse_EntityStreamURLs) ProtoMessage() {} func (x *GetStreamURLsResponse_EntityStreamURLs) ProtoReflect() protoreflect.Message { - mi := &file_core_v1_types_proto_msgTypes[97] + mi := &file_core_v1_types_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6444,7 +6566,7 @@ func (x *GetStreamURLsResponse_EntityStreamURLs) ProtoReflect() protoreflect.Mes // Deprecated: Use GetStreamURLsResponse_EntityStreamURLs.ProtoReflect.Descriptor instead. func (*GetStreamURLsResponse_EntityStreamURLs) Descriptor() ([]byte, []int) { - return file_core_v1_types_proto_rawDescGZIP(), []int{75, 0} + return file_core_v1_types_proto_rawDescGZIP(), []int{76, 0} } func (x *GetStreamURLsResponse_EntityStreamURLs) GetEntityType() string { @@ -6959,7 +7081,7 @@ var file_core_v1_types_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x32, 0x22, 0xdb, 0x06, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x74, 0x69, 0x6f, 0x6e, 0x76, 0x32, 0x22, 0xbc, 0x07, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, @@ -7012,444 +7134,465 @@ var file_core_v1_types_proto_rawDesc = []byte{ 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0xf2, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x36, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x6c, 0x61, - 0x79, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, - 0x6b, 0x50, 0x6c, 0x61, 0x79, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x22, 0xdc, 0x01, 0x0a, - 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x65, - 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x65, 0x74, 0x68, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x74, 0x68, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, - 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x73, 0x70, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, - 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x22, 0xdd, 0x01, 0x0a, 0x09, - 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x38, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, - 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xb5, 0x01, 0x0a, 0x09, - 0x53, 0x6c, 0x61, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 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, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x65, 0x6e, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, - 0x64, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6c, 0x61, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x07, 0x72, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x73, 0x22, 0x59, 0x0a, 0x0d, 0x53, 0x6c, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, - 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6e, 0x75, 0x6d, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x22, 0xeb, - 0x01, 0x0a, 0x12, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x62, 0x0a, 0x22, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, - 0x76, 0x69, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, - 0x22, 0xa6, 0x01, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x70, - 0x72, 0x6f, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x10, - 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, - 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, - 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x48, 0x0a, 0x18, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x72, - 0x6f, 0x6f, 0x66, 0x22, 0xef, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x16, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe8, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x15, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5e, 0x0a, - 0x18, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, - 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x06, 0x0a, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x9b, 0x02, 0x0a, 0x15, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x13, 0x0a, 0x05, 0x73, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x73, 0x70, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x74, 0x68, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, - 0x69, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, - 0x69, 0x6e, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, - 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, - 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x6e, 0x61, 0x70, 0x73, - 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x09, - 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x73, 0x6e, 0x61, 0x70, - 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x7a, 0x0a, 0x10, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x5f, 0x0a, 0x17, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x5f, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0xf3, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4c, 0x65, + 0x67, 0x61, 0x63, 0x79, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, + 0x15, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x36, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x6c, + 0x61, 0x79, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, + 0x63, 0x6b, 0x50, 0x6c, 0x61, 0x79, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x22, 0xdc, 0x01, + 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, + 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, + 0x0a, 0x09, 0x65, 0x74, 0x68, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x65, 0x74, 0x68, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x73, 0x70, 0x5f, 0x69, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x49, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x22, 0xdd, 0x01, 0x0a, + 0x09, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x38, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xb5, 0x01, 0x0a, + 0x09, 0x53, 0x6c, 0x61, 0x52, 0x6f, 0x6c, 0x6c, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 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, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x65, + 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x45, + 0x6e, 0x64, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6c, + 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x07, 0x72, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x73, 0x22, 0x59, 0x0a, 0x0d, 0x53, 0x6c, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x70, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6e, 0x75, + 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x22, + 0xeb, 0x01, 0x0a, 0x12, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0xf4, 0x01, + 0x0a, 0x1b, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4c, 0x65, + 0x67, 0x61, 0x63, 0x79, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, + 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x14, + 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x62, 0x0a, 0x22, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x4d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, + 0x6d, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xa6, 0x01, 0x0a, 0x0c, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, - 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, - 0x64, 0x22, 0x3e, 0x0a, 0x0e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x97, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, - 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x3c, 0x0a, 0x11, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x4a, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x34, 0x0a, 0x07, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, - 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x77, 0x61, 0x72, 0x64, 0x73, 0x22, 0xbb, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, - 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x63, - 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x74, 0x68, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, - 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x77, - 0x61, 0x72, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x27, - 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x63, 0x69, 0x6d, - 0x61, 0x6c, 0x73, 0x22, 0x56, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc7, 0x01, 0x0a, 0x13, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, - 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1e, 0x0a, - 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x53, 0x4c, 0x41, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x53, 0x4c, 0x41, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4e, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, - 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, - 0x68, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, - 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x5c, - 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, - 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, - 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x1c, - 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0c, - 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x45, 0x52, 0x4e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x22, 0x43, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x52, 0x4e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x03, 0x65, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x4e, 0x65, 0x77, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x03, 0x65, 0x72, 0x6e, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x61, 0x72, - 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x22, 0x3d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x05, 0x70, 0x61, 0x72, - 0x74, 0x79, 0x22, 0x2e, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x22, 0x49, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x64, - 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x2d, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x45, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, - 0x61, 0x73, 0x65, 0x22, 0x2a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x61, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, - 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x65, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x64, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x61, 0x6c, 0x52, 0x04, 0x64, 0x65, 0x61, 0x6c, 0x22, 0x2a, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x4d, 0x45, 0x41, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x40, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x45, 0x41, - 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x6d, 0x65, 0x61, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x04, 0x6d, 0x65, 0x61, 0x64, 0x22, 0x29, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, - 0x49, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x22, 0x3c, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x49, 0x45, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x70, 0x69, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x50, 0x69, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x70, 0x69, - 0x65, 0x22, 0x7d, 0x0a, 0x0d, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0xe8, 0x07, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xe9, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x06, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0xef, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, - 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x11, 0x63, 0x6c, - 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x10, - 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x13, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0x7a, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, - 0x72, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x15, - 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x64, 0x65, 0x61, - 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x44, - 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x74, 0x78, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, - 0x68, 0x61, 0x73, 0x68, 0x22, 0xde, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x48, 0x0a, 0x18, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xef, 0x01, 0x0a, 0x0b, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x16, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x15, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5e, 0x0a, 0x18, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x17, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x9b, 0x02, + 0x0a, 0x15, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x73, 0x70, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x49, 0x64, 0x12, 0x1b, + 0x0a, 0x09, 0x65, 0x74, 0x68, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x65, 0x74, 0x68, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x6f, 0x6d, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x6f, 0x77, + 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x12, + 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x17, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x65, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, + 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, + 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x64, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x52, 0x09, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x22, 0x7a, 0x0a, + 0x10, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x3e, 0x0a, 0x0e, 0x43, 0x6c, 0x61, + 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x97, 0x01, 0x0a, 0x06, 0x52, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xf3, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x63, 0x69, - 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x74, 0x68, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, - 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, - 0x72, 0x64, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6c, 0x61, - 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x23, 0x0a, 0x0f, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, - 0x22, 0x96, 0x02, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x29, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, - 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x71, 0x0a, 0x16, 0x47, 0x65, 0x74, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, - 0x02, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x22, 0x8d, 0x01, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x22, 0x87, 0x03, 0x0a, - 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x12, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, - 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x72, 0x6c, 0x73, 0x1a, 0x93, 0x01, 0x0a, 0x10, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, - 0x72, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x65, 0x72, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x72, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x1a, 0x74, 0x0a, 0x15, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x55, 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x52, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x22, 0x3c, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x22, 0x4a, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x22, 0xbb, 0x02, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, + 0x15, 0x65, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x74, + 0x68, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x77, 0x61, 0x72, + 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, + 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x63, + 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x44, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x22, 0x56, 0x0a, 0x1c, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xc7, 0x01, 0x0a, 0x13, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x52, 0x65, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, + 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x64, 0x53, + 0x4c, 0x41, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x65, + 0x64, 0x53, 0x4c, 0x41, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4e, 0x0a, + 0x1a, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x57, 0x0a, + 0x1b, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x5c, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, + 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x45, 0x52, 0x4e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x43, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x45, 0x52, 0x4e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x03, 0x65, + 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x65, 0x72, 0x6e, 0x22, 0x2b, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x3d, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x29, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, + 0x72, 0x74, 0x79, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x79, 0x22, 0x2e, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x13, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x64, 0x64, + 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, + 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x2a, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x64, 0x65, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x61, 0x6c, 0x52, 0x04, 0x64, 0x65, + 0x61, 0x6c, 0x22, 0x2a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4d, 0x45, 0x41, 0x44, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x40, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x45, 0x41, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x6d, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x64, 0x64, 0x65, 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, + 0x65, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6d, 0x65, 0x61, 0x64, + 0x22, 0x29, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x49, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x3c, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x50, 0x49, 0x45, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, + 0x03, 0x70, 0x69, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x64, 0x65, + 0x78, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x69, 0x65, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x70, 0x69, 0x65, 0x22, 0x7d, 0x0a, 0x0d, 0x52, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, + 0x72, 0x64, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x06, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x48, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x08, + 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xef, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x7a, 0x0a, 0x0c, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x13, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x44, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x78, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x68, 0x61, 0x73, 0x68, 0x22, 0xde, 0x01, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xf3, 0x01, + 0x0a, 0x1a, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x32, 0x0a, 0x15, + 0x65, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x74, 0x68, + 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x77, 0x61, + 0x72, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x22, 0x23, 0x0a, 0x0f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x22, 0x96, 0x02, 0x0a, 0x0a, 0x46, 0x69, 0x6c, + 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x43, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x2f, 0x0a, 0x13, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x71, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, + 0x4c, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x41, 0x74, 0x22, 0x8d, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x41, 0x74, 0x22, 0x87, 0x03, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, + 0x0a, 0x12, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, + 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, - 0x64, 0x22, 0xa5, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, - 0x79, 0x43, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x78, - 0x69, 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, - 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, - 0x5f, 0x63, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x69, 0x64, 0x22, 0x73, 0x0a, 0x21, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x77, 0x61, - 0x72, 0x64, 0x73, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, - 0x73, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x5c, - 0x0a, 0x22, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2b, 0x0a, 0x13, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x05, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x22, 0x3c, 0x0a, 0x14, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x24, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x2f, - 0x67, 0x6f, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x10, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x72, + 0x6c, 0x73, 0x1a, 0x93, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x72, 0x6e, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x72, + 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x74, 0x0a, 0x15, 0x45, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x52, 0x4c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, + 0x52, 0x4c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, 0x44, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x63, 0x69, 0x64, 0x22, 0xa5, 0x01, 0x0a, 0x16, 0x47, 0x65, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x79, 0x43, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, + 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x69, + 0x64, 0x22, 0x73, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x53, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x5f, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x14, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x5c, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x52, 0x65, 0x77, + 0x61, 0x72, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2b, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x61, 0x6e, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x61, 0x6e, 0x6f, + 0x6e, 0x22, 0x3c, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x42, + 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x70, + 0x65, 0x6e, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, + 0x75, 0x64, 0x69, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7465,7 +7608,7 @@ func file_core_v1_types_proto_rawDescGZIP() []byte { } var file_core_v1_types_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_core_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 99) +var file_core_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 100) var file_core_v1_types_proto_goTypes = []interface{}{ (GetStatusResponse_ProcessInfo_ProcessState)(0), // 0: core.v1.GetStatusResponse.ProcessInfo.ProcessState (GetStatusResponse_SyncInfo_StateSyncInfo_Phase)(0), // 1: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.Phase @@ -7500,184 +7643,186 @@ var file_core_v1_types_proto_goTypes = []interface{}{ (*SlaRollup)(nil), // 30: core.v1.SlaRollup (*SlaNodeReport)(nil), // 31: core.v1.SlaNodeReport (*ManageEntityLegacy)(nil), // 32: core.v1.ManageEntityLegacy - (*ValidatorMisbehaviorDeregistration)(nil), // 33: core.v1.ValidatorMisbehaviorDeregistration - (*StorageProof)(nil), // 34: core.v1.StorageProof - (*StorageProofVerification)(nil), // 35: core.v1.StorageProofVerification - (*Attestation)(nil), // 36: core.v1.Attestation - (*ValidatorRegistration)(nil), // 37: core.v1.ValidatorRegistration - (*ValidatorDeregistration)(nil), // 38: core.v1.ValidatorDeregistration - (*GetStoredSnapshotsRequest)(nil), // 39: core.v1.GetStoredSnapshotsRequest - (*GetStoredSnapshotsResponse)(nil), // 40: core.v1.GetStoredSnapshotsResponse - (*SnapshotMetadata)(nil), // 41: core.v1.SnapshotMetadata - (*ClaimAuthority)(nil), // 42: core.v1.ClaimAuthority - (*Reward)(nil), // 43: core.v1.Reward - (*GetRewardsRequest)(nil), // 44: core.v1.GetRewardsRequest - (*GetRewardsResponse)(nil), // 45: core.v1.GetRewardsResponse - (*GetRewardAttestationRequest)(nil), // 46: core.v1.GetRewardAttestationRequest - (*GetRewardAttestationResponse)(nil), // 47: core.v1.GetRewardAttestationResponse - (*SlashRecommendation)(nil), // 48: core.v1.SlashRecommendation - (*GetSlashAttestationRequest)(nil), // 49: core.v1.GetSlashAttestationRequest - (*GetSlashAttestationResponse)(nil), // 50: core.v1.GetSlashAttestationResponse - (*GetSlashAttestationsRequest)(nil), // 51: core.v1.GetSlashAttestationsRequest - (*GetSlashAttestationsResponse)(nil), // 52: core.v1.GetSlashAttestationsResponse - (*GetERNRequest)(nil), // 53: core.v1.GetERNRequest - (*GetERNResponse)(nil), // 54: core.v1.GetERNResponse - (*GetPartyRequest)(nil), // 55: core.v1.GetPartyRequest - (*GetPartyResponse)(nil), // 56: core.v1.GetPartyResponse - (*GetResourceRequest)(nil), // 57: core.v1.GetResourceRequest - (*GetResourceResponse)(nil), // 58: core.v1.GetResourceResponse - (*GetReleaseRequest)(nil), // 59: core.v1.GetReleaseRequest - (*GetReleaseResponse)(nil), // 60: core.v1.GetReleaseResponse - (*GetDealRequest)(nil), // 61: core.v1.GetDealRequest - (*GetDealResponse)(nil), // 62: core.v1.GetDealResponse - (*GetMEADRequest)(nil), // 63: core.v1.GetMEADRequest - (*GetMEADResponse)(nil), // 64: core.v1.GetMEADResponse - (*GetPIERequest)(nil), // 65: core.v1.GetPIERequest - (*GetPIEResponse)(nil), // 66: core.v1.GetPIEResponse - (*RewardMessage)(nil), // 67: core.v1.RewardMessage - (*CreateReward)(nil), // 68: core.v1.CreateReward - (*DeleteReward)(nil), // 69: core.v1.DeleteReward - (*GetRewardRequest)(nil), // 70: core.v1.GetRewardRequest - (*GetRewardResponse)(nil), // 71: core.v1.GetRewardResponse - (*RewardAttestationSignature)(nil), // 72: core.v1.RewardAttestationSignature - (*UploadSignature)(nil), // 73: core.v1.UploadSignature - (*FileUpload)(nil), // 74: core.v1.FileUpload - (*GetStreamURLsSignature)(nil), // 75: core.v1.GetStreamURLsSignature - (*GetStreamURLsRequest)(nil), // 76: core.v1.GetStreamURLsRequest - (*GetStreamURLsResponse)(nil), // 77: core.v1.GetStreamURLsResponse - (*GetUploadByCIDRequest)(nil), // 78: core.v1.GetUploadByCIDRequest - (*GetUploadByCIDResponse)(nil), // 79: core.v1.GetUploadByCIDResponse - (*GetRewardSenderAttestationRequest)(nil), // 80: core.v1.GetRewardSenderAttestationRequest - (*GetRewardSenderAttestationResponse)(nil), // 81: core.v1.GetRewardSenderAttestationResponse - (*StreamBlocksRequest)(nil), // 82: core.v1.StreamBlocksRequest - (*StreamBlocksResponse)(nil), // 83: core.v1.StreamBlocksResponse - (*GetStatusResponse_ProcessInfo)(nil), // 84: core.v1.GetStatusResponse.ProcessInfo - (*GetStatusResponse_NodeInfo)(nil), // 85: core.v1.GetStatusResponse.NodeInfo - (*GetStatusResponse_ChainInfo)(nil), // 86: core.v1.GetStatusResponse.ChainInfo - (*GetStatusResponse_SyncInfo)(nil), // 87: core.v1.GetStatusResponse.SyncInfo - (*GetStatusResponse_PruningInfo)(nil), // 88: core.v1.GetStatusResponse.PruningInfo - (*GetStatusResponse_ResourceInfo)(nil), // 89: core.v1.GetStatusResponse.ResourceInfo - (*GetStatusResponse_MempoolInfo)(nil), // 90: core.v1.GetStatusResponse.MempoolInfo - (*GetStatusResponse_SnapshotInfo)(nil), // 91: core.v1.GetStatusResponse.SnapshotInfo - (*GetStatusResponse_PeerInfo)(nil), // 92: core.v1.GetStatusResponse.PeerInfo - (*GetStatusResponse_StorageInfo)(nil), // 93: core.v1.GetStatusResponse.StorageInfo - (*GetStatusResponse_ProcessInfo_ProcessStateInfo)(nil), // 94: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - (*GetStatusResponse_SyncInfo_StateSyncInfo)(nil), // 95: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo - (*GetStatusResponse_SyncInfo_BlockSyncInfo)(nil), // 96: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo - (*GetStatusResponse_PeerInfo_Peer)(nil), // 97: core.v1.GetStatusResponse.PeerInfo.Peer - nil, // 98: core.v1.GetBlocksResponse.BlocksEntry - (*GetStreamURLsResponse_EntityStreamURLs)(nil), // 99: core.v1.GetStreamURLsResponse.EntityStreamURLs - nil, // 100: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry - (*v1beta1.Transaction)(nil), // 101: core.v1beta1.Transaction - (*v1beta1.TransactionReceipt)(nil), // 102: core.v1beta1.TransactionReceipt - (*timestamppb.Timestamp)(nil), // 103: google.protobuf.Timestamp - (*v1beta11.NewReleaseMessage)(nil), // 104: ddex.v1beta1.NewReleaseMessage - (*v1beta11.Party)(nil), // 105: ddex.v1beta1.Party - (*v1beta11.Resource)(nil), // 106: ddex.v1beta1.Resource - (*v1beta11.Release)(nil), // 107: ddex.v1beta1.Release - (*v1beta11.Deal)(nil), // 108: ddex.v1beta1.Deal - (*v1beta11.MeadMessage)(nil), // 109: ddex.v1beta1.MeadMessage - (*v1beta11.PieMessage)(nil), // 110: ddex.v1beta1.PieMessage + (*ManageEntityLegacyMigration)(nil), // 33: core.v1.ManageEntityLegacyMigration + (*ValidatorMisbehaviorDeregistration)(nil), // 34: core.v1.ValidatorMisbehaviorDeregistration + (*StorageProof)(nil), // 35: core.v1.StorageProof + (*StorageProofVerification)(nil), // 36: core.v1.StorageProofVerification + (*Attestation)(nil), // 37: core.v1.Attestation + (*ValidatorRegistration)(nil), // 38: core.v1.ValidatorRegistration + (*ValidatorDeregistration)(nil), // 39: core.v1.ValidatorDeregistration + (*GetStoredSnapshotsRequest)(nil), // 40: core.v1.GetStoredSnapshotsRequest + (*GetStoredSnapshotsResponse)(nil), // 41: core.v1.GetStoredSnapshotsResponse + (*SnapshotMetadata)(nil), // 42: core.v1.SnapshotMetadata + (*ClaimAuthority)(nil), // 43: core.v1.ClaimAuthority + (*Reward)(nil), // 44: core.v1.Reward + (*GetRewardsRequest)(nil), // 45: core.v1.GetRewardsRequest + (*GetRewardsResponse)(nil), // 46: core.v1.GetRewardsResponse + (*GetRewardAttestationRequest)(nil), // 47: core.v1.GetRewardAttestationRequest + (*GetRewardAttestationResponse)(nil), // 48: core.v1.GetRewardAttestationResponse + (*SlashRecommendation)(nil), // 49: core.v1.SlashRecommendation + (*GetSlashAttestationRequest)(nil), // 50: core.v1.GetSlashAttestationRequest + (*GetSlashAttestationResponse)(nil), // 51: core.v1.GetSlashAttestationResponse + (*GetSlashAttestationsRequest)(nil), // 52: core.v1.GetSlashAttestationsRequest + (*GetSlashAttestationsResponse)(nil), // 53: core.v1.GetSlashAttestationsResponse + (*GetERNRequest)(nil), // 54: core.v1.GetERNRequest + (*GetERNResponse)(nil), // 55: core.v1.GetERNResponse + (*GetPartyRequest)(nil), // 56: core.v1.GetPartyRequest + (*GetPartyResponse)(nil), // 57: core.v1.GetPartyResponse + (*GetResourceRequest)(nil), // 58: core.v1.GetResourceRequest + (*GetResourceResponse)(nil), // 59: core.v1.GetResourceResponse + (*GetReleaseRequest)(nil), // 60: core.v1.GetReleaseRequest + (*GetReleaseResponse)(nil), // 61: core.v1.GetReleaseResponse + (*GetDealRequest)(nil), // 62: core.v1.GetDealRequest + (*GetDealResponse)(nil), // 63: core.v1.GetDealResponse + (*GetMEADRequest)(nil), // 64: core.v1.GetMEADRequest + (*GetMEADResponse)(nil), // 65: core.v1.GetMEADResponse + (*GetPIERequest)(nil), // 66: core.v1.GetPIERequest + (*GetPIEResponse)(nil), // 67: core.v1.GetPIEResponse + (*RewardMessage)(nil), // 68: core.v1.RewardMessage + (*CreateReward)(nil), // 69: core.v1.CreateReward + (*DeleteReward)(nil), // 70: core.v1.DeleteReward + (*GetRewardRequest)(nil), // 71: core.v1.GetRewardRequest + (*GetRewardResponse)(nil), // 72: core.v1.GetRewardResponse + (*RewardAttestationSignature)(nil), // 73: core.v1.RewardAttestationSignature + (*UploadSignature)(nil), // 74: core.v1.UploadSignature + (*FileUpload)(nil), // 75: core.v1.FileUpload + (*GetStreamURLsSignature)(nil), // 76: core.v1.GetStreamURLsSignature + (*GetStreamURLsRequest)(nil), // 77: core.v1.GetStreamURLsRequest + (*GetStreamURLsResponse)(nil), // 78: core.v1.GetStreamURLsResponse + (*GetUploadByCIDRequest)(nil), // 79: core.v1.GetUploadByCIDRequest + (*GetUploadByCIDResponse)(nil), // 80: core.v1.GetUploadByCIDResponse + (*GetRewardSenderAttestationRequest)(nil), // 81: core.v1.GetRewardSenderAttestationRequest + (*GetRewardSenderAttestationResponse)(nil), // 82: core.v1.GetRewardSenderAttestationResponse + (*StreamBlocksRequest)(nil), // 83: core.v1.StreamBlocksRequest + (*StreamBlocksResponse)(nil), // 84: core.v1.StreamBlocksResponse + (*GetStatusResponse_ProcessInfo)(nil), // 85: core.v1.GetStatusResponse.ProcessInfo + (*GetStatusResponse_NodeInfo)(nil), // 86: core.v1.GetStatusResponse.NodeInfo + (*GetStatusResponse_ChainInfo)(nil), // 87: core.v1.GetStatusResponse.ChainInfo + (*GetStatusResponse_SyncInfo)(nil), // 88: core.v1.GetStatusResponse.SyncInfo + (*GetStatusResponse_PruningInfo)(nil), // 89: core.v1.GetStatusResponse.PruningInfo + (*GetStatusResponse_ResourceInfo)(nil), // 90: core.v1.GetStatusResponse.ResourceInfo + (*GetStatusResponse_MempoolInfo)(nil), // 91: core.v1.GetStatusResponse.MempoolInfo + (*GetStatusResponse_SnapshotInfo)(nil), // 92: core.v1.GetStatusResponse.SnapshotInfo + (*GetStatusResponse_PeerInfo)(nil), // 93: core.v1.GetStatusResponse.PeerInfo + (*GetStatusResponse_StorageInfo)(nil), // 94: core.v1.GetStatusResponse.StorageInfo + (*GetStatusResponse_ProcessInfo_ProcessStateInfo)(nil), // 95: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + (*GetStatusResponse_SyncInfo_StateSyncInfo)(nil), // 96: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo + (*GetStatusResponse_SyncInfo_BlockSyncInfo)(nil), // 97: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo + (*GetStatusResponse_PeerInfo_Peer)(nil), // 98: core.v1.GetStatusResponse.PeerInfo.Peer + nil, // 99: core.v1.GetBlocksResponse.BlocksEntry + (*GetStreamURLsResponse_EntityStreamURLs)(nil), // 100: core.v1.GetStreamURLsResponse.EntityStreamURLs + nil, // 101: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry + (*v1beta1.Transaction)(nil), // 102: core.v1beta1.Transaction + (*v1beta1.TransactionReceipt)(nil), // 103: core.v1beta1.TransactionReceipt + (*timestamppb.Timestamp)(nil), // 104: google.protobuf.Timestamp + (*v1beta11.NewReleaseMessage)(nil), // 105: ddex.v1beta1.NewReleaseMessage + (*v1beta11.Party)(nil), // 106: ddex.v1beta1.Party + (*v1beta11.Resource)(nil), // 107: ddex.v1beta1.Resource + (*v1beta11.Release)(nil), // 108: ddex.v1beta1.Release + (*v1beta11.Deal)(nil), // 109: ddex.v1beta1.Deal + (*v1beta11.MeadMessage)(nil), // 110: ddex.v1beta1.MeadMessage + (*v1beta11.PieMessage)(nil), // 111: ddex.v1beta1.PieMessage } var file_core_v1_types_proto_depIdxs = []int32{ - 85, // 0: core.v1.GetStatusResponse.node_info:type_name -> core.v1.GetStatusResponse.NodeInfo - 86, // 1: core.v1.GetStatusResponse.chain_info:type_name -> core.v1.GetStatusResponse.ChainInfo - 87, // 2: core.v1.GetStatusResponse.sync_info:type_name -> core.v1.GetStatusResponse.SyncInfo - 88, // 3: core.v1.GetStatusResponse.pruning_info:type_name -> core.v1.GetStatusResponse.PruningInfo - 89, // 4: core.v1.GetStatusResponse.resource_info:type_name -> core.v1.GetStatusResponse.ResourceInfo - 90, // 5: core.v1.GetStatusResponse.mempool_info:type_name -> core.v1.GetStatusResponse.MempoolInfo - 92, // 6: core.v1.GetStatusResponse.peers:type_name -> core.v1.GetStatusResponse.PeerInfo - 91, // 7: core.v1.GetStatusResponse.snapshot_info:type_name -> core.v1.GetStatusResponse.SnapshotInfo - 84, // 8: core.v1.GetStatusResponse.process_info:type_name -> core.v1.GetStatusResponse.ProcessInfo - 93, // 9: core.v1.GetStatusResponse.storage_info:type_name -> core.v1.GetStatusResponse.StorageInfo + 86, // 0: core.v1.GetStatusResponse.node_info:type_name -> core.v1.GetStatusResponse.NodeInfo + 87, // 1: core.v1.GetStatusResponse.chain_info:type_name -> core.v1.GetStatusResponse.ChainInfo + 88, // 2: core.v1.GetStatusResponse.sync_info:type_name -> core.v1.GetStatusResponse.SyncInfo + 89, // 3: core.v1.GetStatusResponse.pruning_info:type_name -> core.v1.GetStatusResponse.PruningInfo + 90, // 4: core.v1.GetStatusResponse.resource_info:type_name -> core.v1.GetStatusResponse.ResourceInfo + 91, // 5: core.v1.GetStatusResponse.mempool_info:type_name -> core.v1.GetStatusResponse.MempoolInfo + 93, // 6: core.v1.GetStatusResponse.peers:type_name -> core.v1.GetStatusResponse.PeerInfo + 92, // 7: core.v1.GetStatusResponse.snapshot_info:type_name -> core.v1.GetStatusResponse.SnapshotInfo + 85, // 8: core.v1.GetStatusResponse.process_info:type_name -> core.v1.GetStatusResponse.ProcessInfo + 94, // 9: core.v1.GetStatusResponse.storage_info:type_name -> core.v1.GetStatusResponse.StorageInfo 24, // 10: core.v1.GetBlockResponse.block:type_name -> core.v1.Block - 98, // 11: core.v1.GetBlocksResponse.blocks:type_name -> core.v1.GetBlocksResponse.BlocksEntry + 99, // 11: core.v1.GetBlocksResponse.blocks:type_name -> core.v1.GetBlocksResponse.BlocksEntry 25, // 12: core.v1.GetTransactionResponse.transaction:type_name -> core.v1.Transaction 26, // 13: core.v1.SendTransactionRequest.transaction:type_name -> core.v1.SignedTransaction - 101, // 14: core.v1.SendTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction + 102, // 14: core.v1.SendTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction 25, // 15: core.v1.SendTransactionResponse.transaction:type_name -> core.v1.Transaction - 102, // 16: core.v1.SendTransactionResponse.transaction_receipt:type_name -> core.v1beta1.TransactionReceipt + 103, // 16: core.v1.SendTransactionResponse.transaction_receipt:type_name -> core.v1beta1.TransactionReceipt 26, // 17: core.v1.ForwardTransactionRequest.transaction:type_name -> core.v1.SignedTransaction - 101, // 18: core.v1.ForwardTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction - 37, // 19: core.v1.GetRegistrationAttestationRequest.registration:type_name -> core.v1.ValidatorRegistration - 37, // 20: core.v1.GetRegistrationAttestationResponse.registration:type_name -> core.v1.ValidatorRegistration - 38, // 21: core.v1.GetDeregistrationAttestationRequest.deregistration:type_name -> core.v1.ValidatorDeregistration - 38, // 22: core.v1.GetDeregistrationAttestationResponse.deregistration:type_name -> core.v1.ValidatorDeregistration - 103, // 23: core.v1.Block.timestamp:type_name -> google.protobuf.Timestamp + 102, // 18: core.v1.ForwardTransactionRequest.transactionv2:type_name -> core.v1beta1.Transaction + 38, // 19: core.v1.GetRegistrationAttestationRequest.registration:type_name -> core.v1.ValidatorRegistration + 38, // 20: core.v1.GetRegistrationAttestationResponse.registration:type_name -> core.v1.ValidatorRegistration + 39, // 21: core.v1.GetDeregistrationAttestationRequest.deregistration:type_name -> core.v1.ValidatorDeregistration + 39, // 22: core.v1.GetDeregistrationAttestationResponse.deregistration:type_name -> core.v1.ValidatorDeregistration + 104, // 23: core.v1.Block.timestamp:type_name -> google.protobuf.Timestamp 25, // 24: core.v1.Block.transactions:type_name -> core.v1.Transaction 26, // 25: core.v1.Transaction.transaction:type_name -> core.v1.SignedTransaction - 103, // 26: core.v1.Transaction.timestamp:type_name -> google.protobuf.Timestamp - 101, // 27: core.v1.Transaction.transactionv2:type_name -> core.v1beta1.Transaction + 104, // 26: core.v1.Transaction.timestamp:type_name -> google.protobuf.Timestamp + 102, // 27: core.v1.Transaction.transactionv2:type_name -> core.v1beta1.Transaction 27, // 28: core.v1.SignedTransaction.plays:type_name -> core.v1.TrackPlays 28, // 29: core.v1.SignedTransaction.validator_registration:type_name -> core.v1.ValidatorRegistrationLegacy 30, // 30: core.v1.SignedTransaction.sla_rollup:type_name -> core.v1.SlaRollup 32, // 31: core.v1.SignedTransaction.manage_entity:type_name -> core.v1.ManageEntityLegacy - 33, // 32: core.v1.SignedTransaction.validator_deregistration:type_name -> core.v1.ValidatorMisbehaviorDeregistration - 34, // 33: core.v1.SignedTransaction.storage_proof:type_name -> core.v1.StorageProof - 35, // 34: core.v1.SignedTransaction.storage_proof_verification:type_name -> core.v1.StorageProofVerification - 36, // 35: core.v1.SignedTransaction.attestation:type_name -> core.v1.Attestation - 104, // 36: core.v1.SignedTransaction.release:type_name -> ddex.v1beta1.NewReleaseMessage - 67, // 37: core.v1.SignedTransaction.reward:type_name -> core.v1.RewardMessage - 74, // 38: core.v1.SignedTransaction.file_upload:type_name -> core.v1.FileUpload - 29, // 39: core.v1.TrackPlays.plays:type_name -> core.v1.TrackPlay - 103, // 40: core.v1.TrackPlay.timestamp:type_name -> google.protobuf.Timestamp - 103, // 41: core.v1.SlaRollup.timestamp:type_name -> google.protobuf.Timestamp - 31, // 42: core.v1.SlaRollup.reports:type_name -> core.v1.SlaNodeReport - 37, // 43: core.v1.Attestation.validator_registration:type_name -> core.v1.ValidatorRegistration - 38, // 44: core.v1.Attestation.validator_deregistration:type_name -> core.v1.ValidatorDeregistration - 41, // 45: core.v1.GetStoredSnapshotsResponse.snapshots:type_name -> core.v1.SnapshotMetadata - 42, // 46: core.v1.Reward.claim_authorities:type_name -> core.v1.ClaimAuthority - 71, // 47: core.v1.GetRewardsResponse.rewards:type_name -> core.v1.GetRewardResponse - 103, // 48: core.v1.SlashRecommendation.start:type_name -> google.protobuf.Timestamp - 103, // 49: core.v1.SlashRecommendation.end:type_name -> google.protobuf.Timestamp - 48, // 50: core.v1.GetSlashAttestationRequest.data:type_name -> core.v1.SlashRecommendation - 49, // 51: core.v1.GetSlashAttestationsRequest.request:type_name -> core.v1.GetSlashAttestationRequest - 50, // 52: core.v1.GetSlashAttestationsResponse.attestations:type_name -> core.v1.GetSlashAttestationResponse - 104, // 53: core.v1.GetERNResponse.ern:type_name -> ddex.v1beta1.NewReleaseMessage - 105, // 54: core.v1.GetPartyResponse.party:type_name -> ddex.v1beta1.Party - 106, // 55: core.v1.GetResourceResponse.resource:type_name -> ddex.v1beta1.Resource - 107, // 56: core.v1.GetReleaseResponse.release:type_name -> ddex.v1beta1.Release - 108, // 57: core.v1.GetDealResponse.deal:type_name -> ddex.v1beta1.Deal - 109, // 58: core.v1.GetMEADResponse.mead:type_name -> ddex.v1beta1.MeadMessage - 110, // 59: core.v1.GetPIEResponse.pie:type_name -> ddex.v1beta1.PieMessage - 68, // 60: core.v1.RewardMessage.create:type_name -> core.v1.CreateReward - 69, // 61: core.v1.RewardMessage.delete:type_name -> core.v1.DeleteReward - 42, // 62: core.v1.CreateReward.claim_authorities:type_name -> core.v1.ClaimAuthority - 103, // 63: core.v1.GetStreamURLsSignature.expires_at:type_name -> google.protobuf.Timestamp - 103, // 64: core.v1.GetStreamURLsRequest.expires_at:type_name -> google.protobuf.Timestamp - 100, // 65: core.v1.GetStreamURLsResponse.entity_stream_urls:type_name -> core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry - 24, // 66: core.v1.StreamBlocksResponse.block:type_name -> core.v1.Block - 94, // 67: core.v1.GetStatusResponse.ProcessInfo.abci:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 68: core.v1.GetStatusResponse.ProcessInfo.registry_bridge:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 69: core.v1.GetStatusResponse.ProcessInfo.echo_server:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 70: core.v1.GetStatusResponse.ProcessInfo.sync_tasks:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 71: core.v1.GetStatusResponse.ProcessInfo.peer_manager:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 72: core.v1.GetStatusResponse.ProcessInfo.data_companion:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 73: core.v1.GetStatusResponse.ProcessInfo.cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 74: core.v1.GetStatusResponse.ProcessInfo.log_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 75: core.v1.GetStatusResponse.ProcessInfo.state_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 94, // 76: core.v1.GetStatusResponse.ProcessInfo.mempool_cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo - 95, // 77: core.v1.GetStatusResponse.SyncInfo.state_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo - 96, // 78: core.v1.GetStatusResponse.SyncInfo.block_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo - 41, // 79: core.v1.GetStatusResponse.SnapshotInfo.snapshots:type_name -> core.v1.SnapshotMetadata - 97, // 80: core.v1.GetStatusResponse.PeerInfo.peers:type_name -> core.v1.GetStatusResponse.PeerInfo.Peer - 0, // 81: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.state:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessState - 103, // 82: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.started_at:type_name -> google.protobuf.Timestamp - 103, // 83: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.completed_at:type_name -> google.protobuf.Timestamp - 1, // 84: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.phase:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.Phase - 41, // 85: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.snapshot:type_name -> core.v1.SnapshotMetadata - 103, // 86: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.started_at:type_name -> google.protobuf.Timestamp - 103, // 87: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.completed_at:type_name -> google.protobuf.Timestamp - 85, // 88: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.head_source:type_name -> core.v1.GetStatusResponse.NodeInfo - 103, // 89: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.started_at:type_name -> google.protobuf.Timestamp - 103, // 90: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.completed_at:type_name -> google.protobuf.Timestamp - 24, // 91: core.v1.GetBlocksResponse.BlocksEntry.value:type_name -> core.v1.Block - 99, // 92: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry.value:type_name -> core.v1.GetStreamURLsResponse.EntityStreamURLs - 93, // [93:93] is the sub-list for method output_type - 93, // [93:93] is the sub-list for method input_type - 93, // [93:93] is the sub-list for extension type_name - 93, // [93:93] is the sub-list for extension extendee - 0, // [0:93] is the sub-list for field type_name + 34, // 32: core.v1.SignedTransaction.validator_deregistration:type_name -> core.v1.ValidatorMisbehaviorDeregistration + 35, // 33: core.v1.SignedTransaction.storage_proof:type_name -> core.v1.StorageProof + 36, // 34: core.v1.SignedTransaction.storage_proof_verification:type_name -> core.v1.StorageProofVerification + 37, // 35: core.v1.SignedTransaction.attestation:type_name -> core.v1.Attestation + 105, // 36: core.v1.SignedTransaction.release:type_name -> ddex.v1beta1.NewReleaseMessage + 68, // 37: core.v1.SignedTransaction.reward:type_name -> core.v1.RewardMessage + 75, // 38: core.v1.SignedTransaction.file_upload:type_name -> core.v1.FileUpload + 33, // 39: core.v1.SignedTransaction.manage_entity_migration:type_name -> core.v1.ManageEntityLegacyMigration + 29, // 40: core.v1.TrackPlays.plays:type_name -> core.v1.TrackPlay + 104, // 41: core.v1.TrackPlay.timestamp:type_name -> google.protobuf.Timestamp + 104, // 42: core.v1.SlaRollup.timestamp:type_name -> google.protobuf.Timestamp + 31, // 43: core.v1.SlaRollup.reports:type_name -> core.v1.SlaNodeReport + 38, // 44: core.v1.Attestation.validator_registration:type_name -> core.v1.ValidatorRegistration + 39, // 45: core.v1.Attestation.validator_deregistration:type_name -> core.v1.ValidatorDeregistration + 42, // 46: core.v1.GetStoredSnapshotsResponse.snapshots:type_name -> core.v1.SnapshotMetadata + 43, // 47: core.v1.Reward.claim_authorities:type_name -> core.v1.ClaimAuthority + 72, // 48: core.v1.GetRewardsResponse.rewards:type_name -> core.v1.GetRewardResponse + 104, // 49: core.v1.SlashRecommendation.start:type_name -> google.protobuf.Timestamp + 104, // 50: core.v1.SlashRecommendation.end:type_name -> google.protobuf.Timestamp + 49, // 51: core.v1.GetSlashAttestationRequest.data:type_name -> core.v1.SlashRecommendation + 50, // 52: core.v1.GetSlashAttestationsRequest.request:type_name -> core.v1.GetSlashAttestationRequest + 51, // 53: core.v1.GetSlashAttestationsResponse.attestations:type_name -> core.v1.GetSlashAttestationResponse + 105, // 54: core.v1.GetERNResponse.ern:type_name -> ddex.v1beta1.NewReleaseMessage + 106, // 55: core.v1.GetPartyResponse.party:type_name -> ddex.v1beta1.Party + 107, // 56: core.v1.GetResourceResponse.resource:type_name -> ddex.v1beta1.Resource + 108, // 57: core.v1.GetReleaseResponse.release:type_name -> ddex.v1beta1.Release + 109, // 58: core.v1.GetDealResponse.deal:type_name -> ddex.v1beta1.Deal + 110, // 59: core.v1.GetMEADResponse.mead:type_name -> ddex.v1beta1.MeadMessage + 111, // 60: core.v1.GetPIEResponse.pie:type_name -> ddex.v1beta1.PieMessage + 69, // 61: core.v1.RewardMessage.create:type_name -> core.v1.CreateReward + 70, // 62: core.v1.RewardMessage.delete:type_name -> core.v1.DeleteReward + 43, // 63: core.v1.CreateReward.claim_authorities:type_name -> core.v1.ClaimAuthority + 104, // 64: core.v1.GetStreamURLsSignature.expires_at:type_name -> google.protobuf.Timestamp + 104, // 65: core.v1.GetStreamURLsRequest.expires_at:type_name -> google.protobuf.Timestamp + 101, // 66: core.v1.GetStreamURLsResponse.entity_stream_urls:type_name -> core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry + 24, // 67: core.v1.StreamBlocksResponse.block:type_name -> core.v1.Block + 95, // 68: core.v1.GetStatusResponse.ProcessInfo.abci:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 69: core.v1.GetStatusResponse.ProcessInfo.registry_bridge:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 70: core.v1.GetStatusResponse.ProcessInfo.echo_server:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 71: core.v1.GetStatusResponse.ProcessInfo.sync_tasks:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 72: core.v1.GetStatusResponse.ProcessInfo.peer_manager:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 73: core.v1.GetStatusResponse.ProcessInfo.data_companion:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 74: core.v1.GetStatusResponse.ProcessInfo.cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 75: core.v1.GetStatusResponse.ProcessInfo.log_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 76: core.v1.GetStatusResponse.ProcessInfo.state_sync:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 95, // 77: core.v1.GetStatusResponse.ProcessInfo.mempool_cache:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo + 96, // 78: core.v1.GetStatusResponse.SyncInfo.state_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo + 97, // 79: core.v1.GetStatusResponse.SyncInfo.block_sync:type_name -> core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo + 42, // 80: core.v1.GetStatusResponse.SnapshotInfo.snapshots:type_name -> core.v1.SnapshotMetadata + 98, // 81: core.v1.GetStatusResponse.PeerInfo.peers:type_name -> core.v1.GetStatusResponse.PeerInfo.Peer + 0, // 82: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.state:type_name -> core.v1.GetStatusResponse.ProcessInfo.ProcessState + 104, // 83: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.started_at:type_name -> google.protobuf.Timestamp + 104, // 84: core.v1.GetStatusResponse.ProcessInfo.ProcessStateInfo.completed_at:type_name -> google.protobuf.Timestamp + 1, // 85: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.phase:type_name -> core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.Phase + 42, // 86: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.snapshot:type_name -> core.v1.SnapshotMetadata + 104, // 87: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.started_at:type_name -> google.protobuf.Timestamp + 104, // 88: core.v1.GetStatusResponse.SyncInfo.StateSyncInfo.completed_at:type_name -> google.protobuf.Timestamp + 86, // 89: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.head_source:type_name -> core.v1.GetStatusResponse.NodeInfo + 104, // 90: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.started_at:type_name -> google.protobuf.Timestamp + 104, // 91: core.v1.GetStatusResponse.SyncInfo.BlockSyncInfo.completed_at:type_name -> google.protobuf.Timestamp + 24, // 92: core.v1.GetBlocksResponse.BlocksEntry.value:type_name -> core.v1.Block + 100, // 93: core.v1.GetStreamURLsResponse.EntityStreamUrlsEntry.value:type_name -> core.v1.GetStreamURLsResponse.EntityStreamURLs + 94, // [94:94] is the sub-list for method output_type + 94, // [94:94] is the sub-list for method input_type + 94, // [94:94] is the sub-list for extension type_name + 94, // [94:94] is the sub-list for extension extendee + 0, // [0:94] is the sub-list for field type_name } func init() { file_core_v1_types_proto_init() } @@ -8059,7 +8204,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidatorMisbehaviorDeregistration); i { + switch v := v.(*ManageEntityLegacyMigration); i { case 0: return &v.state case 1: @@ -8071,7 +8216,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StorageProof); i { + switch v := v.(*ValidatorMisbehaviorDeregistration); i { case 0: return &v.state case 1: @@ -8083,7 +8228,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StorageProofVerification); i { + switch v := v.(*StorageProof); i { case 0: return &v.state case 1: @@ -8095,7 +8240,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Attestation); i { + switch v := v.(*StorageProofVerification); i { case 0: return &v.state case 1: @@ -8107,7 +8252,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidatorRegistration); i { + switch v := v.(*Attestation); i { case 0: return &v.state case 1: @@ -8119,7 +8264,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidatorDeregistration); i { + switch v := v.(*ValidatorRegistration); i { case 0: return &v.state case 1: @@ -8131,7 +8276,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStoredSnapshotsRequest); i { + switch v := v.(*ValidatorDeregistration); i { case 0: return &v.state case 1: @@ -8143,7 +8288,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStoredSnapshotsResponse); i { + switch v := v.(*GetStoredSnapshotsRequest); i { case 0: return &v.state case 1: @@ -8155,7 +8300,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SnapshotMetadata); i { + switch v := v.(*GetStoredSnapshotsResponse); i { case 0: return &v.state case 1: @@ -8167,7 +8312,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClaimAuthority); i { + switch v := v.(*SnapshotMetadata); i { case 0: return &v.state case 1: @@ -8179,7 +8324,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Reward); i { + switch v := v.(*ClaimAuthority); i { case 0: return &v.state case 1: @@ -8191,7 +8336,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardsRequest); i { + switch v := v.(*Reward); i { case 0: return &v.state case 1: @@ -8203,7 +8348,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardsResponse); i { + switch v := v.(*GetRewardsRequest); i { case 0: return &v.state case 1: @@ -8215,7 +8360,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardAttestationRequest); i { + switch v := v.(*GetRewardsResponse); i { case 0: return &v.state case 1: @@ -8227,7 +8372,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardAttestationResponse); i { + switch v := v.(*GetRewardAttestationRequest); i { case 0: return &v.state case 1: @@ -8239,7 +8384,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SlashRecommendation); i { + switch v := v.(*GetRewardAttestationResponse); i { case 0: return &v.state case 1: @@ -8251,7 +8396,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSlashAttestationRequest); i { + switch v := v.(*SlashRecommendation); i { case 0: return &v.state case 1: @@ -8263,7 +8408,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSlashAttestationResponse); i { + switch v := v.(*GetSlashAttestationRequest); i { case 0: return &v.state case 1: @@ -8275,7 +8420,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSlashAttestationsRequest); i { + switch v := v.(*GetSlashAttestationResponse); i { case 0: return &v.state case 1: @@ -8287,7 +8432,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSlashAttestationsResponse); i { + switch v := v.(*GetSlashAttestationsRequest); i { case 0: return &v.state case 1: @@ -8299,7 +8444,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetERNRequest); i { + switch v := v.(*GetSlashAttestationsResponse); i { case 0: return &v.state case 1: @@ -8311,7 +8456,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetERNResponse); i { + switch v := v.(*GetERNRequest); i { case 0: return &v.state case 1: @@ -8323,7 +8468,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPartyRequest); i { + switch v := v.(*GetERNResponse); i { case 0: return &v.state case 1: @@ -8335,7 +8480,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPartyResponse); i { + switch v := v.(*GetPartyRequest); i { case 0: return &v.state case 1: @@ -8347,7 +8492,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourceRequest); i { + switch v := v.(*GetPartyResponse); i { case 0: return &v.state case 1: @@ -8359,7 +8504,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetResourceResponse); i { + switch v := v.(*GetResourceRequest); i { case 0: return &v.state case 1: @@ -8371,7 +8516,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetReleaseRequest); i { + switch v := v.(*GetResourceResponse); i { case 0: return &v.state case 1: @@ -8383,7 +8528,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetReleaseResponse); i { + switch v := v.(*GetReleaseRequest); i { case 0: return &v.state case 1: @@ -8395,7 +8540,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetDealRequest); i { + switch v := v.(*GetReleaseResponse); i { case 0: return &v.state case 1: @@ -8407,7 +8552,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetDealResponse); i { + switch v := v.(*GetDealRequest); i { case 0: return &v.state case 1: @@ -8419,7 +8564,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMEADRequest); i { + switch v := v.(*GetDealResponse); i { case 0: return &v.state case 1: @@ -8431,7 +8576,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMEADResponse); i { + switch v := v.(*GetMEADRequest); i { case 0: return &v.state case 1: @@ -8443,7 +8588,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPIERequest); i { + switch v := v.(*GetMEADResponse); i { case 0: return &v.state case 1: @@ -8455,7 +8600,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPIEResponse); i { + switch v := v.(*GetPIERequest); i { case 0: return &v.state case 1: @@ -8467,7 +8612,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RewardMessage); i { + switch v := v.(*GetPIEResponse); i { case 0: return &v.state case 1: @@ -8479,7 +8624,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateReward); i { + switch v := v.(*RewardMessage); i { case 0: return &v.state case 1: @@ -8491,7 +8636,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteReward); i { + switch v := v.(*CreateReward); i { case 0: return &v.state case 1: @@ -8503,7 +8648,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardRequest); i { + switch v := v.(*DeleteReward); i { case 0: return &v.state case 1: @@ -8515,7 +8660,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardResponse); i { + switch v := v.(*GetRewardRequest); i { case 0: return &v.state case 1: @@ -8527,7 +8672,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RewardAttestationSignature); i { + switch v := v.(*GetRewardResponse); i { case 0: return &v.state case 1: @@ -8539,7 +8684,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadSignature); i { + switch v := v.(*RewardAttestationSignature); i { case 0: return &v.state case 1: @@ -8551,7 +8696,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FileUpload); i { + switch v := v.(*UploadSignature); i { case 0: return &v.state case 1: @@ -8563,7 +8708,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStreamURLsSignature); i { + switch v := v.(*FileUpload); i { case 0: return &v.state case 1: @@ -8575,7 +8720,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStreamURLsRequest); i { + switch v := v.(*GetStreamURLsSignature); i { case 0: return &v.state case 1: @@ -8587,7 +8732,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStreamURLsResponse); i { + switch v := v.(*GetStreamURLsRequest); i { case 0: return &v.state case 1: @@ -8599,7 +8744,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUploadByCIDRequest); i { + switch v := v.(*GetStreamURLsResponse); i { case 0: return &v.state case 1: @@ -8611,7 +8756,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUploadByCIDResponse); i { + switch v := v.(*GetUploadByCIDRequest); i { case 0: return &v.state case 1: @@ -8623,7 +8768,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardSenderAttestationRequest); i { + switch v := v.(*GetUploadByCIDResponse); i { case 0: return &v.state case 1: @@ -8635,7 +8780,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRewardSenderAttestationResponse); i { + switch v := v.(*GetRewardSenderAttestationRequest); i { case 0: return &v.state case 1: @@ -8647,7 +8792,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StreamBlocksRequest); i { + switch v := v.(*GetRewardSenderAttestationResponse); i { case 0: return &v.state case 1: @@ -8659,7 +8804,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StreamBlocksResponse); i { + switch v := v.(*StreamBlocksRequest); i { case 0: return &v.state case 1: @@ -8671,7 +8816,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_ProcessInfo); i { + switch v := v.(*StreamBlocksResponse); i { case 0: return &v.state case 1: @@ -8683,7 +8828,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_NodeInfo); i { + switch v := v.(*GetStatusResponse_ProcessInfo); i { case 0: return &v.state case 1: @@ -8695,7 +8840,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_ChainInfo); i { + switch v := v.(*GetStatusResponse_NodeInfo); i { case 0: return &v.state case 1: @@ -8707,7 +8852,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_SyncInfo); i { + switch v := v.(*GetStatusResponse_ChainInfo); i { case 0: return &v.state case 1: @@ -8719,7 +8864,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_PruningInfo); i { + switch v := v.(*GetStatusResponse_SyncInfo); i { case 0: return &v.state case 1: @@ -8731,7 +8876,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_ResourceInfo); i { + switch v := v.(*GetStatusResponse_PruningInfo); i { case 0: return &v.state case 1: @@ -8743,7 +8888,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_MempoolInfo); i { + switch v := v.(*GetStatusResponse_ResourceInfo); i { case 0: return &v.state case 1: @@ -8755,7 +8900,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_SnapshotInfo); i { + switch v := v.(*GetStatusResponse_MempoolInfo); i { case 0: return &v.state case 1: @@ -8767,7 +8912,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_PeerInfo); i { + switch v := v.(*GetStatusResponse_SnapshotInfo); i { case 0: return &v.state case 1: @@ -8779,7 +8924,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_StorageInfo); i { + switch v := v.(*GetStatusResponse_PeerInfo); i { case 0: return &v.state case 1: @@ -8791,7 +8936,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_ProcessInfo_ProcessStateInfo); i { + switch v := v.(*GetStatusResponse_StorageInfo); i { case 0: return &v.state case 1: @@ -8803,7 +8948,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_SyncInfo_StateSyncInfo); i { + switch v := v.(*GetStatusResponse_ProcessInfo_ProcessStateInfo); i { case 0: return &v.state case 1: @@ -8815,7 +8960,7 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetStatusResponse_SyncInfo_BlockSyncInfo); i { + switch v := v.(*GetStatusResponse_SyncInfo_StateSyncInfo); i { case 0: return &v.state case 1: @@ -8827,6 +8972,18 @@ func file_core_v1_types_proto_init() { } } file_core_v1_types_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse_SyncInfo_BlockSyncInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_v1_types_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetStatusResponse_PeerInfo_Peer); i { case 0: return &v.state @@ -8838,7 +8995,7 @@ func file_core_v1_types_proto_init() { return nil } } - file_core_v1_types_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { + file_core_v1_types_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetStreamURLsResponse_EntityStreamURLs); i { case 0: return &v.state @@ -8863,16 +9020,17 @@ func file_core_v1_types_proto_init() { (*SignedTransaction_Release)(nil), (*SignedTransaction_Reward)(nil), (*SignedTransaction_FileUpload)(nil), + (*SignedTransaction_ManageEntityMigration)(nil), } - file_core_v1_types_proto_msgTypes[34].OneofWrappers = []interface{}{ + file_core_v1_types_proto_msgTypes[35].OneofWrappers = []interface{}{ (*Attestation_ValidatorRegistration)(nil), (*Attestation_ValidatorDeregistration)(nil), } - file_core_v1_types_proto_msgTypes[65].OneofWrappers = []interface{}{ + file_core_v1_types_proto_msgTypes[66].OneofWrappers = []interface{}{ (*RewardMessage_Create)(nil), (*RewardMessage_Delete)(nil), } - file_core_v1_types_proto_msgTypes[85].OneofWrappers = []interface{}{ + file_core_v1_types_proto_msgTypes[86].OneofWrappers = []interface{}{ (*GetStatusResponse_SyncInfo_StateSync)(nil), (*GetStatusResponse_SyncInfo_BlockSync)(nil), } @@ -8882,7 +9040,7 @@ func file_core_v1_types_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_v1_types_proto_rawDesc, NumEnums: 2, - NumMessages: 99, + NumMessages: 100, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/core/server/abci.go b/pkg/core/server/abci.go index c8f3b457..082b0df0 100644 --- a/pkg/core/server/abci.go +++ b/pkg/core/server/abci.go @@ -573,15 +573,18 @@ func (s *Server) OfferSnapshot(_ context.Context, req *abcitypes.OfferSnapshotRe s.snapshotMutex.Lock() defer s.snapshotMutex.Unlock() - // If we've already accepted a snapshot, only accept the same one + // If we've already accepted a snapshot, check if CometBFT is re-offering the + // same one (resume) or a different one (previous snapshot failed verification). if s.acceptedSnapshotHeight != 0 { if req.Snapshot.Height != s.acceptedSnapshotHeight { - s.logger.Info("rejecting snapshot: already syncing to different snapshot", - zap.Uint64("offered_height", req.Snapshot.Height), - zap.Uint64("accepted_height", s.acceptedSnapshotHeight)) - return &abcitypes.OfferSnapshotResponse{ - Result: abcitypes.OFFER_SNAPSHOT_RESULT_REJECT, - }, nil + // CometBFT is offering a different snapshot, which means the previously + // accepted one failed (e.g. consensus params verification error). Clear + // the old state so we can accept the new snapshot. + s.logger.Info("clearing previous snapshot state: CometBFT offered a new snapshot", + zap.Uint64("previous_height", s.acceptedSnapshotHeight), + zap.Uint64("new_height", req.Snapshot.Height)) + s.acceptedSnapshotHeight = 0 + s.acceptedSnapshotHash = nil } // Check hash matches too if !bytes.Equal(req.Snapshot.Hash, s.acceptedSnapshotHash) { @@ -1043,6 +1046,8 @@ func (s *Server) finalizeTransaction(ctx context.Context, req *abcitypes.Finaliz return s.finalizePlayTransaction(ctx, msg) case *v1.SignedTransaction_ManageEntity: return s.finalizeManageEntity(ctx, msg) + case *v1.SignedTransaction_ManageEntityMigration: + return s.finalizeManageEntityMigration(ctx, msg) case *v1.SignedTransaction_Attestation: return s.finalizeAttestation(ctx, msg, req.Height) case *v1.SignedTransaction_ValidatorRegistration: diff --git a/pkg/core/server/manage_entity.go b/pkg/core/server/manage_entity.go index 54042f20..2fdace6e 100644 --- a/pkg/core/server/manage_entity.go +++ b/pkg/core/server/manage_entity.go @@ -23,6 +23,18 @@ func (s *Server) validateManageEntity(_ context.Context, stx *v1.SignedTransacti return manageEntity, nil } +// finalizeManageEntityMigration handles ManageEntityLegacyMigration transactions written +// by genesis-writer. The transaction is stored as-is; no additional chain-side processing +// is performed (e.g. sound_recordings/management_keys are not populated for Track creates +// because genesis migration data does not carry live CIDs). +func (s *Server) finalizeManageEntityMigration(_ context.Context, stx *v1.SignedTransaction) (proto.Message, error) { + me := stx.GetManageEntityMigration() + if me == nil { + return nil, errors.New("not manage entity migration") + } + return me, nil +} + // access_authorities are wallet addresses that can sign to authorize stream access (programmable distribution) type trackMetadata struct { CID string `json:"cid"` diff --git a/proto/core/v1/types.proto b/proto/core/v1/types.proto index 12456856..6719d7c9 100644 --- a/proto/core/v1/types.proto +++ b/proto/core/v1/types.proto @@ -288,6 +288,7 @@ message SignedTransaction { ddex.v1beta1.NewReleaseMessage release = 1008; RewardMessage reward = 1009; FileUpload file_upload = 1010; + ManageEntityLegacyMigration manage_entity_migration = 1011; } } @@ -338,6 +339,22 @@ message ManageEntityLegacy { string nonce = 8; } +// ManageEntityLegacyMigration is structurally identical to ManageEntityLegacy +// but signals to indexers that the transaction was produced by genesis-writer +// during the initial chain migration. Indexers must verify only that the signer +// matches the genesis migration authority; standard ownership, wallet-uniqueness, +// and signer checks must not be applied. +message ManageEntityLegacyMigration { + int64 user_id = 1; + string entity_type = 2; + int64 entity_id = 3; + string action = 4; + string metadata = 5; + string signature = 6; + string signer = 7; + string nonce = 8; +} + message ValidatorMisbehaviorDeregistration { string comet_address = 1; bytes pub_key = 2;