From ee0a9d5be8144a5455576d58efe6515f2bf31f59 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:38:18 -0700 Subject: [PATCH 1/9] Add genesis-writer tool for offline chain history population Replaces genesis-replay with a fully offline tool that reads from a source DP database and writes real CometBFT blocks directly to Core chain PostgreSQL + blockstore.db + state.db. Produces a distributable snapshot that third-party indexers can process from block 1. Includes ManageEntityLegacyMigration proto type to distinguish genesis migration transactions from live ones, managed postgres lifecycle, auto-generated validator keys and genesis.json, and resume support. Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/Makefile | 6 + cmd/genesis-writer/README.md | 270 +++ cmd/genesis-writer/batch.go | 162 ++ cmd/genesis-writer/cmt_state.go | 273 +++ cmd/genesis-writer/docker-compose.yml | 214 ++ cmd/genesis-writer/entities_comment.go | 125 ++ .../entities_dashboard_wallet.go | 54 + cmd/genesis-writer/entities_developer_app.go | 112 + cmd/genesis-writer/entities_email.go | 136 ++ cmd/genesis-writer/entities_play.go | 103 + cmd/genesis-writer/entities_playlist.go | 107 + cmd/genesis-writer/entities_social.go | 220 ++ cmd/genesis-writer/entities_tip.go | 74 + cmd/genesis-writer/entities_track.go | 228 ++ cmd/genesis-writer/entities_user.go | 151 ++ cmd/genesis-writer/integration_test.go | 799 +++++++ cmd/genesis-writer/main.go | 320 +++ cmd/genesis-writer/postgres.go | 230 ++ cmd/genesis-writer/testdata/dp_seed.sql | 7 + cmd/genesis-writer/testdata/seed.sql | 547 +++++ cmd/genesis-writer/testdata/source_init.sh | 18 + cmd/genesis-writer/writer.go | 838 ++++++++ go.mod | 7 +- go.sum | 1 - pkg/api/core/v1/types.pb.go | 1866 +++++++++-------- pkg/core/server/abci.go | 19 +- pkg/core/server/manage_entity.go | 12 + proto/core/v1/types.proto | 17 + 28 files changed, 6052 insertions(+), 864 deletions(-) create mode 100644 cmd/genesis-writer/Makefile create mode 100644 cmd/genesis-writer/README.md create mode 100644 cmd/genesis-writer/batch.go create mode 100644 cmd/genesis-writer/cmt_state.go create mode 100644 cmd/genesis-writer/docker-compose.yml create mode 100644 cmd/genesis-writer/entities_comment.go create mode 100644 cmd/genesis-writer/entities_dashboard_wallet.go create mode 100644 cmd/genesis-writer/entities_developer_app.go create mode 100644 cmd/genesis-writer/entities_email.go create mode 100644 cmd/genesis-writer/entities_play.go create mode 100644 cmd/genesis-writer/entities_playlist.go create mode 100644 cmd/genesis-writer/entities_social.go create mode 100644 cmd/genesis-writer/entities_tip.go create mode 100644 cmd/genesis-writer/entities_track.go create mode 100644 cmd/genesis-writer/entities_user.go create mode 100644 cmd/genesis-writer/integration_test.go create mode 100644 cmd/genesis-writer/main.go create mode 100644 cmd/genesis-writer/postgres.go create mode 100644 cmd/genesis-writer/testdata/dp_seed.sql create mode 100644 cmd/genesis-writer/testdata/seed.sql create mode 100755 cmd/genesis-writer/testdata/source_init.sh create mode 100644 cmd/genesis-writer/writer.go 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..9be61c44 --- /dev/null +++ b/cmd/genesis-writer/README.md @@ -0,0 +1,270 @@ +# 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 `SaveSeenCommit` signed with 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. + +## Indexer integration + +Indexers that consume the Core chain must handle `ManageEntityLegacyMigration` +transactions. When this transaction type is encountered, the indexer should: + +1. Verify that `signer` matches the genesis migration authority configured in + `genesis.json` (`genesis_migration_address`). +2. Apply entity data directly from `metadata` — the same JSON structure as + `ManageEntityLegacy`, with `action` values `Create`, `Follow`, `Save`, `Repost`. +3. **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..9746730b --- /dev/null +++ b/cmd/genesis-writer/batch.go @@ -0,0 +1,162 @@ +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 := 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..21192901 --- /dev/null +++ b/cmd/genesis-writer/cmt_state.go @@ -0,0 +1,273 @@ +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) + } + + // Update app_state with the migration address and end height. + appState := map[string]interface{}{ + "genesis_migration_address": w.signerAddr, + "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..9c5cc66f --- /dev/null +++ b/cmd/genesis-writer/entities_comment.go @@ -0,0 +1,125 @@ +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, _ := json.Marshal(createdAtMeta{CreatedAt: cr.createdAt.Format(time.RFC3339)}) + 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..75dc30ce --- /dev/null +++ b/cmd/genesis-writer/entities_social.go @@ -0,0 +1,220 @@ +package main + +import ( + "context" + "encoding/json" + "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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(f.createdAt)}) + 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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(r.createdAt)}) + 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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(m.createdAt)}) + 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..967c6ab7 --- /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`) + 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..7e9ffea8 --- /dev/null +++ b/cmd/genesis-writer/entities_track.go @@ -0,0 +1,228 @@ +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 + MetadataMultihash *string + TrackSegments []byte // JSONB + 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, + metadata_multihash, track_segments, + 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.MetadataMultihash, &t.TrackSegments, + &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) + + // Extract track CID from track_segments or metadata_multihash. + cid := "genesis-import" + if t.MetadataMultihash != nil && *t.MetadataMultihash != "" { + cid = *t.MetadataMultihash + } + if len(t.TrackSegments) > 0 { + var segs []struct { + MultiHash string `json:"multihash"` + } + if err := json.Unmarshal(t.TrackSegments, &segs); err == nil && len(segs) > 0 { + inner.TrackCID = segs[0].MultiHash + } + } + + metaJSON, err := json.Marshal(trackMetadataWrapper{ + CID: cid, + 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..8ed8cbcd --- /dev/null +++ b/cmd/genesis-writer/integration_test.go @@ -0,0 +1,799 @@ +//go:build integration + +package main + +import ( + "context" + "crypto/ecdsa" + "crypto/tls" + "encoding/hex" + "encoding/json" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + "time" + + "fmt" + + "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/ethereum/go-ethereum/crypto" + "github.com/cometbft/cometbft/privval" + "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-v2") + require.NoError(t, err, "read dev-v2 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..5c60cc6b --- /dev/null +++ b/cmd/genesis-writer/postgres.go @@ -0,0 +1,230 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "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) + } + + dsn := fmt.Sprintf("postgres://%s@localhost:%s/%s?sslmode=disable", os.Getenv("USER"), 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) + } + + 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..680d797c --- /dev/null +++ b/cmd/genesis-writer/writer.go @@ -0,0 +1,838 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "crypto/sha256" + "database/sql" + "encoding/hex" + "fmt" + "path/filepath" + "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 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 { + // Convert pgx DSN to a lib/pq DSN (both accept the same postgresql:// URLs). + db, err := sql.Open("postgres", cfg.DstDSN) + if err != nil { + return nil, fmt.Errorf("open dst db for migrations: %w", err) + } + db.Close() + 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 = 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 + } + return w.blockWriteErr +} + +// writeBlockToDB writes a single block to postgres (using COPY for transactions) +// and blockstore.db. +func (w *Writer) writeBlockToDB(ctx context.Context, pb pendingBlock) error { + // Write to blockstore so peers can serve this block for blocksync. + if w.blockStore != nil { + w.blockStore.SaveBlock(pb.block, pb.blockParts, pb.seenCommit) + } + + 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) + } + + return pgTx.Commit(ctx) +} + +// 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) + 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) + + 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) + } + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + rows.Close() + return fmt.Errorf("scan progress: %w", err) + } + completedSteps[name] = true + } + rows.Close() + } + + 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 w.blockWriteErr != nil { + return fmt.Errorf("block writer: %w", w.blockWriteErr) + } + 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") + break + } + 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) + } + 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 w.blockWriteErr != nil { + return fmt.Errorf("block writer: %w", w.blockWriteErr) + } + 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. + var all []byte + for _, tx := range w.blockTxs { + all = append(all, tx...) + } + appHash := sha256Bytes(all) + + // Pre-compute tx hashes for the COPY insert. + txData := make([]txRow, len(w.blockTxs)) + for i, tx := range w.blockTxs { + txData[i] = txRow{ + hash: 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 7bded061..3b83294b 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.38.0 golang.org/x/mod v0.24.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.13.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.1.0 // indirect diff --git a/go.sum b/go.sum index 424d4588..42bc7ab9 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; From 228eab9746eaa3b2aa1eb11818dbe99374e0d29b Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 23 Apr 2026 18:38:57 -0700 Subject: [PATCH 2/9] Address review comments on genesis writer Fix blockWriteErr data race with atomic.Pointer, restore block linkage on resume, wire BatchSize config, add defer stopBlockWriter for leak safety, use streaming sha256 for appHash, fix README SaveBlock wording. Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/README.md | 2 +- cmd/genesis-writer/batch.go | 5 +++- cmd/genesis-writer/writer.go | 46 ++++++++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/cmd/genesis-writer/README.md b/cmd/genesis-writer/README.md index 9be61c44..d38407ec 100644 --- a/cmd/genesis-writer/README.md +++ b/cmd/genesis-writer/README.md @@ -31,7 +31,7 @@ the main migration work. `core_app_state` in a single PostgreSQL transaction per block. 5. **Prime** — writes CometBFT `state.db` (via `Bootstrap`) and - `blockstore.db` (via `SaveSeenCommit` signed with the validator's ed25519 + `blockstore.db` (via `SaveBlock` signed with 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. diff --git a/cmd/genesis-writer/batch.go b/cmd/genesis-writer/batch.go index 9746730b..b0de48cb 100644 --- a/cmd/genesis-writer/batch.go +++ b/cmd/genesis-writer/batch.go @@ -45,7 +45,10 @@ func processBatched[T any]( defer rows.Close() workers := runtime.NumCPU() - batchSize := workers * 2 + batchSize := w.cfg.BatchSize + if batchSize <= 0 { + batchSize = workers * 2 + } var processed int64 batch := make([]T, 0, batchSize) diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go index 680d797c..713046f7 100644 --- a/cmd/genesis-writer/writer.go +++ b/cmd/genesis-writer/writer.go @@ -106,7 +106,7 @@ type Writer struct { // async block writer pipeline blockWriteCh chan pendingBlock - blockWriteErr error + blockWriteErr atomic.Pointer[error] blockWriteDone chan struct{} // proto marshal buffer pool (reduces GC pressure) @@ -218,7 +218,7 @@ func (w *Writer) startBlockWriter(ctx context.Context) { defer close(w.blockWriteDone) for pb := range w.blockWriteCh { if err := w.writeBlockToDB(ctx, pb); err != nil { - w.blockWriteErr = err + w.blockWriteErr.Store(&err) // Drain remaining blocks to unblock senders. for range w.blockWriteCh { } @@ -234,8 +234,12 @@ func (w *Writer) stopBlockWriter() error { if w.blockWriteCh != nil { close(w.blockWriteCh) <-w.blockWriteDone + w.blockWriteCh = nil } - return w.blockWriteErr + if ep := w.blockWriteErr.Load(); ep != nil { + return *ep + } + return nil } // writeBlockToDB writes a single block to postgres (using COPY for transactions) @@ -333,6 +337,27 @@ func (w *Writer) Run(ctx context.Context) error { if maxHeight > 0 { w.height = maxHeight + 1 w.blockTime = w.cfg.GenesisTime.Add(time.Duration(maxHeight) * time.Second) + + // Restore block linkage from blockstore so new blocks chain correctly. + if w.blockStore != nil { + lastBlock, _ := w.blockStore.LoadBlock(maxHeight) + if lastBlock != nil { + 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(), + } + w.prevAppHash = lastBlock.Header.AppHash + } + lastCommit := w.blockStore.LoadBlockCommit(maxHeight) + if lastCommit != nil { + w.lastCommit = lastCommit + } + } + w.logger.Info("resuming from height", zap.Int64("height", w.height)) } } @@ -347,6 +372,7 @@ func (w *Writer) Run(ctx context.Context) error { // Start the async block writer pipeline. w.startBlockWriter(ctx) + defer w.stopBlockWriter() //nolint:errcheck // explicit stop below captures the error steps := []struct { name string @@ -416,8 +442,8 @@ func (w *Writer) Run(ctx context.Context) error { continue } // Check for async writer errors before starting next step. - if w.blockWriteErr != nil { - return fmt.Errorf("block writer: %w", w.blockWriteErr) + 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 { @@ -600,8 +626,8 @@ func (w *Writer) addTx(ctx context.Context, txBytes []byte) error { w.blockMu.Lock() defer w.blockMu.Unlock() // Check for async writer errors. - if w.blockWriteErr != nil { - return fmt.Errorf("block writer: %w", w.blockWriteErr) + 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 { @@ -622,11 +648,11 @@ func (w *Writer) flushBlock(ctx context.Context) error { // app_hash = SHA256(all tx bytes concatenated) — convention shared with the // core server's FinalizeBlock implementation for genesis-range blocks. - var all []byte + h := sha256.New() for _, tx := range w.blockTxs { - all = append(all, tx...) + h.Write(tx) } - appHash := sha256Bytes(all) + appHash := h.Sum(nil) // Pre-compute tx hashes for the COPY insert. txData := make([]txRow, len(w.blockTxs)) From e632c958b21f4c3fe17e0c8c28fbfc8c7792044f Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Thu, 23 Apr 2026 18:54:24 -0700 Subject: [PATCH 3/9] Address second round of review comments - Remove redundant sql.Open/Close before RunMigrations - Handle json.Marshal errors in social and comment entity writers - Merge into existing app_state instead of replacing it in writeGenesisFile - Improve README SaveBlock documentation Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/README.md | 7 +++--- cmd/genesis-writer/cmt_state.go | 12 ++++++---- cmd/genesis-writer/entities_comment.go | 5 ++++- cmd/genesis-writer/entities_social.go | 31 +++++++++++++++++++++----- cmd/genesis-writer/writer.go | 7 ------ 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/cmd/genesis-writer/README.md b/cmd/genesis-writer/README.md index d38407ec..ef3e194e 100644 --- a/cmd/genesis-writer/README.md +++ b/cmd/genesis-writer/README.md @@ -31,9 +31,10 @@ the main migration work. `core_app_state` in a single PostgreSQL transaction per block. 5. **Prime** — writes CometBFT `state.db` (via `Bootstrap`) and - `blockstore.db` (via `SaveBlock` signed with 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. + `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 diff --git a/cmd/genesis-writer/cmt_state.go b/cmd/genesis-writer/cmt_state.go index 21192901..cbc53e9b 100644 --- a/cmd/genesis-writer/cmt_state.go +++ b/cmd/genesis-writer/cmt_state.go @@ -145,11 +145,15 @@ func (w *Writer) writeGenesisFile() error { return fmt.Errorf("parse genesis file: %w", err) } - // Update app_state with the migration address and end height. - appState := map[string]interface{}{ - "genesis_migration_address": w.signerAddr, - "genesis_migration_end_height": w.finalHeight, + // 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) diff --git a/cmd/genesis-writer/entities_comment.go b/cmd/genesis-writer/entities_comment.go index 9c5cc66f..567de0e7 100644 --- a/cmd/genesis-writer/entities_comment.go +++ b/cmd/genesis-writer/entities_comment.go @@ -112,7 +112,10 @@ func (w *Writer) writeCommentReactions(ctx context.Context) error { return cr, err }, func(ctx context.Context, cr commentReaction) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: cr.createdAt.Format(time.RFC3339)}) + 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", diff --git a/cmd/genesis-writer/entities_social.go b/cmd/genesis-writer/entities_social.go index 75dc30ce..2fb686c1 100644 --- a/cmd/genesis-writer/entities_social.go +++ b/cmd/genesis-writer/entities_social.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "fmt" "time" corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" @@ -37,7 +38,10 @@ func (w *Writer) writeFollows(ctx context.Context) error { return f, err }, func(ctx context.Context, f follow) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(f.createdAt)}) + 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", @@ -69,7 +73,10 @@ func (w *Writer) writeSaves(ctx context.Context) error { return s, err }, func(ctx context.Context, s save) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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), @@ -101,7 +108,10 @@ func (w *Writer) writeReposts(ctx context.Context) error { return r, err }, func(ctx context.Context, r repost) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(r.createdAt)}) + 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), @@ -133,7 +143,10 @@ func (w *Writer) writeShares(ctx context.Context) error { return s, err }, func(ctx context.Context, s share) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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), @@ -164,7 +177,10 @@ func (w *Writer) writeSubscriptions(ctx context.Context) error { return s, err }, func(ctx context.Context, s subscription) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(s.createdAt)}) + 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", @@ -195,7 +211,10 @@ func (w *Writer) writeMutedUsers(ctx context.Context) error { return m, err }, func(ctx context.Context, m mutedUser) error { - metaJSON, _ := json.Marshal(createdAtMeta{CreatedAt: fmtCreatedAt(m.createdAt)}) + 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", diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go index 713046f7..4e766f91 100644 --- a/cmd/genesis-writer/writer.go +++ b/cmd/genesis-writer/writer.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ecdsa" "crypto/sha256" - "database/sql" "encoding/hex" "fmt" "path/filepath" @@ -137,12 +136,6 @@ type txRow struct { // NewWriter connects to both databases and returns a ready Writer. func NewWriter(cfg *WriterConfig, logger *zap.Logger) (*Writer, error) { if cfg.RunMigrations { - // Convert pgx DSN to a lib/pq DSN (both accept the same postgresql:// URLs). - db, err := sql.Open("postgres", cfg.DstDSN) - if err != nil { - return nil, fmt.Errorf("open dst db for migrations: %w", err) - } - db.Close() if err := coredb.RunMigrations(logger, cfg.DstDSN, false); err != nil { return nil, fmt.Errorf("run migrations: %w", err) } From 7ccf27f20dca4b9bcbf6bd3013d83edd675b8c4c Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 09:31:27 -0700 Subject: [PATCH 4/9] Fix resume appHash, document signer override, uppercase tx hashes - Load prevAppHash from core_app_state instead of block header (off-by-one fix) - Make blockstore required for resume (error if CMTHome not set) - Error on missing block/commit in blockstore during resume - Document that Signer field is an identity hint, not signature authority - Use uppercase hex for tx_hash to match CometBFT's HexBytes.String() Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/writer.go | 56 ++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go index 4e766f91..b57c4004 100644 --- a/cmd/genesis-writer/writer.go +++ b/cmd/genesis-writer/writer.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "fmt" "path/filepath" + "strings" "sync" "sync/atomic" "time" @@ -331,25 +332,39 @@ func (w *Writer) Run(ctx context.Context) error { 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 { - lastBlock, _ := w.blockStore.LoadBlock(maxHeight) - if lastBlock != nil { - 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(), - } - w.prevAppHash = lastBlock.Header.AppHash - } - lastCommit := w.blockStore.LoadBlockCommit(maxHeight) - if lastCommit != nil { - w.lastCommit = lastCommit - } + 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)) } @@ -548,6 +563,10 @@ func (w *Writer) signAndMarshal(me *corev1.ManageEntityLegacy, signer string) ([ 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{ @@ -648,10 +667,11 @@ func (w *Writer) flushBlock(ctx context.Context) error { 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: hex.EncodeToString(sha256Bytes(tx)), + hash: strings.ToUpper(hex.EncodeToString(sha256Bytes(tx))), txBytes: tx, } } From 4b672f865534796b54f4cffe44580765e1dd0152 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 09:52:54 -0700 Subject: [PATCH 5/9] Fix blockstore/postgres ordering, ctx interruption, and README guidance - Write to blockstore after postgres commit to keep them in sync on failure - Return ctx.Err() on interruption instead of breaking to success path - Update README: indexers must recover signer from signature, not trust the signer field (which carries the entity wallet address) - Document step-based resume granularity limitation Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/README.md | 15 ++++++++++++--- cmd/genesis-writer/writer.go | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cmd/genesis-writer/README.md b/cmd/genesis-writer/README.md index ef3e194e..b2237b29 100644 --- a/cmd/genesis-writer/README.md +++ b/cmd/genesis-writer/README.md @@ -151,16 +151,25 @@ 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. Verify that `signer` matches the genesis migration authority configured in +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. Apply entity data directly from `metadata` — the same JSON structure as +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`. -3. **Skip** all standard checks that do not apply to historical migration data: +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. diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go index b57c4004..a32523e1 100644 --- a/cmd/genesis-writer/writer.go +++ b/cmd/genesis-writer/writer.go @@ -237,13 +237,9 @@ func (w *Writer) stopBlockWriter() error { } // writeBlockToDB writes a single block to postgres (using COPY for transactions) -// and blockstore.db. +// 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 { - // Write to blockstore so peers can serve this block for blocksync. - if w.blockStore != nil { - w.blockStore.SaveBlock(pb.block, pb.blockParts, pb.seenCommit) - } - pgTx, err := w.dstDB.Begin(ctx) if err != nil { return fmt.Errorf("begin tx height=%d: %w", pb.height, err) @@ -282,7 +278,16 @@ func (w *Writer) writeBlockToDB(ctx context.Context, pb pendingBlock) error { return fmt.Errorf("insert core_app_state height=%d: %w", pb.height, err) } - return pgTx.Commit(ctx) + 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. @@ -456,8 +461,8 @@ func (w *Writer) Run(ctx context.Context) error { 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") - break + w.logger.Info("write interrupted", zap.String("step", step.name)) + return ctx.Err() } return fmt.Errorf("write %s: %w", step.name, err) } From 8cd41b488c9d42a2e524ee562908394252d60ac1 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:03:26 -0700 Subject: [PATCH 6/9] Fix import ordering, TrackCID fallback, deterministic preload, rows.Err, user lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sort imports in integration_test.go per gofmt - Fall back to metadata_multihash for TrackCID when track_segments empty - Add ORDER BY to wallet→user preload for deterministic tip attribution - Check rows.Err() after iterating resume progress query - Use os/user.Current() as fallback when USER env var is unset Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/entities_tip.go | 2 +- cmd/genesis-writer/entities_track.go | 3 +++ cmd/genesis-writer/integration_test.go | 5 ++--- cmd/genesis-writer/postgres.go | 11 ++++++++++- cmd/genesis-writer/writer.go | 6 ++++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cmd/genesis-writer/entities_tip.go b/cmd/genesis-writer/entities_tip.go index 967c6ab7..5c9d06d5 100644 --- a/cmd/genesis-writer/entities_tip.go +++ b/cmd/genesis-writer/entities_tip.go @@ -28,7 +28,7 @@ type sourceTipReaction struct { 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`) + `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) } diff --git a/cmd/genesis-writer/entities_track.go b/cmd/genesis-writer/entities_track.go index 7e9ffea8..dca273f0 100644 --- a/cmd/genesis-writer/entities_track.go +++ b/cmd/genesis-writer/entities_track.go @@ -152,6 +152,9 @@ func (w *Writer) writeTracks(ctx context.Context) error { inner.TrackCID = segs[0].MultiHash } } + if inner.TrackCID == "" && t.MetadataMultihash != nil && *t.MetadataMultihash != "" { + inner.TrackCID = *t.MetadataMultihash + } metaJSON, err := json.Marshal(trackMetadataWrapper{ CID: cid, diff --git a/cmd/genesis-writer/integration_test.go b/cmd/genesis-writer/integration_test.go index 8ed8cbcd..7736aa0a 100644 --- a/cmd/genesis-writer/integration_test.go +++ b/cmd/genesis-writer/integration_test.go @@ -8,6 +8,7 @@ import ( "crypto/tls" "encoding/hex" "encoding/json" + "fmt" "net/http" "os" "os/exec" @@ -18,16 +19,14 @@ import ( "testing" "time" - "fmt" - "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/ethereum/go-ethereum/crypto" "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" diff --git a/cmd/genesis-writer/postgres.go b/cmd/genesis-writer/postgres.go index 5c60cc6b..790a6a7d 100644 --- a/cmd/genesis-writer/postgres.go +++ b/cmd/genesis-writer/postgres.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "os/user" "path/filepath" "strings" "time" @@ -97,7 +98,15 @@ func startManagedPostgres(dataDir string, logger *zap.Logger) (*managedPostgres, return nil, "", fmt.Errorf("ensure database: %w", err) } - dsn := fmt.Sprintf("postgres://%s@localhost:%s/%s?sslmode=disable", os.Getenv("USER"), pg.port, pgDatabase) + 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), diff --git a/cmd/genesis-writer/writer.go b/cmd/genesis-writer/writer.go index a32523e1..e77c935b 100644 --- a/cmd/genesis-writer/writer.go +++ b/cmd/genesis-writer/writer.go @@ -434,15 +434,17 @@ func (w *Writer) Run(ctx context.Context) error { 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 { - rows.Close() return fmt.Errorf("scan progress: %w", err) } completedSteps[name] = true } - rows.Close() + if err := rows.Err(); err != nil { + return fmt.Errorf("iterate progress: %w", err) + } } for _, step := range steps { From bd3845948af065ba4c29157f78310dec0f5dfd60 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:16:13 -0700 Subject: [PATCH 7/9] Read track_cid directly from source DB instead of reconstructing it The Python DP indexer sets track_cid from the metadata JSON field "track_cid", not from track_segments or metadata_multihash. metadata_multihash is the CID of the metadata blob itself (unrelated to the audio CID), so using it as a fallback was incorrect. Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/entities_track.go | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/cmd/genesis-writer/entities_track.go b/cmd/genesis-writer/entities_track.go index dca273f0..3143c24e 100644 --- a/cmd/genesis-writer/entities_track.go +++ b/cmd/genesis-writer/entities_track.go @@ -54,8 +54,7 @@ type sourceTrack struct { Genre *string Mood *string Tags *string - MetadataMultihash *string - TrackSegments []byte // JSONB + TrackCID *string CoverArt *string CoverArtSizes *string PreviewCID *string @@ -82,7 +81,7 @@ func (w *Writer) writeTracks(ctx context.Context) error { `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, - metadata_multihash, track_segments, + 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, @@ -97,7 +96,7 @@ func (w *Writer) writeTracks(ctx context.Context) error { var t sourceTrack err := rows.Scan( &t.TrackID, &t.OwnerID, &t.Title, &t.Description, &t.Duration, &t.Genre, &t.Mood, &t.Tags, - &t.MetadataMultihash, &t.TrackSegments, + &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, @@ -139,25 +138,10 @@ func (w *Writer) writeTracks(ctx context.Context) error { inner.StreamConditions = unmarshalJSONB(t.StreamConditions) inner.DownloadConditions = unmarshalJSONB(t.DownloadConditions) - // Extract track CID from track_segments or metadata_multihash. - cid := "genesis-import" - if t.MetadataMultihash != nil && *t.MetadataMultihash != "" { - cid = *t.MetadataMultihash - } - if len(t.TrackSegments) > 0 { - var segs []struct { - MultiHash string `json:"multihash"` - } - if err := json.Unmarshal(t.TrackSegments, &segs); err == nil && len(segs) > 0 { - inner.TrackCID = segs[0].MultiHash - } - } - if inner.TrackCID == "" && t.MetadataMultihash != nil && *t.MetadataMultihash != "" { - inner.TrackCID = *t.MetadataMultihash - } + inner.TrackCID = deref(t.TrackCID) metaJSON, err := json.Marshal(trackMetadataWrapper{ - CID: cid, + CID: deref(t.TrackCID), Data: inner, }) if err != nil { From 6a34e1550fa08d8243a425b48ce4f624afa01d7e Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:17:59 -0700 Subject: [PATCH 8/9] Pin listen_addresses to localhost when using trust auth Managed postgres uses trust auth for bulk-load performance. Ensure it only listens on localhost to prevent exposing an unauthenticated instance on the network. Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/postgres.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmd/genesis-writer/postgres.go b/cmd/genesis-writer/postgres.go index 790a6a7d..400ca11e 100644 --- a/cmd/genesis-writer/postgres.go +++ b/cmd/genesis-writer/postgres.go @@ -179,6 +179,20 @@ max_wal_size = '4GB' 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 } From 611602524ccae3987d622085e4cf7d6e2180f9f9 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:49:18 -0700 Subject: [PATCH 9/9] Use explicit "dev" genesis network name in integration test Co-Authored-By: Claude Opus 4.6 --- cmd/genesis-writer/integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/genesis-writer/integration_test.go b/cmd/genesis-writer/integration_test.go index 7736aa0a..d7658148 100644 --- a/cmd/genesis-writer/integration_test.go +++ b/cmd/genesis-writer/integration_test.go @@ -133,8 +133,8 @@ func TestGenesisWriter(t *testing.T) { t.Logf("validator address: %s", pv.GetAddress()) // Write the genesis.json that genesis-writer needs for state.db construction. - genDoc, err := genesisPkg.Read("dev-v2") - require.NoError(t, err, "read dev-v2 genesis") + 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")