Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,097 changes: 388 additions & 709 deletions pkg/api/core/v1/types.pb.go

Large diffs are not rendered by default.

69 changes: 0 additions & 69 deletions pkg/common/legacy_reward_signing.go

This file was deleted.

6 changes: 0 additions & 6 deletions pkg/core/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 0 additions & 24 deletions pkg/core/db/reads.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions pkg/core/db/sql/migrations/00034_drop_launchpad_authority_rm.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- +migrate Up

-- launchpad_authority_rm was introduced in 00033 to map per-mint
-- claim authorities (lowercased eth hex) to their Solana reward
-- manager pubkeys. It served two purposes: (1) the 00033 backfill
-- block resolved each existing core_rewards row's RM via this
-- mapping; (2) the wire-compat layer (finalizeLegacyCreateReward)
-- queried it at block-sync replay time to converge with the
-- migration's apphash.
--
-- Both consumers are gone in this PR — the wire-compat layer is
-- removed entirely (the network is being restarted with genesis
-- replay, so no historical legacy bytes exist on chain) and the
-- backfill block in 00033 has already run on existing chains.
-- The mapping table is now pure dead weight; drop it.
drop table if exists launchpad_authority_rm;

-- +migrate Down

-- Restoring the table is intentional non-functionality: the Up
-- direction permanently removes data that we don't want to
-- regenerate. If a downgrade is genuinely needed, the operator must
-- re-run 00033's launchpad_authority_rm INSERT VALUES manually
-- against an empty table created here.
create table if not exists launchpad_authority_rm (
authority text primary key,
rewards_manager_pubkey text not null,
created_at timestamp with time zone default now()
);
16 changes: 0 additions & 16 deletions pkg/core/db/sql/reads.sql
Original file line number Diff line number Diff line change
Expand Up @@ -660,22 +660,6 @@ from core_reward_pools
where authorities @> array[$1::text]
order by rewards_manager_pubkey;

-- name: GetLaunchpadRMByAuthority :one
-- Resolves a launchpad-derived per-mint claim authority (lowercased eth
-- hex) to the Solana reward manager state account that mint's rewards
-- live under. Used by PR2's wire-compat layer at block-sync replay time:
-- when finalizeLegacyCreateReward sees an inline claim_authorities array,
-- it looks up the RM from any one of its lowercased entries and routes
-- the reward into a pool keyed by that RM — matching exactly what PR1's
-- backfill produced for pre-migration rows. Returns ErrNoRows if none of
-- the requested authorities is in the launchpad mapping (e.g., AUDIO
-- rewards or test fixtures).
select rewards_manager_pubkey
from launchpad_authority_rm
where authority = any($1::text[])
order by rewards_manager_pubkey, authority
limit 1;

-- name: GetCoreUpload :one
select * from core_uploads where cid = $1 OR transcoded_cid = $1;

Expand Down
16 changes: 0 additions & 16 deletions pkg/core/db/sql/writes.sql
Original file line number Diff line number Diff line change
Expand Up @@ -356,22 +356,6 @@ where address = $1;
delete from core_rewards
where address = $1;

-- name: UpsertSyntheticRewardPool :exec
-- Used by the legacy CreateReward path to ensure a reward-pool row exists
-- for the provided rewards_manager_pubkey with the requested authority
-- set. The pubkey may be a real Solana RM (when the row's
-- claim_authorities included a known launchpad-derived per-mint key) or
-- a synthetic 'mig_<md5>' identifier (otherwise). DO UPDATE refreshes
-- the stored authorities so multiple CreateReward txs targeting the
-- same pool converge instead of leaving stale rows.
insert into core_reward_pools (
rewards_manager_pubkey,
authorities
) values ($1, $2)
on conflict (rewards_manager_pubkey) do update set
authorities = excluded.authorities,
updated_at = now();

-- name: InsertRewardPool :exec
-- Inserts a first-class reward pool created via a CreateRewardPool cometbft
-- transaction. The pool's identity IS the Solana reward manager pubkey;
Expand Down
27 changes: 0 additions & 27 deletions pkg/core/db/writes.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions pkg/core/server/reward_pools.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,9 @@ func validateAuthorityList(addrs []string) error {
}

// validateRewardsManagerPubkey checks the wire shape of a Solana reward
// manager pubkey: non-empty, base58-decodable, exactly 32 bytes.
//
// First-class pools must use a real RM pubkey because PR3's
// sender-attestation gate uses the same value to bind the pool↔RM. PR1's
// backfill resolves each existing reward row to a real RM via the
// launchpad_authority_rm mapping, so there are no synthetic-pool
// identifiers in production state to special-case here.
// manager pubkey: non-empty, base58-decodable, exactly 32 bytes. Pools are
// keyed by RM pubkey, so this also validates the pool's identity at
// CreateRewardPool / SetRewardPoolAuthorities time.
func validateRewardsManagerPubkey(pubkey string) error {
if pubkey == "" {
return fmt.Errorf("%w: rewards_manager_pubkey is required", ErrRewardMessageValidation)
Expand Down
40 changes: 3 additions & 37 deletions pkg/core/server/rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,30 +92,7 @@ var (

func (s *Server) isValidRewardTransaction(ctx context.Context, signedTx *corev1.SignedTransaction, blockHeight int64) error {
envelope := signedTx.GetReward()
if envelope == nil {
return fmt.Errorf("%w: reward message is nil", ErrRewardMessageValidation)
}
if envelope.Body == nil {
// Legacy wire-format detected (pre-pool-rollout shape with deadline +
// signature embedded in CreateReward / DeleteReward at tags 1000/1001).
//
// We REJECT legacy here — at CheckTx and ProcessProposal — because
// legacy CreateReward is inherently permissionless: it carries no
// pool_address and the old signing scheme had no membership check on
// claim_authorities. Allowing live legacy txs through validation would
// reopen the exact exploit class this PR is closing (an attacker
// crafts legacy bytes, picks any reward_id / amount / claim_authorities,
// and bypasses the pool gate).
//
// The corresponding code path in finalizeRewards still APPLIES legacy
// txs — that's intentional, for block-sync-from-genesis replay of
// already-committed historical blocks. Block sync only invokes
// FinalizeBlock; it does not re-run CheckTx or ProcessProposal. So
// "reject at validate, accept at finalize" gives us correct historical
// replay without admitting any new legacy traffic.
if legacy, err := tryParseLegacyReward(envelope); err == nil && legacy != nil {
return fmt.Errorf("%w: legacy reward wire format is not accepted for new transactions; clients must use the body+signature envelope", ErrRewardMessageValidation)
}
if envelope == nil || envelope.Body == nil {
return fmt.Errorf("%w: reward message body is nil", ErrRewardMessageValidation)
}

Expand Down Expand Up @@ -192,19 +169,8 @@ func (s *Server) finalizeRewardTransaction(ctx context.Context, req *abcitypes.F
}

func (s *Server) finalizeRewards(ctx context.Context, req *abcitypes.FinalizeBlockRequest, txhash string, messageIndex int64, envelope *corev1.RewardMessage, sender string) error {
if envelope == nil {
return fmt.Errorf("tx: %s, message index: %d, reward message not found", txhash, messageIndex)
}
if envelope.Body == nil {
// Legacy wire-format path; see rewards_legacy.go.
legacy, err := tryParseLegacyReward(envelope)
if err != nil {
return errors.Join(ErrRewardMessageFinalization, err)
}
if legacy == nil {
return fmt.Errorf("tx: %s, message index: %d, reward message body not found", txhash, messageIndex)
}
return s.finalizeLegacyRewardTransaction(ctx, req, legacy, txhash, messageIndex)
if envelope == nil || envelope.Body == nil {
return fmt.Errorf("tx: %s, message index: %d, reward message body not found", txhash, messageIndex)
}

signer, err := s.recoverDeadlinedSigner(req.Height, envelope.Body.DeadlineBlockHeight, envelope.Body, envelope.Signature)
Expand Down
Loading